mount-observer 0.1.13 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/Events.js +9 -0
  2. package/Events.ts +12 -0
  3. package/MountObserver.js +21 -0
  4. package/MountObserver.ts +27 -0
  5. package/README.md +406 -69
  6. package/{DefineCustomElementHandler.js → handlers/DefineCustomElement.js} +103 -99
  7. package/handlers/DefineCustomElement.ts +123 -0
  8. package/handlers/EnhanceMountedElement.js +99 -0
  9. package/handlers/EnhanceMountedElement.ts +116 -0
  10. package/handlers/Events.js +110 -0
  11. package/handlers/EvtRt.js +59 -0
  12. package/handlers/GenIds.js +37 -0
  13. package/handlers/GenIds.ts +45 -0
  14. package/handlers/HTMLInclude.js +393 -0
  15. package/handlers/HTMLInclude.ts +453 -0
  16. package/handlers/HoistTemplate.js +77 -0
  17. package/handlers/HoistTemplate.ts +89 -0
  18. package/handlers/MountObserver.js +941 -0
  19. package/handlers/MountObserverScript.js +78 -0
  20. package/handlers/MountObserverScript.ts +89 -0
  21. package/handlers/ScriptExport.js +83 -0
  22. package/handlers/ScriptExport.ts +97 -0
  23. package/handlers/SharedMutationObserver.js +78 -0
  24. package/handlers/arr.js +16 -0
  25. package/handlers/connectionMonitor.js +122 -0
  26. package/handlers/elementIntersection.js +73 -0
  27. package/handlers/emitEvents.js +187 -0
  28. package/handlers/getRegistryRoot.js +52 -0
  29. package/handlers/loadImports.js +129 -0
  30. package/handlers/mediaQuery.js +90 -0
  31. package/handlers/rootSizeObserver.js +131 -0
  32. package/handlers/upShadowSearch.js +70 -0
  33. package/handlers/withScopePerimeter.js +22 -0
  34. package/index.js +2 -2
  35. package/index.ts +2 -2
  36. package/package.json +13 -3
  37. package/types/assign-gingerly/types.d.ts +244 -0
  38. package/types/be-a-beacon/types.d.ts +3 -0
  39. package/types/global.d.ts +29 -0
  40. package/types/id-generation/types.d.ts +26 -0
  41. package/types/mount-observer/types.d.ts +330 -0
  42. package/upShadowSearch.js +6 -3
  43. package/upShadowSearch.ts +6 -3
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _EvtRt_ac;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.EvtRt = void 0;
16
+ var Events_js_1 = require("./Events.js");
17
+ var MountObserver_js_1 = require("./MountObserver.js");
18
+ var EvtRt = /** @class */ (function () {
19
+ function EvtRt(mountedElement, ctx) {
20
+ _EvtRt_ac.set(this, void 0);
21
+ var observer = ctx.observer, mountConfig = ctx.mountConfig;
22
+ __classPrivateFieldSet(this, _EvtRt_ac, new AbortController(), "f");
23
+ var et = observer.getNotifier(mountedElement);
24
+ et.addEventListener(Events_js_1.mountEventName, this, { signal: __classPrivateFieldGet(this, _EvtRt_ac, "f").signal });
25
+ et.addEventListener(Events_js_1.disconnectEventName, this, { signal: __classPrivateFieldGet(this, _EvtRt_ac, "f").signal });
26
+ et.addEventListener(Events_js_1.dismountEventName, this, { signal: __classPrivateFieldGet(this, _EvtRt_ac, "f").signal });
27
+ this.mount(mountedElement, mountConfig, ctx);
28
+ }
29
+ EvtRt.prototype.abort = function () {
30
+ __classPrivateFieldGet(this, _EvtRt_ac, "f").abort();
31
+ };
32
+ EvtRt.prototype.mount = function (mountedElement, mountConfig, context) {
33
+ console.log({ mountedElement: mountedElement, mountConfig: mountConfig, context: context });
34
+ };
35
+ EvtRt.prototype.disconnect = function (mountedElement, mountConfig) {
36
+ console.log({ mountedElement: mountedElement, mountConfig: mountConfig });
37
+ };
38
+ EvtRt.prototype.dismount = function (mountedElement, mountConfig) {
39
+ console.log({ mountedElement: mountedElement, mountConfig: mountConfig });
40
+ };
41
+ EvtRt.prototype.handleEvent = function (evt) {
42
+ if (evt instanceof Events_js_1.MountEvent) {
43
+ var mountedElement = evt.mountedElement, mountContext = evt.mountContext, mountConfig = evt.mountConfig;
44
+ this.mount(mountedElement, mountConfig, mountContext);
45
+ }
46
+ else if (evt instanceof Events_js_1.DismountEvent) {
47
+ var mountedElement = evt.mountedElement, mountConfig = evt.mountConfig;
48
+ this.dismount(mountedElement, mountConfig);
49
+ }
50
+ else if (evt instanceof Events_js_1.DisconnectEvent) {
51
+ var mountedElement = evt.mountedElement, mountConfig = evt.mountConfig;
52
+ this.disconnect(mountedElement, mountConfig);
53
+ }
54
+ };
55
+ return EvtRt;
56
+ }());
57
+ exports.EvtRt = EvtRt;
58
+ _EvtRt_ac = new WeakMap();
59
+ MountObserver_js_1.MountObserver.define('builtIns.logToConsole', EvtRt);
@@ -0,0 +1,37 @@
1
+ import { EvtRt } from '../EvtRt.js';
2
+ /**
3
+ * Handler for automatically generating IDs when elements with -id attribute are mounted.
4
+ * This handler integrates with the id-generation package to provide automatic ID generation
5
+ * for elements within scoped containers (fieldset, [itemscope], or root).
6
+ *
7
+ * Usage:
8
+ * ```javascript
9
+ * document.mount({
10
+ * do: 'builtIns.generateIds'
11
+ * });
12
+ * ```
13
+ *
14
+ * The handler will automatically:
15
+ * 1. Watch for elements with the -id attribute
16
+ * 2. Call genIds from id-generation package
17
+ * 3. Generate IDs for elements with data-id, #, @, or | attributes
18
+ * 4. Replace #{{name}} references with generated IDs
19
+ * 5. Remove -id and defer-* attributes after processing
20
+ */
21
+ export class GenerateIdsHandler extends EvtRt {
22
+ // Static properties to define matching criteria
23
+ static matching = '[-id]';
24
+ static whereInstanceOf = Element;
25
+ async mount(mountedElement, mountConfig, context) {
26
+ this.abort();
27
+ // Dynamically import processScope from id-generation
28
+ const { genIds } = await import('id-generation/genIds.js');
29
+ // Get the root node for fallback container
30
+ const rootNode = context.rootNode || document;
31
+ // Process the scope starting from this trigger element
32
+ genIds(mountedElement, rootNode);
33
+ }
34
+ }
35
+ // Register built-in handler
36
+ import { MountObserver } from '../MountObserver.js';
37
+ MountObserver.define('builtIns.generateIds', GenerateIdsHandler);
@@ -0,0 +1,45 @@
1
+ import { EvtRt } from '../EvtRt.js';
2
+ import { MountConfig, MountContext } from '../types/mount-observer/types.js';
3
+
4
+ /**
5
+ * Handler for automatically generating IDs when elements with -id attribute are mounted.
6
+ * This handler integrates with the id-generation package to provide automatic ID generation
7
+ * for elements within scoped containers (fieldset, [itemscope], or root).
8
+ *
9
+ * Usage:
10
+ * ```javascript
11
+ * document.mount({
12
+ * do: 'builtIns.generateIds'
13
+ * });
14
+ * ```
15
+ *
16
+ * The handler will automatically:
17
+ * 1. Watch for elements with the -id attribute
18
+ * 2. Call genIds from id-generation package
19
+ * 3. Generate IDs for elements with data-id, #, @, or | attributes
20
+ * 4. Replace #{{name}} references with generated IDs
21
+ * 5. Remove -id and defer-* attributes after processing
22
+ */
23
+ export class GenerateIdsHandler extends EvtRt {
24
+ // Static properties to define matching criteria
25
+ static matching = '[-id]';
26
+ static whereInstanceOf = Element;
27
+
28
+ async mount(mountedElement: Element, mountConfig: MountConfig, context: MountContext) {
29
+ this.abort();
30
+
31
+ // Dynamically import processScope from id-generation
32
+ const { genIds } = await import('id-generation/genIds.js');
33
+
34
+ // Get the root node for fallback container
35
+ const rootNode = context.rootNode || document;
36
+
37
+ // Process the scope starting from this trigger element
38
+ genIds(mountedElement, rootNode);
39
+ }
40
+ }
41
+
42
+ // Register built-in handler
43
+ import { MountObserver } from '../MountObserver.js';
44
+
45
+ MountObserver.define('builtIns.generateIds', GenerateIdsHandler);
@@ -0,0 +1,393 @@
1
+ import { EvtRt } from '../EvtRt.js';
2
+ import { upShadowSearch } from '../upShadowSearch.js';
3
+ /**
4
+ * Cache for element lookups by ID.
5
+ * Maps root nodes to a map of ID -> WeakRef<Element> for performance.
6
+ */
7
+ const idCache = new WeakMap();
8
+ /**
9
+ * Tracks IDs currently being processed to detect circular references.
10
+ */
11
+ const processingStack = new Set();
12
+ /**
13
+ * Splits a space-separated string of attribute names into an array.
14
+ */
15
+ function splitRefs(refs) {
16
+ return refs
17
+ .split(' ')
18
+ .map(s => s.trim())
19
+ .filter(s => !!s);
20
+ }
21
+ /**
22
+ * Creates a CSS selector from an element's attributes, classes, and tag name.
23
+ * Excludes the -i attribute and any attributes listed in -i from the selector.
24
+ */
25
+ function toQuery(el) {
26
+ // Get the list of attributes to exclude from the selector
27
+ const insertAttrs = el.getAttribute('-i');
28
+ const excludeAttrs = new Set(['-i']); // Always exclude -i itself
29
+ if (insertAttrs !== null) {
30
+ const attrs = splitRefs(insertAttrs);
31
+ attrs.forEach(attr => excludeAttrs.add(attr));
32
+ }
33
+ const classes = Array.from(el.classList).map(c => `.${c}`).join('');
34
+ const parts = Array.from(el.part).map(p => `[part~="${p}"]`).join('');
35
+ const attributes = Array.from(el.attributes)
36
+ .filter(attr => !excludeAttrs.has(attr.name))
37
+ .map(attr => `[${attr.name}="${attr.value}"]`)
38
+ .join('');
39
+ const { localName } = el;
40
+ return `${localName}${classes}${parts}${attributes}`;
41
+ }
42
+ /**
43
+ * Prepares an element for insertion by extracting its children and insertion attributes.
44
+ * Returns a DocumentFragment with the children and a map of attributes to insert.
45
+ */
46
+ function prepareForInsertion(el) {
47
+ const fragment = new DocumentFragment();
48
+ const clone = el.cloneNode(true);
49
+ // Move all children to the fragment
50
+ while (clone.firstChild) {
51
+ fragment.appendChild(clone.firstChild);
52
+ }
53
+ // Check for -i attribute which specifies which attributes to insert
54
+ const insertAttrs = el.getAttribute('-i');
55
+ let attributeMap = null;
56
+ if (insertAttrs !== null) {
57
+ const attrs = splitRefs(insertAttrs);
58
+ attributeMap = {};
59
+ for (const attr of attrs) {
60
+ const value = el.getAttribute(attr);
61
+ if (value !== null) {
62
+ attributeMap[attr] = value;
63
+ }
64
+ }
65
+ }
66
+ return { fragment, attributeMap };
67
+ }
68
+ /**
69
+ * Applies insertion to a matched element by replacing its children and updating attributes.
70
+ */
71
+ function applyInsertion(targetElement, sourceFragment, attributeMap) {
72
+ // Clone the fragment so it can be reused
73
+ const fragmentClone = sourceFragment.cloneNode(true);
74
+ // Replace all children of the target element
75
+ targetElement.replaceChildren(fragmentClone);
76
+ // Update attributes if specified
77
+ if (attributeMap !== null) {
78
+ for (const key in attributeMap) {
79
+ const value = attributeMap[key];
80
+ targetElement.setAttribute(key, value);
81
+ }
82
+ }
83
+ }
84
+ /**
85
+ * Handler that enables HTML fragment reuse via template[src="#id"] syntax.
86
+ *
87
+ * This handler allows declarative reuse of HTML fragments by cloning content from
88
+ * any element with an ID. It's similar to JavaScript constants for HTML.
89
+ *
90
+ * Features:
91
+ * - Clones content from templates (including hoisted templates with remoteContent)
92
+ * - Clones any element with an ID
93
+ * - Supports matching insertions: template children can match and modify cloned content
94
+ * - Caches lookups for performance (useful for repeated references like periodic tables)
95
+ * - Detects circular references
96
+ * - Searches across shadow DOM boundaries
97
+ *
98
+ * Matching Insertions:
99
+ * When a template has children, they are used to match elements in the cloned content
100
+ * and replace their children/attributes. This enables partial updates and "nulling out" content.
101
+ *
102
+ * The -i attribute specifies which attributes to insert/update on matched elements.
103
+ *
104
+ * @example Basic usage
105
+ * ```html
106
+ * <div id="reusable">
107
+ * <p>This content can be reused</p>
108
+ * </div>
109
+ *
110
+ * <template src="#reusable"></template>
111
+ * <!-- Results in: <div><p>This content can be reused</p></div> -->
112
+ * ```
113
+ *
114
+ * @example Matching insertions
115
+ * ```html
116
+ * <div itemscope id="love">
117
+ * <data value="false" itemprop="todayIsFriday">It's Thursday</data>
118
+ * </div>
119
+ *
120
+ * <template src="#love">
121
+ * <data value="true" itemprop="todayIsFriday" -i="value"></data>
122
+ * </template>
123
+ * <!-- Results in:
124
+ * <div itemscope>
125
+ * <data value="true" itemprop="todayIsFriday">It's Thursday</data>
126
+ * </div>
127
+ * The matched element's value attribute is updated, but children are replaced
128
+ * -->
129
+ * ```
130
+ */
131
+ export class HTMLIncludeHandler extends EvtRt {
132
+ static matching = 'template[src^="#"]';
133
+ static whereInstanceOf = HTMLTemplateElement;
134
+ async mount(mountedElement) {
135
+ try {
136
+ const template = mountedElement;
137
+ const src = template.getAttribute('src');
138
+ if (!src || !src.startsWith('#')) {
139
+ console.warn('HTMLInclude: Invalid src attribute, must start with #');
140
+ return;
141
+ }
142
+ const id = src.substring(1);
143
+ // Try cache first
144
+ const rootNode = template.getRootNode();
145
+ let sourceElement = this.getCachedElement(rootNode, id);
146
+ if (!sourceElement) {
147
+ // Search up through shadow roots
148
+ sourceElement = upShadowSearch(template, id);
149
+ if (!sourceElement) {
150
+ const error = `Element with id="${id}" not found`;
151
+ template.setAttribute('data-include-error', error);
152
+ console.warn(`HTMLInclude: ${error}`);
153
+ return;
154
+ }
155
+ // Cache the result
156
+ this.cacheElement(rootNode, id, sourceElement);
157
+ }
158
+ // Check for circular references only if source is also a template with src
159
+ if (sourceElement instanceof HTMLTemplateElement && sourceElement.hasAttribute('src')) {
160
+ const sourceId = sourceElement.getAttribute('id');
161
+ if (sourceId && processingStack.has(sourceId)) {
162
+ const error = `Circular reference detected: #${id}`;
163
+ template.setAttribute('data-include-error', error);
164
+ console.error(`HTMLInclude: ${error}`);
165
+ return;
166
+ }
167
+ }
168
+ // Mark this template as processing (for circular reference detection)
169
+ const templateId = template.getAttribute('id');
170
+ if (templateId) {
171
+ processingStack.add(templateId);
172
+ }
173
+ try {
174
+ // Clone the content
175
+ const { clone, isLiveElement } = this.cloneContent(sourceElement);
176
+ if (!clone) {
177
+ const error = `Unable to clone content from #${id}`;
178
+ template.setAttribute('data-include-error', error);
179
+ console.warn(`HTMLInclude: ${error}`);
180
+ return;
181
+ }
182
+ // Optimization 4: Copy MOSE exports if cloning live element from different root
183
+ if (isLiveElement) {
184
+ await this.copyMoseExports(sourceElement, clone, rootNode);
185
+ }
186
+ // Check if the template has children - if so, process matching insertions
187
+ const templateChildren = Array.from(template.content.children);
188
+ if (templateChildren.length > 0) {
189
+ // Process matching insertions for each child in the template
190
+ this.processMatchingInsertions(clone, templateChildren);
191
+ }
192
+ // Remove ID from cloned element to avoid duplicate IDs in the DOM
193
+ if (clone instanceof Element && clone.hasAttribute('id')) {
194
+ clone.removeAttribute('id');
195
+ }
196
+ // Check for shadowRootModeOnLoad attribute
197
+ const shadowRootMode = template.getAttribute('shadowrootmodeonload');
198
+ if (shadowRootMode) {
199
+ // Shadow DOM mode - attach to parent's shadow root
200
+ const parent = template.parentElement;
201
+ if (!parent) {
202
+ console.warn('HTMLInclude: Cannot attach shadow root - template has no parent element');
203
+ return;
204
+ }
205
+ // Validate shadow root mode
206
+ if (shadowRootMode !== 'open' && shadowRootMode !== 'closed') {
207
+ console.warn(`HTMLInclude: Invalid shadowRootModeOnLoad value "${shadowRootMode}", must be "open" or "closed"`);
208
+ return;
209
+ }
210
+ // Get or create shadow root
211
+ let shadowRoot = parent.shadowRoot;
212
+ if (!shadowRoot) {
213
+ try {
214
+ shadowRoot = parent.attachShadow({ mode: shadowRootMode });
215
+ }
216
+ catch (error) {
217
+ console.error('HTMLInclude: Failed to attach shadow root:', error);
218
+ return;
219
+ }
220
+ }
221
+ // Append clone to shadow root
222
+ shadowRoot.appendChild(clone);
223
+ template.remove();
224
+ }
225
+ else {
226
+ // Normal mode - insert before template
227
+ template.parentNode?.insertBefore(clone, template);
228
+ template.remove();
229
+ }
230
+ }
231
+ finally {
232
+ // Always remove from processing stack
233
+ if (templateId) {
234
+ processingStack.delete(templateId);
235
+ }
236
+ }
237
+ }
238
+ catch (error) {
239
+ console.error('HTMLInclude: Unexpected error:', error);
240
+ }
241
+ }
242
+ /**
243
+ * Gets a cached element reference if available and still valid.
244
+ */
245
+ getCachedElement(rootNode, id) {
246
+ const rootCache = idCache.get(rootNode);
247
+ if (!rootCache)
248
+ return null;
249
+ const weakRef = rootCache.get(id);
250
+ if (!weakRef)
251
+ return null;
252
+ const element = weakRef.deref();
253
+ if (!element) {
254
+ // Element was garbage collected, remove from cache
255
+ rootCache.delete(id);
256
+ return null;
257
+ }
258
+ return element;
259
+ }
260
+ /**
261
+ * Caches an element reference for future lookups.
262
+ */
263
+ cacheElement(rootNode, id, element) {
264
+ let rootCache = idCache.get(rootNode);
265
+ if (!rootCache) {
266
+ rootCache = new Map();
267
+ idCache.set(rootNode, rootCache);
268
+ }
269
+ rootCache.set(id, new WeakRef(element));
270
+ }
271
+ /**
272
+ * Processes matching insertions by finding elements in the cloned content that match
273
+ * the selectors from template children and applying insertions to them.
274
+ */
275
+ processMatchingInsertions(clonedContent, templateChildren) {
276
+ // For each child in the template, find matching elements in the cloned content
277
+ for (const templateChild of templateChildren) {
278
+ // Generate a selector from the template child
279
+ const selector = toQuery(templateChild);
280
+ // Prepare the insertion content and attribute map
281
+ const { fragment, attributeMap } = prepareForInsertion(templateChild);
282
+ // Find all matching elements in the cloned content
283
+ let matchingElements = [];
284
+ if (clonedContent instanceof Element) {
285
+ // Check if the cloned element itself matches
286
+ if (clonedContent.matches(selector)) {
287
+ matchingElements.push(clonedContent);
288
+ }
289
+ // Find matching descendants
290
+ const descendants = Array.from(clonedContent.querySelectorAll(selector));
291
+ matchingElements = [...matchingElements, ...descendants];
292
+ }
293
+ else if (clonedContent instanceof DocumentFragment) {
294
+ // Search within the fragment
295
+ matchingElements = Array.from(clonedContent.querySelectorAll(selector));
296
+ }
297
+ // Apply insertion to each matching element
298
+ for (const matchingElement of matchingElements) {
299
+ applyInsertion(matchingElement, fragment, attributeMap);
300
+ }
301
+ }
302
+ }
303
+ /**
304
+ * Clones content from the source element.
305
+ * Priority: remoteContent (hoisted templates) > content (templates) > element itself
306
+ * Returns an object with the cloned node and whether it was cloned from a live element
307
+ */
308
+ cloneContent(sourceElement) {
309
+ // Check for remoteContent property (hoisted templates)
310
+ if ('remoteContent' in sourceElement) {
311
+ try {
312
+ const remoteContent = sourceElement.remoteContent;
313
+ return { clone: remoteContent.cloneNode(true), isLiveElement: false };
314
+ }
315
+ catch (e) {
316
+ console.warn('HTMLInclude: Failed to access remoteContent', e);
317
+ }
318
+ }
319
+ // Check for content property (regular templates)
320
+ if (sourceElement instanceof HTMLTemplateElement && sourceElement.content) {
321
+ return { clone: sourceElement.content.cloneNode(true), isLiveElement: false };
322
+ }
323
+ // Clone the element itself (live DOM element)
324
+ return { clone: sourceElement.cloneNode(true), isLiveElement: true };
325
+ }
326
+ /**
327
+ * Copies MOSE script exports from source to cloned scripts.
328
+ * This optimization avoids re-parsing JSON when cloning MOSE scripts across shadow boundaries.
329
+ */
330
+ async copyMoseExports(sourceElement, clone, templateRootNode) {
331
+ const sourceRootNode = sourceElement.getRootNode();
332
+ // Only process if source and template are in different root nodes
333
+ if (sourceRootNode === templateRootNode) {
334
+ return;
335
+ }
336
+ // Find all MOSE scripts in the source element
337
+ const sourceScripts = sourceElement.querySelectorAll('script[type="mountobserver"]');
338
+ if (sourceScripts.length === 0) {
339
+ return;
340
+ }
341
+ // Find all MOSE scripts in the clone
342
+ let cloneScripts;
343
+ if (clone instanceof Element) {
344
+ cloneScripts = clone.querySelectorAll('script[type="mountobserver"]');
345
+ }
346
+ else if (clone instanceof DocumentFragment) {
347
+ cloneScripts = clone.querySelectorAll('script[type="mountobserver"]');
348
+ }
349
+ else {
350
+ return;
351
+ }
352
+ // Copy exports from source scripts to cloned scripts (matching by ID)
353
+ for (let i = 0; i < sourceScripts.length; i++) {
354
+ const sourceScript = sourceScripts[i];
355
+ const sourceId = sourceScript.getAttribute('id');
356
+ if (!sourceId)
357
+ continue;
358
+ // Find matching clone script by ID
359
+ const cloneScript = Array.from(cloneScripts).find(s => s.getAttribute('id') === sourceId);
360
+ if (!cloneScript)
361
+ continue;
362
+ // Check if source script has export
363
+ let sourceExport = sourceScript.export;
364
+ if (!sourceExport) {
365
+ // Wait for the source script to resolve
366
+ try {
367
+ // Create a promise that waits for the resolved event
368
+ const event = await new Promise((resolve, reject) => {
369
+ const timeout = setTimeout(() => {
370
+ reject(new Error('Timeout'));
371
+ }, 5000);
372
+ sourceScript.addEventListener('resolved', (e) => {
373
+ clearTimeout(timeout);
374
+ resolve(e);
375
+ }, { once: true });
376
+ });
377
+ sourceExport = event.export;
378
+ }
379
+ catch (error) {
380
+ console.warn(`HTMLInclude: Timeout waiting for MOSE script #${sourceId} to resolve`);
381
+ continue;
382
+ }
383
+ }
384
+ // Copy export to cloned script
385
+ if (sourceExport) {
386
+ cloneScript.export = sourceExport;
387
+ }
388
+ }
389
+ }
390
+ }
391
+ // Register the handler
392
+ import { MountObserver } from '../MountObserver.js';
393
+ MountObserver.define('builtIns.HTMLInclude', HTMLIncludeHandler);