@memberjunction/react-runtime 2.77.0 → 2.79.0

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.
@@ -138,9 +138,8 @@ export class ReactRootManager {
138
138
  if (managedRoot.container) {
139
139
  // Clear React's internal references
140
140
  delete (managedRoot.container as any)._reactRootContainer;
141
- // Clear all event listeners
142
- const newContainer = managedRoot.container.cloneNode(false) as HTMLElement;
143
- managedRoot.container.parentNode?.replaceChild(newContainer, managedRoot.container);
141
+ // Clear the container content without replacing the node
142
+ managedRoot.container.innerHTML = '';
144
143
  }
145
144
 
146
145
  resolve();
@@ -4,13 +4,59 @@
4
4
  * @module @memberjunction/react-runtime/utilities
5
5
  */
6
6
 
7
+ // Type alias for timer IDs that works in both browser and Node.js
8
+ export type TimerId = number | NodeJS.Timeout;
9
+
7
10
  export interface ManagedResource {
8
11
  type: 'timer' | 'interval' | 'animationFrame' | 'eventListener' | 'domElement' | 'observable' | 'reactRoot';
9
- id: string | number;
12
+ id: string | TimerId;
10
13
  cleanup: () => void;
11
14
  metadata?: Record<string, any>;
12
15
  }
13
16
 
17
+ /**
18
+ * Environment-agnostic timer functions that work in both browser and Node.js
19
+ */
20
+ const getTimerFunctions = () => {
21
+ // Check if we're in a browser environment
22
+ if (typeof window !== 'undefined' && window.setTimeout) {
23
+ return {
24
+ setTimeout: window.setTimeout.bind(window),
25
+ clearTimeout: window.clearTimeout.bind(window),
26
+ setInterval: window.setInterval.bind(window),
27
+ clearInterval: window.clearInterval.bind(window),
28
+ requestAnimationFrame: window.requestAnimationFrame?.bind(window),
29
+ cancelAnimationFrame: window.cancelAnimationFrame?.bind(window)
30
+ };
31
+ }
32
+ // Node.js environment
33
+ else if (typeof global !== 'undefined' && global.setTimeout) {
34
+ return {
35
+ setTimeout: global.setTimeout,
36
+ clearTimeout: global.clearTimeout,
37
+ setInterval: global.setInterval,
38
+ clearInterval: global.clearInterval,
39
+ requestAnimationFrame: undefined, // Not available in Node.js
40
+ cancelAnimationFrame: undefined
41
+ };
42
+ }
43
+ // Fallback - return no-op functions
44
+ else {
45
+ const noop = () => {};
46
+ const noopWithReturn = () => 0;
47
+ return {
48
+ setTimeout: noopWithReturn,
49
+ clearTimeout: noop,
50
+ setInterval: noopWithReturn,
51
+ clearInterval: noop,
52
+ requestAnimationFrame: undefined,
53
+ cancelAnimationFrame: undefined
54
+ };
55
+ }
56
+ };
57
+
58
+ const timers = getTimerFunctions();
59
+
14
60
  /**
15
61
  * ResourceManager provides centralized management of resources that need cleanup.
16
62
  * This prevents memory leaks by ensuring all resources are properly disposed.
@@ -29,7 +75,7 @@ export class ResourceManager {
29
75
  delay: number,
30
76
  metadata?: Record<string, any>
31
77
  ): number {
32
- const id = window.setTimeout(() => {
78
+ const id = timers.setTimeout(() => {
33
79
  this.removeResource(componentId, 'timer', id);
34
80
  callback();
35
81
  }, delay);
@@ -37,11 +83,11 @@ export class ResourceManager {
37
83
  this.addResource(componentId, {
38
84
  type: 'timer',
39
85
  id,
40
- cleanup: () => window.clearTimeout(id),
86
+ cleanup: () => timers.clearTimeout(id),
41
87
  metadata
42
88
  });
43
89
 
44
- return id;
90
+ return id as any;
45
91
  }
46
92
 
47
93
  /**
@@ -53,16 +99,16 @@ export class ResourceManager {
53
99
  delay: number,
54
100
  metadata?: Record<string, any>
55
101
  ): number {
56
- const id = window.setInterval(callback, delay);
102
+ const id = timers.setInterval(callback, delay);
57
103
 
58
104
  this.addResource(componentId, {
59
105
  type: 'interval',
60
106
  id,
61
- cleanup: () => window.clearInterval(id),
107
+ cleanup: () => timers.clearInterval(id),
62
108
  metadata
63
109
  });
64
110
 
65
- return id;
111
+ return id as any;
66
112
  }
67
113
 
68
114
  /**
@@ -72,8 +118,13 @@ export class ResourceManager {
72
118
  componentId: string,
73
119
  callback: FrameRequestCallback,
74
120
  metadata?: Record<string, any>
75
- ): number {
76
- const id = window.requestAnimationFrame((time) => {
121
+ ): TimerId {
122
+ if (!timers.requestAnimationFrame) {
123
+ // Fallback to setTimeout in non-browser environments
124
+ return this.setTimeout(componentId, () => callback(Date.now()), 16, metadata);
125
+ }
126
+
127
+ const id = timers.requestAnimationFrame((time) => {
77
128
  this.removeResource(componentId, 'animationFrame', id);
78
129
  callback(time);
79
130
  });
@@ -81,18 +132,18 @@ export class ResourceManager {
81
132
  this.addResource(componentId, {
82
133
  type: 'animationFrame',
83
134
  id,
84
- cleanup: () => window.cancelAnimationFrame(id),
135
+ cleanup: () => timers.cancelAnimationFrame?.(id),
85
136
  metadata
86
137
  });
87
138
 
88
- return id;
139
+ return id as any;
89
140
  }
90
141
 
91
142
  /**
92
143
  * Clear a specific timeout
93
144
  */
94
145
  clearTimeout(componentId: string, id: number): void {
95
- window.clearTimeout(id);
146
+ timers.clearTimeout(id);
96
147
  this.removeResource(componentId, 'timer', id);
97
148
  }
98
149
 
@@ -100,15 +151,20 @@ export class ResourceManager {
100
151
  * Clear a specific interval
101
152
  */
102
153
  clearInterval(componentId: string, id: number): void {
103
- window.clearInterval(id);
154
+ timers.clearInterval(id);
104
155
  this.removeResource(componentId, 'interval', id);
105
156
  }
106
157
 
107
158
  /**
108
159
  * Cancel a specific animation frame
109
160
  */
110
- cancelAnimationFrame(componentId: string, id: number): void {
111
- window.cancelAnimationFrame(id);
161
+ cancelAnimationFrame(componentId: string, id: TimerId): void {
162
+ if (timers.cancelAnimationFrame) {
163
+ timers.cancelAnimationFrame(id as number);
164
+ } else {
165
+ // If we fell back to setTimeout, use clearTimeout
166
+ timers.clearTimeout(id as any);
167
+ }
112
168
  this.removeResource(componentId, 'animationFrame', id);
113
169
  }
114
170
 
@@ -122,15 +178,22 @@ export class ResourceManager {
122
178
  listener: EventListener,
123
179
  options?: AddEventListenerOptions
124
180
  ): void {
125
- target.addEventListener(type, listener, options);
126
-
127
- const resourceId = `${type}-${Date.now()}-${Math.random()}`;
128
- this.addResource(componentId, {
129
- type: 'eventListener',
130
- id: resourceId,
131
- cleanup: () => target.removeEventListener(type, listener, options),
132
- metadata: { target, type, options }
133
- });
181
+ // Only add event listeners if we have a valid EventTarget (browser environment)
182
+ if (target && typeof target.addEventListener === 'function') {
183
+ target.addEventListener(type, listener, options);
184
+
185
+ const resourceId = `${type}-${Date.now()}-${Math.random()}`;
186
+ this.addResource(componentId, {
187
+ type: 'eventListener',
188
+ id: resourceId,
189
+ cleanup: () => {
190
+ if (target && typeof target.removeEventListener === 'function') {
191
+ target.removeEventListener(type, listener, options);
192
+ }
193
+ },
194
+ metadata: { target, type, options }
195
+ });
196
+ }
134
197
  }
135
198
 
136
199
  /**
@@ -138,23 +201,26 @@ export class ResourceManager {
138
201
  */
139
202
  registerDOMElement(
140
203
  componentId: string,
141
- element: HTMLElement,
204
+ element: any, // Use 'any' to avoid HTMLElement type in Node.js
142
205
  cleanup?: () => void
143
206
  ): void {
144
- const resourceId = `dom-${Date.now()}-${Math.random()}`;
145
- this.addResource(componentId, {
146
- type: 'domElement',
147
- id: resourceId,
148
- cleanup: () => {
149
- if (cleanup) {
150
- cleanup();
151
- }
152
- if (element.parentNode) {
153
- element.parentNode.removeChild(element);
154
- }
155
- },
156
- metadata: { element }
157
- });
207
+ // Only register if we're in a browser environment with DOM support
208
+ if (typeof document !== 'undefined' && element && element.parentNode) {
209
+ const resourceId = `dom-${Date.now()}-${Math.random()}`;
210
+ this.addResource(componentId, {
211
+ type: 'domElement',
212
+ id: resourceId,
213
+ cleanup: () => {
214
+ if (cleanup) {
215
+ cleanup();
216
+ }
217
+ if (element && element.parentNode && typeof element.parentNode.removeChild === 'function') {
218
+ element.parentNode.removeChild(element);
219
+ }
220
+ },
221
+ metadata: { element }
222
+ });
223
+ }
158
224
  }
159
225
 
160
226
  /**
@@ -206,7 +272,7 @@ export class ResourceManager {
206
272
  private removeResource(
207
273
  componentId: string,
208
274
  type: ManagedResource['type'],
209
- id: string | number
275
+ id: string | number | NodeJS.Timeout
210
276
  ): void {
211
277
  const componentResources = this.resources.get(componentId);
212
278
  if (componentResources) {