@memberjunction/react-runtime 2.93.0 → 2.95.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 (64) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/CHANGELOG.md +28 -0
  3. package/README.md +180 -2
  4. package/dist/compiler/component-compiler.d.ts +1 -0
  5. package/dist/compiler/component-compiler.d.ts.map +1 -1
  6. package/dist/compiler/component-compiler.js +253 -61
  7. package/dist/compiler/component-compiler.js.map +1 -1
  8. package/dist/index.d.ts +3 -2
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +15 -5
  11. package/dist/index.js.map +1 -1
  12. package/dist/registry/component-registry-service.d.ts +6 -3
  13. package/dist/registry/component-registry-service.d.ts.map +1 -1
  14. package/dist/registry/component-registry-service.js +38 -11
  15. package/dist/registry/component-registry-service.js.map +1 -1
  16. package/dist/registry/component-registry.d.ts +6 -3
  17. package/dist/registry/component-registry.d.ts.map +1 -1
  18. package/dist/registry/component-registry.js +17 -0
  19. package/dist/registry/component-registry.js.map +1 -1
  20. package/dist/registry/component-resolver.d.ts +2 -1
  21. package/dist/registry/component-resolver.d.ts.map +1 -1
  22. package/dist/registry/component-resolver.js +101 -14
  23. package/dist/registry/component-resolver.js.map +1 -1
  24. package/dist/runtime/component-hierarchy.d.ts.map +1 -1
  25. package/dist/runtime/component-hierarchy.js +75 -13
  26. package/dist/runtime/component-hierarchy.js.map +1 -1
  27. package/dist/runtime/prop-builder.d.ts +2 -2
  28. package/dist/runtime/prop-builder.d.ts.map +1 -1
  29. package/dist/runtime/prop-builder.js +32 -14
  30. package/dist/runtime/prop-builder.js.map +1 -1
  31. package/dist/runtime.umd.js +1 -1
  32. package/dist/types/dependency-types.d.ts +62 -0
  33. package/dist/types/dependency-types.d.ts.map +1 -0
  34. package/dist/types/dependency-types.js +3 -0
  35. package/dist/types/dependency-types.js.map +1 -0
  36. package/dist/types/index.d.ts +8 -10
  37. package/dist/types/index.d.ts.map +1 -1
  38. package/dist/types/index.js +1 -0
  39. package/dist/types/index.js.map +1 -1
  40. package/dist/utilities/index.d.ts +1 -0
  41. package/dist/utilities/index.d.ts.map +1 -1
  42. package/dist/utilities/index.js +1 -0
  43. package/dist/utilities/index.js.map +1 -1
  44. package/dist/utilities/library-dependency-resolver.d.ts +19 -0
  45. package/dist/utilities/library-dependency-resolver.d.ts.map +1 -0
  46. package/dist/utilities/library-dependency-resolver.js +419 -0
  47. package/dist/utilities/library-dependency-resolver.js.map +1 -0
  48. package/dist/utilities/library-loader.d.ts +9 -0
  49. package/dist/utilities/library-loader.d.ts.map +1 -1
  50. package/dist/utilities/library-loader.js +164 -0
  51. package/dist/utilities/library-loader.js.map +1 -1
  52. package/package.json +5 -5
  53. package/src/compiler/component-compiler.ts +280 -82
  54. package/src/index.ts +20 -5
  55. package/src/registry/component-registry-service.ts +53 -14
  56. package/src/registry/component-registry.ts +36 -7
  57. package/src/registry/component-resolver.ts +130 -16
  58. package/src/runtime/component-hierarchy.ts +101 -27
  59. package/src/runtime/prop-builder.ts +38 -18
  60. package/src/types/dependency-types.ts +110 -0
  61. package/src/types/index.ts +17 -21
  62. package/src/utilities/index.ts +1 -0
  63. package/src/utilities/library-dependency-resolver.ts +613 -0
  64. package/src/utilities/library-loader.ts +274 -0
@@ -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,10 +555,33 @@ 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
  }
