@memberjunction/react-runtime 2.70.0 → 2.72.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 (32) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +24 -0
  3. package/dist/index.d.ts +5 -0
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +17 -1
  6. package/dist/utilities/component-error-analyzer.d.ts +20 -0
  7. package/dist/utilities/component-error-analyzer.d.ts.map +1 -0
  8. package/dist/utilities/component-error-analyzer.js +204 -0
  9. package/dist/utilities/component-styles.d.ts +4 -0
  10. package/dist/utilities/component-styles.d.ts.map +1 -0
  11. package/dist/utilities/component-styles.js +97 -0
  12. package/dist/utilities/index.d.ts +6 -0
  13. package/dist/utilities/index.d.ts.map +1 -0
  14. package/dist/utilities/index.js +21 -0
  15. package/dist/utilities/library-loader.d.ts +33 -0
  16. package/dist/utilities/library-loader.d.ts.map +1 -0
  17. package/dist/utilities/library-loader.js +193 -0
  18. package/dist/utilities/runtime-utilities.d.ts +10 -0
  19. package/dist/utilities/runtime-utilities.d.ts.map +1 -0
  20. package/dist/utilities/runtime-utilities.js +92 -0
  21. package/dist/utilities/standard-libraries.d.ts +27 -0
  22. package/dist/utilities/standard-libraries.d.ts.map +1 -0
  23. package/dist/utilities/standard-libraries.js +55 -0
  24. package/package.json +4 -1
  25. package/src/index.ts +31 -0
  26. package/src/utilities/component-error-analyzer.ts +315 -0
  27. package/src/utilities/component-styles.ts +121 -0
  28. package/src/utilities/index.ts +10 -0
  29. package/src/utilities/library-loader.ts +307 -0
  30. package/src/utilities/runtime-utilities.ts +122 -0
  31. package/src/utilities/standard-libraries.ts +97 -0
  32. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,307 @@
