@memberjunction/react-runtime 2.75.0 → 2.76.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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +13 -0
- package/dist/compiler/component-compiler.d.ts +0 -1
- package/dist/compiler/component-compiler.d.ts.map +1 -1
- package/dist/compiler/component-compiler.js +34 -25
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +1 -2
- package/dist/runtime/prop-builder.d.ts +1 -2
- package/dist/runtime/prop-builder.d.ts.map +1 -1
- package/dist/runtime/prop-builder.js +4 -76
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utilities/core-libraries.d.ts +5 -0
- package/dist/utilities/core-libraries.d.ts.map +1 -0
- package/dist/utilities/core-libraries.js +52 -0
- package/dist/utilities/library-loader.d.ts +2 -2
- package/dist/utilities/library-loader.d.ts.map +1 -1
- package/dist/utilities/library-loader.js +27 -16
- package/package.json +4 -4
- package/samples/entities-1.js +493 -0
- package/src/compiler/component-compiler.ts +64 -35
- package/src/index.ts +0 -1
- package/src/runtime/index.ts +0 -1
- package/src/runtime/prop-builder.ts +5 -112
- package/src/types/index.ts +2 -0
- package/src/utilities/core-libraries.ts +61 -0
- package/src/utilities/library-loader.ts +45 -26
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -41,7 +41,8 @@ export function buildComponentProps(
|
|
|
41
41
|
callbacks: ComponentCallbacks = {},
|
|
42
42
|
components: Record<string, any> = {},
|
|
43
43
|
styles?: ComponentStyles,
|
|
44
|
-
options: PropBuilderOptions = {}
|
|
44
|
+
options: PropBuilderOptions = {},
|
|
45
|
+
onStateChanged?: (stateUpdate: Record<string, any>) => void
|
|
45
46
|
): ComponentProps {
|
|
46
47
|
const {
|
|
47
48
|
validate = true,
|
|
@@ -61,7 +62,8 @@ export function buildComponentProps(
|
|
|
61
62
|
utilities,
|
|
62
63
|
callbacks: normalizeCallbacks(callbacks, debounceUpdateUserState),
|
|
63
64
|
components,
|
|
64
|
-
styles: normalizeStyles(styles)
|
|
65
|
+
styles: normalizeStyles(styles),
|
|
66
|
+
onStateChanged
|
|
65
67
|
};
|
|
66
68
|
|
|
67
69
|
// Validate if enabled
|
|
@@ -124,74 +126,6 @@ export function normalizeCallbacks(callbacks: any, debounceMs: number = 3000): C
|
|
|
124
126
|
normalized.OpenEntityRecord = callbacks.OpenEntityRecord;
|
|
125
127
|
}
|
|
126
128
|
|
|
127
|
-
if (callbacks.UpdateUserState && typeof callbacks.UpdateUserState === 'function') {
|
|
128
|
-
// Create a debounced version of UpdateUserState with loop detection
|
|
129
|
-
const originalCallback = callbacks.UpdateUserState;
|
|
130
|
-
|
|
131
|
-
// Get or create a subject for this callback
|
|
132
|
-
let subject = updateUserStateSubjects.get(originalCallback);
|
|
133
|
-
if (!subject) {
|
|
134
|
-
subject = new Subject<any>();
|
|
135
|
-
updateUserStateSubjects.set(originalCallback, subject);
|
|
136
|
-
|
|
137
|
-
// Subscribe to the subject with debounce
|
|
138
|
-
const subscription = subject.pipe(
|
|
139
|
-
debounceTime(debounceMs)
|
|
140
|
-
).subscribe(state => {
|
|
141
|
-
console.log(`[Skip Component] UpdateUserState called after ${debounceMs}ms debounce`);
|
|
142
|
-
originalCallback(state);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// Store the subscription for cleanup
|
|
146
|
-
updateUserStateSubscriptions.set(originalCallback, subscription);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Get or create loop detection state
|
|
150
|
-
let loopState = loopDetectionStates.get(originalCallback);
|
|
151
|
-
if (!loopState) {
|
|
152
|
-
loopState = { count: 0, lastUpdate: 0, lastState: null };
|
|
153
|
-
loopDetectionStates.set(originalCallback, loopState);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Return a function that prevents redundant updates
|
|
157
|
-
normalized.UpdateUserState = (state: any) => {
|
|
158
|
-
// Check if this is a redundant update
|
|
159
|
-
if (loopState!.lastState && deepEqual(state, loopState!.lastState)) {
|
|
160
|
-
console.log('[Skip Component] Skipping redundant state update');
|
|
161
|
-
return; // Don't process identical state updates
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const now = Date.now();
|
|
165
|
-
const timeSinceLastUpdate = now - loopState!.lastUpdate;
|
|
166
|
-
|
|
167
|
-
// Check for rapid updates
|
|
168
|
-
if (timeSinceLastUpdate < 100) {
|
|
169
|
-
loopState!.count++;
|
|
170
|
-
|
|
171
|
-
if (loopState!.count > 5) {
|
|
172
|
-
console.error('[Skip Component] Rapid state updates detected - possible infinite loop');
|
|
173
|
-
console.error('Updates in last 100ms:', loopState!.count);
|
|
174
|
-
// Still process the update but warn
|
|
175
|
-
}
|
|
176
|
-
} else {
|
|
177
|
-
// Reset counter if more than 100ms has passed
|
|
178
|
-
loopState!.count = 0;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
loopState!.lastUpdate = now;
|
|
182
|
-
loopState!.lastState = JSON.parse(JSON.stringify(state)); // Deep clone to preserve state
|
|
183
|
-
|
|
184
|
-
console.log('[Skip Component] Processing state update');
|
|
185
|
-
|
|
186
|
-
// Push to debounce subject (which already has 3 second debounce)
|
|
187
|
-
subject!.next(state);
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (callbacks.NotifyEvent && typeof callbacks.NotifyEvent === 'function') {
|
|
192
|
-
normalized.NotifyEvent = callbacks.NotifyEvent;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
129
|
return normalized;
|
|
196
130
|
}
|
|
197
131
|
|
|
@@ -284,34 +218,7 @@ export function mergeProps(...propsList: Partial<ComponentProps>[]): ComponentPr
|
|
|
284
218
|
|
|
285
219
|
return merged;
|
|
286
220
|
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Cleanup function for prop builder resources
|
|
290
|
-
* @param callbacks - The callbacks object that was used to build props
|
|
291
|
-
*/
|
|
292
|
-
export function cleanupPropBuilder(callbacks: ComponentCallbacks): void {
|
|
293
|
-
if (callbacks.UpdateUserState && typeof callbacks.UpdateUserState === 'function') {
|
|
294
|
-
const originalCallback = callbacks.UpdateUserState;
|
|
295
|
-
|
|
296
|
-
// Unsubscribe from the subject
|
|
297
|
-
const subscription = updateUserStateSubscriptions.get(originalCallback);
|
|
298
|
-
if (subscription) {
|
|
299
|
-
subscription.unsubscribe();
|
|
300
|
-
updateUserStateSubscriptions.delete(originalCallback);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Complete and remove the subject
|
|
304
|
-
const subject = updateUserStateSubjects.get(originalCallback);
|
|
305
|
-
if (subject) {
|
|
306
|
-
subject.complete();
|
|
307
|
-
updateUserStateSubjects.delete(originalCallback);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Clear loop detection state
|
|
311
|
-
loopDetectionStates.delete(originalCallback);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
221
|
+
|
|
315
222
|
/**
|
|
316
223
|
* Creates a props transformer function
|
|
317
224
|
* @param transformations - Map of prop paths to transformer functions
|
|
@@ -372,20 +279,6 @@ export function wrapCallbacksWithLogging(
|
|
|
372
279
|
};
|
|
373
280
|
}
|
|
374
281
|
|
|
375
|
-
if (callbacks.UpdateUserState) {
|
|
376
|
-
wrapped.UpdateUserState = (state: any) => {
|
|
377
|
-
console.log(`[${componentName}] UpdateUserState called:`, state);
|
|
378
|
-
callbacks.UpdateUserState!(state);
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
if (callbacks.NotifyEvent) {
|
|
383
|
-
wrapped.NotifyEvent = (event: string, data: any) => {
|
|
384
|
-
console.log(`[${componentName}] NotifyEvent called:`, { event, data });
|
|
385
|
-
callbacks.NotifyEvent!(event, data);
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
|
|
389
282
|
return wrapped;
|
|
390
283
|
}
|
|
391
284
|
|
package/src/types/index.ts
CHANGED
|
@@ -114,6 +114,8 @@ export interface ComponentProps {
|
|
|
114
114
|
components?: Record<string, any>;
|
|
115
115
|
/** Component styles */
|
|
116
116
|
styles?: ComponentStyles;
|
|
117
|
+
/** Standard state change handler for controlled components */
|
|
118
|
+
onStateChanged?: (stateUpdate: Record<string, any>) => void;
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
/**
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ExternalLibraryConfig } from '../types/library-config';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Core runtime libraries required for the React runtime to function.
|
|
5
|
+
* These are not plugin libraries and are always loaded.
|
|
6
|
+
*/
|
|
7
|
+
export const CORE_RUNTIME_LIBRARIES: ExternalLibraryConfig[] = [
|
|
8
|
+
{
|
|
9
|
+
id: 'react',
|
|
10
|
+
name: 'react',
|
|
11
|
+
displayName: 'React',
|
|
12
|
+
category: 'runtime',
|
|
13
|
+
globalVariable: 'React',
|
|
14
|
+
version: '18.2.0',
|
|
15
|
+
cdnUrl: 'https://unpkg.com/react@18.2.0/umd/react.production.min.js',
|
|
16
|
+
description: 'React core library',
|
|
17
|
+
isEnabled: true,
|
|
18
|
+
isCore: true,
|
|
19
|
+
isRuntimeOnly: true
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 'react-dom',
|
|
23
|
+
name: 'react-dom',
|
|
24
|
+
displayName: 'ReactDOM',
|
|
25
|
+
category: 'runtime',
|
|
26
|
+
globalVariable: 'ReactDOM',
|
|
27
|
+
version: '18.2.0',
|
|
28
|
+
cdnUrl: 'https://unpkg.com/react-dom@18.2.0/umd/react-dom.production.min.js',
|
|
29
|
+
description: 'React DOM library',
|
|
30
|
+
isEnabled: true,
|
|
31
|
+
isCore: true,
|
|
32
|
+
isRuntimeOnly: true
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 'babel-standalone',
|
|
36
|
+
name: '@babel/standalone',
|
|
37
|
+
displayName: 'Babel Standalone',
|
|
38
|
+
category: 'runtime',
|
|
39
|
+
globalVariable: 'Babel',
|
|
40
|
+
version: '7.24.4',
|
|
41
|
+
cdnUrl: 'https://unpkg.com/@babel/standalone@7.24.4/babel.min.js',
|
|
42
|
+
description: 'Babel compiler for JSX transformation',
|
|
43
|
+
isEnabled: true,
|
|
44
|
+
isCore: true,
|
|
45
|
+
isRuntimeOnly: true
|
|
46
|
+
}
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get the core runtime libraries configuration
|
|
51
|
+
*/
|
|
52
|
+
export function getCoreRuntimeLibraries(): ExternalLibraryConfig[] {
|
|
53
|
+
return CORE_RUNTIME_LIBRARIES;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Check if a library ID is a core runtime library
|
|
58
|
+
*/
|
|
59
|
+
export function isCoreRuntimeLibrary(libraryId: string): boolean {
|
|
60
|
+
return CORE_RUNTIME_LIBRARIES.some(lib => lib.id === libraryId);
|
|
61
|
+
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
StandardLibraryManager
|
|
10
10
|
} from './standard-libraries';
|
|
11
11
|
import { LibraryConfiguration, ExternalLibraryConfig, LibraryLoadOptions as ConfigLoadOptions } from '../types/library-config';
|
|
12
|
+
import { getCoreRuntimeLibraries, isCoreRuntimeLibrary } from './core-libraries';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Represents a loaded script or CSS resource
|
|
@@ -52,12 +53,30 @@ export class LibraryLoader {
|
|
|
52
53
|
/**
|
|
53
54
|
* Load all standard libraries (core + UI + CSS)
|
|
54
55
|
* This is the main method that should be used by test harness and Angular wrapper
|
|
56
|
+
* @param config Optional full library configuration to replace the default
|
|
57
|
+
* @param additionalLibraries Optional additional libraries to merge with the configuration
|
|
55
58
|
*/
|
|
56
|
-
static async loadAllLibraries(
|
|
59
|
+
static async loadAllLibraries(
|
|
60
|
+
config?: LibraryConfiguration,
|
|
61
|
+
additionalLibraries?: ExternalLibraryConfig[]
|
|
62
|
+
): Promise<LibraryLoadResult> {
|
|
57
63
|
if (config) {
|
|
58
64
|
StandardLibraryManager.setConfiguration(config);
|
|
59
65
|
}
|
|
60
66
|
|
|
67
|
+
// If additional libraries are provided, merge them with the current configuration
|
|
68
|
+
if (additionalLibraries && additionalLibraries.length > 0) {
|
|
69
|
+
const currentConfig = StandardLibraryManager.getConfiguration();
|
|
70
|
+
const mergedConfig: LibraryConfiguration = {
|
|
71
|
+
libraries: [...currentConfig.libraries, ...additionalLibraries],
|
|
72
|
+
metadata: {
|
|
73
|
+
...currentConfig.metadata,
|
|
74
|
+
lastUpdated: new Date().toISOString()
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
StandardLibraryManager.setConfiguration(mergedConfig);
|
|
78
|
+
}
|
|
79
|
+
|
|
61
80
|
return this.loadLibrariesFromConfig();
|
|
62
81
|
}
|
|
63
82
|
|
|
@@ -65,55 +84,55 @@ export class LibraryLoader {
|
|
|
65
84
|
* Load libraries based on the current configuration
|
|
66
85
|
*/
|
|
67
86
|
static async loadLibrariesFromConfig(options?: ConfigLoadOptions): Promise<LibraryLoadResult> {
|
|
87
|
+
// Always load core runtime libraries first
|
|
88
|
+
const coreLibraries = getCoreRuntimeLibraries();
|
|
89
|
+
const corePromises = coreLibraries.map(lib =>
|
|
90
|
+
this.loadScript(lib.cdnUrl, lib.globalVariable)
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const coreResults = await Promise.all(corePromises);
|
|
94
|
+
const React = coreResults.find((_, i) => coreLibraries[i].globalVariable === 'React');
|
|
95
|
+
const ReactDOM = coreResults.find((_, i) => coreLibraries[i].globalVariable === 'ReactDOM');
|
|
96
|
+
const Babel = coreResults.find((_, i) => coreLibraries[i].globalVariable === 'Babel');
|
|
97
|
+
|
|
98
|
+
// Now load plugin libraries from configuration
|
|
68
99
|
const config = StandardLibraryManager.getConfiguration();
|
|
69
100
|
const enabledLibraries = StandardLibraryManager.getEnabledLibraries();
|
|
70
101
|
|
|
102
|
+
// Filter out any core runtime libraries from plugin configuration
|
|
103
|
+
let pluginLibraries = enabledLibraries.filter(lib => !isCoreRuntimeLibrary(lib.id));
|
|
104
|
+
|
|
71
105
|
// Apply options filters if provided
|
|
72
|
-
let librariesToLoad = enabledLibraries;
|
|
73
106
|
if (options) {
|
|
74
107
|
if (options.categories) {
|
|
75
|
-
|
|
108
|
+
pluginLibraries = pluginLibraries.filter(lib =>
|
|
76
109
|
options.categories!.includes(lib.category)
|
|
77
110
|
);
|
|
78
111
|
}
|
|
79
112
|
if (options.excludeRuntimeOnly) {
|
|
80
|
-
|
|
113
|
+
pluginLibraries = pluginLibraries.filter(lib => !lib.isRuntimeOnly);
|
|
81
114
|
}
|
|
82
115
|
}
|
|
83
116
|
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
const componentLibs = librariesToLoad.filter(lib => lib.category !== 'runtime');
|
|
87
|
-
|
|
88
|
-
// Load runtime libraries first (React, ReactDOM, Babel)
|
|
89
|
-
const runtimePromises = runtimeLibs.map(lib =>
|
|
90
|
-
this.loadScript(lib.cdnUrl, lib.globalVariable)
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
const runtimeResults = await Promise.all(runtimePromises);
|
|
94
|
-
const React = runtimeResults.find((_, i) => runtimeLibs[i].globalVariable === 'React');
|
|
95
|
-
const ReactDOM = runtimeResults.find((_, i) => runtimeLibs[i].globalVariable === 'ReactDOM');
|
|
96
|
-
const Babel = runtimeResults.find((_, i) => runtimeLibs[i].globalVariable === 'Babel');
|
|
97
|
-
|
|
98
|
-
// Load CSS files (non-blocking)
|
|
99
|
-
componentLibs.forEach(lib => {
|
|
117
|
+
// Load CSS files for plugin libraries (non-blocking)
|
|
118
|
+
pluginLibraries.forEach(lib => {
|
|
100
119
|
if (lib.cdnCssUrl) {
|
|
101
120
|
this.loadCSS(lib.cdnCssUrl);
|
|
102
121
|
}
|
|
103
122
|
});
|
|
104
123
|
|
|
105
|
-
// Load
|
|
106
|
-
const
|
|
124
|
+
// Load plugin libraries
|
|
125
|
+
const pluginPromises = pluginLibraries.map(lib =>
|
|
107
126
|
this.loadScript(lib.cdnUrl, lib.globalVariable)
|
|
108
127
|
);
|
|
109
128
|
|
|
110
|
-
const
|
|
129
|
+
const pluginResults = await Promise.all(pluginPromises);
|
|
111
130
|
|
|
112
|
-
// Build libraries object
|
|
131
|
+
// Build libraries object (only contains plugin libraries)
|
|
113
132
|
const libraries: StandardLibraries = {};
|
|
114
133
|
|
|
115
|
-
|
|
116
|
-
libraries[lib.globalVariable] =
|
|
134
|
+
pluginLibraries.forEach((lib, index) => {
|
|
135
|
+
libraries[lib.globalVariable] = pluginResults[index];
|
|
117
136
|
});
|
|
118
137
|
|
|
119
138
|
return {
|