564
+
565
+ /**
566
+ * Force clear all compiled components
567
+ * Used for Component Studio to ensure fresh loads
568
+ */
569
+ forceClearAll(): void {
570
+ this.compiledComponentCache.clear();
571
+ this.componentReferences.clear();
572
+ console.log('🧹 Component cache force cleared');
573
+ }
574
+
575
+ /**
576
+ * Reset the singleton instance
577
+ * Forces new instance creation on next access
578
+ */
579
+ static reset(): void {
580
+ if (ComponentRegistryService.instance) {
581
+ ComponentRegistryService.instance.forceClearAll();
582
+ ComponentRegistryService.instance = null;
583
+ }
584
+ }
546
585
 
547
586
  /**
548
587
  * Generate a cache key for a component
@@ -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) {
@@ -242,6 +243,34 @@ export class ComponentRegistry {
242
243
  this.registry.clear();
243
244
  }
244
245
 
246
+ /**
247
+ * Clear all components in a specific namespace
248
+ * @param namespace - Namespace to clear (default: 'Global')
249
+ * @returns Number of components removed
250
+ */
251
+ clearNamespace(namespace: string = 'Global'): number {
252
+ const toRemove: string[] = [];
253
+ for (const [key, entry] of this.registry) {
254
+ if (entry.metadata.namespace === namespace) {
255
+ toRemove.push(key);
256
+ }
257
+ }
258
+ for (const key of toRemove) {
259
+ this.registry.delete(key);
260
+ }
261
+ return toRemove.length;
262
+ }
263
+
264
+ /**
265
+ * Force clear all components and reset registry
266
+ * Used for development/testing scenarios
267
+ */
268
+ forceClear(): void {
269
+ this.stopCleanupTimer();
270
+ this.registry.clear();
271
+ console.log('🧹 Registry force cleared - all components removed');
272
+ }
273
+
245
274
  /**
246
275
  * Gets the current size of the registry
247
276
  * @returns Number of registered components
@@ -30,25 +30,29 @@ export class ComponentResolver {
30
30
  private compiler: ComponentCompiler | null = null;
31
31
  private runtimeContext: RuntimeContext | null = null;
32
32
  private componentEngine = ComponentMetadataEngine.Instance;
33
+ private debug: boolean = false;
33
34
 
34
35
  /**
35
36
  * Creates a new ComponentResolver instance
36
37
  * @param registry - Component registry to use for resolution
37
38
  * @param compiler - Optional compiler for registry-based components
38
39
  * @param runtimeContext - Optional runtime context for registry-based components
40
+ * @param debug - Enable debug logging (defaults to false)
39
41
  */
40
42
  constructor(
41
43
  registry: ComponentRegistry,
42
44
  compiler?: ComponentCompiler,
43
- runtimeContext?: RuntimeContext
45
+ runtimeContext?: RuntimeContext,
46
+ debug: boolean = false
44
47
  ) {
45
48
  this.registry = registry;
46
49
  this.resolverInstanceId = `resolver-${Date.now()}-${Math.random()}`;
50
+ this.debug = debug;
47
51
 
48
52
  if (compiler && runtimeContext) {
49
53
  this.compiler = compiler;
50
54
  this.runtimeContext = runtimeContext;
51
- this.registryService = ComponentRegistryService.getInstance(compiler, runtimeContext);
55
+ this.registryService = ComponentRegistryService.getInstance(compiler, runtimeContext, debug);
52
56
  }
53
57
  }
54
58
 
