@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.
Files changed (62) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/CHANGELOG.md +16 -0
  3. package/README.md +180 -2
  4. package/dist/compiler/component-compiler.d.ts.map +1 -1
  5. package/dist/compiler/component-compiler.js +206 -57
  6. package/dist/compiler/component-compiler.js.map +1 -1
  7. package/dist/index.d.ts +2 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +13 -4
  10. package/dist/index.js.map +1 -1
  11. package/dist/registry/component-registry-service.d.ts +4 -3
  12. package/dist/registry/component-registry-service.d.ts.map +1 -1
  13. package/dist/registry/component-registry-service.js +27 -11
  14. package/dist/registry/component-registry-service.js.map +1 -1
  15. package/dist/registry/component-registry.d.ts +4 -3
  16. package/dist/registry/component-registry.d.ts.map +1 -1
  17. package/dist/registry/component-registry.js.map +1 -1
  18. package/dist/registry/component-resolver.d.ts +2 -1
  19. package/dist/registry/component-resolver.d.ts.map +1 -1
  20. package/dist/registry/component-resolver.js +42 -10
  21. package/dist/registry/component-resolver.js.map +1 -1
  22. package/dist/runtime/component-hierarchy.d.ts.map +1 -1
  23. package/dist/runtime/component-hierarchy.js +8 -2
  24. package/dist/runtime/component-hierarchy.js.map +1 -1
  25. package/dist/runtime/prop-builder.d.ts +2 -2
  26. package/dist/runtime/prop-builder.d.ts.map +1 -1
  27. package/dist/runtime/prop-builder.js +32 -14
  28. package/dist/runtime/prop-builder.js.map +1 -1
  29. package/dist/runtime.umd.js +1 -1
  30. package/dist/types/dependency-types.d.ts +62 -0
  31. package/dist/types/dependency-types.d.ts.map +1 -0
  32. package/dist/types/dependency-types.js +3 -0
  33. package/dist/types/dependency-types.js.map +1 -0
  34. package/dist/types/index.d.ts +8 -10
  35. package/dist/types/index.d.ts.map +1 -1
  36. package/dist/types/index.js +1 -0
  37. package/dist/types/index.js.map +1 -1
  38. package/dist/utilities/index.d.ts +1 -0
  39. package/dist/utilities/index.d.ts.map +1 -1
  40. package/dist/utilities/index.js +1 -0
  41. package/dist/utilities/index.js.map +1 -1
  42. package/dist/utilities/library-dependency-resolver.d.ts +19 -0
  43. package/dist/utilities/library-dependency-resolver.d.ts.map +1 -0
  44. package/dist/utilities/library-dependency-resolver.js +410 -0
  45. package/dist/utilities/library-dependency-resolver.js.map +1 -0
  46. package/dist/utilities/library-loader.d.ts +9 -0
  47. package/dist/utilities/library-loader.d.ts.map +1 -1
  48. package/dist/utilities/library-loader.js +143 -0
  49. package/dist/utilities/library-loader.js.map +1 -1
  50. package/package.json +5 -5
  51. package/src/compiler/component-compiler.ts +227 -77
  52. package/src/index.ts +18 -4
  53. package/src/registry/component-registry-service.ts +32 -14
  54. package/src/registry/component-registry.ts +8 -7
  55. package/src/registry/component-resolver.ts +51 -10
  56. package/src/runtime/component-hierarchy.ts +12 -4
  57. package/src/runtime/prop-builder.ts +38 -18
  58. package/src/types/dependency-types.ts +110 -0
  59. package/src/types/index.ts +17 -21
  60. package/src/utilities/index.ts +1 -0
  61. package/src/utilities/library-dependency-resolver.ts +603 -0
  62. 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
