mount-observer 0.1.12 → 0.1.14

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.
@@ -1,99 +1,99 @@
1
- import { EvtRt } from './EvtRt.js';
2
- export class DefineCustomElementHandler extends EvtRt {
3
- mount(mountedElement, MountConfig, context) {
4
- this.abort();
5
- // Check if modules are specified
6
- if (!context.modules || context.modules.length === 0) {
7
- throw new Error('Must specify an ES Module');
8
- }
9
- const module = context.modules[0];
10
- const tagName = mountedElement.localName;
11
- // Check if already defined
12
- if (customElements.get(tagName)) {
13
- return;
14
- }
15
- // Find suitable class
16
- const ElementClass = this.findSuitableClass(module);
17
- // Validate that ElementClass is a constructor
18
- if (typeof ElementClass !== 'function') {
19
- throw new Error(`Found class is not a constructor: ${typeof ElementClass}`);
20
- }
21
- // Create wrapper class to allow reuse
22
- // Use anonymous class expression which works across all browsers
23
- const WrapperClass = class extends ElementClass {
24
- };
25
- // Define the custom element using the define method
26
- this.define(tagName, WrapperClass, mountedElement);
27
- }
28
- /**
29
- * Define the custom element in the appropriate registry.
30
- * Override this method in subclasses to use scoped registries.
31
- * @param tagName - The custom element tag name
32
- * @param ElementClass - The element class constructor
33
- * @param mountedElement - The mounted element (used for scoped registry access)
34
- */
35
- define(tagName, ElementClass, mountedElement) {
36
- customElements.define(tagName, ElementClass);
37
- }
38
- findSuitableClass(module) {
39
- // Check default export first
40
- const defaultExport = module.default;
41
- if (defaultExport && this.extendsHTMLElement(defaultExport)) {
42
- return defaultExport;
43
- }
44
- // Find all exports that extend HTMLElement
45
- const htmlElementClasses = Object.values(module)
46
- .filter(exp => typeof exp === 'function' && this.extendsHTMLElement(exp));
47
- if (htmlElementClasses.length === 0) {
48
- throw new Error('No suitable class found in module');
49
- }
50
- if (htmlElementClasses.length > 1) {
51
- throw new Error('More than one class found in module');
52
- }
53
- return htmlElementClasses[0];
54
- }
55
- extendsHTMLElement(cls) {
56
- try {
57
- // Must be a function
58
- if (typeof cls !== 'function') {
59
- return false;
60
- }
61
- // Handle direct HTMLElement export
62
- if (cls === HTMLElement) {
63
- return true;
64
- }
65
- // Check if it has a prototype and extends HTMLElement
66
- if (cls.prototype && cls.prototype instanceof HTMLElement) {
67
- return true;
68
- }
69
- return false;
70
- }
71
- catch {
72
- return false;
73
- }
74
- }
75
- }
76
- /**
77
- * Handler for defining custom elements in scoped registries.
78
- * Uses the element's customElementRegistry property to define elements
79
- * in the appropriate scoped registry instead of the global registry.
80
- */
81
- export class DefineScopedCustomElementHandler extends DefineCustomElementHandler {
82
- /**
83
- * Define the custom element in the element's scoped registry.
84
- * @param tagName - The custom element tag name
85
- * @param ElementClass - The element class constructor
86
- * @param mountedElement - The mounted element with customElementRegistry
87
- */
88
- define(tagName, ElementClass, mountedElement) {
89
- const registry = mountedElement.customElementRegistry;
90
- if (!registry) {
91
- throw new Error('Element does not have a customElementRegistry. Scoped registries require Chrome 146+ or latest WebKit/Safari.');
92
- }
93
- // Check if already defined in this scoped registry
94
- if (registry.get(tagName)) {
95
- return;
96
- }
97
- registry.define(tagName, ElementClass);
98
- }
99
- }
1
+ import { EvtRt } from './EvtRt.js';
2
+ export class DefineCustomElementHandler extends EvtRt {
3
+ mount(mountedElement, MountConfig, context) {
4
+ this.abort();
5
+ // Check if modules are specified
6
+ if (!context.modules || context.modules.length === 0) {
7
+ throw new Error('Must specify an ES Module');
8
+ }
9
+ const module = context.modules[0];
10
+ const tagName = mountedElement.localName;
11
+ // Check if already defined
12
+ if (customElements.get(tagName)) {
13
+ return;
14
+ }
15
+ // Find suitable class
16
+ const ElementClass = this.findSuitableClass(module);
17
+ // Validate that ElementClass is a constructor
18
+ if (typeof ElementClass !== 'function') {
19
+ throw new Error(`Found class is not a constructor: ${typeof ElementClass}`);
20
+ }
21
+ // Create wrapper class to allow reuse
22
+ // Use anonymous class expression which works across all browsers
23
+ const WrapperClass = class extends ElementClass {
24
+ };
25
+ // Define the custom element using the define method
26
+ this.define(tagName, WrapperClass, mountedElement);
27
+ }
28
+ /**
29
+ * Define the custom element in the appropriate registry.
30
+ * Override this method in subclasses to use scoped registries.
31
+ * @param tagName - The custom element tag name
32
+ * @param ElementClass - The element class constructor
33
+ * @param mountedElement - The mounted element (used for scoped registry access)
34
+ */
35
+ define(tagName, ElementClass, mountedElement) {
36
+ customElements.define(tagName, ElementClass);
37
+ }
38
+ findSuitableClass(module) {
39
+ // Check default export first
40
+ const defaultExport = module.default;
41
+ if (defaultExport && this.extendsHTMLElement(defaultExport)) {
42
+ return defaultExport;
43
+ }
44
+ // Find all exports that extend HTMLElement
45
+ const htmlElementClasses = Object.values(module)
46
+ .filter(exp => typeof exp === 'function' && this.extendsHTMLElement(exp));
47
+ if (htmlElementClasses.length === 0) {
48
+ throw new Error('No suitable class found in module');
49
+ }
50
+ if (htmlElementClasses.length > 1) {
51
+ throw new Error('More than one class found in module');
52
+ }
53
+ return htmlElementClasses[0];
54
+ }
55
+ extendsHTMLElement(cls) {
56
+ try {
57
+ // Must be a function
58
+ if (typeof cls !== 'function') {
59
+ return false;
60
+ }
61
+ // Handle direct HTMLElement export
62
+ if (cls === HTMLElement) {
63
+ return true;
64
+ }
65
+ // Check if it has a prototype and extends HTMLElement
66
+ if (cls.prototype && cls.prototype instanceof HTMLElement) {
67
+ return true;
68
+ }
69
+ return false;
70
+ }
71
+ catch {
72
+ return false;
73
+ }
74
+ }
75
+ }
76
+ /**
77
+ * Handler for defining custom elements in scoped registries.
78
+ * Uses the element's customElementRegistry property to define elements
79
+ * in the appropriate scoped registry instead of the global registry.
80
+ */
81
+ export class DefineScopedCustomElementHandler extends DefineCustomElementHandler {
82
+ /**
83
+ * Define the custom element in the element's scoped registry.
84
+ * @param tagName - The custom element tag name
85
+ * @param ElementClass - The element class constructor
86
+ * @param mountedElement - The mounted element with customElementRegistry
87
+ */
88
+ define(tagName, ElementClass, mountedElement) {
89
+ const registry = mountedElement.customElementRegistry;
90
+ if (!registry) {
91
+ throw new Error('Element does not have a customElementRegistry. Scoped registries require Chrome 146+ or latest WebKit/Safari.');
92
+ }
93
+ // Check if already defined in this scoped registry
94
+ if (registry.get(tagName)) {
95
+ return;
96
+ }
97
+ registry.define(tagName, ElementClass);
98
+ }
99
+ }
@@ -3,22 +3,100 @@
3
3
  * This finds the appropriate scoped registry container and observes it.