@@ -64,17 +68,60 @@ export class ComponentResolver {
64
68
  namespace: string = 'Global',
65
69
  contextUser?: UserInfo
66
70
  ): Promise<ResolvedComponents> {
71
+ console.log(`🚀 [ComponentResolver] Starting component resolution for: ${spec.name}`);
72
+ console.log(`📋 [ComponentResolver] Dependencies to resolve:`, (spec.dependencies || []).map(d => ({
73
+ name: d.name,
74
+ location: d.location,
75
+ namespace: d.namespace
76
+ })));
77
+
67
78
  const resolved: ResolvedComponents = {};
68
79
 
69
80
  // Initialize component engine if we have registry service
70
81
  if (this.registryService) {
82
+ console.log(`🔄 [ComponentResolver] Initializing component engine...`);
71
83
  await this.componentEngine.Config(false, contextUser);
84
+ console.log(`✅ [ComponentResolver] Component engine initialized with ${this.componentEngine.Components?.length || 0} components`);
72
85
  }
73
86
 
74
87
  // Resolve the component hierarchy
75
88
  await this.resolveComponentHierarchy(spec, resolved, namespace, new Set(), contextUser);
76
89
 
77
- return resolved;
90
+ console.log(`📊 [ComponentResolver] Resolved components before unwrapping:`, Object.keys(resolved));
91
+
92
+ // Unwrap component wrappers before returning
93
+ // Components from the registry come as objects with component/print/refresh properties
94
+ // We need to extract just the component function for use in child components
95
+ const unwrapped: ResolvedComponents = {};
96
+ for (const [name, value] of Object.entries(resolved)) {
97
+ if (value && typeof value === 'object' && 'component' in value) {
98
+ if (typeof value.component === 'function') {
99
+ // This is a wrapped component - extract the actual React component function
100
+ unwrapped[name] = value.component;
101
+ console.log(`✅ [ComponentResolver] Unwrapped component: ${name} (was object with .component)`);
102
+ } else {
103
+ // ComponentObject has a component property but it's not a function
104
+ console.error(`❌ [ComponentResolver] Component ${name} has invalid component property:`, typeof value.component, value);
105
+ unwrapped[name] = value; // Pass through the problematic value so we can see the error
106
+ }
107
+ } else if (typeof value === 'function') {
108
+ // Already a function - use as is
109
+ unwrapped[name] = value;
110
+ console.log(`✅ [ComponentResolver] Component already a function: ${name}`);
111
+ } else {
112
+ // Something else - could be undefined or an error
113
+ console.warn(`⚠️ [ComponentResolver] Component ${name} is not a function or wrapped component:`, typeof value, value);
114
+ unwrapped[name] = value; // Pass through for debugging
115
+ }
116
+ }
117
+
118
+ console.log(`🎯 [ComponentResolver] Final resolved components:`, Object.keys(unwrapped).map(name => ({
119
+ name,
120
+ type: typeof unwrapped[name],
121
+ isUndefined: unwrapped[name] === undefined
122
+ })));
123
+
124
+ return unwrapped;
78
125
  }
79
126
 
