@memberjunction/react-runtime 2.93.0 → 2.94.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 +6 -6
- package/CHANGELOG.md +16 -0
- package/README.md +180 -2
- package/dist/compiler/component-compiler.d.ts.map +1 -1
- package/dist/compiler/component-compiler.js +206 -57
- package/dist/compiler/component-compiler.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -4
- package/dist/index.js.map +1 -1
- package/dist/registry/component-registry-service.d.ts +4 -3
- package/dist/registry/component-registry-service.d.ts.map +1 -1
- package/dist/registry/component-registry-service.js +27 -11
- package/dist/registry/component-registry-service.js.map +1 -1
- package/dist/registry/component-registry.d.ts +4 -3
- package/dist/registry/component-registry.d.ts.map +1 -1
- package/dist/registry/component-registry.js.map +1 -1
- package/dist/registry/component-resolver.d.ts +2 -1
- package/dist/registry/component-resolver.d.ts.map +1 -1
- package/dist/registry/component-resolver.js +42 -10
- package/dist/registry/component-resolver.js.map +1 -1
- package/dist/runtime/component-hierarchy.d.ts.map +1 -1
- package/dist/runtime/component-hierarchy.js +8 -2
- package/dist/runtime/component-hierarchy.js.map +1 -1
- package/dist/runtime/prop-builder.d.ts +2 -2
- package/dist/runtime/prop-builder.d.ts.map +1 -1
- package/dist/runtime/prop-builder.js +32 -14
- package/dist/runtime/prop-builder.js.map +1 -1
- package/dist/runtime.umd.js +1 -1
- package/dist/types/dependency-types.d.ts +62 -0
- package/dist/types/dependency-types.d.ts.map +1 -0
- package/dist/types/dependency-types.js +3 -0
- package/dist/types/dependency-types.js.map +1 -0
- package/dist/types/index.d.ts +8 -10
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utilities/index.d.ts +1 -0
- package/dist/utilities/index.d.ts.map +1 -1
- package/dist/utilities/index.js +1 -0
- package/dist/utilities/index.js.map +1 -1
- package/dist/utilities/library-dependency-resolver.d.ts +19 -0
- package/dist/utilities/library-dependency-resolver.d.ts.map +1 -0
- package/dist/utilities/library-dependency-resolver.js +410 -0
- package/dist/utilities/library-dependency-resolver.js.map +1 -0
- package/dist/utilities/library-loader.d.ts +9 -0
- package/dist/utilities/library-loader.d.ts.map +1 -1
- package/dist/utilities/library-loader.js +143 -0
- package/dist/utilities/library-loader.js.map +1 -1
- package/package.json +5 -5
- package/src/compiler/component-compiler.ts +227 -77
- package/src/index.ts +18 -4
- package/src/registry/component-registry-service.ts +32 -14
- package/src/registry/component-registry.ts +8 -7
- package/src/registry/component-resolver.ts +51 -10
- package/src/runtime/component-hierarchy.ts +12 -4
- package/src/runtime/prop-builder.ts +38 -18
- package/src/types/dependency-types.ts +110 -0
- package/src/types/index.ts +17 -21
- package/src/utilities/index.ts +1 -0
- package/src/utilities/library-dependency-resolver.ts +603 -0
- package/src/utilities/library-loader.ts +252 -0
|
@@ -13,7 +13,9 @@ import {
|
|
|
13
13
|
ComponentError,
|
|
14
14
|
RuntimeContext
|
|
15
15
|
} from '../types';
|
|
16
|
+
import { ComponentStyles, ComponentObject } from '@memberjunction/interactive-component-types';
|
|
16
17
|
import { LibraryRegistry } from '../utilities/library-registry';
|
|
18
|
+
import { LibraryLoader } from '../utilities/library-loader';
|
|
17
19
|
import { ComponentLibraryEntity } from '@memberjunction/core-entities';
|
|
18
20
|
|
|
19
21
|
/**
|
|
@@ -27,7 +29,8 @@ const DEFAULT_COMPILER_CONFIG: CompilerConfig = {
|
|
|
27
29
|
minify: false,
|
|
28
30
|
sourceMaps: false,
|
|
29
31
|
cache: true,
|
|
30
|
-
maxCacheSize: 100
|
|
32
|
+
maxCacheSize: 100,
|
|
33
|
+
debug: false
|
|
31
34
|
};
|
|
32
35
|
|
|
33
36
|
/**
|
|
@@ -100,7 +103,7 @@ export class ComponentCompiler {
|
|
|
100
103
|
|
|
101
104
|
// Build the compiled component
|
|
102
105
|
const compiledComponent: CompiledComponent = {
|
|
103
|
-
|
|
106
|
+
factory: componentFactory,
|
|
104
107
|
id: this.generateComponentId(options.componentName),
|
|
105
108
|
name: options.componentName,
|
|
106
109
|
compiledAt: new Date(),
|
|
@@ -191,6 +194,7 @@ export class ComponentCompiler {
|
|
|
191
194
|
useState, useEffect, useCallback, useMemo, useRef, useContext, useReducer, useLayoutEffect,
|
|
192
195
|
libraries, styles, console, components
|
|
193
196
|
) {
|
|
197
|
+
// Parse the component code once to get the component definition
|
|
194
198
|
${libraryDeclarations ? libraryDeclarations + '\n ' : ''}${componentDeclarations ? componentDeclarations + '\n ' : ''}${componentCode}
|
|
195
199
|
|
|
196
200
|
// Ensure the component exists
|
|
@@ -198,16 +202,137 @@ export class ComponentCompiler {
|
|
|
198
202
|
throw new Error('Component "${componentName}" is not defined in the provided code');
|
|
199
203
|
}
|
|
200
204
|
|
|
201
|
-
//
|
|
205
|
+
// Store the component in a variable so we don't lose it
|
|
206
|
+
const UserComponent = ${componentName};
|
|
207
|
+
|
|
208
|
+
// Debug logging to understand what we're getting
|
|
209
|
+
console.log('Component ${componentName} type:', typeof UserComponent);
|
|
210
|
+
if (typeof UserComponent === 'object' && UserComponent !== null) {
|
|
211
|
+
console.log('Component ${componentName} keys:', Object.keys(UserComponent));
|
|
212
|
+
console.log('Component ${componentName} has .component:', 'component' in UserComponent);
|
|
213
|
+
if ('component' in UserComponent) {
|
|
214
|
+
console.log('Component ${componentName}.component type:', typeof UserComponent.component);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Check if the component is already a ComponentObject (has a .component property)
|
|
219
|
+
// If so, extract the actual React component
|
|
220
|
+
const ActualComponent = (typeof UserComponent === 'object' && UserComponent !== null && 'component' in UserComponent)
|
|
221
|
+
? UserComponent.component
|
|
222
|
+
: UserComponent;
|
|
223
|
+
|
|
224
|
+
// Validate that we have a function (React component)
|
|
225
|
+
if (typeof ActualComponent !== 'function') {
|
|
226
|
+
console.error('Invalid component type for ${componentName}:', typeof ActualComponent);
|
|
227
|
+
console.error('ActualComponent value:', ActualComponent);
|
|
228
|
+
console.error('Original UserComponent value:', UserComponent);
|
|
229
|
+
throw new Error('Component "${componentName}" must be a function (React component) or an object with a .component property that is a function. Got: ' + typeof ActualComponent);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Create a fresh method registry for each factory call
|
|
233
|
+
const methodRegistry = new Map();
|
|
234
|
+
|
|
235
|
+
// Create a wrapper component that provides RegisterMethod in callbacks
|
|
236
|
+
const ComponentWithMethodRegistry = React.forwardRef((props, ref) => {
|
|
237
|
+
// Register methods on mount
|
|
238
|
+
React.useEffect(() => {
|
|
239
|
+
// Clear previous methods
|
|
240
|
+
methodRegistry.clear();
|
|
241
|
+
|
|
242
|
+
// Provide RegisterMethod callback if callbacks exist
|
|
243
|
+
if (props.callbacks && typeof props.callbacks.RegisterMethod === 'function') {
|
|
244
|
+
// Component can now register its methods
|
|
245
|
+
// This will be called from within the component
|
|
246
|
+
}
|
|
247
|
+
}, [props.callbacks]);
|
|
248
|
+
|
|
249
|
+
// Create enhanced callbacks with RegisterMethod
|
|
250
|
+
const enhancedCallbacks = React.useMemo(() => {
|
|
251
|
+
if (!props.callbacks) return {};
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
...props.callbacks,
|
|
255
|
+
RegisterMethod: (methodName, handler) => {
|
|
256
|
+
if (methodName && handler) {
|
|
257
|
+
methodRegistry.set(methodName, handler);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
}, [props.callbacks]);
|
|
262
|
+
|
|
263
|
+
// Render the original component with enhanced callbacks
|
|
264
|
+
return React.createElement(ActualComponent, {
|
|
265
|
+
...props,
|
|
266
|
+
callbacks: enhancedCallbacks
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
ComponentWithMethodRegistry.displayName = '${componentName}WithMethods';
|
|
271
|
+
|
|
272
|
+
// Return the component object with method access
|
|
202
273
|
return {
|
|
203
|
-
component:
|
|
274
|
+
component: ComponentWithMethodRegistry,
|
|
275
|
+
|
|
276
|
+
// Legacy methods for backward compatibility
|
|
204
277
|
print: function() {
|
|
205
|
-
|
|
278
|
+
const printMethod = methodRegistry.get('print');
|
|
279
|
+
if (printMethod) {
|
|
280
|
+
printMethod();
|
|
281
|
+
} else if (typeof window !== 'undefined' && window.print) {
|
|
206
282
|
window.print();
|
|
207
283
|
}
|
|
208
284
|
},
|
|
209
285
|
refresh: function(data) {
|
|
286
|
+
const refreshMethod = methodRegistry.get('refresh');
|
|
287
|
+
if (refreshMethod) {
|
|
288
|
+
refreshMethod(data);
|
|
289
|
+
}
|
|
210
290
|
// Refresh functionality is handled by the host environment
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
// Standard method accessors with type safety
|
|
294
|
+
getCurrentDataState: function() {
|
|
295
|
+
const method = methodRegistry.get('getCurrentDataState');
|
|
296
|
+
return method ? method() : undefined;
|
|
297
|
+
},
|
|
298
|
+
getDataStateHistory: function() {
|
|
299
|
+
const method = methodRegistry.get('getDataStateHistory');
|
|
300
|
+
return method ? method() : [];
|
|
301
|
+
},
|
|
302
|
+
validate: function() {
|
|
303
|
+
const method = methodRegistry.get('validate');
|
|
304
|
+
return method ? method() : true;
|
|
305
|
+
},
|
|
306
|
+
isDirty: function() {
|
|
307
|
+
const method = methodRegistry.get('isDirty');
|
|
308
|
+
return method ? method() : false;
|
|
309
|
+
},
|
|
310
|
+
reset: function() {
|
|
311
|
+
const method = methodRegistry.get('reset');
|
|
312
|
+
if (method) method();
|
|
313
|
+
},
|
|
314
|
+
scrollTo: function(target) {
|
|
315
|
+
const method = methodRegistry.get('scrollTo');
|
|
316
|
+
if (method) method(target);
|
|
317
|
+
},
|
|
318
|
+
focus: function(target) {
|
|
319
|
+
const method = methodRegistry.get('focus');
|
|
320
|
+
if (method) method(target);
|
|
321
|
+
},
|
|
322
|
+
|
|
323
|
+
// Generic method invoker for custom methods
|
|
324
|
+
invokeMethod: function(methodName, ...args) {
|
|
325
|
+
const method = methodRegistry.get(methodName);
|
|
326
|
+
if (method) {
|
|
327
|
+
return method(...args);
|
|
328
|
+
}
|
|
329
|
+
console.warn(\`Method '\${methodName}' is not registered on component ${componentName}\`);
|
|
330
|
+
return undefined;
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
// Check if a method exists
|
|
334
|
+
hasMethod: function(methodName) {
|
|
335
|
+
return methodRegistry.has(methodName);
|
|
211
336
|
}
|
|
212
337
|
};
|
|
213
338
|
}
|
|
@@ -215,21 +340,25 @@ export class ComponentCompiler {
|
|
|
215
340
|
}
|
|
216
341
|
|
|
217
342
|
/**
|
|
218
|
-
* Load required libraries from the registry
|
|
343
|
+
* Load required libraries from the registry with dependency resolution
|
|
219
344
|
* @param libraries - Array of library dependencies
|
|
220
|
-
* @param
|
|
345
|
+
* @param componentLibraries - All available component libraries for dependency resolution
|
|
221
346
|
* @returns Map of loaded libraries
|
|
222
347
|
*/
|
|
223
348
|
private async loadRequiredLibraries(libraries: any[], componentLibraries: ComponentLibraryEntity[]): Promise<Map<string, any>> {
|
|
224
349
|
const loadedLibraries = new Map<string, any>();
|
|
225
350
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
351
|
+
if (this.config.debug) {
|
|
352
|
+
console.log('🔍 loadRequiredLibraries called with:', {
|
|
353
|
+
librariesCount: libraries?.length || 0,
|
|
354
|
+
libraries: libraries?.map(l => ({ name: l.name, version: l.version, globalVariable: l.globalVariable }))
|
|
355
|
+
});
|
|
356
|
+
}
|
|
230
357
|
|
|
231
358
|
if (!libraries || libraries.length === 0) {
|
|
232
|
-
|
|
359
|
+
if (this.config.debug) {
|
|
360
|
+
console.log('📚 No libraries to load, returning empty map');
|
|
361
|
+
}
|
|
233
362
|
return loadedLibraries;
|
|
234
363
|
}
|
|
235
364
|
|
|
@@ -246,75 +375,90 @@ export class ComponentCompiler {
|
|
|
246
375
|
console.warn('⚠️ No componentLibraries provided for LibraryRegistry initialization');
|
|
247
376
|
}
|
|
248
377
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (!isApproved) {
|
|
257
|
-
console.error(` ❌ Library '${lib.name}' is not approved`);
|
|
258
|
-
throw new Error(`Library '${lib.name}' is not approved. Only approved libraries can be used.`);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Get library definition for complete info
|
|
262
|
-
const libraryDef = LibraryRegistry.getLibrary(lib.name);
|
|
263
|
-
console.log(` ✓ Library definition found for ${lib.name}: ${!!libraryDef}`);
|
|
264
|
-
|
|
265
|
-
if (!libraryDef) {
|
|
266
|
-
console.error(` ❌ Library '${lib.name}' not found in registry`);
|
|
267
|
-
throw new Error(`Library '${lib.name}' not found in registry`);
|
|
268
|
-
}
|
|
378
|
+
// Extract library names from the requested libraries
|
|
379
|
+
const libraryNames = libraries.map(lib => lib.name);
|
|
380
|
+
|
|
381
|
+
if (this.config.debug) {
|
|
382
|
+
console.log('📦 Using dependency-aware loading for libraries:', libraryNames);
|
|
383
|
+
}
|
|
269
384
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
console.error(` ❌ No CDN URL found for library '${lib.name}' version '${lib.version || 'default'}'`);
|
|
279
|
-
throw new Error(`No CDN URL found for library '${lib.name}' version '${lib.version || 'default'}'`);
|
|
280
|
-
}
|
|
385
|
+
try {
|
|
386
|
+
// Use the new dependency-aware loading
|
|
387
|
+
const loadedLibraryMap = await LibraryLoader.loadLibrariesWithDependencies(
|
|
388
|
+
libraryNames,
|
|
389
|
+
componentLibraries,
|
|
390
|
+
'component-compiler',
|
|
391
|
+
{ debug: this.config.debug }
|
|
392
|
+
);
|
|
281
393
|
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
394
|
+
// Map the results to match the expected format
|
|
395
|
+
// We need to map from library name to global variable
|
|
396
|
+
for (const lib of libraries) {
|
|
397
|
+
// Check if library is approved first
|
|
398
|
+
const isApproved = LibraryRegistry.isApproved(lib.name);
|
|
399
|
+
if (!isApproved) {
|
|
400
|
+
console.error(`❌ Library '${lib.name}' is not approved`);
|
|
401
|
+
throw new Error(`Library '${lib.name}' is not approved. Only approved libraries can be used.`);
|
|
402
|
+
}
|
|
288
403
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
404
|
+
// Get the loaded library from the map
|
|
405
|
+
const loadedValue = loadedLibraryMap.get(lib.name);
|
|
406
|
+
|
|
407
|
+
if (loadedValue) {
|
|
408
|
+
// Store by global variable name for component access
|
|
409
|
+
loadedLibraries.set(lib.globalVariable, loadedValue);
|
|
410
|
+
if (this.config.debug) {
|
|
411
|
+
console.log(`✅ Mapped ${lib.name} to global variable ${lib.globalVariable}`);
|
|
412
|
+
}
|
|
413
|
+
} else {
|
|
414
|
+
// Fallback: check if it's already globally available (might be a dependency)
|
|
415
|
+
const globalValue = (window as any)[lib.globalVariable];
|
|
416
|
+
if (globalValue) {
|
|
417
|
+
loadedLibraries.set(lib.globalVariable, globalValue);
|
|
418
|
+
if (this.config.debug) {
|
|
419
|
+
console.log(`✅ Found ${lib.name} already loaded as ${lib.globalVariable}`);
|
|
420
|
+
}
|
|
421
|
+
} else {
|
|
422
|
+
console.error(`❌ Library '${lib.name}' failed to load`);
|
|
423
|
+
throw new Error(`Library '${lib.name}' failed to load or did not expose '${lib.globalVariable}'`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
293
426
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
console.log(` 📥 Loading script from CDN for ${lib.name}...`);
|
|
297
|
-
await this.loadScript(cdnUrl!, lib.globalVariable);
|
|
427
|
+
} catch (error: any) {
|
|
428
|
+
console.error('Failed to load libraries with dependencies:', error);
|
|
298
429
|
|
|
299
|
-
//
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
console.log(` ✓ Library ${lib.name} loaded successfully, global variable ${lib.globalVariable} is:`, typeof libraryValue);
|
|
430
|
+
// Fallback to old loading method if dependency resolution fails
|
|
431
|
+
if (this.config.debug) {
|
|
432
|
+
console.warn('⚠️ Falling back to non-dependency-aware loading due to error');
|
|
433
|
+
}
|
|
304
434
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
435
|
+
// Load each library independently (old method)
|
|
436
|
+
for (const lib of libraries) {
|
|
437
|
+
if ((window as any)[lib.globalVariable]) {
|
|
438
|
+
loadedLibraries.set(lib.globalVariable, (window as any)[lib.globalVariable]);
|
|
439
|
+
} else {
|
|
440
|
+
// Try to load using LibraryRegistry
|
|
441
|
+
const libraryDef = LibraryRegistry.getLibrary(lib.name);
|
|
442
|
+
if (libraryDef) {
|
|
443
|
+
const resolvedVersion = LibraryRegistry.resolveVersion(lib.name, lib.version);
|
|
444
|
+
const cdnUrl = LibraryRegistry.getCdnUrl(lib.name, resolvedVersion);
|
|
445
|
+
|
|
446
|
+
if (cdnUrl) {
|
|
447
|
+
await this.loadScript(cdnUrl, lib.globalVariable);
|
|
448
|
+
const libraryValue = (window as any)[lib.globalVariable];
|
|
449
|
+
if (libraryValue) {
|
|
450
|
+
loadedLibraries.set(lib.globalVariable, libraryValue);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
311
455
|
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
await Promise.all(loadPromises);
|
|
456
|
+
}
|
|
315
457
|
|
|
316
|
-
|
|
317
|
-
|
|
458
|
+
if (this.config.debug) {
|
|
459
|
+
console.log(`✅ All libraries loaded successfully. Total: ${loadedLibraries.size}`);
|
|
460
|
+
console.log('📚 Loaded libraries map:', Array.from(loadedLibraries.keys()));
|
|
461
|
+
}
|
|
318
462
|
|
|
319
463
|
return loadedLibraries;
|
|
320
464
|
}
|
|
@@ -390,12 +534,18 @@ export class ComponentCompiler {
|
|
|
390
534
|
|
|
391
535
|
const checkGlobal = () => {
|
|
392
536
|
if ((window as any)[globalName]) {
|
|
393
|
-
|
|
537
|
+
if (this.config.debug) {
|
|
538
|
+
console.log(` ✓ Global variable ${globalName} found after ${attempts * checkInterval}ms`);
|
|
539
|
+
}
|
|
394
540
|
resolve();
|
|
395
541
|
} else if (attempts >= maxAttempts) {
|
|
396
542
|
// Final check - some libraries might use a different global name pattern
|
|
397
|
-
|
|
398
|
-
|
|
543
|
+
if (this.config.debug) {
|
|
544
|
+
console.error(` ❌ ${globalName} not found after ${attempts * checkInterval}ms`);
|
|
545
|
+
// Only log matching property names, not the entire window object
|
|
546
|
+
const matchingKeys = Object.keys(window).filter(k => k.toLowerCase().includes(globalName.toLowerCase()));
|
|
547
|
+
console.log(` ℹ️ Matching window properties: ${matchingKeys.join(', ') || 'none'}`);
|
|
548
|
+
}
|
|
399
549
|
reject(new Error(`${globalName} not found after loading script from ${url}`));
|
|
400
550
|
} else {
|
|
401
551
|
attempts++;
|
|
@@ -426,7 +576,7 @@ export class ComponentCompiler {
|
|
|
426
576
|
transpiledCode: string,
|
|
427
577
|
componentName: string,
|
|
428
578
|
loadedLibraries: Map<string, any>
|
|
429
|
-
):
|
|
579
|
+
): (context: RuntimeContext, styles?: ComponentStyles) => ComponentObject {
|
|
430
580
|
try {
|
|
431
581
|
// Create the factory function with all React hooks
|
|
432
582
|
const factoryCreator = new Function(
|
package/src/index.ts
CHANGED
|
@@ -139,6 +139,7 @@ export const DEFAULT_CONFIGS = {
|
|
|
139
139
|
* @param babelInstance - Babel standalone instance for compilation
|
|
140
140
|
* @param config - Optional configuration overrides
|
|
141
141
|
* @param runtimeContext - Optional runtime context for registry-based components
|
|
142
|
+
* @param debug - Enable debug logging (defaults to false)
|
|
142
143
|
* @returns Object containing compiler, registry, and resolver instances
|
|
143
144
|
*/
|
|
144
145
|
export function createReactRuntime(
|
|
@@ -147,18 +148,31 @@ export function createReactRuntime(
|
|
|
147
148
|
compiler?: Partial<import('./types').CompilerConfig>;
|
|
148
149
|
registry?: Partial<import('./types').RegistryConfig>;
|
|
149
150
|
},
|
|
150
|
-
runtimeContext?: import('./types').RuntimeContext
|
|
151
|
+
runtimeContext?: import('./types').RuntimeContext,
|
|
152
|
+
debug: boolean = false
|
|
151
153
|
) {
|
|
152
|
-
|
|
154
|
+
// Merge debug flag into configs
|
|
155
|
+
const compilerConfig = {
|
|
156
|
+
...config?.compiler,
|
|
157
|
+
debug: config?.compiler?.debug ?? debug
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const registryConfig = {
|
|
161
|
+
...config?.registry,
|
|
162
|
+
debug: config?.registry?.debug ?? debug
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const compiler = new ComponentCompiler(compilerConfig);
|
|
153
166
|
compiler.setBabelInstance(babelInstance);
|
|
154
167
|
|
|
155
|
-
const registry = new ComponentRegistry(
|
|
168
|
+
const registry = new ComponentRegistry(registryConfig);
|
|
156
169
|
const resolver = new ComponentResolver(registry, compiler, runtimeContext);
|
|
157
170
|
|
|
158
171
|
return {
|
|
159
172
|
compiler,
|
|
160
173
|
registry,
|
|
161
174
|
resolver,
|
|
162
|
-
version: VERSION
|
|
175
|
+
version: VERSION,
|
|
176
|
+
debug
|
|
163
177
|
};
|
|
164
178
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @module @memberjunction/react-runtime/registry
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { ComponentSpec } from '@memberjunction/interactive-component-types';
|
|
6
|
+
import { ComponentSpec, ComponentObject } from '@memberjunction/interactive-component-types';
|
|
7
7
|
import { ComponentCompiler } from '../compiler';
|
|
8
8
|
import { RuntimeContext } from '../types';
|
|
9
9
|
import {
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
* Cached compiled component with metadata
|
|
27
27
|
*/
|
|
28
28
|
interface CachedCompiledComponent {
|
|
29
|
-
component:
|
|
29
|
+
component: ComponentObject;
|
|
30
30
|
metadata: RegistryComponentResponse['metadata'];
|
|
31
31
|
compiledAt: Date;
|
|
32
32
|
lastUsed: Date;
|
|
@@ -48,13 +48,16 @@ export class ComponentRegistryService {
|
|
|
48
48
|
private runtimeContext: RuntimeContext;
|
|
49
49
|
private componentEngine = ComponentMetadataEngine.Instance;
|
|
50
50
|
private registryProviders = new Map<string, RegistryProvider>();
|
|
51
|
+
private debug: boolean = false;
|
|
51
52
|
|
|
52
53
|
private constructor(
|
|
53
54
|
compiler: ComponentCompiler,
|
|
54
|
-
runtimeContext: RuntimeContext
|
|
55
|
+
runtimeContext: RuntimeContext,
|
|
56
|
+
debug: boolean = false
|
|
55
57
|
) {
|
|
56
58
|
this.compiler = compiler;
|
|
57
59
|
this.runtimeContext = runtimeContext;
|
|
60
|
+
this.debug = debug;
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
/**
|
|
@@ -62,10 +65,11 @@ export class ComponentRegistryService {
|
|
|
62
65
|
*/
|
|
63
66
|
static getInstance(
|
|
64
67
|
compiler: ComponentCompiler,
|
|
65
|
-
context: RuntimeContext
|
|
68
|
+
context: RuntimeContext,
|
|
69
|
+
debug: boolean = false
|
|
66
70
|
): ComponentRegistryService {
|
|
67
71
|
if (!ComponentRegistryService.instance) {
|
|
68
|
-
ComponentRegistryService.instance = new ComponentRegistryService(compiler, context);
|
|
72
|
+
ComponentRegistryService.instance = new ComponentRegistryService(compiler, context, debug);
|
|
69
73
|
}
|
|
70
74
|
return ComponentRegistryService.instance;
|
|
71
75
|
}
|
|
@@ -85,7 +89,7 @@ export class ComponentRegistryService {
|
|
|
85
89
|
componentId: string,
|
|
86
90
|
referenceId?: string,
|
|
87
91
|
contextUser?: UserInfo
|
|
88
|
-
): Promise<
|
|
92
|
+
): Promise<ComponentObject> {
|
|
89
93
|
await this.initialize(contextUser);
|
|
90
94
|
|
|
91
95
|
// Find component in metadata
|
|
@@ -107,12 +111,16 @@ export class ComponentRegistryService {
|
|
|
107
111
|
this.addComponentReference(key, referenceId);
|
|
108
112
|
}
|
|
109
113
|
|
|
110
|
-
|
|
114
|
+
if (this.debug) {
|
|
115
|
+
console.log(`✅ Reusing compiled component from cache: ${key} (use count: ${cached.useCount})`);
|
|
116
|
+
}
|
|
111
117
|
return cached.component;
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
// Not in cache, need to load and compile
|
|
115
|
-
|
|
121
|
+
if (this.debug) {
|
|
122
|
+
console.log(`🔄 Loading and compiling component: ${key}`);
|
|
123
|
+
}
|
|
116
124
|
|
|
117
125
|
// Get the component specification
|
|
118
126
|
const spec = await this.getComponentSpec(componentId, contextUser);
|
|
@@ -152,7 +160,7 @@ export class ComponentRegistryService {
|
|
|
152
160
|
if (!compilationResult.component) {
|
|
153
161
|
throw new Error(`Component compilation succeeded but no component returned`);
|
|
154
162
|
}
|
|
155
|
-
const compiledComponent = compilationResult.component.
|
|
163
|
+
const compiledComponent = compilationResult.component.factory(this.runtimeContext);
|
|
156
164
|
this.compiledComponentCache.set(key, {
|
|
157
165
|
component: compiledComponent,
|
|
158
166
|
metadata,
|
|
@@ -194,7 +202,9 @@ export class ComponentRegistryService {
|
|
|
194
202
|
// EXTERNAL: Check if we have a cached version
|
|
195
203
|
if (component.Specification && component.LastSyncedAt) {
|
|
196
204
|
// For now, always use cached version (no expiration)
|
|
197
|
-
|
|
205
|
+
if (this.debug) {
|
|
206
|
+
console.log(`Using cached external component: ${component.Name} (synced: ${component.LastSyncedAt})`);
|
|
207
|
+
}
|
|
198
208
|
return JSON.parse(component.Specification);
|
|
199
209
|
}
|
|
200
210
|
|
|
@@ -245,7 +255,9 @@ export class ComponentRegistryService {
|
|
|
245
255
|
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
246
256
|
}
|
|
247
257
|
|
|
248
|
-
|
|
258
|
+
if (this.debug) {
|
|
259
|
+
console.log(`Fetching from external registry: ${url}`);
|
|
260
|
+
}
|
|
249
261
|
|
|
250
262
|
const response = await fetch(url, { headers });
|
|
251
263
|
|
|
@@ -339,7 +351,9 @@ export class ComponentRegistryService {
|
|
|
339
351
|
throw new Error(`Failed to save cached component: ${componentEntity.Name}\n${componentEntity.LatestResult.Message || componentEntity.LatestResult.Error || componentEntity.LatestResult.Errors?.join(',')}`);
|
|
340
352
|
}
|
|
341
353
|
|
|
342
|
-
|
|
354
|
+
if (this.debug) {
|
|
355
|
+
console.log(`Cached external component: ${componentEntity.Name} at ${componentEntity.LastSyncedAt}`);
|
|
356
|
+
}
|
|
343
357
|
|
|
344
358
|
// Refresh metadata cache after saving
|
|
345
359
|
await this.componentEngine.Config(true, contextUser);
|
|
@@ -496,7 +510,9 @@ export class ComponentRegistryService {
|
|
|
496
510
|
const evictionThreshold = 5 * 60 * 1000; // 5 minutes
|
|
497
511
|
|
|
498
512
|
if (timeSinceLastUse > evictionThreshold) {
|
|
499
|
-
|
|
513
|
+
if (this.debug) {
|
|
514
|
+
console.log(`🗑️ Evicting unused component from cache: ${componentKey}`);
|
|
515
|
+
}
|
|
500
516
|
this.compiledComponentCache.delete(componentKey);
|
|
501
517
|
}
|
|
502
518
|
}
|
|
@@ -539,7 +555,9 @@ export class ComponentRegistryService {
|
|
|
539
555
|
* Clear all caches
|
|
540
556
|
*/
|
|
541
557
|
clearCache(): void {
|
|
542
|
-
|
|
558
|
+
if (this.debug) {
|
|
559
|
+
console.log('🧹 Clearing all component caches');
|
|
560
|
+
}
|
|
543
561
|
this.compiledComponentCache.clear();
|
|
544
562
|
this.componentReferences.clear();
|
|
545
563
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
ComponentMetadata,
|
|
10
10
|
RegistryConfig
|
|
11
11
|
} from '../types';
|
|
12
|
+
import { ComponentObject } from '@memberjunction/interactive-component-types';
|
|
12
13
|
import { resourceManager } from '../utilities/resource-manager';
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -49,7 +50,7 @@ export class ComponentRegistry {
|
|
|
49
50
|
/**
|
|
50
51
|
* Registers a compiled component
|
|
51
52
|
* @param name - Component name
|
|
52
|
-
* @param component - Compiled component
|
|
53
|
+
* @param component - Compiled component object
|
|
53
54
|
* @param namespace - Component namespace (default: 'Global')
|
|
54
55
|
* @param version - Component version (default: 'v1')
|
|
55
56
|
* @param tags - Optional tags for categorization
|
|
@@ -57,7 +58,7 @@ export class ComponentRegistry {
|
|
|
57
58
|
*/
|
|
58
59
|
register(
|
|
59
60
|
name: string,
|
|
60
|
-
component:
|
|
61
|
+
component: ComponentObject,
|
|
61
62
|
namespace: string = 'Global',
|
|
62
63
|
version: string = 'v1',
|
|
63
64
|
tags?: string[]
|
|
@@ -98,9 +99,9 @@ export class ComponentRegistry {
|
|
|
98
99
|
* @param name - Component name
|
|
99
100
|
* @param namespace - Component namespace
|
|
100
101
|
* @param version - Component version
|
|
101
|
-
* @returns The component if found, undefined otherwise
|
|
102
|
+
* @returns The component object if found, undefined otherwise
|
|
102
103
|
*/
|
|
103
|
-
get(name: string, namespace: string = 'Global', version?: string):
|
|
104
|
+
get(name: string, namespace: string = 'Global', version?: string): ComponentObject | undefined {
|
|
104
105
|
const id = version
|
|
105
106
|
? this.generateRegistryKey(name, namespace, version)
|
|
106
107
|
: this.findLatestVersion(name, namespace);
|
|
@@ -171,10 +172,10 @@ export class ComponentRegistry {
|
|
|
171
172
|
* Gets all components in a namespace and version as a map
|
|
172
173
|
* @param namespace - Namespace to query (default: 'Global')
|
|
173
174
|
* @param version - Version to query (default: 'v1')
|
|
174
|
-
* @returns Object mapping component names to
|
|
175
|
+
* @returns Object mapping component names to component objects
|
|
175
176
|
*/
|
|
176
|
-
getAll(namespace: string = 'Global', version: string = 'v1'): Record<string,
|
|
177
|
-
const components: Record<string,
|
|
177
|
+
getAll(namespace: string = 'Global', version: string = 'v1'): Record<string, ComponentObject> {
|
|
178
|
+
const components: Record<string, ComponentObject> = {};
|
|
178
179
|
|
|
179
180
|
for (const entry of this.registry.values()) {
|
|
180
181
|
if (entry.metadata.namespace === namespace && entry.metadata.version === version) {
|