assign-gingerly 0.0.31 → 0.0.33

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/index.js CHANGED
@@ -7,4 +7,6 @@ export { parseWithAttrs } from './parseWithAttrs.js';
7
7
  export { buildCSSQuery } from './buildCSSQuery.js';
8
8
  export { resolveTemplate } from './resolveTemplate.js';
9
9
  export { getHost } from './getHost.js';
10
+ export { resolveValues } from './resolveValues.js';
11
+ export { assignFrom } from './assignFrom.js';
10
12
  import './object-extension.js';
package/index.ts CHANGED
@@ -7,4 +7,6 @@ export {parseWithAttrs} from './parseWithAttrs.js';
7
7
  export {buildCSSQuery} from './buildCSSQuery.js';
8
8
  export {resolveTemplate} from './resolveTemplate.js';
9
9
  export {getHost} from './getHost.js';
10
+ export {resolveValues} from './resolveValues.js';
11
+ export {assignFrom} from './assignFrom.js';
10
12
  import './object-extension.js';
@@ -64,9 +64,27 @@ class ElementEnhancementContainer {
64
64
  constructor(element) {
65
65
  this.element = element;
66
66
  }
67
+ /**
68
+ * Resolve a registryItem parameter to an EnhancementConfig.
69
+ * If a string or symbol is passed, looks it up via findByEnhKey in the registry.
70
+ * @param registryItem - EnhancementConfig object, or string/symbol enhKey
71
+ * @param registry - The enhancement registry to search
72
+ * @returns The resolved EnhancementConfig
73
+ * @throws Error if string/symbol not found in registry
74
+ */
75
+ resolveRegistryItem(registryItem, registry) {
76
+ if (typeof registryItem === 'string' || typeof registryItem === 'symbol') {
77
+ const found = registry.findByEnhKey(registryItem);
78
+ if (!found) {
79
+ throw new Error(`${String(registryItem)} not in registry`);
80
+ }
81
+ return found;
82
+ }
83
+ return registryItem;
84
+ }
67
85
  /**
68
86
  * Get or spawn an instance for a registry item
69
- * @param registryItem - The registry item to get/spawn instance for
87
+ * @param registryItem - The registry item (EnhancementConfig object, or string/symbol enhKey) to get/spawn instance for
70
88
  * @param mountCtx - Optional context to pass to the spawned instance
71
89
  * @returns The spawned instance
72
90
  */
@@ -77,6 +95,8 @@ class ElementEnhancementContainer {
77
95
  if (!registry) {
78
96
  throw new Error('customElementRegistry.enhancementRegistry not available');
79
97
  }
98
+ // Resolve string/symbol to EnhancementConfig via enhKey lookup
99
+ registryItem = this.resolveRegistryItem(registryItem, registry);
80
100
  // Check if registryItem is in the registry
81
101
  const items = registry.getItems();
82
102
  if (!items.includes(registryItem)) {
@@ -109,7 +129,7 @@ class ElementEnhancementContainer {
109
129
  mountCtx,
110
130
  synthesizerElement: mountCtx?.synthesizerElement
111
131
  };
112
- attrInitVals = parseWithAttrs(element, registryItem.withAttrs, registryItem.allowUnprefixed || false, spawnContext);
132
+ attrInitVals = parseWithAttrs(element, registryItem.withAttrs, registryItem.allowUnprefixed, spawnContext);
113
133
  }
114
134
  catch (e) {
115
135
  console.error('Error parsing attributes:', e);
@@ -145,48 +165,81 @@ class ElementEnhancementContainer {
145
165
  }
146
166
  /**
147
167
  * Dispose of an enhancement instance
148
- * @param registryItem - The registry item to dispose
168
+ * @param registryItem - The registry item (EnhancementConfig object, or string/symbol enhKey) to dispose
149
169
  */
150
170
  dispose(registryItem) {
151
171
  const element = this.element;
172
+ // Resolve string/symbol to EnhancementConfig via enhKey lookup
173
+ let resolved;
174
+ if (typeof registryItem === 'string' || typeof registryItem === 'symbol') {
175
+ const registry = element.customElementRegistry?.enhancementRegistry;
176
+ if (!registry) {
177
+ return; // No registry, nothing to dispose
178
+ }
179
+ const found = registry.findByEnhKey(registryItem);
180
+ if (!found) {
181
+ throw new Error(`${String(registryItem)} not in registry`);
182
+ }
183
+ resolved = found;
184
+ }
185
+ else {
186
+ resolved = registryItem;
187
+ }
152
188
  // Get the instance map
153
189
  const instanceMap = getInstanceMap();
154
190
  if (!instanceMap.has(element)) {
155
191
  return; // No instances for this element
156
192
  }
157
193
  const instances = instanceMap.get(element);
158
- const spawnedInstance = instances.get(registryItem);
194
+ const spawnedInstance = instances.get(resolved);
159
195
  if (!spawnedInstance) {
160
196
  return; // No instance for this registry item
161
197
  }
162
198
  // Call dispose lifecycle method if it exists
163
- const lifecycleKeys = normalizeLifecycleKeys(registryItem?.lifecycleKeys);
199
+ const lifecycleKeys = normalizeLifecycleKeys(resolved.lifecycleKeys);
164
200
  const disposeKey = lifecycleKeys?.dispose;
165
201
  if (disposeKey && typeof spawnedInstance[disposeKey] === 'function') {
166
- spawnedInstance[disposeKey](registryItem);
202
+ spawnedInstance[disposeKey](resolved);
167
203
  }
168
204
  // Remove from instance map
169
- instances.delete(registryItem);
205
+ instances.delete(resolved);
170
206
  // Remove from enh container if it has an enhKey
171
- if (registryItem.enhKey) {
207
+ if (resolved.enhKey) {
172
208
  const self = this;
173
- delete self[registryItem.enhKey];
209
+ delete self[resolved.enhKey];
174
210
  }
175
211
  }
176
212
  /**
177
213
  * Wait for an enhancement instance to be resolved
178
- * @param registryItem - The registry item to wait for
214
+ * @param registryItem - The registry item (EnhancementConfig object, or string/symbol enhKey) to wait for
179
215
  * @param mountCtx - Optional context to pass to the spawned instance
180
216
  * @returns Promise that resolves with the spawned instance
181
217
  */
182
218
  async whenResolved(registryItem, mountCtx) {
183
- const lifecycleKeys = normalizeLifecycleKeys(registryItem?.lifecycleKeys);
219
+ // Resolve string/symbol to EnhancementConfig via enhKey lookup
220
+ let resolved;
221
+ if (typeof registryItem === 'string' || typeof registryItem === 'symbol') {
222
+ const element = this.element;
223
+ const registry = element.customElementRegistry?.enhancementRegistry;
224
+ if (!registry) {
225
+ throw new Error('customElementRegistry.enhancementRegistry not available');
226
+ }
227
+ const found = registry.findByEnhKey(registryItem);
228
+ if (!found) {
229
+ throw new Error(`${String(registryItem)} not in registry`);
230
+ }
231
+ resolved = found;
232
+ }
233
+ else {
234
+ resolved = registryItem;
235
+ }
236
+ const lifecycleKeys = normalizeLifecycleKeys(resolved.lifecycleKeys);
184
237
  const resolvedKey = lifecycleKeys?.resolved;
185
238
  if (resolvedKey === undefined) {
186
239
  throw new Error('Must specify resolved key in lifecycleKeys');
187
240
  }
188
241
  // Get or spawn the instance (pass mountCtx through)
189
- const spawnedInstance = this.get(registryItem, mountCtx);
242
+ const spawnedInstance = this.get(resolved, mountCtx);
190
243
  // Check if already resolved
191
244
  if (spawnedInstance[resolvedKey]) {
192
245
  return spawnedInstance;
@@ -1,4 +1,5 @@
1
1
  import assignGingerly, { EnhancementRegistry, ItemscopeRegistry, IAssignGingerlyOptions, getInstanceMap, INSTANCE_MAP_GUID } from './assignGingerly.js';
2
+ import { EnhancementConfig } from './types/assign-gingerly/types.js';
2
3
  import { parseWithAttrs } from './parseWithAttrs.js';
3
4
 
4
5
  /**
@@ -132,13 +133,35 @@ class ElementEnhancementContainer {
132
133
  this.element = element;
133
134
  }
134
135
 
136
+ /**
137
+ * Resolve a registryItem parameter to an EnhancementConfig.
138
+ * If a string or symbol is passed, looks it up via findByEnhKey in the registry.
139
+ * @param registryItem - EnhancementConfig object, or string/symbol enhKey
140
+ * @param registry - The enhancement registry to search
141
+ * @returns The resolved EnhancementConfig
142
+ * @throws Error if string/symbol not found in registry
143
+ */
144
+ private resolveRegistryItem(
145
+ registryItem: EnhancementConfig | string | symbol,
146
+ registry: EnhancementRegistry
147
+ ): EnhancementConfig {
148
+ if (typeof registryItem === 'string' || typeof registryItem === 'symbol') {
149
+ const found = registry.findByEnhKey(registryItem);
150
+ if (!found) {
151
+ throw new Error(`${String(registryItem)} not in registry`);
152
+ }
153
+ return found;
154
+ }
155
+ return registryItem;
156
+ }
157
+
135
158
  /**
136
159
  * Get or spawn an instance for a registry item
137
- * @param registryItem - The registry item to get/spawn instance for
160
+ * @param registryItem - The registry item (EnhancementConfig object, or string/symbol enhKey) to get/spawn instance for
138
161
  * @param mountCtx - Optional context to pass to the spawned instance
139
162
  * @returns The spawned instance
140
163
  */
141
- get(registryItem: any, mountCtx?: any): any {
164
+ get(registryItem: EnhancementConfig | string | symbol, mountCtx?: any): any {
142
165
  const element = this.element;
143
166
 
144
167
  // Get the registry from customElementRegistry
@@ -148,6 +171,9 @@ class ElementEnhancementContainer {
148
171
  throw new Error('customElementRegistry.enhancementRegistry not available');
149
172
  }
150
173
 
174
+ // Resolve string/symbol to EnhancementConfig via enhKey lookup
175
+ registryItem = this.resolveRegistryItem(registryItem, registry);
176
+
151
177
  // Check if registryItem is in the registry
152
178
  const items = registry.getItems();
153
179
  if (!items.includes(registryItem)) {
@@ -188,7 +214,7 @@ class ElementEnhancementContainer {
188
214
  attrInitVals = parseWithAttrs(
189
215
  element,
190
216
  registryItem.withAttrs,
191
- registryItem.allowUnprefixed || false,
217
+ registryItem.allowUnprefixed,
192
218
  spawnContext
193
219
  );
194
220
  } catch (e) {
@@ -232,11 +258,27 @@ class ElementEnhancementContainer {
232
258
 
233
259
  /**
234
260
  * Dispose of an enhancement instance
235
- * @param registryItem - The registry item to dispose
261
+ * @param registryItem - The registry item (EnhancementConfig object, or string/symbol enhKey) to dispose
236
262
  */
237
- dispose(registryItem: any): void {
263
+ dispose(registryItem: EnhancementConfig | string | symbol): void {
238
264
  const element = this.element;
239
265
 
266
+ // Resolve string/symbol to EnhancementConfig via enhKey lookup
267
+ let resolved: EnhancementConfig;
268
+ if (typeof registryItem === 'string' || typeof registryItem === 'symbol') {
269
+ const registry = (element as any).customElementRegistry?.enhancementRegistry;
270
+ if (!registry) {
271
+ return; // No registry, nothing to dispose
272
+ }
273
+ const found = registry.findByEnhKey(registryItem);
274
+ if (!found) {
275
+ throw new Error(`${String(registryItem)} not in registry`);
276
+ }
277
+ resolved = found;
278
+ } else {
279
+ resolved = registryItem;
280
+ }
281
+
240
282
  // Get the instance map
241
283
  const instanceMap = getInstanceMap();
242
284
  if (!instanceMap.has(element)) {
@@ -244,37 +286,54 @@ class ElementEnhancementContainer {
244
286
  }
245
287
 
246
288
  const instances = instanceMap.get(element)!;
247
- const spawnedInstance = instances.get(registryItem);
289
+ const spawnedInstance = instances.get(resolved);
248
290
 
249
291
  if (!spawnedInstance) {
250
292
  return; // No instance for this registry item
251
293
  }
252
294
 
253
295
  // Call dispose lifecycle method if it exists
254
- const lifecycleKeys = normalizeLifecycleKeys(registryItem?.lifecycleKeys);
296
+ const lifecycleKeys = normalizeLifecycleKeys(resolved.lifecycleKeys);
255
297
  const disposeKey = lifecycleKeys?.dispose;
256
298
  if (disposeKey && typeof spawnedInstance[disposeKey] === 'function') {
257
- spawnedInstance[disposeKey](registryItem);
299
+ spawnedInstance[disposeKey](resolved);
258
300
  }
259
301
 
260
302
  // Remove from instance map
261
- instances.delete(registryItem);
303
+ instances.delete(resolved);
262
304
 
263
305
  // Remove from enh container if it has an enhKey
264
- if (registryItem.enhKey) {
306
+ if (resolved.enhKey) {
265
307
  const self = this as any;
266
- delete self[registryItem.enhKey];
308
+ delete self[resolved.enhKey];
267
309
  }
268
310
  }
269
311
 
270
312
  /**
271
313
  * Wait for an enhancement instance to be resolved
272
- * @param registryItem - The registry item to wait for
314
+ * @param registryItem - The registry item (EnhancementConfig object, or string/symbol enhKey) to wait for
273
315
  * @param mountCtx - Optional context to pass to the spawned instance
274
316
  * @returns Promise that resolves with the spawned instance
275
317
  */
276
- async whenResolved(registryItem: any, mountCtx?: any): Promise<any> {
277
- const lifecycleKeys = normalizeLifecycleKeys(registryItem?.lifecycleKeys);
318
+ async whenResolved(registryItem: EnhancementConfig | string | symbol, mountCtx?: any): Promise<any> {
319
+ // Resolve string/symbol to EnhancementConfig via enhKey lookup
320
+ let resolved: EnhancementConfig;
321
+ if (typeof registryItem === 'string' || typeof registryItem === 'symbol') {
322
+ const element = this.element;
323
+ const registry = (element as any).customElementRegistry?.enhancementRegistry;
324
+ if (!registry) {
325
+ throw new Error('customElementRegistry.enhancementRegistry not available');
326
+ }
327
+ const found = registry.findByEnhKey(registryItem);
328
+ if (!found) {
329
+ throw new Error(`${String(registryItem)} not in registry`);
330
+ }
331
+ resolved = found;
332
+ } else {
333
+ resolved = registryItem;
334
+ }
335
+
336
+ const lifecycleKeys = normalizeLifecycleKeys(resolved.lifecycleKeys);
278
337
  const resolvedKey = lifecycleKeys?.resolved;
279
338
 
280
339
  if (resolvedKey === undefined) {
@@ -282,7 +341,7 @@ class ElementEnhancementContainer {
282
341
  }
283
342
 
284
343
  // Get or spawn the instance (pass mountCtx through)
285
- const spawnedInstance = this.get(registryItem, mountCtx);
344
+ const spawnedInstance = this.get(resolved, mountCtx);
286
345
 
287
346
  // Check if already resolved
288
347
  if ((spawnedInstance as any)[resolvedKey]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assign-gingerly",
3
- "version": "0.0.31",
3
+ "version": "0.0.33",
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": {
@@ -55,6 +55,14 @@
55
55
  "./getHost.js": {
56
56
  "default": "./getHost.js",
57
57
  "types": "./getHost.ts"
58
+ },
59
+ "./resolveValues.js": {
60
+ "default": "./resolveValues.js",
61
+ "types": "./resolveValues.ts"
62
+ },
63
+ "./assignFrom.js": {
64
+ "default": "./assignFrom.js",
65
+ "types": "./assignFrom.ts"
58
66
  }
59
67
  },
60
68
  "main": "index.js",
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Resolve RHS path strings in a pattern object against a source object.
3
+ *
4
+ * Any value that is a string starting with `?.` is treated as a path
5
+ * and resolved against the source object using optional chaining semantics.
6
+ * Non-string values and strings not starting with `?.` pass through unchanged.
7
+ *
8
+ * @param pattern - Object whose RHS values may contain `?.` path strings
9
+ * @param source - Object to resolve paths against
10
+ * @returns New object with path strings replaced by resolved values
11
+ *
12
+ * @example
13
+ * const pattern = {
14
+ * hello: '?.myPropContainer?.stringProp',
15
+ * foo: '?.myFooString',
16
+ * literal: 42
17
+ * };
18
+ * const source = {
19
+ * myPropContainer: { stringProp: 'Venus' },
20
+ * myFooString: 'bar'
21
+ * };
22
+ * const result = resolveValues(pattern, source);
23
+ * // { hello: 'Venus', foo: 'bar', literal: 42 }
24
+ */
25
+ export function resolveValues(pattern, source) {
26
+ const result = {};
27
+ for (const [key, value] of Object.entries(pattern)) {
28
+ if (typeof value === 'string' && value.startsWith('?.')) {
29
+ // Parse path: split by '.', strip '?', filter empties
30
+ const parts = value.split('.').map(p => p.replace(/\?/g, '')).filter(p => p.length > 0);
31
+ let current = source;
32
+ for (const part of parts) {
33
+ if (current == null)
34
+ break;
35
+ current = current[part];
36
+ }
37
+ result[key] = current;
38
+ }
39
+ else {
40
+ result[key] = value;
41
+ }
42
+ }
43
+ return result;
44
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Resolve RHS path strings in a pattern object against a source object.
3
+ *
4
+ * Any value that is a string starting with `?.` is treated as a path
5
+ * and resolved against the source object using optional chaining semantics.
6
+ * Non-string values and strings not starting with `?.` pass through unchanged.
7
+ *
8
+ * @param pattern - Object whose RHS values may contain `?.` path strings
9
+ * @param source - Object to resolve paths against
10
+ * @returns New object with path strings replaced by resolved values
11
+ *
12
+ * @example
13
+ * const pattern = {
14
+ * hello: '?.myPropContainer?.stringProp',
15
+ * foo: '?.myFooString',
16
+ * literal: 42
17
+ * };
18
+ * const source = {
19
+ * myPropContainer: { stringProp: 'Venus' },
20
+ * myFooString: 'bar'
21
+ * };
22
+ * const result = resolveValues(pattern, source);
23
+ * // { hello: 'Venus', foo: 'bar', literal: 42 }
24
+ */
25
+ export function resolveValues(
26
+ pattern: Record<string, any>,
27
+ source: any
28
+ ): Record<string, any> {
29
+ const result: Record<string, any> = {};
30
+ for (const [key, value] of Object.entries(pattern)) {
31
+ if (typeof value === 'string' && value.startsWith('?.')) {
32
+ // Parse path: split by '.', strip '?', filter empties
33
+ const parts = value.split('.').map(p => p.replace(/\?/g, '')).filter(p => p.length > 0);
34
+ let current = source;
35
+ for (const part of parts) {
36
+ if (current == null) break;
37
+ current = current[part];
38
+ }
39
+ result[key] = current;
40
+ } else {
41
+ result[key] = value;
42
+ }
43
+ }
44
+ return result;
45
+ }
@@ -226,6 +226,14 @@ export interface IAssignGingerlyOptions {
226
226
  registry?: typeof EnhancementRegistry | EnhancementRegistry;
227
227
  bypassChecks?: boolean;
228
228
  withMethods?: string[] | Set<string>;
229
+ aka?: Record<string, string>;
230
+
231
+ /**
232
+ * AbortSignal for cleaning up reactive subscriptions (@eachTime)
233
+ * Required when using @eachTime symbol for reactive iteration
234
+ * When the signal is aborted, all event listeners are automatically removed
235
+ */
236
+ signal?: AbortSignal;
229
237
  }
230
238
 
231
239
  /**
@@ -242,7 +250,6 @@ export declare class EnhancementRegisteredEvent extends Event {
242
250
  * Extends EventTarget to dispatch events when configs are registered
243
251
  */
244
252
  export declare class EnhancementRegistry extends EventTarget {
245
- private items;
246
253
  push(items: EnhancementConfig | EnhancementConfig[]): void;
247
254
  getItems(): EnhancementConfig[];
248
255
  findBySymbol(symbol: symbol | string): EnhancementConfig | undefined;
@@ -303,5 +310,7 @@ export declare class ElementEnhancementGateway{
303
310
  }
304
311
 
305
312
  export interface ElementEnhancement{
306
- dispose(regItem: EnhancementConfig): void;
313
+ get(registryItem: EnhancementConfig | string | symbol, mountCtx?: any): any;
314
+ dispose(registryItem: EnhancementConfig | string | symbol): void;
315
+ whenResolved(registryItem: EnhancementConfig | string | symbol, mountCtx?: any): Promise<any>;
307
316
  }