4
4
  */
5
5
  import { MountObserver } from './MountObserver.js';
6
- import { getRootRegistryContainer } from './getRootRegistryContainer.js';
6
+ import { getRegistryRoot } from './getRegistryRoot.js';
7
+ import { getOrInsertObserverEntry } from './RegistryMountCoordinator.js';
7
8
  /**
8
- * Adds a mount method to Element.prototype that:
9
+ * Registry for tracking MountConfig objects associated with a CustomElementRegistry.
10
+ * This enables coordination of mount observers across multiple DOM scopes that share
11
+ * the same registry.
12
+ */
13
+ export class MountConfigRegistry extends EventTarget {
14
+ #items = new Set();
15
+ get items() {
16
+ return Array.from(this.#items);
17
+ }
18
+ push(items) {
19
+ if (Array.isArray(items)) {
20
+ for (const item of items) {
21
+ this.#items.add(item);
22
+ }
23
+ }
24
+ else {
25
+ this.#items.add(items);
26
+ }
27
+ }
28
+ }
29
+ // Add mountConfigRegistry property to CustomElementRegistry prototype
30
+ if (typeof CustomElementRegistry !== 'undefined') {
31
+ Object.defineProperty(CustomElementRegistry.prototype, 'mountConfigRegistry', {
32
+ get: function () {
33
+ // Create a new MountConfigRegistry instance on first access and cache it
34
+ const registry = new MountConfigRegistry();
35
+ // Replace the getter with the actual value
36
+ Object.defineProperty(this, 'mountConfigRegistry', {
37
+ value: registry,
38
+ writable: true,
39
+ enumerable: false,
40
+ configurable: true,
41
+ });
42
+ return registry;
43
+ },
44
+ enumerable: false,
45
+ configurable: true,
46
+ });
47
+ }
48
+ /**
49
+ * Adds a mount method to Node.prototype that works for Element, ShadowRoot, and Document.
50
+ * This provides a unified API for mounting observers on any node type.
51
+ *
52
+ * For Elements:
9
53
  * 1. Determines the observation scope based on options.scope
10
54
  * 2. Creates a MountObserver with the provided config
11
- * 3. Observes that scope
12
- * 4. Returns the element for chaining
55
+ * 3. Observes that scope with registry coordination
56
+ * 4. Returns the node for chaining
57
+ *
58
+ * For ShadowRoot and Document:
59
+ * 1. Observes the node directly (no registry coordination)
60
+ * 2. Returns the node for chaining
13
61
  */
14
- Object.defineProperty(Element.prototype, 'mount', {
62
+ Object.defineProperty(Node.prototype, 'mount', {
15
63
  value: async function (config, options = {}) {
16
- const scope = options.scope ?? 'registry';
64
+ // For ShadowRoot and Document, observe directly
65
+ if (this instanceof ShadowRoot || this instanceof Document) {
66
+ const mo = new MountObserver(config, options);
67
+ await mo.observe(this);
68
+ return this;
69
+ }
70
+ // For Element, use the robust registry-aware logic
71
+ if (!(this instanceof Element)) {
72
+ throw new Error('mount() can only be called on Element, ShadowRoot, or Document');
73
+ }
74
+ const scope = options.scope ?? 'registry'; // NEW DEFAULT
17
75
  let thingToObserve;
18
76
  if (scope === 'registry') {
19
- const registryContainer = getRootRegistryContainer(this);
77
+ // Find this element's registry root
78
+ const registryContainer = getRegistryRoot(this);
79
+ if (!registryContainer) {
80
+ throw new Error('Could not find registry root');
81
+ }
82
+ thingToObserve = registryContainer;
83
+ // Get the registry for coordination
84
+ const registry = this.customElementRegistry;
85
+ // Register with coordinator if registry exists
86
+ if (registry) {
87
+ await getOrInsertObserverEntry(registry, config, thingToObserve);
88
+ }
89
+ else {
90
+ // No registry, just create a standalone observer
91
+ const mo = new MountObserver(config, options);
92
+ await mo.observe(thingToObserve);
93
+ }
94
+ return this;
95
+ }
96
+ else if (scope === 'registryRoot') {
97
+ const registryContainer = getRegistryRoot(this);
20
98
  if (!registryContainer) {
21
- throw new Error('Could not find root registry container');
99
+ throw new Error('Could not find registry root');
22
100
  }
23
101
  thingToObserve = registryContainer;
24
102
  }
@@ -47,3 +125,100 @@ Object.defineProperty(Element.prototype, 'mount', {
47
125
  enumerable: false,
48
126
  configurable: true,
49
127
  });
128
+ /**
129
+ * Adds a mountScope method to Element.prototype that:
130
+ * 1. Finds the registry root for this element
131
+ * 2. Gets all active configs for this registry
132
+ * 3. Creates new MountObservers for each config to observe this scope
133
+ */
134
+ Object.defineProperty(Element.prototype, 'mountScope', {
135
+ value: async function () {
136
+ const registry = this.customElementRegistry;
137
+ if (!registry) {
138
+ return;
139
+ }
140
+ // Find the root of this scope
141
+ const registryRoot = getRegistryRoot(this);
142
+ if (!registryRoot) {
143
+ return;
144
+ }
145
+ // Get all configs for this registry
146
+ const configs = registry.mountConfigRegistry.items;
147
+ // For each config, ensure an observer exists for this registry root
148
+ for (const config of configs) {
149
+ await getOrInsertObserverEntry(registry, config, registryRoot);
150
+ }
151
+ },
152
+ writable: true,
153
+ enumerable: false,
154
+ configurable: true,
155
+ });
156
+ /**
157
+ * Adds a mountGlobally method to Node.prototype that works for Element, ShadowRoot, and Document.
158
+ *
159
+ * For Elements:
160
+ * 1. Mounts the config in the current registry
161
+ * 2. Creates propagators to automatically mount in:
162
+ * - Elements with different custom element registries
163
+ * - Shadow roots within the same registry
164
+ *
165
+ * For ShadowRoot and Document:
166
+ * 1. Mounts in the current node
167
+ * 2. Creates propagators for child registries and shadow roots
168
+ *
169
+ * This enables "viral" propagation of mount observers across registry boundaries,
170
+ * useful for bootstrapping core handlers like builtIns.mountObserverScript.
171
+ */
172
+ Object.defineProperty(Node.prototype, 'mountGlobally', {
173
+ value: async function (config, options = {}) {
174
+ // Mount in current node first
175
+ await this.mount(config, options);
176
+ // Propagator 1: Watch for elements in different registries
177
+ const crossCustomElementRegistryPropagator = new MountObserver({
178
+ matching: '*',
179
+ whereDifferentCustomElementRegistry: true,
180
+ do: async (el) => {
181
+ // Wait for custom element to be defined so it has the chance to add shadowRoot
182
+ const { localName } = el;
183
+ if (localName.includes('-')) {
184
+ const registry = el.customElementRegistry;
185
+ if (registry && typeof registry.whenDefined === 'function') {
186
+ await registry.whenDefined(localName);
187
+ }
188
+ }
189
+ const shadowRoot = el.shadowRoot;
190
+ if (shadowRoot) {
191
+ // Use mountGlobally to propagate recursively
192
+ await shadowRoot.mountGlobally(config, options);
193
+ }
194
+ else {
195
+ // No shadow root, mount on element
196
+ await el.mount(config, options);
197
+ }
198
+ }
199
+ }, options);
200
+ await crossCustomElementRegistryPropagator.observe(this);
201
+ // Propagator 2: Watch for shadow roots within the same registry
202
+ const crossShadowRootPropagator = new MountObserver({
203
+ matching: '*',
204
+ whereLocalNameMatches: /-/,
205
+ do: async (el) => {
206
+ const { localName } = el;
207
+ const registry = el.customElementRegistry;
208
+ if (registry && typeof registry.whenDefined === 'function') {
209
+ await registry.whenDefined(localName);
210
+ }
211
+ const shadowRoot = el.shadowRoot;
212
+ if (shadowRoot === null)
213
+ return;
214
+ // Use mountGlobally to propagate recursively
215
+ await shadowRoot.mountGlobally(config, options);
216
+ }
217
+ }, options);
218
+ await crossShadowRootPropagator.observe(this);
219
+ return this;
220
+ },
221
+ writable: true,
222
+ enumerable: false,
223
+ configurable: true,
224
+ });
@@ -4,39 +4,137 @@
4
4
  */
5
5
 
6
6
  import { MountObserver } from './MountObserver.js';
7
- import { getRootRegistryContainer } from './getRootRegistryContainer.js';
7
+ import { getRegistryRoot } from './getRegistryRoot.js';
8
+ import { getOrInsertObserverEntry } from './RegistryMountCoordinator.js';
8
9
  import type { MountConfig, MountObserverOptions } from './types/mount-observer/types.js';
9
10
 
10
11
  declare global {
11
- interface Element {
12
- mount<T extends Element>(
12
+ interface Node {
13
+ mount<T extends Node>(
13
14
  this: T,
14
15
  config: MountConfig,
15
16
  options?: MountObserverOptions
16
17
  ): Promise<T>;
18
+
19
+ mountGlobally<T extends Node>(
20
+ this: T,
21
+ config: MountConfig,
22
+ options?: MountObserverOptions
23
+ ): Promise<T>;
24
+ }
25
+
26
+ interface Element {
27
+ mountScope(): Promise<void>;
28
+ }
29
+
30
+ interface CustomElementRegistry {
31
+ mountConfigRegistry: MountConfigRegistry;
17
32
  }
18
33
  }
19
34
 
20
35
  /**
21
- * Adds a mount method to Element.prototype that:
36
+ * Registry for tracking MountConfig objects associated with a CustomElementRegistry.
37
+ * This enables coordination of mount observers across multiple DOM scopes that share
38
+ * the same registry.
39
+ */
40
+ export class MountConfigRegistry extends EventTarget {
41
+ #items: Set<MountConfig> = new Set();
42
+
43
+ get items(): MountConfig[] {
44
+ return Array.from(this.#items);
45
+ }
46
+
47
+ push(items: MountConfig | MountConfig[]): void {
48
+ if (Array.isArray(items)) {
49
+ for (const item of items) {
50
+ this.#items.add(item);
51
+ }
52
+ } else {
53
+ this.#items.add(items);
54
+ }
55
+ }
56
+ }
57
+
58
+ // Add mountConfigRegistry property to CustomElementRegistry prototype
59
+ if (typeof CustomElementRegistry !== 'undefined') {
60
+ Object.defineProperty(CustomElementRegistry.prototype, 'mountConfigRegistry', {
61
+ get: function () {
62
+ // Create a new MountConfigRegistry instance on first access and cache it
63
+ const registry = new MountConfigRegistry();
64
+ // Replace the getter with the actual value
65
+ Object.defineProperty(this, 'mountConfigRegistry', {
66
+ value: registry,
67
+ writable: true,
68
+ enumerable: false,
69
+ configurable: true,
70
+ });
71
+ return registry;
72
+ },
73
+ enumerable: false,
74
+ configurable: true,
75
+ });
76
+ }
77
+
78
+ /**
79
+ * Adds a mount method to Node.prototype that works for Element, ShadowRoot, and Document.
80
+ * This provides a unified API for mounting observers on any node type.
81
+ *
82
+ * For Elements:
22
83
  * 1. Determines the observation scope based on options.scope
23
84
  * 2. Creates a MountObserver with the provided config
24
- * 3. Observes that scope
25
- * 4. Returns the element for chaining
85
+ * 3. Observes that scope with registry coordination
86
+ * 4. Returns the node for chaining
87
+ *
88
+ * For ShadowRoot and Document:
89
+ * 1. Observes the node directly (no registry coordination)
90
+ * 2. Returns the node for chaining
26
91
  */
27
- Object.defineProperty(Element.prototype, 'mount', {
28
- value: async function <T extends Element>(
92
+ Object.defineProperty(Node.prototype, 'mount', {
93
+ value: async function <T extends Node>(
29
94
  this: T,
30
95
  config: MountConfig,
31
96
  options: MountObserverOptions = {}
32
97
  ): Promise<T> {
33
- const scope = options.scope ?? 'registry';
98
+ // For ShadowRoot and Document, observe directly
99
+ if (this instanceof ShadowRoot || this instanceof Document) {
100
+ const mo = new MountObserver(config, options);
101
+ await mo.observe(this);
102
+ return this;
103
+ }
104
+
105
+ // For Element, use the robust registry-aware logic
106
+ if (!(this instanceof Element)) {
107
+ throw new Error('mount() can only be called on Element, ShadowRoot, or Document');
108
+ }
109
+
110
+ const scope = options.scope ?? 'registry'; // NEW DEFAULT
34
111
  let thingToObserve: Node;
35
112
 
36
113
  if (scope === 'registry') {
37
- const registryContainer = getRootRegistryContainer(this);
114
+ // Find this element's registry root
115
+ const registryContainer = getRegistryRoot(this);
38
116
  if (!registryContainer) {
39
- throw new Error('Could not find root registry container');
117
+ throw new Error('Could not find registry root');
118
+ }
119
+ thingToObserve = registryContainer;
120
+
121
+ // Get the registry for coordination
122
+ const registry = (this as any).customElementRegistry;
123
+
124
+ // Register with coordinator if registry exists
125
+ if (registry) {
126
+ await getOrInsertObserverEntry(registry, config, thingToObserve);
127
+ } else {
128
+ // No registry, just create a standalone observer
129
+ const mo = new MountObserver(config, options);
130
+ await mo.observe(thingToObserve);
131
+ }
132
+
133
+ return this;
134
+ } else if (scope === 'registryRoot') {
135
+ const registryContainer = getRegistryRoot(this);
136
+ if (!registryContainer) {
137
+ throw new Error('Could not find registry root');
40
138
  }
41
139
  thingToObserve = registryContainer;
42
140
  } else if (scope === 'self') {
@@ -62,3 +160,112 @@ Object.defineProperty(Element.prototype, 'mount', {
62
160
  enumerable: false,
63
161
  configurable: true,
64
162
  });
163
+
164
+ /**
165
+ * Adds a mountScope method to Element.prototype that:
166
+ * 1. Finds the registry root for this element
167
+ * 2. Gets all active configs for this registry
168
+ * 3. Creates new MountObservers for each config to observe this scope
169
+ */
170
+ Object.defineProperty(Element.prototype, 'mountScope', {
171
+ value: async function(): Promise<void> {
172
+ const registry = (this as any).customElementRegistry;
173
+ if (!registry) {
174
+ return;
175
+ }
176
+
177
+ // Find the root of this scope
178
+ const registryRoot = getRegistryRoot(this);
179
+ if (!registryRoot) {
180
+ return;
181
+ }
182
+
183
+ // Get all configs for this registry
184
+ const configs = registry.mountConfigRegistry.items;
185
+
186
+ // For each config, ensure an observer exists for this registry root
187
+ for (const config of configs) {
188
+ await getOrInsertObserverEntry(registry, config, registryRoot);
189
+ }
190
+ },
191
+ writable: true,
192
+ enumerable: false,
193
+ configurable: true,
194
+ });
195
+
196
+ /**
197
+ * Adds a mountGlobally method to Node.prototype that works for Element, ShadowRoot, and Document.
198
+ *
199
+ * For Elements:
200
+ * 1. Mounts the config in the current registry
201
+ * 2. Creates propagators to automatically mount in:
202
+ * - Elements with different custom element registries
203
+ * - Shadow roots within the same registry
204
+ *
205
+ * For ShadowRoot and Document:
206
+ * 1. Mounts in the current node
207
+ * 2. Creates propagators for child registries and shadow roots
208
+ *
209
+ * This enables "viral" propagation of mount observers across registry boundaries,
210
+ * useful for bootstrapping core handlers like builtIns.mountObserverScript.
211
+ */
212
+ Object.defineProperty(Node.prototype, 'mountGlobally', {
213
+ value: async function <T extends Node>(
214
+ this: T,
215
+ config: MountConfig,
216
+ options: MountObserverOptions = {}
217
+ ): Promise<T> {
218
+ // Mount in current node first
219
+ await this.mount(config, options);
220
+
221
+ // Propagator 1: Watch for elements in different registries
222
+ const crossCustomElementRegistryPropagator = new MountObserver({
223
+ matching: '*',
224
+ whereDifferentCustomElementRegistry: true,
225
+ do: async (el: Element) => {
226
+ // Wait for custom element to be defined so it has the chance to add shadowRoot
227
+ const { localName } = el;
228
+ if (localName.includes('-')) {
229
+ const registry = (el as any).customElementRegistry;
230
+ if (registry && typeof registry.whenDefined === 'function') {
231
+ await registry.whenDefined(localName);
232
+ }
233
+ }
234
+ const shadowRoot = (el as any).shadowRoot;
235
+ if (shadowRoot) {
236
+ // Use mountGlobally to propagate recursively
237
+ await shadowRoot.mountGlobally(config, options);
238
+ } else {
239
+ // No shadow root, mount on element
240
+ await el.mount(config, options);
241
+ }
242
+ }
243
+ }, options);
244
+
245
+ await crossCustomElementRegistryPropagator.observe(this);
246
+
247
+ // Propagator 2: Watch for shadow roots within the same registry
248
+ const crossShadowRootPropagator = new MountObserver({
249
+ matching: '*',
250
+ whereLocalNameMatches: /-/,
251
+ do: async (el: Element) => {
252
+ const { localName } = el;
253
+ const registry = (el as any).customElementRegistry;
254
+ if (registry && typeof registry.whenDefined === 'function') {
255
+ await registry.whenDefined(localName);
256
+ }
257
+ const shadowRoot = (el as any).shadowRoot;
258
+ if (shadowRoot === null) return;
259
+ // Use mountGlobally to propagate recursively
260
+ await shadowRoot.mountGlobally(config, options);
261
+ }
262
+ }, options);
263
+
264
+ await crossShadowRootPropagator.observe(this);
265
+
266
+ return this;
267
+ },
268
+ writable: true,
269
+ enumerable: false,
270
+ configurable: true,
271
+ });