@memberjunction/react-runtime 2.75.0 → 2.77.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.
Files changed (52) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +23 -0
  3. package/dist/compiler/component-compiler.d.ts +0 -1
  4. package/dist/compiler/component-compiler.d.ts.map +1 -1
  5. package/dist/compiler/component-compiler.js +34 -25
  6. package/dist/index.d.ts +4 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +10 -2
  9. package/dist/registry/component-registry.d.ts +1 -0
  10. package/dist/registry/component-registry.d.ts.map +1 -1
  11. package/dist/registry/component-registry.js +6 -3
  12. package/dist/runtime/index.d.ts +2 -1
  13. package/dist/runtime/index.d.ts.map +1 -1
  14. package/dist/runtime/index.js +4 -2
  15. package/dist/runtime/prop-builder.d.ts +1 -2
  16. package/dist/runtime/prop-builder.d.ts.map +1 -1
  17. package/dist/runtime/prop-builder.js +4 -76
  18. package/dist/runtime/react-root-manager.d.ts +26 -0
  19. package/dist/runtime/react-root-manager.d.ts.map +1 -0
  20. package/dist/runtime/react-root-manager.js +122 -0
  21. package/dist/types/index.d.ts +1 -0
  22. package/dist/types/index.d.ts.map +1 -1
  23. package/dist/utilities/cache-manager.d.ts +38 -0
  24. package/dist/utilities/cache-manager.d.ts.map +1 -0
  25. package/dist/utilities/cache-manager.js +156 -0
  26. package/dist/utilities/core-libraries.d.ts +5 -0
  27. package/dist/utilities/core-libraries.d.ts.map +1 -0
  28. package/dist/utilities/core-libraries.js +52 -0
  29. package/dist/utilities/index.d.ts +2 -0
  30. package/dist/utilities/index.d.ts.map +1 -1
  31. package/dist/utilities/index.js +2 -0
  32. package/dist/utilities/library-loader.d.ts +2 -2
  33. package/dist/utilities/library-loader.d.ts.map +1 -1
  34. package/dist/utilities/library-loader.js +52 -24
  35. package/dist/utilities/resource-manager.d.ts +34 -0
  36. package/dist/utilities/resource-manager.d.ts.map +1 -0
  37. package/dist/utilities/resource-manager.js +174 -0
  38. package/package.json +4 -4
  39. package/samples/entities-1.js +493 -0
  40. package/src/compiler/component-compiler.ts +64 -35
  41. package/src/index.ts +18 -1
  42. package/src/registry/component-registry.ts +14 -4
  43. package/src/runtime/index.ts +7 -2
  44. package/src/runtime/prop-builder.ts +5 -112
  45. package/src/runtime/react-root-manager.ts +218 -0
  46. package/src/types/index.ts +2 -0
  47. package/src/utilities/cache-manager.ts +253 -0
  48. package/src/utilities/core-libraries.ts +61 -0
  49. package/src/utilities/index.ts +3 -1
  50. package/src/utilities/library-loader.ts +111 -47
  51. package/src/utilities/resource-manager.ts +305 -0
  52. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,305 @@
