@memberjunction/react-runtime 2.92.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 (87) hide show
  1. package/.turbo/turbo-build.log +14 -16
  2. package/CHANGELOG.md +31 -0
  3. package/README.md +180 -2
  4. package/dist/compiler/babel-config.js.map +1 -1
  5. package/dist/compiler/component-compiler.d.ts.map +1 -1
  6. package/dist/compiler/component-compiler.js +206 -57
  7. package/dist/compiler/component-compiler.js.map +1 -1
  8. package/dist/compiler/index.js.map +1 -1
  9. package/dist/index.d.ts +2 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +14 -5
  12. package/dist/index.js.map +1 -1
  13. package/dist/registry/component-registry-service.d.ts +37 -0
  14. package/dist/registry/component-registry-service.d.ts.map +1 -0
  15. package/dist/registry/component-registry-service.js +319 -0
  16. package/dist/registry/component-registry-service.js.map +1 -0
  17. package/dist/registry/component-registry.d.ts +4 -3
  18. package/dist/registry/component-registry.d.ts.map +1 -1
  19. package/dist/registry/component-registry.js.map +1 -1
  20. package/dist/registry/component-resolver.d.ts +12 -2
  21. package/dist/registry/component-resolver.d.ts.map +1 -1
  22. package/dist/registry/component-resolver.js +96 -12
  23. package/dist/registry/component-resolver.js.map +1 -1
  24. package/dist/registry/index.d.ts +2 -0
  25. package/dist/registry/index.d.ts.map +1 -1
  26. package/dist/registry/index.js +3 -1
  27. package/dist/registry/index.js.map +1 -1
  28. package/dist/registry/registry-provider.d.ts +54 -0
  29. package/dist/registry/registry-provider.d.ts.map +1 -0
  30. package/dist/registry/registry-provider.js +3 -0
  31. package/dist/registry/registry-provider.js.map +1 -0
  32. package/dist/runtime/component-hierarchy.d.ts.map +1 -1
  33. package/dist/runtime/component-hierarchy.js +8 -2
  34. package/dist/runtime/component-hierarchy.js.map +1 -1
  35. package/dist/runtime/error-boundary.js.map +1 -1
  36. package/dist/runtime/index.js.map +1 -1
  37. package/dist/runtime/prop-builder.d.ts +2 -2
  38. package/dist/runtime/prop-builder.d.ts.map +1 -1
  39. package/dist/runtime/prop-builder.js +32 -14
  40. package/dist/runtime/prop-builder.js.map +1 -1
  41. package/dist/runtime/react-root-manager.js.map +1 -1
  42. package/dist/runtime.umd.js +1 -1
  43. package/dist/types/dependency-types.d.ts +62 -0
  44. package/dist/types/dependency-types.d.ts.map +1 -0
  45. package/dist/types/dependency-types.js +3 -0
  46. package/dist/types/dependency-types.js.map +1 -0
  47. package/dist/types/index.d.ts +8 -10
  48. package/dist/types/index.d.ts.map +1 -1
  49. package/dist/types/index.js +1 -0
  50. package/dist/types/index.js.map +1 -1
  51. package/dist/types/library-config.js.map +1 -1
  52. package/dist/utilities/cache-manager.js.map +1 -1
  53. package/dist/utilities/component-error-analyzer.js.map +1 -1
  54. package/dist/utilities/component-styles.js.map +1 -1
  55. package/dist/utilities/core-libraries.js.map +1 -1
  56. package/dist/utilities/index.d.ts +1 -0
  57. package/dist/utilities/index.d.ts.map +1 -1
  58. package/dist/utilities/index.js +1 -0
  59. package/dist/utilities/index.js.map +1 -1
  60. package/dist/utilities/library-dependency-resolver.d.ts +19 -0
  61. package/dist/utilities/library-dependency-resolver.d.ts.map +1 -0
  62. package/dist/utilities/library-dependency-resolver.js +410 -0
  63. package/dist/utilities/library-dependency-resolver.js.map +1 -0
  64. package/dist/utilities/library-loader.d.ts +9 -0
  65. package/dist/utilities/library-loader.d.ts.map +1 -1
  66. package/dist/utilities/library-loader.js +143 -0
  67. package/dist/utilities/library-loader.js.map +1 -1
  68. package/dist/utilities/library-registry.js.map +1 -1
  69. package/dist/utilities/resource-manager.js.map +1 -1
  70. package/dist/utilities/standard-libraries.js.map +1 -1
  71. package/package.json +5 -6
  72. package/src/compiler/component-compiler.ts +227 -77
  73. package/src/index.ts +21 -5
  74. package/src/registry/component-registry-service.ts +578 -0
  75. package/src/registry/component-registry.ts +8 -7
  76. package/src/registry/component-resolver.ts +146 -16
  77. package/src/registry/index.ts +9 -0
  78. package/src/registry/registry-provider.ts +119 -0
  79. package/src/runtime/component-hierarchy.ts +13 -5
  80. package/src/runtime/prop-builder.ts +38 -18
  81. package/src/types/dependency-types.ts +110 -0
  82. package/src/types/index.ts +17 -21
  83. package/src/utilities/index.ts +1 -0
  84. package/src/utilities/library-dependency-resolver.ts +603 -0
  85. package/src/utilities/library-loader.ts +252 -0
  86. package/tsconfig.json +2 -1
  87. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,578 @@