- component: componentFactory,
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
- // Return the component with utilities
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: ${componentName},
274
+ component: ComponentWithMethodRegistry,
275
+
276
+ // Legacy methods for backward compatibility
204
277
  print: function() {
205
- if (typeof window !== 'undefined' && window.print) {
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 contextUser - Context user for accessing library registry
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
- console.log('🔍 loadRequiredLibraries called with:', {
227
- librariesCount: libraries?.length || 0,
228
- libraries: libraries?.map(l => ({ name: l.name, version: l.version, globalVariable: l.globalVariable }))
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
- console.log('📚 No libraries to load, returning empty map');
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
- const loadPromises = libraries.map(async (lib) => {
250
- console.log(`📦 Processing library: ${lib.name}`);
251
-
252
- // Check if library is approved
253
- const isApproved = LibraryRegistry.isApproved(lib.name);
254
- console.log(` ✓ Approved check for ${lib.name}: ${isApproved}`);
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
- // Get CDN URL for the library
271
- const resolvedVersion = LibraryRegistry.resolveVersion(lib.name, lib.version);
272
- console.log(` ✓ Resolved version for ${lib.name}: ${resolvedVersion}`);
273
-
274
- const cdnUrl = LibraryRegistry.getCdnUrl(lib.name, resolvedVersion);
275
- console.log(` ✓ CDN URL for ${lib.name}: ${cdnUrl}`);
276
-
277
- if (!cdnUrl) {
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
- // Check if already loaded
283
- if ((window as any)[lib.globalVariable]) {
284
- console.log(` ℹ️ Library ${lib.name} already loaded globally as ${lib.globalVariable}`);
285
- loadedLibraries.set(lib.globalVariable, (window as any)[lib.globalVariable]);
286
- return;
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
- // Load CSS files if the library requires them
290
- const versionInfo = libraryDef.versions[resolvedVersion || libraryDef.defaultVersion];
291
- if (versionInfo?.cssUrls) {
292
- await this.loadStyles(versionInfo.cssUrls);
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
- // Load the library dynamically (cdnUrl is guaranteed to be non-null here due to check above)
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
- // Capture the library value from global scope
300
- // Note: Libraries loaded from CDN typically attach to window automatically
301
- // We capture them here to pass through the component's closure
302
- const libraryValue = (window as any)[lib.globalVariable];
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
- if (libraryValue) {
306
- loadedLibraries.set(lib.globalVariable, libraryValue);
307
- console.log(` ✅ Added ${lib.name} to loaded libraries map`);
308
- } else {
309
- console.error(` ❌ Library '${lib.name}' failed to expose global variable '${lib.globalVariable}'`);
310
- throw new Error(`Library '${lib.name}' failed to load or did not expose '${lib.globalVariable}'`);
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
- console.log(`✅ All libraries loaded successfully. Total: ${loadedLibraries.size}`);
317
- console.log('📚 Loaded libraries map:', Array.from(loadedLibraries.keys()));
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
- console.log(` ✓ Global variable ${globalName} found after ${attempts * checkInterval}ms`);
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
- console.error(` ❌ ${globalName} not found after ${attempts * checkInterval}ms`);
398
- console.log(` ℹ️ Window properties:`, Object.keys(window).filter(k => k.toLowerCase().includes(globalName.toLowerCase())));
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
- ): Function {
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
- const compiler = new ComponentCompiler(config?.compiler);
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(config?.registry);
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: Function;
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<Function> {
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
- console.log(`✅ Reusing compiled component from cache: ${key} (use count: ${cached.useCount})`);
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
- console.log(`🔄 Loading and compiling component: ${key}`);
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.component(this.runtimeContext);
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
- console.log(`Using cached external component: ${component.Name} (synced: ${component.LastSyncedAt})`);
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
- console.log(`Fetching from external registry: ${url}`);
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
- console.log(`Cached external component: ${componentEntity.Name} at ${componentEntity.LastSyncedAt}`);
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
- console.log(`🗑️ Evicting unused component from cache: ${componentKey}`);
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
- console.log('🧹 Clearing all component caches');
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: any,
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): any {
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 components
175
+ * @returns Object mapping component names to component objects
175
176
  */
176
- getAll(namespace: string = 'Global', version: string = 'v1'): Record<string, any> {
177
- const components: Record<string, any> = {};
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) {