80
127
  /**
@@ -95,24 +142,70 @@ export class ComponentResolver {
95
142
  // Create a unique identifier for this component
96
143
  const componentId = `${spec.namespace || namespace}/${spec.name}@${spec.version || 'latest'}`;
97
144
 
145
+ // Check if already resolved (not just visited)
146
+ if (resolved[spec.name]) {
147
+ console.log(`⏭️ [ComponentResolver] Component already resolved: ${spec.name}`);
148
+ return;
149
+ }
150
+
98
151
  // Prevent circular dependencies
99
152
  if (visited.has(componentId)) {
100
- console.warn(`Circular dependency detected for component: ${componentId}`);
153
+ if (this.debug) {
154
+ console.warn(`Circular dependency detected for component: ${componentId}`);
155
+ }
101
156
  return;
102
157
  }
103
158
  visited.add(componentId);
104
159
 
160
+ // *** CRITICAL: Process child components FIRST (depth-first, post-order) ***
161
+ console.log(`🔄 [ComponentResolver] Resolving dependencies for ${spec.name} BEFORE resolving itself`);
162
+ const children = spec.dependencies || [];
163
+ for (const child of children) {
164
+ console.log(` ↳ [ComponentResolver] Resolving dependency: ${child.name} for parent ${spec.name}`);
165
+ await this.resolveComponentHierarchy(child, resolved, namespace, visited, contextUser);
166
+ }
167
+ if (children.length > 0) {
168
+ console.log(`✅ [ComponentResolver] All ${children.length} dependencies resolved for ${spec.name}, now resolving itself`);
169
+ }
170
+
171
+ // NOW resolve the current component (it can access its dependencies)
105
172
  // Handle based on location
106
173
  if (spec.location === 'registry' && this.registryService) {
107
174
  // Registry component - need to load from database or external source
175
+ console.log(`🔍 [ComponentResolver] Looking for registry component: ${spec.name} in namespace: ${spec.namespace || namespace}`);
176
+
108
177
  try {
109
178
  // First, try to find the component in the metadata engine
179
+ const allComponents = this.componentEngine.Components || [];
180
+ console.log(`📊 [ComponentResolver] Total components in engine: ${allComponents.length}`);
181
+
182
+ // Log all matching names to see duplicates
183
+ const matchingNames = allComponents.filter((c: any) => c.Name === spec.name);
184
+ if (matchingNames.length > 0) {
185
+ console.log(`🔎 [ComponentResolver] Found ${matchingNames.length} components with name "${spec.name}":`,
186
+ matchingNames.map((c: any) => ({
187
+ ID: c.ID,
188
+ Name: c.Name,
189
+ Namespace: c.Namespace,
190
+ Version: c.Version,
191
+ Status: c.Status
192
+ }))
193
+ );
194
+ }
195
+
110
196
  const component = this.componentEngine.Components?.find(
111
197
  (c: any) => c.Name === spec.name &&
112
198
  c.Namespace === (spec.namespace || namespace)
113
199
  );
114
200
 
115
201
  if (component) {
202
+ console.log(`✅ [ComponentResolver] Found component in DB:`, {
203
+ ID: component.ID,
204
+ Name: component.Name,
205
+ Namespace: component.Namespace,
206
+ Version: component.Version
207
+ });
208
+
116
209
  // Get compiled component from registry service
117
210
  const compiledComponent = await this.registryService.getCompiledComponent(
118
211
  component.ID,
@@ -120,38 +213,57 @@ export class ComponentResolver {
120
213
  contextUser
121
214
  );
122
215
  resolved[spec.name] = compiledComponent;
123
- console.log(`📦 Resolved registry component: ${spec.name} from ${componentId}`);
216
+ console.log(`📦 [ComponentResolver] Successfully compiled and resolved: ${spec.name}, type: ${typeof compiledComponent}`);
217
+
218
+ if (this.debug) {
219
+ console.log(`📦 Resolved registry component: ${spec.name} from ${componentId}`);
220
+ }
124
221
  } else {
125
- console.warn(`Registry component not found in database: ${spec.name}`);
222
+ console.error(`❌❌❌❌❌ [ComponentResolver] Registry component NOT found in database: ${spec.name} with namespace: ${spec.namespace || namespace} ❌❌❌❌`);
223
+ if (this.debug) {
224
+ console.warn(`Registry component not found in database: ${spec.name}`);
225
+ }
126
226
  }
127
227
  } catch (error) {
128
- console.error(`Failed to load registry component ${spec.name}:`, error);
228
+ if (this.debug) {
229
+ console.error(`Failed to load registry component ${spec.name}:`, error);
230
+ }
129
231
  }
130
232
  } else {
131
233
  // Embedded component - get from local registry
132
234
  // Use the component's specified namespace if it has one, otherwise use parent's namespace
133
235
  const componentNamespace = spec.namespace || namespace;
236
+ console.log(`🔍 [ComponentResolver] Looking for embedded component: ${spec.name} in namespace: ${componentNamespace}`);
237
+
134
238
  const component = this.registry.get(spec.name, componentNamespace);
135
239
  if (component) {
136
240
  resolved[spec.name] = component;
137
- console.log(`📄 Resolved embedded component: ${spec.name} from namespace ${componentNamespace}`);
241
+ console.log(`✅ [ComponentResolver] Found embedded component: ${spec.name}, type: ${typeof component}`);
242
+ if (this.debug) {
243
+ console.log(`📄 Resolved embedded component: ${spec.name} from namespace ${componentNamespace}, type:`, typeof component);
244
+ }
138
245
  } else {
139
246
  // If not found with specified namespace, try the parent namespace as fallback
247
+ console.log(`⚠️ [ComponentResolver] Not found in namespace ${componentNamespace}, trying fallback namespace: ${namespace}`);
140
248
  const fallbackComponent = this.registry.get(spec.name, namespace);
141
249
  if (fallbackComponent) {
142
250
  resolved[spec.name] = fallbackComponent;
143
- console.log(`📄 Resolved embedded component: ${spec.name} from fallback namespace ${namespace}`);
251
+ console.log(`✅ [ComponentResolver] Found embedded component in fallback namespace: ${spec.name}, type: ${typeof fallbackComponent}`);
252
+ if (this.debug) {
253
+ console.log(`📄 Resolved embedded component: ${spec.name} from fallback namespace ${namespace}, type:`, typeof fallbackComponent);
254
+ }
144
255
  } else {
256
+ // Component not found - this might cause issues later
257
+ console.error(`❌ [ComponentResolver] Could not resolve embedded component: ${spec.name} in namespace ${componentNamespace} or ${namespace}`);
145
258
  console.warn(`⚠️ Could not resolve embedded component: ${spec.name} in namespace ${componentNamespace} or ${namespace}`);
259
+ // Store undefined explicitly so we know it failed to resolve
260
+ resolved[spec.name] = undefined;
146
261
  }
147
262
  }
148
263
  }
149
-
150
- // Process child components recursively
151
- const children = spec.dependencies || [];
152
- for (const child of children) {
153
- await this.resolveComponentHierarchy(child, resolved, namespace, visited, contextUser);
154
- }
264
+
265
+ // Child components have already been processed at the beginning of this method
266
+ // No need to process them again - we're using depth-first, post-order traversal
155
267
  }
156
268
 
157
269
  /**
@@ -162,7 +274,9 @@ export class ComponentResolver {
162
274
  if (this.registryService) {
163
275
  // This would allow the registry service to clean up unused components
164
276
  // Implementation would track which components this resolver referenced
165
- console.log(`Cleaning up resolver: ${this.resolverInstanceId}`);
277
+ if (this.debug) {
278
+ console.log(`Cleaning up resolver: ${this.resolverInstanceId}`);
279
+ }
166
280
  }
167
281
  }
168
282
 
@@ -7,7 +7,8 @@
7
7
  import {
8
8
  CompilationResult,
9
9
  CompileOptions,
10
- RuntimeContext
10
+ RuntimeContext,
11
+ CompiledComponent
11
12
  } from '../types';
12
13
  import { ComponentCompiler } from '../compiler';
13
14
  import { ComponentRegistry } from '../registry';
@@ -94,31 +95,97 @@ export class ComponentHierarchyRegistrar {
94
95
  const errors: ComponentRegistrationError[] = [];
95
96
  const warnings: string[] = [];
96
97
 
97
- // Register the root component
98
- const rootResult = await this.registerSingleComponent(
99
- rootSpec,
100
- { styles, namespace, version, allowOverride, allLibraries: options.allLibraries }
101
- );
102
-
103
- if (rootResult.success) {
104
- registeredComponents.push(rootSpec.name);
105
- } else {
106
- errors.push(rootResult.error!);
107
- if (!continueOnError) {
108
- return { success: false, registeredComponents, errors, warnings };
98
+ // PHASE 1: Compile all components first (but defer factory execution)
99
+ const compiledMap = new Map<string, CompiledComponent>();
100
+ const specMap = new Map<string, ComponentSpec>();
101
+
102
+ // Helper to compile a component without calling its factory
103
+ const compileOnly = async (spec: ComponentSpec): Promise<{ success: boolean; error?: ComponentRegistrationError }> => {
104
+ if (!spec.code) return { success: true };
105
+
106
+ try {
107
+ const compileOptions: CompileOptions = {
108
+ componentName: spec.name,
109
+ componentCode: spec.code,
110
+ styles,
111
+ libraries: spec.libraries,
112
+ dependencies: spec.dependencies,
113
+ allLibraries: options.allLibraries
114
+ };
115
+
116
+ const result = await this.compiler.compile(compileOptions);
117
+ if (result.success && result.component) {
118
+ compiledMap.set(spec.name, result.component);
119
+ specMap.set(spec.name, spec);
120
+ return { success: true };
121
+ } else {
122
+ return {
123
+ success: false,
124
+ error: {
125
+ componentName: spec.name,
126
+ error: result.error?.message || 'Unknown compilation error',
127
+ phase: 'compilation'
128
+ }
129
+ };
130
+ }
131
+ } catch (error) {
132
+ return {
133
+ success: false,
134
+ error: {
135
+ componentName: spec.name,
136
+ error: error instanceof Error ? error.message : String(error),
137
+ phase: 'compilation'
138
+ }
139
+ };
140
+ }
141
+ };
142
+
143
+ // Compile all components in hierarchy
144
+ const compileQueue = [rootSpec];
145
+ const visited = new Set<string>();
146
+
147
+ while (compileQueue.length > 0) {
148
+ const spec = compileQueue.shift()!;
149
+ if (visited.has(spec.name)) continue;
150
+ visited.add(spec.name);
151
+
152
+ const result = await compileOnly(spec);
153
+ if (!result.success) {
154
+ errors.push(result.error!);
155
+ if (!continueOnError) {
156
+ return { success: false, registeredComponents, errors, warnings };
157
+ }
158
+ }
159
+
160
+ if (spec.dependencies) {
161
+ compileQueue.push(...spec.dependencies);
109
162
  }
110
163
  }
111
-
112
- // Register child components recursively
113
- const childComponents = rootSpec.dependencies || [];
114
- if (childComponents.length > 0) {
115
- const childResult = await this.registerChildComponents(
116
- childComponents,
117
- { styles, namespace, version, continueOnError, allowOverride, allLibraries: options.allLibraries },
118
- registeredComponents,
119
- errors,
120
- warnings
164
+
165
+ // PHASE 2: Execute all factories with components available
166
+ for (const [name, compiled] of compiledMap) {
167
+ const spec = specMap.get(name)!;
168
+
169
+ // Build components object from all registered components
170
+ const components: Record<string, any> = {};
171
+ for (const [depName, depCompiled] of compiledMap) {
172
+ // Call factory to get ComponentObject, then extract React component
173
+ const depObject = depCompiled.factory(this.runtimeContext, styles);
174
+ components[depName] = depObject.component;
175
+ }
176
+
177
+ // Now call factory with components available
178
+ const componentObject = compiled.factory(this.runtimeContext, styles, components);
179
+
180
+ // Register in registry
181
+ this.registry.register(
182
+ spec.name,
183
+ componentObject,
184
+ spec.namespace || namespace,
185
+ version
121
186
  );
187
+
188
+ registeredComponents.push(spec.name);
122
189
  }
123
190
 
124
191
  return {
@@ -197,13 +264,20 @@ export class ComponentHierarchyRegistrar {
197
264
  };
198
265
  }
199
266
 
200
- // Create component factory
201
- const componentFactory = compilationResult.component!.component(this.runtimeContext, styles);
267
+ // Call the factory to create the ComponentObject
268
+ // IMPORTANT: We don't pass components here because child components may not be registered yet
269
+ // Components are resolved later when the component is actually rendered
270
+ console.log(`🏭 Calling factory for ${spec.name} with runtime context:`, {
271
+ hasReact: !!this.runtimeContext.React,
272
+ hasReactDOM: !!this.runtimeContext.ReactDOM,
273
+ libraryKeys: Object.keys(this.runtimeContext.libraries || {})
274
+ });
275
+ const componentObject = compilationResult.component!.factory(this.runtimeContext, styles);
202
276
 
203
- // Register the component
277
+ // Register the full ComponentObject (not just the React component)
204
278
  this.registry.register(
205
279
  spec.name,
206
- componentFactory.component,
280
+ componentObject,
207
281
  spec.namespace || namespace,
208
282
  version
209
283
  );