assign-gingerly 0.0.21 → 0.0.23

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/assignGingerly.js CHANGED
@@ -1,22 +1,3 @@
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
- }
20
1
  /**
21
2
  * GUID for global instance map storage to ensure uniqueness across package versions
22
3
  */
@@ -35,34 +16,126 @@ export function getInstanceMap() {
35
16
  /**
36
17
  * Base registry class for managing enhancement configurations
37
18
  */
38
- export class EnhancementRegistry {
39
- #items = [];
19
+ /**
20
+ * Event dispatched when enhancement configs are registered
21
+ */
22
+ export class EnhancementRegisteredEvent extends Event {
23
+ config;
24
+ static eventName = 'register';
25
+ constructor(config) {
26
+ super(EnhancementRegisteredEvent.eventName);
27
+ this.config = config;
28
+ }
29
+ }
30
+ /**
31
+ * Registry for enhancement configurations
32
+ * Extends EventTarget to dispatch events when configs are registered
33
+ */
34
+ export class EnhancementRegistry extends EventTarget {
35
+ #items = new Set();
40
36
  push(items) {
41
37
  if (Array.isArray(items)) {
42
- this.#items.push(...items);
38
+ items.forEach(item => this.#items.add(item));
43
39
  }
44
40
  else {
45
- this.#items.push(items);
41
+ this.#items.add(items);
46
42
  }
43
+ // Dispatch event after adding items
44
+ this.dispatchEvent(new EnhancementRegisteredEvent(items));
47
45
  }
48
46
  getItems() {
49
- return this.#items;
47
+ return Array.from(this.#items);
50
48
  }
51
49
  findBySymbol(symbol) {
52
- return this.#items.find(item => {
50
+ for (const item of this.#items) {
53
51
  const symlinks = item.symlinks;
54
52
  if (!symlinks)
55
- return false;
56
- return Object.keys(symlinks).some(key => {
53
+ continue;
54
+ const hasSymbol = Object.keys(symlinks).some(key => {
57
55
  if (typeof key === 'symbol' || (typeof symlinks[key] === 'symbol')) {
58
56
  return key === symbol || symlinks[key] === symbol;
59
57
  }
60
58
  return false;
61
59
  }) || Object.getOwnPropertySymbols(symlinks).some(sym => sym === symbol);
62
- });
60
+ if (hasSymbol)
61
+ return item;
62
+ }
63
+ return undefined;
63
64
  }
64
65
  findByEnhKey(enhKey) {
65
- return this.#items.find(item => item.enhKey === enhKey);
66
+ for (const item of this.#items) {
67
+ if (item.enhKey === enhKey)
68
+ return item;
69
+ }
70
+ return undefined;
71
+ }
72
+ }
73
+ /**
74
+ * Registry for ItemScope Manager configurations
75
+ * Extends EventTarget to support lazy registration via events
76
+ */
77
+ export class ItemscopeRegistry extends EventTarget {
78
+ #configs = new Map();
79
+ #pendingSetups = new Map();
80
+ /**
81
+ * Define a new manager configuration
82
+ * @param name - Manager name (matches itemscope attribute value)
83
+ * @param config - Manager configuration object
84
+ * @throws Error if name is already registered
85
+ */
86
+ define(name, config) {
87
+ if (this.#configs.has(name)) {
88
+ throw new Error('Already registered');
89
+ }
90
+ this.#configs.set(name, config);
91
+ this.dispatchEvent(new Event(name));
92
+ }
93
+ /**
94
+ * Get a manager configuration by name
95
+ * @param name - Manager name
96
+ * @returns Manager configuration or undefined
97
+ */
98
+ get(name) {
99
+ return this.#configs.get(name);
100
+ }
101
+ /**
102
+ * Wait for a manager to be defined and all pending setups to complete
103
+ * @param name - Manager name to wait for
104
+ * @returns Promise that resolves when manager is defined and all setups are complete
105
+ */
106
+ async whenDefined(name) {
107
+ // If not yet defined, wait for definition
108
+ if (!this.#configs.has(name)) {
109
+ await new Promise((resolve) => {
110
+ this.addEventListener(name, () => resolve(), { once: true });
111
+ });
112
+ }
113
+ // Wait for all pending setups for this manager
114
+ const pending = this.#pendingSetups.get(name);
115
+ if (pending && pending.length > 0) {
116
+ await Promise.all(pending);
117
+ }
118
+ }
119
+ /**
120
+ * Internal method to track a pending setup
121
+ * @param name - Manager name
122
+ * @param promise - Promise representing the setup operation
123
+ */
124
+ _trackSetup(name, promise) {
125
+ if (!this.#pendingSetups.has(name)) {
126
+ this.#pendingSetups.set(name, []);
127
+ }
128
+ this.#pendingSetups.get(name).push(promise);
129
+ // Clean up after completion
130
+ promise.finally(() => {
131
+ const pending = this.#pendingSetups.get(name);
132
+ if (pending) {
133
+ const index = pending.indexOf(promise);
134
+ if (index > -1) {
135
+ pending.splice(index, 1);
136
+ }
137
+ }
138
+ });
66
139
  }
67
140
  }
68
141
  /**
@@ -187,6 +260,38 @@ export function assignGingerly(target, source, options) {
187
260
  for (const sym of Object.getOwnPropertySymbols(source)) {
188
261
  processedSource[sym] = source[sym];
189
262
  }
263
+ // Process 'ish' property for HTMLElements with itemscope (async, non-blocking)
264
+ if ('ish' in processedSource) {
265
+ if (typeof HTMLElement !== 'undefined' && target instanceof HTMLElement) {
266
+ // Capture the value before deleting
267
+ const ishValue = processedSource['ish'];
268
+ // Remove 'ish' from processedSource to prevent normal assignment
269
+ delete processedSource['ish'];
270
+ // Get the itemscope attribute to track the setup
271
+ const itemscopeValue = target.getAttribute('itemscope');
272
+ // Load handler on demand and process asynchronously
273
+ const setupPromise = (async () => {
274
+ try {
275
+ const { handleIshProperty } = await import('./handleIshProperty.js');
276
+ await handleIshProperty(target, ishValue, options, assignGingerly);
277
+ }
278
+ catch (err) {
279
+ console.error('Error in handleIshProperty:', err);
280
+ // Re-throw errors asynchronously so they're visible
281
+ setTimeout(() => { throw err; }, 0);
282
+ }
283
+ })();
284
+ // Track the setup promise with the registry if we have an itemscope value
285
+ if (itemscopeValue && typeof itemscopeValue === 'string' && itemscopeValue.length > 0) {
286
+ const registry = target.customElementRegistry?.itemscopeRegistry
287
+ ?? (typeof customElements !== 'undefined' ? customElements.itemscopeRegistry : undefined);
288
+ if (registry && typeof registry._trackSetup === 'function') {
289
+ registry._trackSetup(itemscopeValue, setupPromise);
290
+ }
291
+ }
292
+ }
293
+ // For non-HTMLElement targets, 'ish' is processed as a normal property
294
+ }
190
295
  // First pass: handle all non-symbol keys and sync operations
191
296
  for (const key of Object.keys(processedSource)) {
192
297
  const value = processedSource[key];
package/assignGingerly.ts CHANGED
@@ -2,34 +2,55 @@
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
- };
5
+ /**
6
+ * Constructor signature for ItemScope Manager classes
7
+ */
8
+ export type ItemscopeManager<T = any> = {
9
+ new (element: HTMLElement, initVals?: Partial<T>): T;
21
10
  }
22
11
 
23
12
  /**
24
- * @deprecated Use EnhancementConfig instead
13
+ * Configuration for ItemScope Manager registration
25
14
  */
26
- export type IBaseRegistryItem<T = any> = EnhancementConfig<T>;
15
+ export interface ItemscopeManagerConfig<T = any> {
16
+ /**
17
+ * Manager class constructor
18
+ */
19
+ manager: ItemscopeManager<T>;
20
+
21
+ /**
22
+ * Optional lifecycle method keys
23
+ * - dispose: Method name to call when manager is disposed
24
+ * - resolved: Property/event name indicating manager is ready
25
+ */
26
+ lifecycleKeys?: {
27
+ dispose?: string | symbol;
28
+ resolved?: string | symbol;
29
+ };
30
+ }
31
+
32
+ // Polyfill for WeakMap.prototype.getOrInsert
33
+
34
+ // if (typeof WeakMap.prototype.getOrInsertComputed !== 'function') {
35
+ // WeakMap.prototype.getOrInsertComputed = function(key, insert) {
36
+ // if (this.has(key)) return this.get(key);
37
+ // const value = insert();
38
+ // this.set(key, value);
39
+ // return value;
40
+ // };
41
+ // }
42
+
43
+ // /**
44
+ // * @deprecated Use EnhancementConfig instead
45
+ // */
46
+ // export type IBaseRegistryItem<T = any> = EnhancementConfig<T>;
27
47
 
28
48
  /**
29
49
  * Interface for the options passed to assignGingerly
30
50
  */
31
51
  export interface IAssignGingerlyOptions {
32
52
  registry?: typeof EnhancementRegistry | EnhancementRegistry;
53
+ bypassChecks?: boolean;
33
54
  }
34
55
 
35
56
  /**
@@ -52,39 +73,142 @@ export function getInstanceMap(): WeakMap<object, Map<EnhancementConfig, any>> {
52
73
  /**
53
74
  * Base registry class for managing enhancement configurations
54
75
  */
55
- export class EnhancementRegistry {
56
- #items: EnhancementConfig[] = [];
76
+ /**
77
+ * Event dispatched when enhancement configs are registered
78
+ */
79
+ export class EnhancementRegisteredEvent extends Event {
80
+ static eventName = 'register';
81
+
82
+ constructor(
83
+ public config: EnhancementConfig | EnhancementConfig[]
84
+ ) {
85
+ super(EnhancementRegisteredEvent.eventName);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Registry for enhancement configurations
91
+ * Extends EventTarget to dispatch events when configs are registered
92
+ */
93
+ export class EnhancementRegistry extends EventTarget {
94
+ #items: Set<EnhancementConfig> = new Set();
57
95
 
58
96
  push(items: EnhancementConfig | EnhancementConfig[]): void {
59
97
  if (Array.isArray(items)) {
60
- this.#items.push(...items);
98
+ items.forEach(item => this.#items.add(item));
61
99
  } else {
62
- this.#items.push(items);
100
+ this.#items.add(items);
63
101
  }
102
+
103
+ // Dispatch event after adding items
104
+ this.dispatchEvent(new EnhancementRegisteredEvent(items));
64
105
  }
65
106
 
66
107
  getItems(): EnhancementConfig[] {
67
- return this.#items;
108
+ return Array.from(this.#items);
68
109
  }
69
110
 
70
111
  findBySymbol(symbol: symbol | string): EnhancementConfig | undefined {
71
- return this.#items.find(item => {
112
+ for (const item of this.#items) {
72
113
  const symlinks = item.symlinks;
73
- if (!symlinks) return false;
74
- return Object.keys(symlinks).some(key => {
114
+ if (!symlinks) continue;
115
+
116
+ const hasSymbol = Object.keys(symlinks).some(key => {
75
117
  if (typeof key === 'symbol' || (typeof symlinks[key as any] === 'symbol')) {
76
118
  return key === symbol || symlinks[key as any] === symbol;
77
119
  }
78
120
  return false;
79
121
  }) || Object.getOwnPropertySymbols(symlinks).some(sym => sym === symbol);
80
- });
122
+
123
+ if (hasSymbol) return item;
124
+ }
125
+ return undefined;
81
126
  }
82
127
 
83
128
  findByEnhKey(enhKey: string | symbol): EnhancementConfig | undefined {
84
- return this.#items.find(item => item.enhKey === enhKey);
129
+ for (const item of this.#items) {
130
+ if (item.enhKey === enhKey) return item;
131
+ }
132
+ return undefined;
85
133
  }
86
134
  }
87
135
 
136
+ /**
137
+ * Registry for ItemScope Manager configurations
138
+ * Extends EventTarget to support lazy registration via events
139
+ */
140
+ export class ItemscopeRegistry extends EventTarget {
141
+ #configs: Map<string, ItemscopeManagerConfig> = new Map();
142
+ #pendingSetups: Map<string, Promise<void>[]> = new Map();
143
+
144
+ /**
145
+ * Define a new manager configuration
146
+ * @param name - Manager name (matches itemscope attribute value)
147
+ * @param config - Manager configuration object
148
+ * @throws Error if name is already registered
149
+ */
150
+ define(name: string, config: ItemscopeManagerConfig): void {
151
+ if (this.#configs.has(name)) {
152
+ throw new Error('Already registered');
153
+ }
154
+ this.#configs.set(name, config);
155
+ this.dispatchEvent(new Event(name));
156
+ }
157
+
158
+ /**
159
+ * Get a manager configuration by name
160
+ * @param name - Manager name
161
+ * @returns Manager configuration or undefined
162
+ */
163
+ get(name: string): ItemscopeManagerConfig | undefined {
164
+ return this.#configs.get(name);
165
+ }
166
+
167
+ /**
168
+ * Wait for a manager to be defined and all pending setups to complete
169
+ * @param name - Manager name to wait for
170
+ * @returns Promise that resolves when manager is defined and all setups are complete
171
+ */
172
+ async whenDefined(name: string): Promise<void> {
173
+ // If not yet defined, wait for definition
174
+ if (!this.#configs.has(name)) {
175
+ await new Promise<void>((resolve) => {
176
+ this.addEventListener(name, () => resolve(), { once: true });
177
+ });
178
+ }
179
+
180
+ // Wait for all pending setups for this manager
181
+ const pending = this.#pendingSetups.get(name);
182
+ if (pending && pending.length > 0) {
183
+ await Promise.all(pending);
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Internal method to track a pending setup
189
+ * @param name - Manager name
190
+ * @param promise - Promise representing the setup operation
191
+ */
192
+ _trackSetup(name: string, promise: Promise<void>): void {
193
+ if (!this.#pendingSetups.has(name)) {
194
+ this.#pendingSetups.set(name, []);
195
+ }
196
+ this.#pendingSetups.get(name)!.push(promise);
197
+
198
+ // Clean up after completion
199
+ promise.finally(() => {
200
+ const pending = this.#pendingSetups.get(name);
201
+ if (pending) {
202
+ const index = pending.indexOf(promise);
203
+ if (index > -1) {
204
+ pending.splice(index, 1);
205
+ }
206
+ }
207
+ });
208
+ }
209
+ }
210
+
211
+
88
212
  /**
89
213
  * Helper function to check if a string key represents a Symbol.for expression
90
214
  */
@@ -223,6 +347,42 @@ export function assignGingerly(
223
347
  processedSource[sym] = source[sym];
224
348
  }
225
349
 
350
+ // Process 'ish' property for HTMLElements with itemscope (async, non-blocking)
351
+ if ('ish' in processedSource) {
352
+ if (typeof HTMLElement !== 'undefined' && target instanceof HTMLElement) {
353
+ // Capture the value before deleting
354
+ const ishValue = processedSource['ish'];
355
+ // Remove 'ish' from processedSource to prevent normal assignment
356
+ delete processedSource['ish'];
357
+
358
+ // Get the itemscope attribute to track the setup
359
+ const itemscopeValue = target.getAttribute('itemscope');
360
+
361
+ // Load handler on demand and process asynchronously
362
+ const setupPromise = (async () => {
363
+ try {
364
+ const { handleIshProperty } = await import('./handleIshProperty.js');
365
+ await handleIshProperty(target, ishValue, options, assignGingerly);
366
+ } catch (err) {
367
+ console.error('Error in handleIshProperty:', err);
368
+ // Re-throw errors asynchronously so they're visible
369
+ setTimeout(() => { throw err; }, 0);
370
+ }
371
+ })();
372
+
373
+ // Track the setup promise with the registry if we have an itemscope value
374
+ if (itemscopeValue && typeof itemscopeValue === 'string' && itemscopeValue.length > 0) {
375
+ const registry = (target as any).customElementRegistry?.itemscopeRegistry
376
+ ?? (typeof customElements !== 'undefined' ? customElements.itemscopeRegistry : undefined);
377
+
378
+ if (registry && typeof registry._trackSetup === 'function') {
379
+ registry._trackSetup(itemscopeValue, setupPromise);
380
+ }
381
+ }
382
+ }
383
+ // For non-HTMLElement targets, 'ish' is processed as a normal property
384
+ }
385
+
226
386
  // First pass: handle all non-symbol keys and sync operations
227
387
  for (const key of Object.keys(processedSource)) {
228
388
  const value = processedSource[key];
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Handle the 'ish' property assignment for HTMLElements with itemscope attributes.
3
+ * This function validates the element and value, then defines or updates the 'ish' property.
4
+ *
5
+ * @param element - The HTMLElement to assign the 'ish' property to
6
+ * @param value - The value to assign (must be an object)
7
+ * @param options - Optional assignGingerly options
8
+ * @param assignGingerlyFn - Reference to the assignGingerly function for recursive calls
9
+ */
10
+ export async function handleIshProperty(element, value, options, assignGingerlyFn) {
11
+ // Validate itemscope attribute
12
+ const itemscopeValue = element.getAttribute('itemscope');
13
+ if (typeof itemscopeValue !== 'string' || itemscopeValue.length === 0) {
14
+ throw new Error('Element must have itemscope attribute set to a non-empty string value');
15
+ }
16
+ // Validate value is an object
17
+ if (typeof value !== 'object' || value === null) {
18
+ throw new Error('ish property value must be an object');
19
+ }
20
+ // Get or create the 'ish' property on the element
21
+ if (!('ish' in element)) {
22
+ await defineIshProperty(element, itemscopeValue, options, assignGingerlyFn);
23
+ }
24
+ // Queue the value for assignment
25
+ const ishDescriptor = Object.getOwnPropertyDescriptor(element, 'ish');
26
+ if (ishDescriptor && ishDescriptor.set) {
27
+ ishDescriptor.set.call(element, value);
28
+ }
29
+ }
30
+ /**
31
+ * Define the 'ish' property on an HTMLElement with itemscope attribute.
32
+ * This function handles both immediate and lazy manager instantiation.
33
+ *
34
+ * @param element - The HTMLElement to define the 'ish' property on
35
+ * @param managerName - The name of the manager (from itemscope attribute)
36
+ * @param options - Optional assignGingerly options
37
+ * @param assignGingerlyFn - Reference to the assignGingerly function for recursive calls
38
+ */
39
+ async function defineIshProperty(element, managerName, options, assignGingerlyFn) {
40
+ // Determine which registry to use
41
+ const registry = element.customElementRegistry?.itemscopeRegistry
42
+ ?? (typeof customElements !== 'undefined' ? customElements.itemscopeRegistry : undefined);
43
+ if (!registry) {
44
+ throw new Error('ItemscopeRegistry not available');
45
+ }
46
+ // Check if manager is registered
47
+ let config = registry.get(managerName);
48
+ // If not registered, wait for registration
49
+ if (!config) {
50
+ const { waitForEvent } = await import('./waitForEvent.js');
51
+ await waitForEvent(registry, managerName);
52
+ config = registry.get(managerName);
53
+ if (!config) {
54
+ throw new Error(`Manager "${managerName}" not found after registration event`);
55
+ }
56
+ }
57
+ // Create manager instance
58
+ let managerInstance = null;
59
+ const valueQueue = [];
60
+ // Define the 'ish' property
61
+ Object.defineProperty(element, 'ish', {
62
+ get() {
63
+ return managerInstance;
64
+ },
65
+ set(newValue) {
66
+ // If setting the same instance, do nothing
67
+ if (newValue === managerInstance) {
68
+ return;
69
+ }
70
+ // Queue the value
71
+ valueQueue.push(newValue);
72
+ // If manager not yet instantiated, create it
73
+ if (!managerInstance) {
74
+ // Merge all queued values for initVals
75
+ const initVals = Object.assign({}, ...valueQueue);
76
+ managerInstance = new config.manager(element, initVals);
77
+ valueQueue.length = 0; // Clear queue
78
+ }
79
+ else {
80
+ // Process queue asynchronously
81
+ (async () => {
82
+ while (valueQueue.length > 0) {
83
+ const queuedValue = valueQueue.shift();
84
+ await assignGingerlyFn(managerInstance, queuedValue, options);
85
+ }
86
+ })();
87
+ }
88
+ },
89
+ enumerable: true,
90
+ configurable: true,
91
+ });
92
+ }
@@ -0,0 +1,115 @@
1
+ import type { IAssignGingerlyOptions, ItemscopeManagerConfig } from './assignGingerly.js';
2
+
3
+ /**
4
+ * Handle the 'ish' property assignment for HTMLElements with itemscope attributes.
5
+ * This function validates the element and value, then defines or updates the 'ish' property.
6
+ *
7
+ * @param element - The HTMLElement to assign the 'ish' property to
8
+ * @param value - The value to assign (must be an object)
9
+ * @param options - Optional assignGingerly options
10
+ * @param assignGingerlyFn - Reference to the assignGingerly function for recursive calls
11
+ */
12
+ export async function handleIshProperty(
13
+ element: HTMLElement,
14
+ value: any,
15
+ options: IAssignGingerlyOptions | undefined,
16
+ assignGingerlyFn: (target: any, source: any, options?: IAssignGingerlyOptions) => any
17
+ ): Promise<void> {
18
+ // Validate itemscope attribute
19
+ const itemscopeValue = element.getAttribute('itemscope');
20
+ if (typeof itemscopeValue !== 'string' || itemscopeValue.length === 0) {
21
+ throw new Error('Element must have itemscope attribute set to a non-empty string value');
22
+ }
23
+
24
+ // Validate value is an object
25
+ if (typeof value !== 'object' || value === null) {
26
+ throw new Error('ish property value must be an object');
27
+ }
28
+
29
+ // Get or create the 'ish' property on the element
30
+ if (!('ish' in element)) {
31
+ await defineIshProperty(element, itemscopeValue, options, assignGingerlyFn);
32
+ }
33
+
34
+ // Queue the value for assignment
35
+ const ishDescriptor = Object.getOwnPropertyDescriptor(element, 'ish');
36
+ if (ishDescriptor && ishDescriptor.set) {
37
+ ishDescriptor.set.call(element, value);
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Define the 'ish' property on an HTMLElement with itemscope attribute.
43
+ * This function handles both immediate and lazy manager instantiation.
44
+ *
45
+ * @param element - The HTMLElement to define the 'ish' property on
46
+ * @param managerName - The name of the manager (from itemscope attribute)
47
+ * @param options - Optional assignGingerly options
48
+ * @param assignGingerlyFn - Reference to the assignGingerly function for recursive calls
49
+ */
50
+ async function defineIshProperty(
51
+ element: HTMLElement,
52
+ managerName: string,
53
+ options: IAssignGingerlyOptions | undefined,
54
+ assignGingerlyFn: (target: any, source: any, options?: IAssignGingerlyOptions) => any
55
+ ): Promise<void> {
56
+ // Determine which registry to use
57
+ const registry = (element as any).customElementRegistry?.itemscopeRegistry
58
+ ?? (typeof customElements !== 'undefined' ? customElements.itemscopeRegistry : undefined);
59
+
60
+ if (!registry) {
61
+ throw new Error('ItemscopeRegistry not available');
62
+ }
63
+
64
+ // Check if manager is registered
65
+ let config = registry.get(managerName);
66
+
67
+ // If not registered, wait for registration
68
+ if (!config) {
69
+ const { waitForEvent } = await import('./waitForEvent.js');
70
+ await waitForEvent(registry, managerName);
71
+ config = registry.get(managerName);
72
+
73
+ if (!config) {
74
+ throw new Error(`Manager "${managerName}" not found after registration event`);
75
+ }
76
+ }
77
+
78
+ // Create manager instance
79
+ let managerInstance: any = null;
80
+ const valueQueue: any[] = [];
81
+
82
+ // Define the 'ish' property
83
+ Object.defineProperty(element, 'ish', {
84
+ get() {
85
+ return managerInstance;
86
+ },
87
+ set(newValue: any) {
88
+ // If setting the same instance, do nothing
89
+ if (newValue === managerInstance) {
90
+ return;
91
+ }
92
+
93
+ // Queue the value
94
+ valueQueue.push(newValue);
95
+
96
+ // If manager not yet instantiated, create it
97
+ if (!managerInstance) {
98
+ // Merge all queued values for initVals
99
+ const initVals = Object.assign({}, ...valueQueue);
100
+ managerInstance = new config!.manager(element, initVals);
101
+ valueQueue.length = 0; // Clear queue
102
+ } else {
103
+ // Process queue asynchronously
104
+ (async () => {
105
+ while (valueQueue.length > 0) {
106
+ const queuedValue = valueQueue.shift();
107
+ await assignGingerlyFn(managerInstance, queuedValue, options);
108
+ }
109
+ })();
110
+ }
111
+ },
112
+ enumerable: true,
113
+ configurable: true,
114
+ });
115
+ }
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export { assignGingerly } from './assignGingerly.js';
2
2
  export { assignTentatively } from './assignTentatively.js';
3
- export { EnhancementRegistry } from './assignGingerly.js';
3
+ export { EnhancementRegistry, ItemscopeRegistry, EnhancementRegisteredEvent } 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';
package/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export {assignGingerly} from './assignGingerly.js';
2
2
  export {assignTentatively} from './assignTentatively.js';
3
- export {EnhancementRegistry} from './assignGingerly.js';
3
+ export {EnhancementRegistry, ItemscopeRegistry, EnhancementRegisteredEvent} 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';