1
+ /**
2
+ * @fileoverview Centralized resource management for React Runtime
3
+ * Handles timers, DOM elements, event listeners, and other resources that need cleanup
4
+ * @module @memberjunction/react-runtime/utilities
5
+ */
6
+
7
+ export interface ManagedResource {
8
+ type: 'timer' | 'interval' | 'animationFrame' | 'eventListener' | 'domElement' | 'observable' | 'reactRoot';
9
+ id: string | number;
10
+ cleanup: () => void;
11
+ metadata?: Record<string, any>;
12
+ }
13
+
14
+ /**
15
+ * ResourceManager provides centralized management of resources that need cleanup.
16
+ * This prevents memory leaks by ensuring all resources are properly disposed.
17
+ */
18
+ export class ResourceManager {
19
+ private resources = new Map<string, Set<ManagedResource>>();
20
+ private globalResources = new Set<ManagedResource>();
21
+ private cleanupCallbacks = new Map<string, (() => void)[]>();
22
+
23
+ /**
24
+ * Register a timeout with automatic cleanup
25
+ */
26
+ setTimeout(
27
+ componentId: string,
28
+ callback: () => void,
29
+ delay: number,
30
+ metadata?: Record<string, any>
31
+ ): number {
32
+ const id = window.setTimeout(() => {
33
+ this.removeResource(componentId, 'timer', id);
34
+ callback();
35
+ }, delay);
36
+
37
+ this.addResource(componentId, {
38
+ type: 'timer',
39
+ id,
40
+ cleanup: () => window.clearTimeout(id),
41
+ metadata
42
+ });
43
+
44
+ return id;
45
+ }
46
+
47
+ /**
48
+ * Register an interval with automatic cleanup
49
+ */
50
+ setInterval(
51
+ componentId: string,
52
+ callback: () => void,
53
+ delay: number,
54
+ metadata?: Record<string, any>
55
+ ): number {
56
+ const id = window.setInterval(callback, delay);
57
+
58
+ this.addResource(componentId, {
59
+ type: 'interval',
60
+ id,
61
+ cleanup: () => window.clearInterval(id),
62
+ metadata
63
+ });
64
+
65
+ return id;
66
+ }
67
+
68
+ /**
69
+ * Register an animation frame with automatic cleanup
70
+ */
71
+ requestAnimationFrame(
72
+ componentId: string,
73
+ callback: FrameRequestCallback,
74
+ metadata?: Record<string, any>
75
+ ): number {
76
+ const id = window.requestAnimationFrame((time) => {
77
+ this.removeResource(componentId, 'animationFrame', id);
78
+ callback(time);
79
+ });
80
+
81
+ this.addResource(componentId, {
82
+ type: 'animationFrame',
83
+ id,
84
+ cleanup: () => window.cancelAnimationFrame(id),
85
+ metadata
86
+ });
87
+
88
+ return id;
89
+ }
90
+
91
+ /**
92
+ * Clear a specific timeout
93
+ */
94
+ clearTimeout(componentId: string, id: number): void {
95
+ window.clearTimeout(id);
96
+ this.removeResource(componentId, 'timer', id);
97
+ }
98
+
99
+ /**
100
+ * Clear a specific interval
101
+ */
102
+ clearInterval(componentId: string, id: number): void {
103
+ window.clearInterval(id);
104
+ this.removeResource(componentId, 'interval', id);
105
+ }
106
+
107
+ /**
108
+ * Cancel a specific animation frame
109
+ */
110
+ cancelAnimationFrame(componentId: string, id: number): void {
111
+ window.cancelAnimationFrame(id);
112
+ this.removeResource(componentId, 'animationFrame', id);
113
+ }
114
+
115
+ /**
116
+ * Register an event listener with automatic cleanup
117
+ */
118
+ addEventListener(
119
+ componentId: string,
120
+ target: EventTarget,
121
+ type: string,
122
+ listener: EventListener,
123
+ options?: AddEventListenerOptions
124
+ ): 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
+ });
134
+ }
135
+
136
+ /**
137
+ * Register a DOM element that needs cleanup
138
+ */
139
+ registerDOMElement(
140
+ componentId: string,
141
+ element: HTMLElement,
142
+ cleanup?: () => void
143
+ ): 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
+ });
158
+ }
159
+
160
+ /**
161
+ * Register a React root for cleanup
162
+ */
163
+ registerReactRoot(
164
+ componentId: string,
165
+ root: any,
166
+ unmountFn: () => void
167
+ ): void {
168
+ this.addResource(componentId, {
169
+ type: 'reactRoot',
170
+ id: `react-root-${componentId}`,
171
+ cleanup: unmountFn,
172
+ metadata: { root }
173
+ });
174
+ }
175
+
176
+ /**
177
+ * Register a generic cleanup callback for a component
178
+ */
179
+ registerCleanup(componentId: string, cleanup: () => void): void {
180
+ if (!this.cleanupCallbacks.has(componentId)) {
181
+ this.cleanupCallbacks.set(componentId, []);
182
+ }
183
+ this.cleanupCallbacks.get(componentId)!.push(cleanup);
184
+ }
185
+
186
+ /**
187
+ * Register a global resource (not tied to a specific component)
188
+ */
189
+ registerGlobalResource(resource: ManagedResource): void {
190
+ this.globalResources.add(resource);
191
+ }
192
+
193
+ /**
194
+ * Add a resource to be managed
195
+ */
196
+ private addResource(componentId: string, resource: ManagedResource): void {
197
+ if (!this.resources.has(componentId)) {
198
+ this.resources.set(componentId, new Set());
199
+ }
200
+ this.resources.get(componentId)!.add(resource);
201
+ }
202
+
203
+ /**
204
+ * Remove a specific resource
205
+ */
206
+ private removeResource(
207
+ componentId: string,
208
+ type: ManagedResource['type'],
209
+ id: string | number
210
+ ): void {
211
+ const componentResources = this.resources.get(componentId);
212
+ if (componentResources) {
213
+ const toRemove = Array.from(componentResources).find(
214
+ r => r.type === type && r.id === id
215
+ );
216
+ if (toRemove) {
217
+ componentResources.delete(toRemove);
218
+ }
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Clean up all resources for a specific component
224
+ */
225
+ cleanupComponent(componentId: string): void {
226
+ // Clean up tracked resources
227
+ const componentResources = this.resources.get(componentId);
228
+ if (componentResources) {
229
+ componentResources.forEach(resource => {
230
+ try {
231
+ resource.cleanup();
232
+ } catch (error) {
233
+ console.error(`Error cleaning up ${resource.type} resource:`, error);
234
+ }
235
+ });
236
+ this.resources.delete(componentId);
237
+ }
238
+
239
+ // Execute cleanup callbacks
240
+ const callbacks = this.cleanupCallbacks.get(componentId);
241
+ if (callbacks) {
242
+ callbacks.forEach(callback => {
243
+ try {
244
+ callback();
245
+ } catch (error) {
246
+ console.error('Error executing cleanup callback:', error);
247
+ }
248
+ });
249
+ this.cleanupCallbacks.delete(componentId);
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Clean up all global resources
255
+ */
256
+ cleanupGlobal(): void {
257
+ this.globalResources.forEach(resource => {
258
+ try {
259
+ resource.cleanup();
260
+ } catch (error) {
261
+ console.error(`Error cleaning up global ${resource.type} resource:`, error);
262
+ }
263
+ });
264
+ this.globalResources.clear();
265
+ }
266
+
267
+ /**
268
+ * Clean up all resources (components and global)
269
+ */
270
+ cleanupAll(): void {
271
+ // Clean up all component resources
272
+ for (const componentId of this.resources.keys()) {
273
+ this.cleanupComponent(componentId);
274
+ }
275
+
276
+ // Clean up global resources
277
+ this.cleanupGlobal();
278
+ }
279
+
280
+ /**
281
+ * Get resource statistics for debugging
282
+ */
283
+ getStats(): {
284
+ componentCount: number;
285
+ resourceCounts: Record<string, number>;
286
+ globalResourceCount: number;
287
+ } {
288
+ const resourceCounts: Record<string, number> = {};
289
+
290
+ for (const resources of this.resources.values()) {
291
+ resources.forEach(resource => {
292
+ resourceCounts[resource.type] = (resourceCounts[resource.type] || 0) + 1;
293
+ });
294
+ }
295
+
296
+ return {
297
+ componentCount: this.resources.size,
298
+ resourceCounts,
299
+ globalResourceCount: this.globalResources.size
300
+ };
301
+ }
302
+ }
303
+
304
+ // Singleton instance
305
+ export const resourceManager = new ResourceManager();