mount-observer 0.1.4 → 0.1.6

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/MountObserver.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  import {
2
- MountInit,
2
+ MountConfig,
3
3
  MountObserverOptions,
4
4
  IMountObserver,
5
5
  MountContext,
6
- AttrChange,
7
6
  WeakDual,
8
7
  EventConfig,
9
8
  EventConstructor,
@@ -15,14 +14,15 @@ import {
15
14
  DismountEvent,
16
15
  DisconnectEvent,
17
16
  LoadEvent,
18
- AttrChangeEvent,
19
17
  } from './Events.js';
20
18
  import {
21
19
  registerSharedObserver,
22
20
  unregisterSharedObserver,
23
21
  type MutationCallback
24
22
  } from './SharedMutationObserver.js';
25
- import { whereOutside } from './whereOutside.js';
23
+ import { withScopePerimeter } from './withScopePerimeter.js';
24
+ import type { assignTentatively as AssignTentativelyType } from 'assign-gingerly/assignTentatively.js';
25
+ import type { BaseRegistry, EnhancementConfig } from 'assign-gingerly/types.js';
26
26
 
27
27
  export class MountObserver extends EventTarget implements IMountObserver {
28
28
  // Static registry for registered handlers
@@ -35,7 +35,7 @@ export class MountObserver extends EventTarget implements IMountObserver {
35
35
  this.#handlerRegistry.set(name, handler);
36
36
  }
37
37
 
38
- #init: MountInit;
38
+ #init: MountConfig;
39
39
  #options: MountObserverOptions;
40
40
  #abortController: AbortController;
41
41
  #modules: any[] = [];
@@ -48,26 +48,36 @@ export class MountObserver extends EventTarget implements IMountObserver {
48
48
  #mutationCallback: MutationCallback | undefined;
49
49
  #rootNode: WeakRef<Node> | undefined;
50
50
  #importsLoaded = false;
51
- #elementAttrStates = new WeakMap<Element, Map<string, string | null>>();
52
- #elementOnceAttrs = new WeakMap<Element, Set<string>>();
53
- #matchesWhereAttrFn: ((element: Element, whereAttr: any) => boolean) | null = null;
54
- #buildAttrCoordinateMapFn: ((whereAttr: any, isCustomElement: boolean) => any) | null = null;
55
- #checkAttrChangesFn: ((element: Element) => AttrChange[]) | null = null;
56
51
  #mediaQueryCleanup?: () => void;
57
52
  #mediaMatches: boolean = true;
58
53
  #asgMtSource: Record<string, any> | undefined;
59
54
  #asgDisMtSource: Record<string, any> | undefined;
55
+ #stageMtSource: Record<string, any> | undefined;
56
+ #stageReversals = new WeakMap<Element, Record<string, any>>();
57
+ #assignTentatively: typeof AssignTentativelyType | undefined;
60
58
  #elementNotifiers = new WeakMap<Element, EventTarget>();
61
59
  #notifierMountedElements = new WeakSet<Element>();
62
60
 
63
- constructor(init: MountInit, options: MountObserverOptions = {}) {
61
+ constructor(config: MountConfig | EnhancementConfig[], options: MountObserverOptions = {}) {
64
62
  super();
63
+
64
+ // Handle array shorthand - convert EnhancementConfig[] to MountConfig
65
+ let init: MountConfig;
66
+ if (Array.isArray(config)) {
67
+ init = {
68
+ matching: '*', // Match all elements, let withAttrs do the filtering
69
+ enhancementConfig: config
70
+ };
71
+ } else {
72
+ init = config;
73
+ }
74
+
65
75
  this.#init = init;
66
76
  this.#options = options;
67
77
  this.#abortController = new AbortController();
68
78
 
69
79
  const {
70
- assignOnMount, assignOnDismount, do: doValue, reference, whereAttr, loadingEagerness,
80
+ assignOnMount, assignOnDismount, stageOnMount, do: doValue, reference, loadingEagerness,
71
81
  import: imp
72
82
  } = init;
73
83
  // Make a copy of assignOnMount config using structuredClone
@@ -77,6 +87,9 @@ export class MountObserver extends EventTarget implements IMountObserver {
77
87
  if (assignOnDismount !== undefined) {
78
88
  this.#asgDisMtSource = structuredClone(assignOnDismount);
79
89
  }
90
+ if (stageOnMount !== undefined) {
91
+ this.#stageMtSource = structuredClone(stageOnMount);
92
+ }
80
93
 
81
94
  if (options.disconnectedSignal) {
82
95
  options.disconnectedSignal.addEventListener('abort', () => {
@@ -94,11 +107,6 @@ export class MountObserver extends EventTarget implements IMountObserver {
94
107
  this.#validateReference();
95
108
  }
96
109
 
97
- // Preload whereAttr utilities if needed
98
- if (whereAttr) {
99
- this.#preloadWhereAttrUtilities();
100
- }
101
-
102
110
  // Start loading imports if eager
103
111
  if (loadingEagerness === 'eager' && imp) {
104
112
  this.#loadImports();
@@ -149,29 +157,6 @@ export class MountObserver extends EventTarget implements IMountObserver {
149
157
  }
150
158
  }
151
159
 
152
- async #preloadWhereAttrUtilities(): Promise<void> {
153
- if (!this.#matchesWhereAttrFn) {
154
- const { matchesWhereAttr } = await import('./whereAttr.js');
155
- this.#matchesWhereAttrFn = matchesWhereAttr;
156
- }
157
- if (!this.#buildAttrCoordinateMapFn) {
158
- const { buildAttrCoordinateMap } = await import('./attrCoordinates.js');
159
- this.#buildAttrCoordinateMapFn = buildAttrCoordinateMap;
160
- }
161
- if (!this.#checkAttrChangesFn) {
162
- const { checkAttrChanges } = await import('./attrChanges.js');
163
- // Create a bound function that passes the required parameters
164
- this.#checkAttrChangesFn = (element: Element) => {
165
- return checkAttrChanges(
166
- element,
167
- this.#init,
168
- this.#buildAttrCoordinateMapFn!,
169
- this.#elementAttrStates,
170
- this.#elementOnceAttrs
171
- );
172
- };
173
- }
174
- }
175
160
 
176
161
  async #setupMediaQuery(): Promise<void> {
177
162
  if (!this.#rootNode) {
@@ -216,24 +201,29 @@ export class MountObserver extends EventTarget implements IMountObserver {
216
201
  if(this.#asgMtSource || this.#asgDisMtSource){
217
202
  await import('assign-gingerly/object-extension.js');
218
203
  }
204
+ if(this.#stageMtSource){
205
+ const { assignTentatively } = await import('assign-gingerly/assignTentatively.js');
206
+ this.#assignTentatively = assignTentatively;
207
+ }
219
208
 
220
209
  this.#rootNode = new WeakRef(rootNode);
221
210
 
222
211
  // Set up media query if specified (needs rootNode to be set first)
223
- if (this.#init.whereMediaMatches) {
212
+ if (this.#init.withMediaMatching) {
224
213
  await this.#setupMediaQuery();
225
214
  }
226
-
227
- // Wait for whereAttr utilities to load if needed
228
- if (this.#init.whereAttr && !this.#matchesWhereAttrFn) {
229
- await this.#preloadWhereAttrUtilities();
230
- }
231
215
 
232
216
  // Wait for eager imports to complete if they were started in constructor
233
217
  if (this.#init.loadingEagerness === 'eager' && this.#init.import && !this.#importsLoaded) {
234
218
  await this.#loadImports();
235
219
  }
236
220
 
221
+ // Register enhancement configs if no imports (inline only)
222
+ // If imports exist, registration happens in #loadImports after modules are loaded
223
+ if (!this.#init.import && this.#init.enhancementConfig) {
224
+ await this.#registerEnhancementConfigs();
225
+ }
226
+
237
227
  // Process existing elements only if media matches
238
228
  if (this.#mediaMatches) {
239
229
  this.#processNode(rootNode);
@@ -246,8 +236,6 @@ export class MountObserver extends EventTarget implements IMountObserver {
246
236
  return;
247
237
  }
248
238
 
249
- const attrChanges: AttrChange[] = [];
250
-
251
239
  for (const mutation of mutations) {
252
240
  if (mutation.type === 'childList') {
253
241
  for (const node of mutation.addedNodes) {
@@ -260,34 +248,6 @@ export class MountObserver extends EventTarget implements IMountObserver {
260
248
  this.#handleRemoval(node as Element);
261
249
  }
262
250
  });
263
- } else if (mutation.type === 'attributes' && mutation.target.nodeType === Node.ELEMENT_NODE) {
264
- // Handle attribute changes for mounted elements
265
- const element = mutation.target as Element;
266
- if (this.#mountedElements.weakSet.has(element) && this.#checkAttrChangesFn) {
267
- const changes = this.#checkAttrChangesFn(element);
268
- attrChanges.push(...changes);
269
- }
270
- }
271
- }
272
-
273
- // Batch and dispatch attribute changes
274
- if (attrChanges.length > 0) {
275
- this.dispatchEvent(new AttrChangeEvent(attrChanges, this.#init));
276
-
277
- // Dispatch filtered attrchange events to element-specific notifiers
278
- const changesByElement = new Map<Element, AttrChange[]>();
279
- for (const change of attrChanges) {
280
- if (!changesByElement.has(change.element)) {
281
- changesByElement.set(change.element, []);
282
- }
283
- changesByElement.get(change.element)!.push(change);
284
- }
285
-
286
- for (const [element, changes] of changesByElement) {
287
- const notifier = this.#elementNotifiers.get(element);
288
- if (notifier) {
289
- notifier.dispatchEvent(new AttrChangeEvent(changes, this.#init));
290
- }
291
251
  }
292
252
  }
293
253
  };
@@ -296,12 +256,6 @@ export class MountObserver extends EventTarget implements IMountObserver {
296
256
  childList: true,
297
257
  subtree: true
298
258
  };
299
-
300
- // Add attribute observation if whereAttr is configured
301
- if (this.#init.whereAttr) {
302
- observerConfig.attributes = true;
303
- observerConfig.attributeOldValue = true;
304
- }
305
259
 
306
260
  // Register with shared mutation observer
307
261
  registerSharedObserver(rootNode, this.#mutationCallback, observerConfig);
@@ -336,29 +290,133 @@ export class MountObserver extends EventTarget implements IMountObserver {
336
290
  this.#modules = await loadImports(this.#init.import);
337
291
  this.#importsLoaded = true;
338
292
 
339
- // Validate referenced whereInstanceOf if reference is specified
293
+ // Validate referenced withInstance if reference is specified
340
294
  if (this.#init.reference !== undefined) {
341
295
  const references = arr(this.#init.reference);
342
296
 
343
297
  for (const index of references) {
344
298
  const module = this.#modules[index];
345
- if (module && module.whereInstanceOf !== undefined) {
299
+ if (module && module.withInstance !== undefined) {
346
300
  // Validate that it's a Constructor or array of Constructors
347
- const whereInstanceOf = module.whereInstanceOf;
348
- const constructors = arr(whereInstanceOf);
301
+ const withInstance = module.withInstance;
302
+ const constructors = arr(withInstance);
349
303
 
350
304
  for (const constructor of constructors) {
351
305
  if (typeof constructor !== 'function') {
352
- throw new Error(`Referenced module at index ${index} exports invalid whereInstanceOf: must be a Constructor or array of Constructors`);
306
+ throw new Error(`Referenced module at index ${index} exports invalid withInstance: must be a Constructor or array of Constructors`);
353
307
  }
354
308
  }
355
309
  }
356
310
  }
357
311
  }
358
312
 
313
+ // Register enhancement configs after imports are loaded
314
+ await this.#registerEnhancementConfigs();
315
+
359
316
  this.dispatchEvent(new LoadEvent(this.#modules, this.#init));
360
317
  }
361
318
 
319
+ async #registerEnhancementConfigs(): Promise<void> {
320
+ const rootNode = this.#rootNode?.deref();
321
+ if (!rootNode || !(rootNode instanceof Element)) {
322
+ return;
323
+ }
324
+
325
+ const registry = (rootNode as any).customElementRegistry?.enhancementRegistry as BaseRegistry | undefined;
326
+ if (!registry) {
327
+ return;
328
+ }
329
+
330
+ const items = registry.getItems();
331
+
332
+ // Collect all enhancement configs to register
333
+ const configsToRegister: EnhancementConfig[] = [];
334
+
335
+ // First, add inline enhancementConfig(s)
336
+ if (this.#init.enhancementConfig) {
337
+ const inlineConfigs = arr(this.#init.enhancementConfig);
338
+ configsToRegister.push(...inlineConfigs);
339
+ }
340
+
341
+ // Then, add referenced enhancementConfig(s) from imported modules
342
+ if (this.#importsLoaded && this.#init.reference !== undefined) {
343
+ const references = arr(this.#init.reference);
344
+
345
+ for (const index of references) {
346
+ const module = this.#modules[index];
347
+ if (module && module.enhancementConfig !== undefined) {
348
+ const referencedConfigs = arr(module.enhancementConfig);
349
+ configsToRegister.push(...referencedConfigs);
350
+ }
351
+ }
352
+ }
353
+
354
+ // Register each config if not already registered (using reference equality)
355
+ for (const config of configsToRegister) {
356
+ if (!items.includes(config)) {
357
+ registry.push(config);
358
+ }
359
+ }
360
+ }
361
+
362
+ /**
363
+ * Resolves template variables in a string recursively
364
+ * @param template - Template string with ${var} placeholders
365
+ * @param patterns - The patterns object containing variable values
366
+ * @returns Resolved string
367
+ */
368
+ #resolveAttrTemplate(template: string, patterns: Record<string, any>): string {
369
+ return template.replace(/\$\{(\w+)\}/g, (match, varName) => {
370
+ const value = patterns[varName];
371
+ if (value === undefined) {
372
+ throw new Error(`Undefined template variable: ${varName}`);
373
+ }
374
+ if (typeof value === 'string') {
375
+ // Recursively resolve
376
+ return this.#resolveAttrTemplate(value, patterns);
377
+ }
378
+ return String(value);
379
+ });
380
+ }
381
+
382
+ /**
383
+ * Checks if element has attribute with enh- prefix handling
384
+ * @param element - The element to check
385
+ * @param attrName - The attribute name (without enh- prefix)
386
+ * @param allowUnprefixed - Pattern that element tag name must match to allow unprefixed attributes
387
+ * @returns true if element has the attribute
388
+ */
389
+ #hasAttributeWithEnhPrefix(
390
+ element: Element,
391
+ attrName: string,
392
+ allowUnprefixed?: string | RegExp
393
+ ): boolean {
394
+ const isCustomElement = element.tagName.includes('-');
395
+ const isSVGElement = element instanceof SVGElement;
396
+
397
+ // For custom elements and SVG - strict enh- requirement
398
+ if (isCustomElement || isSVGElement) {
399
+ if (element.hasAttribute(`enh-${attrName}`)) {
400
+ return true;
401
+ }
402
+
403
+ // Only check unprefixed if tag name matches allowUnprefixed pattern
404
+ if (allowUnprefixed) {
405
+ const pattern = typeof allowUnprefixed === 'string'
406
+ ? new RegExp(allowUnprefixed)
407
+ : allowUnprefixed;
408
+ const tagName = element.tagName.toLowerCase();
409
+ if (pattern.test(tagName)) {
410
+ return element.hasAttribute(attrName);
411
+ }
412
+ }
413
+ return false;
414
+ }
415
+
416
+ // For built-in elements - enh- is alias (check both)
417
+ return element.hasAttribute(`enh-${attrName}`) || element.hasAttribute(attrName);
418
+ }
419
+
362
420
  #processNode(node: Node): void {
363
421
  // If it's an element node, check if it matches
364
422
  if (node.nodeType === Node.ELEMENT_NODE) {
@@ -370,11 +428,11 @@ export class MountObserver extends EventTarget implements IMountObserver {
370
428
  }
371
429
 
372
430
  // Process children
373
- if ('querySelectorAll' in node) {
431
+ if ('querySelectorAll' in node && this.#init.matching) {
374
432
  const root = node as DocumentFragment;
375
433
 
376
434
  // Get all elements matching the CSS selector first
377
- root.querySelectorAll(this.#init.whereElementMatches).forEach(child => {
435
+ root.querySelectorAll(this.#init.matching).forEach(child => {
378
436
  if (this.#matchesSelector(child)) {
379
437
  this.#handleMatch(child);
380
438
  }
@@ -384,36 +442,27 @@ export class MountObserver extends EventTarget implements IMountObserver {
384
442
 
385
443
  #matchesSelector(element: Element): boolean {
386
444
  //TODO: reduce redundncy with this.#init?
387
- // Check whereElementMatches condition
388
- const matchesElement = element.matches(this.#init.whereElementMatches);
389
- if (!matchesElement) {
445
+ // Check matching condition
446
+ if (!this.#init.matching) {
390
447
  return false;
391
448
  }
392
449
 
393
- // Check whereOutside condition if specified (donut hole scoping)
394
- if (this.#init.whereOutside) {
395
- const rootNode = this.#rootNode?.deref();
396
- if (!rootNode || !whereOutside(rootNode, element, this.#init.whereOutside)) {
397
- return false;
398
- }
450
+ const matchesElement = element.matches(this.#init.matching);
451
+ if (!matchesElement) {
452
+ return false;
399
453
  }
400
454
 
401
- // Check whereAttr condition if specified
402
- if (this.#init.whereAttr) {
403
- // Use cached function (should be loaded by now from constructor)
404
- if (!this.#matchesWhereAttrFn) {
405
- console.warn('whereAttr utilities not loaded yet');
406
- return false;
407
- }
408
-
409
- if (!this.#matchesWhereAttrFn(element, this.#init.whereAttr)) {
455
+ // Check withScopePerimeter condition if specified (donut hole scoping)
456
+ if (this.#init.withScopePerimeter) {
457
+ const rootNode = this.#rootNode?.deref();
458
+ if (!rootNode || !withScopePerimeter(rootNode, element, this.#init.withScopePerimeter)) {
410
459
  return false;
411
460
  }
412
461
  }
413
462
 
414
- // Check whereInstanceOf condition if specified
415
- if (this.#init.whereInstanceOf) {
416
- const constructors = arr(this.#init.whereInstanceOf);
463
+ // Check withInstance condition if specified
464
+ if (this.#init.withInstance) {
465
+ const constructors = arr(this.#init.withInstance);
417
466
 
418
467
  // Element must be an instance of at least one constructor (OR logic for array)
419
468
  const matchesInstanceOf = constructors.some(constructor => element instanceof constructor);
@@ -423,14 +472,14 @@ export class MountObserver extends EventTarget implements IMountObserver {
423
472
  }
424
473
  }
425
474
 
426
- // Check referenced whereInstanceOf if imports are loaded and reference is specified
475
+ // Check referenced withInstance if imports are loaded and reference is specified
427
476
  if (this.#importsLoaded && this.#init.reference !== undefined) {
428
477
  const references = arr(this.#init.reference);
429
478
 
430
479
  for (const index of references) {
431
480
  const module = this.#modules[index];
432
- if (module && module.whereInstanceOf !== undefined) {
433
- const constructors = arr(module.whereInstanceOf);
481
+ if (module && module.withInstance !== undefined) {
482
+ const constructors = arr(module.withInstance);
434
483
 
435
484
  // Element must be an instance of at least one constructor (OR logic within this module)
436
485
  const matchesInstanceOf = constructors.some((constructor: Constructor) => element instanceof constructor);
@@ -441,6 +490,79 @@ export class MountObserver extends EventTarget implements IMountObserver {
441
490
  }
442
491
  }
443
492
  }
493
+ //TODO: move to a separate file?
494
+ // Check withAttrs condition if specified (attribute-based matching)
495
+ // Check ALL enhancementConfigs (inline + referenced)
496
+ const enhancementConfigs: EnhancementConfig[] = [];
497
+
498
+ // Add inline configs
499
+ if (this.#init.enhancementConfig) {
500
+ enhancementConfigs.push(...arr(this.#init.enhancementConfig));
501
+ }
502
+
503
+ // Add referenced configs if imports are loaded
504
+ if (this.#importsLoaded && this.#init.reference !== undefined) {
505
+ const references = arr(this.#init.reference);
506
+ for (const index of references) {
507
+ const module = this.#modules[index];
508
+ if (module && module.enhancementConfig !== undefined) {
509
+ enhancementConfigs.push(...arr(module.enhancementConfig));
510
+ }
511
+ }
512
+ }
513
+
514
+ // Check if ANY enhancementConfig has withAttrs - if so, element must match at least ONE
515
+ let hasAnyWithAttrs = false;
516
+ let matchesAnyWithAttrs = false;
517
+
518
+ for (const config of enhancementConfigs) {
519
+ if (!config.withAttrs) {
520
+ continue; // Skip configs without withAttrs
521
+ }
522
+
523
+ hasAnyWithAttrs = true;
524
+ const withAttrs = config.withAttrs;
525
+ const allowUnprefixed = config.allowUnprefixed;
526
+
527
+ // Collect all attribute names to check for this config
528
+ const attrNames: string[] = [];
529
+
530
+ for (const key in withAttrs) {
531
+ // Skip base and underscore-prefixed config keys
532
+ if (key === 'base' || key.startsWith('_')) {
533
+ continue;
534
+ }
535
+
536
+ const value = withAttrs[key];
537
+ if (typeof value === 'string') {
538
+ // Resolve template string to get actual attribute name
539
+ const attrName = this.#resolveAttrTemplate(value, withAttrs);
540
+ attrNames.push(attrName);
541
+ }
542
+ }
543
+
544
+ // Handle base attribute specially if present
545
+ if ('base' in withAttrs && typeof withAttrs.base === 'string') {
546
+ attrNames.push(withAttrs.base);
547
+ }
548
+
549
+ // Check if element has at least ONE of the specified attributes (OR logic within config)
550
+ if (attrNames.length > 0) {
551
+ const hasAnyAttribute = attrNames.some(attrName =>
552
+ this.#hasAttributeWithEnhPrefix(element, attrName, allowUnprefixed)
553
+ );
554
+
555
+ if (hasAnyAttribute) {
556
+ matchesAnyWithAttrs = true;
557
+ break; // Found a matching config, no need to check others
558
+ }
559
+ }
560
+ }
561
+
562
+ // If any config has withAttrs but element doesn't match any of them, reject
563
+ if (hasAnyWithAttrs && !matchesAnyWithAttrs) {
564
+ return false;
565
+ }
444
566
 
445
567
  // All conditions passed
446
568
  return true;
@@ -474,7 +596,7 @@ export class MountObserver extends EventTarget implements IMountObserver {
474
596
  modules: this.#modules,
475
597
  observer: this,
476
598
  rootNode,
477
- mountInit: this.#init,
599
+ MountConfig: this.#init,
478
600
  };
479
601
 
480
602
  // Apply assignGingerly if specified
@@ -482,6 +604,43 @@ export class MountObserver extends EventTarget implements IMountObserver {
482
604
  element.assignGingerly(this.#asgMtSource);
483
605
  }
484
606
 
607
+ // Apply assignTentatively if specified (staged assignments)
608
+ if (this.#stageMtSource && this.#assignTentatively) {
609
+ const reversal = {};
610
+ this.#assignTentatively(element, this.#stageMtSource, { reversal });
611
+ this.#stageReversals.set(element, reversal);
612
+ }
613
+
614
+ // Spawn enhancements if configured
615
+ // Process inline configs first, then referenced configs
616
+ const enhancementConfigs: EnhancementConfig[] = [];
617
+
618
+ // Add inline configs
619
+ if (this.#init.enhancementConfig) {
620
+ enhancementConfigs.push(...arr(this.#init.enhancementConfig));
621
+ }
622
+
623
+ // Add referenced configs if imports are loaded
624
+ if (this.#importsLoaded && this.#init.reference !== undefined) {
625
+ const references = arr(this.#init.reference);
626
+ for (const index of references) {
627
+ const module = this.#modules[index];
628
+ if (module && module.enhancementConfig !== undefined) {
629
+ enhancementConfigs.push(...arr(module.enhancementConfig));
630
+ }
631
+ }
632
+ }
633
+
634
+ // Spawn each enhancement that has a spawn property
635
+ if (enhancementConfigs.length > 0) {
636
+ await import('assign-gingerly/object-extension.js');
637
+ for (const config of enhancementConfigs) {
638
+ if (config.spawn) {
639
+ (element as any).enh.get(config, context);
640
+ }
641
+ }
642
+ }
643
+
485
644
  // Check if notifier exists BEFORE calling do callback
486
645
  const notifierExistedBeforeDo = this.#elementNotifiers.has(element);
487
646
 
@@ -535,20 +694,6 @@ export class MountObserver extends EventTarget implements IMountObserver {
535
694
  const { emitMountedElementEvents } = await import('./emitEvents.js');
536
695
  await emitMountedElementEvents(element, this.#init, this.#processedEventsForElement);
537
696
  }
538
-
539
- // Check for initial attribute changes if whereAttr is configured
540
- if (this.#checkAttrChangesFn) {
541
- const changes = this.#checkAttrChangesFn(element);
542
- if (changes.length > 0) {
543
- this.dispatchEvent(new AttrChangeEvent(changes, this.#init));
544
-
545
- // Also dispatch to element-specific notifier
546
- const notifier = this.#elementNotifiers.get(element);
547
- if (notifier) {
548
- notifier.dispatchEvent(new AttrChangeEvent(changes, this.#init));
549
- }
550
- }
551
- }
552
697
  }
553
698
 
554
699
  async assignGingerly(config: Record<string, any> | undefined): Promise<void> {
@@ -585,7 +730,14 @@ export class MountObserver extends EventTarget implements IMountObserver {
585
730
  return;
586
731
  }
587
732
 
588
-
733
+ // Reverse tentative assignments first (restore original values)
734
+ if (this.#stageMtSource && this.#assignTentatively) {
735
+ const reversal = this.#stageReversals.get(element);
736
+ if (reversal) {
737
+ this.#assignTentatively(element, reversal);
738
+ this.#stageReversals.delete(element);
739
+ }
740
+ }
589
741
 
590
742
  // Apply assignGingerly if specified for dismount
591
743
  if (this.#asgDisMtSource) {
@@ -616,12 +768,12 @@ export class MountObserver extends EventTarget implements IMountObserver {
616
768
  modules: this.#modules,
617
769
  observer: this,
618
770
  rootNode,
619
- mountInit: this.#init,
771
+ MountConfig: this.#init,
620
772
  };
621
773
 
622
774
 
623
775
  // Dispatch dismount event
624
- const dismountEvent = new DismountEvent(element, 'where-element-matches-failed', this.#init);
776
+ const dismountEvent = new DismountEvent(element, 'with-matching-failed', this.#init);
625
777
  this.dispatchEvent(dismountEvent);
626
778
 
627
779
  // Dispatch to element-specific notifier