1
+ /**
2
+ * @fileoverview Library loading utilities for React runtime
3
+ * Provides methods to load and manage external JavaScript libraries and CSS
4
+ * @module @memberjunction/react-runtime/utilities
5
+ */
6
+
7
+ import {
8
+ STANDARD_LIBRARY_URLS,
9
+ StandardLibraries,
10
+ getCoreLibraryUrls,
11
+ getUILibraryUrls,
12
+ getCSSUrls
13
+ } from './standard-libraries';
14
+
15
+ /**
16
+ * Represents a loaded script or CSS resource
17
+ */
18
+ interface LoadedResource {
19
+ element: HTMLScriptElement | HTMLLinkElement;
20
+ promise: Promise<any>;
21
+ }
22
+
23
+ /**
24
+ * Options for loading libraries
25
+ */
26
+ export interface LibraryLoadOptions {
27
+ /** Load core libraries (lodash, d3, Chart.js, dayjs) */
28
+ loadCore?: boolean;
29
+ /** Load UI libraries (antd, React Bootstrap) */
30
+ loadUI?: boolean;
31
+ /** Load CSS files for UI libraries */
32
+ loadCSS?: boolean;
33
+ /** Custom library URLs to load */
34
+ customLibraries?: { url: string; globalName: string }[];
35
+ }
36
+
37
+ /**
38
+ * Result of loading libraries
39
+ */
40
+ export interface LibraryLoadResult {
41
+ React: any;
42
+ ReactDOM: any;
43
+ Babel: any;
44
+ libraries: StandardLibraries;
45
+ }
46
+
47
+ /**
48
+ * Library loader class for managing external script loading
49
+ */
50
+ export class LibraryLoader {
51
+ private static loadedResources = new Map<string, LoadedResource>();
52
+
53
+ /**
54
+ * Load all standard libraries (core + UI + CSS)
55
+ * This is the main method that should be used by test harness and Angular wrapper
56
+ */
57
+ static async loadAllLibraries(): Promise<LibraryLoadResult> {
58
+ return this.loadLibraries({
59
+ loadCore: true,
60
+ loadUI: true,
61
+ loadCSS: true
62
+ });
63
+ }
64
+
65
+ /**
66
+ * Load libraries with specific options
67
+ */
68
+ static async loadLibraries(options: LibraryLoadOptions): Promise<LibraryLoadResult> {
69
+ const {
70
+ loadCore = true,
71
+ loadUI = true,
72
+ loadCSS = true,
73
+ customLibraries = []
74
+ } = options;
75
+
76
+ // Load React ecosystem first
77
+ const [React, ReactDOM, Babel] = await Promise.all([
78
+ this.loadScript(STANDARD_LIBRARY_URLS.REACT, 'React'),
79
+ this.loadScript(STANDARD_LIBRARY_URLS.REACT_DOM, 'ReactDOM'),
80
+ this.loadScript(STANDARD_LIBRARY_URLS.BABEL, 'Babel')
81
+ ]);
82
+
83
+ // Load CSS files if requested (non-blocking)
84
+ if (loadCSS) {
85
+ getCSSUrls().forEach(url => this.loadCSS(url));
86
+ }
87
+
88
+ // Prepare library loading promises
89
+ const libraryPromises: Promise<any>[] = [];
90
+ const libraryNames: string[] = [];
91
+
92
+ // Core libraries
93
+ if (loadCore) {
94
+ const coreUrls = getCoreLibraryUrls();
95
+ coreUrls.forEach(url => {
96
+ const name = this.getLibraryNameFromUrl(url);
97
+ libraryNames.push(name);
98
+ libraryPromises.push(this.loadScript(url, name));
99
+ });
100
+ }
101
+
102
+ // UI libraries
103
+ if (loadUI) {
104
+ const uiUrls = getUILibraryUrls();
105
+ uiUrls.forEach(url => {
106
+ const name = this.getLibraryNameFromUrl(url);
107
+ libraryNames.push(name);
108
+ libraryPromises.push(this.loadScript(url, name));
109
+ });
110
+ }
111
+
112
+ // Custom libraries
113
+ customLibraries.forEach(({ url, globalName }) => {
114
+ libraryNames.push(globalName);
115
+ libraryPromises.push(this.loadScript(url, globalName));
116
+ });
117
+
118
+ // Load all libraries
119
+ const loadedLibraries = await Promise.all(libraryPromises);
120
+
121
+ // Build libraries object
122
+ const libraries: StandardLibraries = {
123
+ _: undefined // Initialize with required property
124
+ };
125
+ libraryNames.forEach((name, index) => {
126
+ // Map common names
127
+ if (name === '_') {
128
+ libraries._ = loadedLibraries[index];
129
+ } else {
130
+ libraries[name] = loadedLibraries[index];
131
+ }
132
+ });
133
+
134
+ // Ensure all standard properties exist
135
+ if (!libraries._) libraries._ = (window as any)._;
136
+ if (!libraries.d3) libraries.d3 = (window as any).d3;
137
+ if (!libraries.Chart) libraries.Chart = (window as any).Chart;
138
+ if (!libraries.dayjs) libraries.dayjs = (window as any).dayjs;
139
+ if (!libraries.antd) libraries.antd = (window as any).antd;
140
+ if (!libraries.ReactBootstrap) libraries.ReactBootstrap = (window as any).ReactBootstrap;
141
+
142
+ return {
143
+ React,
144
+ ReactDOM,
145
+ Babel,
146
+ libraries
147
+ };
148
+ }
149
+
150
+ /**
151
+ * Load a script from URL
152
+ */
153
+ private static async loadScript(url: string, globalName: string): Promise<any> {
154
+ // Check if already loaded
155
+ const existing = this.loadedResources.get(url);
156
+ if (existing) {
157
+ return existing.promise;
158
+ }
159
+
160
+ const promise = new Promise((resolve, reject) => {
161
+ // Check if global already exists
162
+ const existingGlobal = (window as any)[globalName];
163
+ if (existingGlobal) {
164
+ resolve(existingGlobal);
165
+ return;
166
+ }
167
+
168
+ // Check if script tag exists
169
+ const existingScript = document.querySelector(`script[src="${url}"]`);
170
+ if (existingScript) {
171
+ this.waitForScriptLoad(existingScript as HTMLScriptElement, globalName, resolve, reject);
172
+ return;
173
+ }
174
+
175
+ // Create new script
176
+ const script = document.createElement('script');
177
+ script.src = url;
178
+ script.async = true;
179
+ script.crossOrigin = 'anonymous';
180
+
181
+ script.onload = () => {
182
+ const global = (window as any)[globalName];
183
+ if (global) {
184
+ resolve(global);
185
+ } else {
186
+ // Some libraries may take a moment to initialize
187
+ setTimeout(() => {
188
+ const delayedGlobal = (window as any)[globalName];
189
+ if (delayedGlobal) {
190
+ resolve(delayedGlobal);
191
+ } else {
192
+ reject(new Error(`${globalName} not found after script load`));
193
+ }
194
+ }, 100);
195
+ }
196
+ };
197
+
198
+ script.onerror = () => {
199
+ reject(new Error(`Failed to load script: ${url}`));
200
+ };
201
+
202
+ document.head.appendChild(script);
203
+ });
204
+
205
+ this.loadedResources.set(url, {
206
+ element: document.querySelector(`script[src="${url}"]`)!,
207
+ promise
208
+ });
209
+
210
+ return promise;
211
+ }
212
+
213
+ /**
214
+ * Load CSS from URL
215
+ */
216
+ private static loadCSS(url: string): void {
217
+ if (this.loadedResources.has(url)) {
218
+ return;
219
+ }
220
+
221
+ const existingLink = document.querySelector(`link[href="${url}"]`);
222
+ if (existingLink) {
223
+ return;
224
+ }
225
+
226
+ const link = document.createElement('link');
227
+ link.rel = 'stylesheet';
228
+ link.href = url;
229
+ document.head.appendChild(link);
230
+
231
+ this.loadedResources.set(url, {
232
+ element: link,
233
+ promise: Promise.resolve()
234
+ });
235
+ }
236
+
237
+ /**
238
+ * Wait for existing script to load
239
+ */
240
+ private static waitForScriptLoad(
241
+ script: HTMLScriptElement,
242
+ globalName: string,
243
+ resolve: (value: any) => void,
244
+ reject: (reason: any) => void
245
+ ): void {
246
+ const checkGlobal = () => {
247
+ const global = (window as any)[globalName];
248
+ if (global) {
249
+ resolve(global);
250
+ } else {
251
+ // Retry after a short delay
252
+ setTimeout(() => {
253
+ const delayedGlobal = (window as any)[globalName];
254
+ if (delayedGlobal) {
255
+ resolve(delayedGlobal);
256
+ } else {
257
+ reject(new Error(`${globalName} not found after script load`));
258
+ }
259
+ }, 100);
260
+ }
261
+ };
262
+
263
+ // Check if already loaded
264
+ if ((script as any).complete || (script as any).readyState === 'complete') {
265
+ checkGlobal();
266
+ return;
267
+ }
268
+
269
+ // Wait for load
270
+ const loadHandler = () => {
271
+ script.removeEventListener('load', loadHandler);
272
+ checkGlobal();
273
+ };
274
+ script.addEventListener('load', loadHandler);
275
+ }
276
+
277
+ /**
278
+ * Get library name from URL for global variable mapping
279
+ */
280
+ private static getLibraryNameFromUrl(url: string): string {
281
+ // Map known URLs to their global variable names
282
+ if (url.includes('lodash')) return '_';
283
+ if (url.includes('d3')) return 'd3';
284
+ if (url.includes('Chart.js') || url.includes('chart')) return 'Chart';
285
+ if (url.includes('dayjs')) return 'dayjs';
286
+ if (url.includes('antd')) return 'antd';
287
+ if (url.includes('react-bootstrap')) return 'ReactBootstrap';
288
+
289
+ // Default: extract name from URL
290
+ const match = url.match(/\/([^\/]+)(?:\.min)?\.js$/);
291
+ return match ? match[1] : 'unknown';
292
+ }
293
+
294
+ /**
295
+ * Get all loaded resources (for cleanup)
296
+ */
297
+ static getLoadedResources(): Map<string, LoadedResource> {
298
+ return this.loadedResources;
299
+ }
300
+
301
+ /**
302
+ * Clear loaded resources cache
303
+ */
304
+ static clearCache(): void {
305
+ this.loadedResources.clear();
306
+ }
307
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * @fileoverview Runtime utilities for React components providing access to MemberJunction core functionality
3
+ * @module @memberjunction/react-runtime/utilities
4
+ */
5
+
6
+ import {
7
+ Metadata,
8
+ RunView,
9
+ RunQuery,
10
+ RunViewParams,
11
+ RunQueryParams,
12
+ LogError
13
+ } from '@memberjunction/core';
14
+
15
+ import { MJGlobal, RegisterClass } from '@memberjunction/global';
16
+ import { ComponentUtilities, SimpleMetadata, SimpleRunQuery, SimpleRunView } from '@memberjunction/interactive-component-types';
17
+
18
+ /**
19
+ * Base class for providing runtime utilities to React components.
20
+ * This class can be extended and registered with MJ's ClassFactory
21
+ * to provide custom implementations of data access methods.
22
+ */
23
+ @RegisterClass(RuntimeUtilities, 'RuntimeUtilities')
24
+ export class RuntimeUtilities {
25
+ /**
26
+ * Builds the complete utilities object for React components
27
+ * This is the main method that components will use
28
+ */
29
+ public buildUtilities(): ComponentUtilities {
30
+ const md = new Metadata();
31
+ return this.SetupUtilities(md);
32
+ }
33
+
34
+ /**
35
+ * Sets up the utilities object - copied from skip-chat implementation
36
+ */
37
+ private SetupUtilities(md: Metadata): ComponentUtilities {
38
+ const rv = new RunView();
39
+ const rq = new RunQuery();
40
+ const u: ComponentUtilities = {
41
+ md: this.CreateSimpleMetadata(md),
42
+ rv: this.CreateSimpleRunView(rv),
43
+ rq: this.CreateSimpleRunQuery(rq)
44
+ };
45
+ return u;
46
+ }
47
+
48
+ private CreateSimpleMetadata(md: Metadata): SimpleMetadata {
49
+ return {
50
+ Entities: md.Entities,
51
+ GetEntityObject: (entityName: string) => {
52
+ return md.GetEntityObject(entityName)
53
+ }
54
+ }
55
+ }
56
+
57
+ private CreateSimpleRunQuery(rq: RunQuery): SimpleRunQuery {
58
+ return {
59
+ RunQuery: async (params: RunQueryParams) => {
60
+ // Run a single query and return the results
61
+ try {
62
+ const result = await rq.RunQuery(params);
63
+ return result;
64
+ } catch (error) {
65
+ LogError(error);
66
+ throw error; // Re-throw to handle it in the caller
67
+ }
68
+ }
69
+ }
70
+ }
71
+
72
+ private CreateSimpleRunView(rv: RunView): SimpleRunView {
73
+ return {
74
+ RunView: async (params: RunViewParams) => {
75
+ // Run a single view and return the results
76
+ try {
77
+ const result = await rv.RunView(params);
78
+ return result;
79
+ } catch (error) {
80
+ LogError(error);
81
+ throw error; // Re-throw to handle it in the caller
82
+ }
83
+ },
84
+ RunViews: async (params: RunViewParams[]) => {
85
+ // Runs multiple views and returns the results
86
+ try {
87
+ const results = await rv.RunViews(params);
88
+ return results;
89
+ } catch (error) {
90
+ LogError(error);
91
+ throw error; // Re-throw to handle it in the caller
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Factory function to create RuntimeUtilities
100
+ * In a Node.js environment, this will use MJ's ClassFactory for runtime substitution
101
+ * In a browser environment, it will use the base class directly
102
+ */
103
+ export function createRuntimeUtilities(): RuntimeUtilities {
104
+ // Check if we're in a Node.js environment with MJGlobal available
105
+ if (typeof window === 'undefined') {
106
+ try {
107
+ // Use ClassFactory to get the registered class, defaulting to base RuntimeUtilities
108
+ const obj = MJGlobal.Instance.ClassFactory.CreateInstance<RuntimeUtilities>(RuntimeUtilities);
109
+ if (!obj) {
110
+ throw new Error('Failed to create RuntimeUtilities instance');
111
+ }
112
+
113
+ // Ensure the object is an instance of RuntimeUtilities
114
+ return obj;
115
+ } catch (e) {
116
+ // Fall through to default
117
+ }
118
+ }
119
+
120
+ // Default: just use the base class
121
+ return new RuntimeUtilities();
122
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * @fileoverview Standard libraries configuration for React components
3
+ * @module @memberjunction/react-runtime/utilities
4
+ */
5
+
6
+ /**
7
+ * CDN URLs for standard libraries used by React components
8
+ */
9
+ export const STANDARD_LIBRARY_URLS = {
10
+ // Core React libraries
11
+ REACT: 'https://unpkg.com/react@18/umd/react.development.js',
12
+ REACT_DOM: 'https://unpkg.com/react-dom@18/umd/react-dom.development.js',
13
+ BABEL: 'https://unpkg.com/@babel/standalone/babel.min.js',
14
+
15
+ // Data Visualization
16
+ D3: 'https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js',
17
+ CHART_JS: 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.0/chart.umd.js',
18
+
19
+ // Utilities
20
+ LODASH: 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js',
21
+ DAYJS: 'https://unpkg.com/dayjs@1.11.10/dayjs.min.js',
22
+
23
+ // UI Libraries (optional)
24
+ ANTD: 'https://unpkg.com/antd@5.11.5/dist/antd.min.js',
25
+ REACT_BOOTSTRAP: 'https://unpkg.com/react-bootstrap@2.9.1/dist/react-bootstrap.min.js',
26
+
27
+ // CSS
28
+ ANTD_CSS: 'https://unpkg.com/antd@5.11.5/dist/reset.css',
29
+ BOOTSTRAP_CSS: 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css'
30
+ };
31
+
32
+ /**
33
+ * Interface for standard libraries available to React components
34
+ */
35
+ export interface StandardLibraries {
36
+ _: any; // lodash
37
+ d3?: any;
38
+ Chart?: any;
39
+ dayjs?: any;
40
+ antd?: any;
41
+ ReactBootstrap?: any;
42
+ [key: string]: any; // Allow additional libraries
43
+ }
44
+
45
+ /**
46
+ * Get the list of core libraries that should always be loaded
47
+ */
48
+ export function getCoreLibraryUrls(): string[] {
49
+ return [
50
+ STANDARD_LIBRARY_URLS.LODASH,
51
+ STANDARD_LIBRARY_URLS.D3,
52
+ STANDARD_LIBRARY_URLS.CHART_JS,
53
+ STANDARD_LIBRARY_URLS.DAYJS
54
+ ];
55
+ }
56
+
57
+ /**
58
+ * Get the list of optional UI library URLs
59
+ */
60
+ export function getUILibraryUrls(): string[] {
61
+ return [
62
+ STANDARD_LIBRARY_URLS.ANTD,
63
+ STANDARD_LIBRARY_URLS.REACT_BOOTSTRAP
64
+ ];
65
+ }
66
+
67
+ /**
68
+ * Get the list of CSS URLs for UI libraries
69
+ */
70
+ export function getCSSUrls(): string[] {
71
+ return [
72
+ STANDARD_LIBRARY_URLS.ANTD_CSS,
73
+ STANDARD_LIBRARY_URLS.BOOTSTRAP_CSS
74
+ ];
75
+ }
76
+
77
+ /**
78
+ * Creates a standard libraries object for browser environments
79
+ * This assumes the libraries are already loaded as globals
80
+ */
81
+ export function createStandardLibraries(): StandardLibraries {
82
+ if (typeof window === 'undefined') {
83
+ // Return empty object in Node.js environments
84
+ return {
85
+ _: undefined
86
+ };
87
+ }
88
+
89
+ return {
90
+ _: (window as any)._,
91
+ d3: (window as any).d3,
92
+ Chart: (window as any).Chart,
93
+ dayjs: (window as any).dayjs,
94
+ antd: (window as any).antd,
95
+ ReactBootstrap: (window as any).ReactBootstrap
96
+ };
97
+ }