1
+ /**
2
+ * @fileoverview Service for managing registry-based component loading with caching
3
+ * @module @memberjunction/react-runtime/registry
4
+ */
5
+
6
+ import { ComponentSpec, ComponentObject } from '@memberjunction/interactive-component-types';
7
+ import { ComponentCompiler } from '../compiler';
8
+ import { RuntimeContext } from '../types';
9
+ import {
10
+ RegistryProvider,
11
+ RegistryComponentResponse,
12
+ ComponentDependencyInfo,
13
+ DependencyTree,
14
+ RegistryComponentMetadata
15
+ } from './registry-provider';
16
+ import { UserInfo, Metadata } from '@memberjunction/core';
17
+ import {
18
+ ComponentEntity,
19
+ ComponentRegistryEntity,
20
+ ComponentDependencyEntity,
21
+ ComponentLibraryLinkEntity,
22
+ ComponentMetadataEngine
23
+ } from '@memberjunction/core-entities';
24
+
25
+ /**
26
+ * Cached compiled component with metadata
27
+ */
28
+ interface CachedCompiledComponent {
29
+ component: ComponentObject;
30
+ metadata: RegistryComponentResponse['metadata'];
31
+ compiledAt: Date;
32
+ lastUsed: Date;
33
+ useCount: number;
34
+ }
35
+
36
+ /**
37
+ * Service for managing component loading from registries with compilation caching
38
+ */
39
+ export class ComponentRegistryService {
40
+ private static instance: ComponentRegistryService | null = null;
41
+
42
+ // Caches
43
+ private compiledComponentCache = new Map<string, CachedCompiledComponent>();
44
+ private componentReferences = new Map<string, Set<string>>();
45
+
46
+ // Dependencies
47
+ private compiler: ComponentCompiler;
48
+ private runtimeContext: RuntimeContext;
49
+ private componentEngine = ComponentMetadataEngine.Instance;
50
+ private registryProviders = new Map<string, RegistryProvider>();
51
+ private debug: boolean = false;
52
+
53
+ private constructor(
54
+ compiler: ComponentCompiler,
55
+ runtimeContext: RuntimeContext,
56
+ debug: boolean = false
57
+ ) {
58
+ this.compiler = compiler;
59
+ this.runtimeContext = runtimeContext;
60
+ this.debug = debug;
61
+ }
62
+
63
+ /**
64
+ * Get or create the singleton instance
65
+ */
66
+ static getInstance(
67
+ compiler: ComponentCompiler,
68
+ context: RuntimeContext,
69
+ debug: boolean = false
70
+ ): ComponentRegistryService {
71
+ if (!ComponentRegistryService.instance) {
72
+ ComponentRegistryService.instance = new ComponentRegistryService(compiler, context, debug);
73
+ }
74
+ return ComponentRegistryService.instance;
75
+ }
76
+
77
+ /**
78
+ * Initialize the service with metadata
79
+ */
80
+ async initialize(contextUser?: UserInfo): Promise<void> {
81
+ // Initialize metadata engine
82
+ await this.componentEngine.Config(false, contextUser);
83
+ }
84
+
85
+ /**
86
+ * Get a compiled component, using cache if available
87
+ */
88
+ async getCompiledComponent(
89
+ componentId: string,
90
+ referenceId?: string,
91
+ contextUser?: UserInfo
92
+ ): Promise<ComponentObject> {
93
+ await this.initialize(contextUser);
94
+
95
+ // Find component in metadata
96
+ const component = this.componentEngine.Components.find((c: ComponentEntity) => c.ID === componentId);
97
+ if (!component) {
98
+ throw new Error(`Component not found: ${componentId}`);
99
+ }
100
+
101
+ const key = this.getComponentKey(component.Name, component.Namespace, component.Version, component.SourceRegistryID);
102
+
103
+ // Check if already compiled and cached
104
+ if (this.compiledComponentCache.has(key)) {
105
+ const cached = this.compiledComponentCache.get(key)!;
106
+ cached.lastUsed = new Date();
107
+ cached.useCount++;
108
+
109
+ // Track reference if provided
110
+ if (referenceId) {
111
+ this.addComponentReference(key, referenceId);
112
+ }
113
+
114
+ if (this.debug) {
115
+ console.log(`โœ… Reusing compiled component from cache: ${key} (use count: ${cached.useCount})`);
116
+ }
117
+ return cached.component;
118
+ }
119
+
120
+ // Not in cache, need to load and compile
121
+ if (this.debug) {
122
+ console.log(`๐Ÿ”„ Loading and compiling component: ${key}`);
123
+ }
124
+
125
+ // Get the component specification
126
+ const spec = await this.getComponentSpec(componentId, contextUser);
127
+
128
+ // Compile the component
129
+ // Load all libraries from metadata engine
130
+ const allLibraries = this.componentEngine.ComponentLibraries || [];
131
+
132
+ const compilationResult = await this.compiler.compile({
133
+ componentName: component.Name,
134
+ componentCode: spec.code,
135
+ libraries: spec.libraries,
136
+ allLibraries
137
+ });
138
+
139
+ if (!compilationResult.success) {
140
+ throw new Error(`Failed to compile component ${component.Name}: ${compilationResult.error}`);
141
+ }
142
+
143
+ // Cache the compiled component
144
+ const metadata: RegistryComponentMetadata = {
145
+ name: component.Name,
146
+ namespace: component.Namespace || '',
147
+ version: component.Version,
148
+ description: component.Description || '',
149
+ title: component.Title || undefined,
150
+ type: component.Type || undefined,
151
+ status: component.Status || undefined,
152
+ properties: spec.properties,
153
+ events: spec.events,
154
+ libraries: spec.libraries,
155
+ dependencies: spec.dependencies,
156
+ sourceRegistryID: component.SourceRegistryID,
157
+ isLocal: !component.SourceRegistryID
158
+ };
159
+
160
+ if (!compilationResult.component) {
161
+ throw new Error(`Component compilation succeeded but no component returned`);
162
+ }
163
+ const compiledComponent = compilationResult.component.factory(this.runtimeContext);
164
+ this.compiledComponentCache.set(key, {
165
+ component: compiledComponent,
166
+ metadata,
167
+ compiledAt: new Date(),
168
+ lastUsed: new Date(),
169
+ useCount: 1
170
+ });
171
+
172
+ // Track reference
173
+ if (referenceId) {
174
+ this.addComponentReference(key, referenceId);
175
+ }
176
+
177
+ return compiledComponent;
178
+ }
179
+
180
+ /**
181
+ * Get component specification from database or external registry
182
+ */
183
+ async getComponentSpec(
184
+ componentId: string,
185
+ contextUser?: UserInfo
186
+ ): Promise<ComponentSpec> {
187
+ await this.initialize(contextUser);
188
+
189
+ const component = this.componentEngine.Components.find((c: ComponentEntity) => c.ID === componentId);
190
+ if (!component) {
191
+ throw new Error(`Component not found: ${componentId}`);
192
+ }
193
+
194
+ if (!component.SourceRegistryID) {
195
+ // LOCAL: Use specification from database
196
+ if (!component.Specification) {
197
+ throw new Error(`Local component ${component.Name} has no specification`);
198
+ }
199
+ return JSON.parse(component.Specification);
200
+ }
201
+
202
+ // EXTERNAL: Check if we have a cached version
203
+ if (component.Specification && component.LastSyncedAt) {
204
+ // For now, always use cached version (no expiration)
205
+ if (this.debug) {
206
+ console.log(`Using cached external component: ${component.Name} (synced: ${component.LastSyncedAt})`);
207
+ }
208
+ return JSON.parse(component.Specification);
209
+ }
210
+
211
+ // Need to fetch from external registry
212
+ const registry = this.componentEngine.ComponentRegistries?.find(
213
+ r => r.ID === component.SourceRegistryID
214
+ );
215
+
216
+ if (!registry) {
217
+ throw new Error(`Registry not found: ${component.SourceRegistryID}`);
218
+ }
219
+
220
+ if (!registry) {
221
+ throw new Error(`Registry not found: ${component.SourceRegistryID}`);
222
+ }
223
+
224
+ const spec = await this.fetchFromExternalRegistry(
225
+ registry.URI || '',
226
+ component.Name,
227
+ component.Namespace || '',
228
+ component.Version,
229
+ this.getRegistryApiKey(registry.ID) // API keys stored in env vars or secure config
230
+ );
231
+
232
+ // Store in local database for future use
233
+ await this.cacheExternalComponent(componentId, spec, contextUser);
234
+
235
+ return spec;
236
+ }
237
+
238
+ /**
239
+ * Fetch component from external registry via HTTP
240
+ */
241
+ private async fetchFromExternalRegistry(
242
+ uri: string,
243
+ name: string,
244
+ namespace: string,
245
+ version: string,
246
+ apiKey?: string
247
+ ): Promise<ComponentSpec> {
248
+ const url = `${uri}/components/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}/${version}`;
249
+
250
+ const headers: HeadersInit = {
251
+ 'Accept': 'application/json'
252
+ };
253
+
254
+ if (apiKey) {
255
+ headers['Authorization'] = `Bearer ${apiKey}`;
256
+ }
257
+
258
+ if (this.debug) {
259
+ console.log(`Fetching from external registry: ${url}`);
260
+ }
261
+
262
+ const response = await fetch(url, { headers });
263
+
264
+ if (!response.ok) {
265
+ throw new Error(`Registry fetch failed: ${response.status} ${response.statusText}`);
266
+ }
267
+
268
+ const spec = await response.json() as ComponentSpec;
269
+ return spec;
270
+ }
271
+
272
+ /**
273
+ * Cache an external component in the local database
274
+ */
275
+ private async cacheExternalComponent(
276
+ componentId: string,
277
+ spec: ComponentSpec,
278
+ contextUser?: UserInfo
279
+ ): Promise<void> {
280
+ // Get the actual entity object to save
281
+ const md = new Metadata();
282
+ const componentEntity = await md.GetEntityObject<ComponentEntity>('MJ: Components', contextUser);
283
+
284
+ // Load the existing component
285
+ if (!await componentEntity.Load(componentId)) {
286
+ throw new Error(`Failed to load component entity: ${componentId}`);
287
+ }
288
+
289
+ // Update with fetched specification and all fields from spec
290
+ componentEntity.Specification = JSON.stringify(spec);
291
+ componentEntity.LastSyncedAt = new Date();
292
+
293
+ // Set ReplicatedAt only on first fetch
294
+ if (!componentEntity.ReplicatedAt) {
295
+ componentEntity.ReplicatedAt = new Date();
296
+ }
297
+
298
+ // Update all fields from the spec with strong typing
299
+ if (spec.name) {
300
+ componentEntity.Name = spec.name;
301
+ }
302
+
303
+ if (spec.namespace) {
304
+ componentEntity.Namespace = spec.namespace;
305
+ }
306
+
307
+ if (spec.version) {
308
+ componentEntity.Version = spec.version;
309
+ }
310
+
311
+ if (spec.title) {
312
+ componentEntity.Title = spec.title;
313
+ }
314
+
315
+ if (spec.description) {
316
+ componentEntity.Description = spec.description;
317
+ }
318
+
319
+ if (spec.type) {
320
+ // Map spec type to entity type (entity has specific enum values)
321
+ const typeMap: Record<string, ComponentEntity['Type']> = {
322
+ 'report': 'Report',
323
+ 'dashboard': 'Dashboard',
324
+ 'form': 'Form',
325
+ 'table': 'Table',
326
+ 'chart': 'Chart',
327
+ 'navigation': 'Navigation',
328
+ 'search': 'Search',
329
+ 'widget': 'Widget',
330
+ 'utility': 'Utility',
331
+ 'other': 'Other'
332
+ };
333
+
334
+ const mappedType = typeMap[spec.type.toLowerCase()];
335
+ if (mappedType) {
336
+ componentEntity.Type = mappedType;
337
+ }
338
+ }
339
+
340
+ if (spec.functionalRequirements) {
341
+ componentEntity.FunctionalRequirements = spec.functionalRequirements;
342
+ }
343
+
344
+ if (spec.technicalDesign) {
345
+ componentEntity.TechnicalDesign = spec.technicalDesign;
346
+ }
347
+
348
+ // Save back to database
349
+ const result = await componentEntity.Save();
350
+ if (!result) {
351
+ throw new Error(`Failed to save cached component: ${componentEntity.Name}\n${componentEntity.LatestResult.Message || componentEntity.LatestResult.Error || componentEntity.LatestResult.Errors?.join(',')}`);
352
+ }
353
+
354
+ if (this.debug) {
355
+ console.log(`Cached external component: ${componentEntity.Name} at ${componentEntity.LastSyncedAt}`);
356
+ }
357
+
358
+ // Refresh metadata cache after saving
359
+ await this.componentEngine.Config(true, contextUser);
360
+ }
361
+
362
+ /**
363
+ * Load component dependencies from database
364
+ */
365
+ async loadDependencies(
366
+ componentId: string,
367
+ contextUser?: UserInfo
368
+ ): Promise<ComponentDependencyInfo[]> {
369
+ await this.initialize(contextUser);
370
+
371
+ // Get dependencies from metadata cache
372
+ const dependencies = this.componentEngine.ComponentDependencies?.filter(
373
+ d => d.ComponentID === componentId
374
+ ) || [];
375
+
376
+ const result: ComponentDependencyInfo[] = [];
377
+
378
+ for (const dep of dependencies) {
379
+ // Find the dependency component
380
+ const depComponent = this.componentEngine.Components.find(
381
+ (c: ComponentEntity) => c.ID === dep.DependencyComponentID
382
+ );
383
+
384
+ if (depComponent) {
385
+ result.push({
386
+ name: depComponent.Name,
387
+ namespace: depComponent.Namespace || '',
388
+ version: depComponent.Version, // Version comes from the linked Component record
389
+ isRequired: true, // All dependencies are required in MemberJunction
390
+ location: depComponent.SourceRegistryID ? 'registry' : 'embedded',
391
+ sourceRegistryID: depComponent.SourceRegistryID
392
+ });
393
+ }
394
+ }
395
+
396
+ return result;
397
+ }
398
+
399
+ /**
400
+ * Resolve full dependency tree for a component
401
+ */
402
+ async resolveDependencyTree(
403
+ componentId: string,
404
+ contextUser?: UserInfo,
405
+ visited = new Set<string>()
406
+ ): Promise<DependencyTree> {
407
+ if (visited.has(componentId)) {
408
+ return {
409
+ componentId,
410
+ circular: true
411
+ };
412
+ }
413
+ visited.add(componentId);
414
+
415
+ await this.initialize(contextUser);
416
+
417
+ const component = this.componentEngine.Components.find((c: ComponentEntity) => c.ID === componentId);
418
+ if (!component) {
419
+ return { componentId, dependencies: [] };
420
+ }
421
+
422
+ // Get direct dependencies
423
+ const directDeps = await this.loadDependencies(componentId, contextUser);
424
+
425
+ // Recursively resolve each dependency
426
+ const dependencies: DependencyTree[] = [];
427
+ for (const dep of directDeps) {
428
+ // Find the dependency component
429
+ const depComponent = this.componentEngine.Components.find(
430
+ c => c.Name.trim().toLowerCase() === dep.name.trim().toLowerCase() &&
431
+ c.Namespace?.trim().toLowerCase() === dep.namespace?.trim().toLowerCase()
432
+ );
433
+
434
+ if (depComponent) {
435
+ const subTree = await this.resolveDependencyTree(
436
+ depComponent.ID,
437
+ contextUser,
438
+ visited
439
+ );
440
+ dependencies.push(subTree);
441
+ }
442
+ }
443
+
444
+ return {
445
+ componentId,
446
+ name: component.Name,
447
+ namespace: component.Namespace || undefined,
448
+ version: component.Version,
449
+ dependencies,
450
+ totalCount: dependencies.reduce((sum, d) => sum + (d.totalCount || 1), 1)
451
+ };
452
+ }
453
+
454
+ /**
455
+ * Get components to load in dependency order
456
+ */
457
+ async getComponentsToLoad(
458
+ rootComponentId: string,
459
+ contextUser?: UserInfo
460
+ ): Promise<string[]> {
461
+ const tree = await this.resolveDependencyTree(rootComponentId, contextUser);
462
+
463
+ // Flatten tree in dependency order (depth-first)
464
+ const ordered: string[] = [];
465
+ const processNode = (node: DependencyTree) => {
466
+ if (node.dependencies) {
467
+ node.dependencies.forEach(processNode);
468
+ }
469
+ if (!ordered.includes(node.componentId)) {
470
+ ordered.push(node.componentId);
471
+ }
472
+ };
473
+ processNode(tree);
474
+
475
+ return ordered;
476
+ }
477
+
478
+ /**
479
+ * Add a reference to a component
480
+ */
481
+ private addComponentReference(componentKey: string, referenceId: string): void {
482
+ if (!this.componentReferences.has(componentKey)) {
483
+ this.componentReferences.set(componentKey, new Set());
484
+ }
485
+ this.componentReferences.get(componentKey)!.add(referenceId);
486
+ }
487
+
488
+ /**
489
+ * Remove a reference to a component
490
+ */
491
+ removeComponentReference(componentKey: string, referenceId: string): void {
492
+ const refs = this.componentReferences.get(componentKey);
493
+ if (refs) {
494
+ refs.delete(referenceId);
495
+
496
+ // If no more references and cache cleanup is enabled
497
+ if (refs.size === 0) {
498
+ this.considerCacheEviction(componentKey);
499
+ }
500
+ }
501
+ }
502
+
503
+ /**
504
+ * Consider evicting a component from cache
505
+ */
506
+ private considerCacheEviction(componentKey: string): void {
507
+ const cached = this.compiledComponentCache.get(componentKey);
508
+ if (cached) {
509
+ const timeSinceLastUse = Date.now() - cached.lastUsed.getTime();
510
+ const evictionThreshold = 5 * 60 * 1000; // 5 minutes
511
+
512
+ if (timeSinceLastUse > evictionThreshold) {
513
+ if (this.debug) {
514
+ console.log(`๐Ÿ—‘๏ธ Evicting unused component from cache: ${componentKey}`);
515
+ }
516
+ this.compiledComponentCache.delete(componentKey);
517
+ }
518
+ }
519
+ }
520
+
521
+ /**
522
+ * Get API key for a registry from secure configuration
523
+ * @param registryId - Registry ID
524
+ * @returns API key or undefined
525
+ */
526
+ private getRegistryApiKey(registryId: string): string | undefined {
527
+ // API keys should be stored in environment variables or secure configuration
528
+ // Format: REGISTRY_API_KEY_{registryId} or similar
529
+ // This is a placeholder - actual implementation would depend on the security infrastructure
530
+ const envKey = `REGISTRY_API_KEY_${registryId.replace(/-/g, '_').toUpperCase()}`;
531
+ return process.env[envKey];
532
+ }
533
+
534
+ /**
535
+ * Get cache statistics
536
+ */
537
+ getCacheStats(): {
538
+ compiledComponents: number;
539
+ totalUseCount: number;
540
+ memoryEstimate: string;
541
+ } {
542
+ let totalUseCount = 0;
543
+ this.compiledComponentCache.forEach(cached => {
544
+ totalUseCount += cached.useCount;
545
+ });
546
+
547
+ return {
548
+ compiledComponents: this.compiledComponentCache.size,
549
+ totalUseCount,
550
+ memoryEstimate: `~${(this.compiledComponentCache.size * 50)}KB` // Rough estimate
551
+ };
552
+ }
553
+
554
+ /**
555
+ * Clear all caches
556
+ */
557
+ clearCache(): void {
558
+ if (this.debug) {
559
+ console.log('๐Ÿงน Clearing all component caches');
560
+ }
561
+ this.compiledComponentCache.clear();
562
+ this.componentReferences.clear();
563
+ }
564
+
565
+ /**
566
+ * Generate a cache key for a component
567
+ */
568
+ private getComponentKey(
569
+ name: string,
570
+ namespace: string | null | undefined,
571
+ version: string,
572
+ sourceRegistryId: string | null | undefined
573
+ ): string {
574
+ const registryPart = sourceRegistryId || 'local';
575
+ const namespacePart = namespace || 'global';
576
+ return `${registryPart}/${namespacePart}/${name}@${version}`;
577
+ }
578
+ }
@@ -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) {