mount-observer 0.1.0 → 0.1.2

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/attrChanges.js ADDED
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Checks for attribute changes on a mounted element.
3
+ * This module is dynamically loaded only when whereAttr is configured.
4
+ */
5
+ export function checkAttrChanges(element, mountInit, buildAttrCoordinateMapFn, elementAttrStates, elementOnceAttrs) {
6
+ if (!mountInit.whereAttr || !buildAttrCoordinateMapFn) {
7
+ return [];
8
+ }
9
+ const isCustomElement = element.tagName.toLowerCase().includes('-');
10
+ const attrCoordMap = buildAttrCoordinateMapFn(mountInit.whereAttr, isCustomElement);
11
+ // Get or create the attribute state for this element
12
+ let attrState = elementAttrStates.get(element);
13
+ if (!attrState) {
14
+ attrState = new Map();
15
+ elementAttrStates.set(element, attrState);
16
+ }
17
+ const changes = [];
18
+ const currentAttrs = new Set();
19
+ // Check all possible attributes from the coordinate map
20
+ for (const attrName of Object.keys(attrCoordMap)) {
21
+ const coordinate = attrCoordMap[attrName];
22
+ const currentValue = element.getAttribute(attrName);
23
+ const previousValue = attrState.get(attrName);
24
+ if (currentValue !== null) {
25
+ currentAttrs.add(attrName);
26
+ }
27
+ // Check if this attribute has "once: true" in its map entry
28
+ const mapEntry = mountInit.map?.[coordinate] || null;
29
+ const isOnce = mapEntry?.once === true;
30
+ // If "once" is true, check if we've already seen this attribute
31
+ if (isOnce) {
32
+ let onceAttrs = elementOnceAttrs.get(element);
33
+ if (!onceAttrs) {
34
+ onceAttrs = new Set();
35
+ elementOnceAttrs.set(element, onceAttrs);
36
+ }
37
+ // If we've already seen this attribute, skip it
38
+ if (onceAttrs.has(attrName)) {
39
+ continue;
40
+ }
41
+ // Mark this attribute as seen if it currently has a value
42
+ if (currentValue !== null) {
43
+ onceAttrs.add(attrName);
44
+ }
45
+ }
46
+ // Include if: currently has value OR previously had value but now removed
47
+ if (currentValue !== null || (previousValue !== undefined && currentValue === null)) {
48
+ // Check if value changed
49
+ if (currentValue !== previousValue) {
50
+ const attrNode = currentValue !== null ? element.getAttributeNode(attrName) : null;
51
+ changes.push({
52
+ value: currentValue,
53
+ attrNode,
54
+ mapEntry,
55
+ attrName,
56
+ coordinate,
57
+ element
58
+ });
59
+ // Update state
60
+ if (currentValue !== null) {
61
+ attrState.set(attrName, currentValue);
62
+ }
63
+ else {
64
+ attrState.delete(attrName);
65
+ }
66
+ }
67
+ }
68
+ }
69
+ return changes;
70
+ }
package/attrChanges.ts ADDED
@@ -0,0 +1,90 @@
1
+ import type { AttrChange, MountInit } from './types.d.ts';
2
+
3
+ /**
4
+ * Checks for attribute changes on a mounted element.
5
+ * This module is dynamically loaded only when whereAttr is configured.
6
+ */
7
+ export function checkAttrChanges(
8
+ element: Element,
9
+ mountInit: MountInit,
10
+ buildAttrCoordinateMapFn: (whereAttr: any, isCustomElement: boolean) => any,
11
+ elementAttrStates: WeakMap<Element, Map<string, string | null>>,
12
+ elementOnceAttrs: WeakMap<Element, Set<string>>
13
+ ): AttrChange[] {
14
+ if (!mountInit.whereAttr || !buildAttrCoordinateMapFn) {
15
+ return [];
16
+ }
17
+
18
+ const isCustomElement = element.tagName.toLowerCase().includes('-');
19
+ const attrCoordMap = buildAttrCoordinateMapFn(mountInit.whereAttr, isCustomElement);
20
+
21
+ // Get or create the attribute state for this element
22
+ let attrState = elementAttrStates.get(element);
23
+ if (!attrState) {
24
+ attrState = new Map<string, string | null>();
25
+ elementAttrStates.set(element, attrState);
26
+ }
27
+
28
+ const changes: AttrChange[] = [];
29
+ const currentAttrs = new Set<string>();
30
+
31
+ // Check all possible attributes from the coordinate map
32
+ for (const attrName of Object.keys(attrCoordMap)) {
33
+ const coordinate = attrCoordMap[attrName];
34
+ const currentValue = element.getAttribute(attrName);
35
+ const previousValue = attrState.get(attrName);
36
+
37
+ if (currentValue !== null) {
38
+ currentAttrs.add(attrName);
39
+ }
40
+
41
+ // Check if this attribute has "once: true" in its map entry
42
+ const mapEntry = mountInit.map?.[coordinate] || null;
43
+ const isOnce = mapEntry?.once === true;
44
+
45
+ // If "once" is true, check if we've already seen this attribute
46
+ if (isOnce) {
47
+ let onceAttrs = elementOnceAttrs.get(element);
48
+ if (!onceAttrs) {
49
+ onceAttrs = new Set<string>();
50
+ elementOnceAttrs.set(element, onceAttrs);
51
+ }
52
+
53
+ // If we've already seen this attribute, skip it
54
+ if (onceAttrs.has(attrName)) {
55
+ continue;
56
+ }
57
+
58
+ // Mark this attribute as seen if it currently has a value
59
+ if (currentValue !== null) {
60
+ onceAttrs.add(attrName);
61
+ }
62
+ }
63
+
64
+ // Include if: currently has value OR previously had value but now removed
65
+ if (currentValue !== null || (previousValue !== undefined && currentValue === null)) {
66
+ // Check if value changed
67
+ if (currentValue !== previousValue) {
68
+ const attrNode = currentValue !== null ? element.getAttributeNode(attrName) : null;
69
+
70
+ changes.push({
71
+ value: currentValue,
72
+ attrNode,
73
+ mapEntry,
74
+ attrName,
75
+ coordinate,
76
+ element
77
+ });
78
+
79
+ // Update state
80
+ if (currentValue !== null) {
81
+ attrState.set(attrName, currentValue);
82
+ } else {
83
+ attrState.delete(attrName);
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ return changes;
90
+ }
package/emitEvents.js ADDED
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Emits events from a mounted element based on the mountedElemEmits configuration.
3
+ * This module is dynamically loaded only when mountedElemEmits is configured.
4
+ */
5
+ export async function emitMountedElementEvents(element, mountInit, processedEventsForElement) {
6
+ const configs = Array.isArray(mountInit.mountedElemEmits)
7
+ ? mountInit.mountedElemEmits
8
+ : [mountInit.mountedElemEmits];
9
+ for (const config of configs) {
10
+ await emitSingleEvent(element, mountInit, config, processedEventsForElement);
11
+ }
12
+ }
13
+ async function emitSingleEvent(element, mountInit, config, processedEventsForElement) {
14
+ // Check if this event should only fire once per element
15
+ if (config.oncePerMountedElement) {
16
+ const eventId = getEventId(config);
17
+ let processedEvents = processedEventsForElement.get(element);
18
+ if (!processedEvents) {
19
+ processedEvents = new Set();
20
+ processedEventsForElement.set(element, processedEvents);
21
+ }
22
+ if (processedEvents.has(eventId)) {
23
+ return; // Already emitted for this element
24
+ }
25
+ processedEvents.add(eventId);
26
+ }
27
+ // Resolve event constructor
28
+ const EventCtor = resolveEventConstructor(config.event);
29
+ // Process args with magic string substitution
30
+ const processedArgs = config.args !== undefined
31
+ ? processMagicStrings(config.args, element, mountInit)
32
+ : undefined;
33
+ // Construct the event
34
+ let event;
35
+ if (processedArgs === undefined) {
36
+ event = new EventCtor();
37
+ }
38
+ else if (Array.isArray(processedArgs)) {
39
+ // For array args, ensure bubbles is set if second arg is an options object
40
+ if (processedArgs.length === 2 && typeof processedArgs[0] === 'string' && typeof processedArgs[1] === 'object' && processedArgs[1] !== null) {
41
+ // Merge bubbles: true into the options object if not already set
42
+ const options = { bubbles: true, ...processedArgs[1] };
43
+ event = new EventCtor(processedArgs[0], options);
44
+ }
45
+ else {
46
+ event = new EventCtor(...processedArgs);
47
+ }
48
+ }
49
+ else {
50
+ // Single arg - if it's a string (event name), add bubbles: true by default
51
+ if (typeof processedArgs === 'string') {
52
+ event = new EventCtor(processedArgs, { bubbles: true });
53
+ }
54
+ else {
55
+ event = new EventCtor(processedArgs);
56
+ }
57
+ }
58
+ // Apply eventProps if specified
59
+ if (config.eventProps) {
60
+ const { assignGingerly } = await import('assign-gingerly/index.js');
61
+ const processedProps = processMagicStrings(config.eventProps, element, mountInit);
62
+ assignGingerly(event, processedProps);
63
+ }
64
+ // Dispatch the event from the mounted element
65
+ element.dispatchEvent(event);
66
+ }
67
+ function resolveEventConstructor(event) {
68
+ if (typeof event === 'string') {
69
+ const EventCtor = globalThis[event];
70
+ if (!EventCtor || typeof EventCtor !== 'function') {
71
+ throw new Error(`Event constructor "${event}" not found in globalThis`);
72
+ }
73
+ return EventCtor;
74
+ }
75
+ return event;
76
+ }
77
+ function getEventId(config) {
78
+ const eventName = typeof config.event === 'string' ? config.event : config.event.name;
79
+ const argsStr = JSON.stringify(config.args || '');
80
+ return `${eventName}:${argsStr}`;
81
+ }
82
+ function processMagicStrings(value, element, mountInit) {
83
+ if (typeof value === 'string') {
84
+ if (value === '{{mountedElement}}') {
85
+ return element;
86
+ }
87
+ if (value === '{{mountInit}}') {
88
+ return mountInit;
89
+ }
90
+ return value;
91
+ }
92
+ if (Array.isArray(value)) {
93
+ return value.map(item => processMagicStrings(item, element, mountInit));
94
+ }
95
+ if (value && typeof value === 'object') {
96
+ const processed = {};
97
+ for (const [key, val] of Object.entries(value)) {
98
+ processed[key] = processMagicStrings(val, element, mountInit);
99
+ }
100
+ return processed;
101
+ }
102
+ return value;
103
+ }
package/emitEvents.ts ADDED
@@ -0,0 +1,126 @@
1
+ import type { EventConfig, EventConstructor, MountInit } from './types.d.ts';
2
+
3
+ /**
4
+ * Emits events from a mounted element based on the mountedElemEmits configuration.
5
+ * This module is dynamically loaded only when mountedElemEmits is configured.
6
+ */
7
+ export async function emitMountedElementEvents(
8
+ element: Element,
9
+ mountInit: MountInit,
10
+ processedEventsForElement: WeakMap<Element, Set<string>>
11
+ ): Promise<void> {
12
+ const configs = Array.isArray(mountInit.mountedElemEmits)
13
+ ? mountInit.mountedElemEmits
14
+ : [mountInit.mountedElemEmits!];
15
+
16
+ for (const config of configs) {
17
+ await emitSingleEvent(element, mountInit, config, processedEventsForElement);
18
+ }
19
+ }
20
+
21
+ async function emitSingleEvent(
22
+ element: Element,
23
+ mountInit: MountInit,
24
+ config: EventConfig,
25
+ processedEventsForElement: WeakMap<Element, Set<string>>
26
+ ): Promise<void> {
27
+ // Check if this event should only fire once per element
28
+ if (config.oncePerMountedElement) {
29
+ const eventId = getEventId(config);
30
+ let processedEvents = processedEventsForElement.get(element);
31
+
32
+ if (!processedEvents) {
33
+ processedEvents = new Set<string>();
34
+ processedEventsForElement.set(element, processedEvents);
35
+ }
36
+
37
+ if (processedEvents.has(eventId)) {
38
+ return; // Already emitted for this element
39
+ }
40
+
41
+ processedEvents.add(eventId);
42
+ }
43
+
44
+ // Resolve event constructor
45
+ const EventCtor = resolveEventConstructor(config.event);
46
+
47
+ // Process args with magic string substitution
48
+ const processedArgs = config.args !== undefined
49
+ ? processMagicStrings(config.args, element, mountInit)
50
+ : undefined;
51
+
52
+ // Construct the event
53
+ let event: Event;
54
+ if (processedArgs === undefined) {
55
+ event = new EventCtor();
56
+ } else if (Array.isArray(processedArgs)) {
57
+ // For array args, ensure bubbles is set if second arg is an options object
58
+ if (processedArgs.length === 2 && typeof processedArgs[0] === 'string' && typeof processedArgs[1] === 'object' && processedArgs[1] !== null) {
59
+ // Merge bubbles: true into the options object if not already set
60
+ const options = { bubbles: true, ...processedArgs[1] };
61
+ event = new EventCtor(processedArgs[0], options);
62
+ } else {
63
+ event = new EventCtor(...processedArgs);
64
+ }
65
+ } else {
66
+ // Single arg - if it's a string (event name), add bubbles: true by default
67
+ if (typeof processedArgs === 'string') {
68
+ event = new EventCtor(processedArgs, { bubbles: true });
69
+ } else {
70
+ event = new EventCtor(processedArgs);
71
+ }
72
+ }
73
+
74
+ // Apply eventProps if specified
75
+ if (config.eventProps) {
76
+ const { assignGingerly } = await import('assign-gingerly/index.js');
77
+ const processedProps = processMagicStrings(config.eventProps, element, mountInit);
78
+ assignGingerly(event, processedProps);
79
+ }
80
+
81
+ // Dispatch the event from the mounted element
82
+ element.dispatchEvent(event);
83
+ }
84
+
85
+ function resolveEventConstructor(event: string | EventConstructor): EventConstructor {
86
+ if (typeof event === 'string') {
87
+ const EventCtor = (globalThis as any)[event];
88
+ if (!EventCtor || typeof EventCtor !== 'function') {
89
+ throw new Error(`Event constructor "${event}" not found in globalThis`);
90
+ }
91
+ return EventCtor;
92
+ }
93
+ return event;
94
+ }
95
+
96
+ function getEventId(config: EventConfig): string {
97
+ const eventName = typeof config.event === 'string' ? config.event : config.event.name;
98
+ const argsStr = JSON.stringify(config.args || '');
99
+ return `${eventName}:${argsStr}`;
100
+ }
101
+
102
+ function processMagicStrings(value: any, element: Element, mountInit: MountInit): any {
103
+ if (typeof value === 'string') {
104
+ if (value === '{{mountedElement}}') {
105
+ return element;
106
+ }
107
+ if (value === '{{mountInit}}') {
108
+ return mountInit;
109
+ }
110
+ return value;
111
+ }
112
+
113
+ if (Array.isArray(value)) {
114
+ return value.map(item => processMagicStrings(item, element, mountInit));
115
+ }
116
+
117
+ if (value && typeof value === 'object') {
118
+ const processed: any = {};
119
+ for (const [key, val] of Object.entries(value)) {
120
+ processed[key] = processMagicStrings(val, element, mountInit);
121
+ }
122
+ return processed;
123
+ }
124
+
125
+ return value;
126
+ }
package/index.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  // Main entry point for MountObserver v2
2
2
  export { MountObserver } from './MountObserver.js';
3
+ export { whereOutside } from './whereOutside.js';
4
+ export { emitMountedElementEvents } from './emitEvents.js';
5
+ export { checkAttrChanges } from './attrChanges.js';
3
6
  export type {
4
7
  MountInit,
5
8
  MountObserverOptions,
@@ -15,5 +18,9 @@ export {
15
18
  mountEventName,
16
19
  dismountEventName,
17
20
  disconnectEventName,
18
- loadEventName
19
- } from './constants.js';
21
+ loadEventName,
22
+ attrchangeEventName,
23
+ mediamatchEventName,
24
+ mediaunmatchEventName
25
+ } from './Events.js';
26
+
package/mediaQuery.js ADDED
@@ -0,0 +1,89 @@
1
+ import { MediaMatchEvent, MediaUnmatchEvent, DismountEvent } from './Events.js';
2
+ export function setupMediaQuery(init, rootNodeRef, mountedElements, modules, observer, processNode) {
3
+ const { whereMediaMatches } = init;
4
+ // Create or use MediaQueryList
5
+ let mediaQueryList;
6
+ if (typeof whereMediaMatches === 'string') {
7
+ mediaQueryList = window.matchMedia(whereMediaMatches);
8
+ }
9
+ else {
10
+ mediaQueryList = whereMediaMatches;
11
+ }
12
+ // Track current state
13
+ let mediaMatches = mediaQueryList.matches;
14
+ // Set up change listener
15
+ const mediaChangeHandler = (e) => {
16
+ const previousMatches = mediaMatches;
17
+ mediaMatches = e.matches;
18
+ if (e.matches && !previousMatches) {
19
+ // Media query now matches - wake up and process elements
20
+ handleMediaMatch();
21
+ }
22
+ else if (!e.matches && previousMatches) {
23
+ // Media query no longer matches - dismount all elements
24
+ handleMediaUnmatch();
25
+ }
26
+ };
27
+ function handleMediaMatch() {
28
+ // Dispatch mediamatch event if requested
29
+ if (init.getPlayByPlay) {
30
+ observer.dispatchEvent(new MediaMatchEvent(init));
31
+ }
32
+ // Process all elements in the observed node
33
+ const rootNode = rootNodeRef.deref();
34
+ if (rootNode) {
35
+ processNode(rootNode);
36
+ }
37
+ }
38
+ function handleMediaUnmatch() {
39
+ // Dispatch mediaunmatch event if requested
40
+ if (init.getPlayByPlay) {
41
+ observer.dispatchEvent(new MediaUnmatchEvent(init));
42
+ }
43
+ // Dismount all currently mounted elements
44
+ const rootNode = rootNodeRef.deref();
45
+ if (!rootNode) {
46
+ return;
47
+ }
48
+ const context = {
49
+ modules,
50
+ observer: observer,
51
+ observeInfo: {
52
+ rootNode
53
+ }
54
+ };
55
+ // Get all mounted elements from the WeakDual setWeak
56
+ const mountedElementsList = [];
57
+ for (const ref of mountedElements.setWeak) {
58
+ const element = ref.deref();
59
+ if (element) {
60
+ mountedElementsList.push(element);
61
+ }
62
+ }
63
+ // Dismount each element
64
+ for (const element of mountedElementsList) {
65
+ // Remove from both structures
66
+ mountedElements.weakSet.delete(element);
67
+ for (const ref of mountedElements.setWeak) {
68
+ if (ref.deref() === element) {
69
+ mountedElements.setWeak.delete(ref);
70
+ break;
71
+ }
72
+ }
73
+ // Call dismount callback
74
+ if (init.do && typeof init.do !== 'function' && init.do.dismount) {
75
+ init.do.dismount(element, context);
76
+ }
77
+ // Dispatch dismount event with reason
78
+ observer.dispatchEvent(new DismountEvent(element, 'media-query-failed', init));
79
+ }
80
+ }
81
+ mediaQueryList.addEventListener('change', mediaChangeHandler);
82
+ return {
83
+ mediaQueryList,
84
+ mediaMatches,
85
+ cleanup: () => {
86
+ mediaQueryList.removeEventListener('change', mediaChangeHandler);
87
+ }
88
+ };
89
+ }
package/mediaQuery.ts ADDED
@@ -0,0 +1,116 @@
1
+ // Media query handling for MountObserver
2
+ import type { MountInit, MountContext, WeakDual } from './types.js';
3
+ import { MediaMatchEvent, MediaUnmatchEvent, DismountEvent } from './Events.js';
4
+
5
+ export function setupMediaQuery(
6
+ init: MountInit,
7
+ rootNodeRef: WeakRef<Node>,
8
+ mountedElements: WeakDual<Element>,
9
+ modules: any[],
10
+ observer: EventTarget,
11
+ processNode: (node: Node) => void
12
+ ): {
13
+ mediaQueryList: MediaQueryList;
14
+ mediaMatches: boolean;
15
+ cleanup: () => void;
16
+ } {
17
+ const { whereMediaMatches } = init;
18
+
19
+ // Create or use MediaQueryList
20
+ let mediaQueryList: MediaQueryList;
21
+ if (typeof whereMediaMatches === 'string') {
22
+ mediaQueryList = window.matchMedia(whereMediaMatches);
23
+ } else {
24
+ mediaQueryList = whereMediaMatches!;
25
+ }
26
+
27
+ // Track current state
28
+ let mediaMatches = mediaQueryList.matches;
29
+
30
+ // Set up change listener
31
+ const mediaChangeHandler = (e: MediaQueryListEvent) => {
32
+ const previousMatches = mediaMatches;
33
+ mediaMatches = e.matches;
34
+
35
+ if (e.matches && !previousMatches) {
36
+ // Media query now matches - wake up and process elements
37
+ handleMediaMatch();
38
+ } else if (!e.matches && previousMatches) {
39
+ // Media query no longer matches - dismount all elements
40
+ handleMediaUnmatch();
41
+ }
42
+ };
43
+
44
+ function handleMediaMatch(): void {
45
+ // Dispatch mediamatch event if requested
46
+ if (init.getPlayByPlay) {
47
+ observer.dispatchEvent(new MediaMatchEvent(init));
48
+ }
49
+
50
+ // Process all elements in the observed node
51
+ const rootNode = rootNodeRef.deref();
52
+ if (rootNode) {
53
+ processNode(rootNode);
54
+ }
55
+ }
56
+
57
+ function handleMediaUnmatch(): void {
58
+ // Dispatch mediaunmatch event if requested
59
+ if (init.getPlayByPlay) {
60
+ observer.dispatchEvent(new MediaUnmatchEvent(init));
61
+ }
62
+
63
+ // Dismount all currently mounted elements
64
+ const rootNode = rootNodeRef.deref();
65
+ if (!rootNode) {
66
+ return;
67
+ }
68
+
69
+ const context: MountContext = {
70
+ modules,
71
+ observer: observer as any,
72
+ observeInfo: {
73
+ rootNode
74
+ }
75
+ };
76
+
77
+ // Get all mounted elements from the WeakDual setWeak
78
+ const mountedElementsList: Element[] = [];
79
+ for (const ref of mountedElements.setWeak) {
80
+ const element = ref.deref();
81
+ if (element) {
82
+ mountedElementsList.push(element);
83
+ }
84
+ }
85
+
86
+ // Dismount each element
87
+ for (const element of mountedElementsList) {
88
+ // Remove from both structures
89
+ mountedElements.weakSet.delete(element);
90
+ for (const ref of mountedElements.setWeak) {
91
+ if (ref.deref() === element) {
92
+ mountedElements.setWeak.delete(ref);
93
+ break;
94
+ }
95
+ }
96
+
97
+ // Call dismount callback
98
+ if (init.do && typeof init.do !== 'function' && init.do.dismount) {
99
+ init.do.dismount(element, context);
100
+ }
101
+
102
+ // Dispatch dismount event with reason
103
+ observer.dispatchEvent(new DismountEvent(element, 'media-query-failed', init));
104
+ }
105
+ }
106
+
107
+ mediaQueryList.addEventListener('change', mediaChangeHandler);
108
+
109
+ return {
110
+ mediaQueryList,
111
+ mediaMatches,
112
+ cleanup: () => {
113
+ mediaQueryList.removeEventListener('change', mediaChangeHandler);
114
+ }
115
+ };
116
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mount-observer",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Observe and act on css matches.",
5
5
  "main": "MountObserver.js",
6
6
  "module": "MountObserver.js",
@@ -13,22 +13,37 @@
13
13
  },
14
14
  "exports": {
15
15
  ".": {
16
+ "default": "./index.js",
17
+ "types": "./index.ts"
18
+ },
19
+ "./MountObserver.js": {
16
20
  "default": "./MountObserver.js",
17
21
  "types": "./MountObserver.ts"
22
+ },
23
+ "./whereOutside.js": {
24
+ "default": "./whereOutside.js",
25
+ "types": "./whereOutside.ts"
26
+ },
27
+ "./emitEvents.js": {
28
+ "default": "./emitEvents.js",
29
+ "types": "./emitEvents.ts"
30
+ },
31
+ "./attrChanges.js": {
32
+ "default": "./attrChanges.js",
33
+ "types": "./attrChanges.ts"
18
34
  }
19
-
20
35
  },
21
36
  "files": [
22
37
  "*.js",
23
38
  "*.ts"
24
39
  ],
25
- "types": "./ts-refs/mount-observer/types.d.ts",
40
+ "types": "./types.d.ts",
26
41
  "scripts": {
27
42
  "serve": "node ./node_modules/spa-ssi/serve.js",
28
43
  "test": "playwright test",
29
44
  "safari": "npx playwright wk http://localhost:8000",
30
45
  "update": "ncu -u && npm install"
31
46
  },
32
- "author": "",
47
+ "author": "Bruce B. Anderson <anderson.bruce.b@gmail.com>",
33
48
  "license": "MIT"
34
49
  }