@memberjunction/react-runtime 2.99.0 → 2.100.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 (61) hide show
  1. package/.turbo/turbo-build.log +15 -20
  2. package/CHANGELOG.md +14 -0
  3. package/README.md +171 -1
  4. package/dist/compiler/component-compiler.d.ts.map +1 -1
  5. package/dist/compiler/component-compiler.js +10 -1
  6. package/dist/compiler/component-compiler.js.map +1 -1
  7. package/dist/component-manager/component-manager.d.ts +39 -0
  8. package/dist/component-manager/component-manager.d.ts.map +1 -0
  9. package/dist/component-manager/component-manager.js +474 -0
  10. package/dist/component-manager/component-manager.js.map +1 -0
  11. package/dist/component-manager/index.d.ts +3 -0
  12. package/dist/component-manager/index.d.ts.map +1 -0
  13. package/dist/component-manager/index.js +6 -0
  14. package/dist/component-manager/index.js.map +1 -0
  15. package/dist/component-manager/types.d.ts +62 -0
  16. package/dist/component-manager/types.d.ts.map +1 -0
  17. package/dist/component-manager/types.js +3 -0
  18. package/dist/component-manager/types.js.map +1 -0
  19. package/dist/index.d.ts +6 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +11 -2
  22. package/dist/index.js.map +1 -1
  23. package/dist/registry/component-registry-service.d.ts +16 -1
  24. package/dist/registry/component-registry-service.d.ts.map +1 -1
  25. package/dist/registry/component-registry-service.js +212 -10
  26. package/dist/registry/component-registry-service.js.map +1 -1
  27. package/dist/registry/component-registry.d.ts +1 -1
  28. package/dist/registry/component-registry.d.ts.map +1 -1
  29. package/dist/registry/component-registry.js.map +1 -1
  30. package/dist/registry/component-resolver.d.ts.map +1 -1
  31. package/dist/registry/component-resolver.js +122 -52
  32. package/dist/registry/component-resolver.js.map +1 -1
  33. package/dist/registry/index.d.ts +1 -1
  34. package/dist/registry/index.d.ts.map +1 -1
  35. package/dist/registry/index.js.map +1 -1
  36. package/dist/runtime/component-hierarchy.d.ts +4 -0
  37. package/dist/runtime/component-hierarchy.d.ts.map +1 -1
  38. package/dist/runtime/component-hierarchy.js +127 -12
  39. package/dist/runtime/component-hierarchy.js.map +1 -1
  40. package/dist/runtime.umd.js +535 -1
  41. package/dist/types/index.d.ts +1 -0
  42. package/dist/types/index.d.ts.map +1 -1
  43. package/dist/types/index.js.map +1 -1
  44. package/dist/utilities/library-loader.d.ts +3 -0
  45. package/dist/utilities/library-loader.d.ts.map +1 -1
  46. package/dist/utilities/library-loader.js +85 -17
  47. package/dist/utilities/library-loader.js.map +1 -1
  48. package/examples/component-registry-integration.ts +191 -0
  49. package/package.json +6 -5
  50. package/src/compiler/component-compiler.ts +14 -1
  51. package/src/component-manager/component-manager.ts +736 -0
  52. package/src/component-manager/index.ts +13 -0
  53. package/src/component-manager/types.ts +204 -0
  54. package/src/index.ts +27 -1
  55. package/src/registry/component-registry-service.ts +315 -18
  56. package/src/registry/component-registry.ts +1 -1
  57. package/src/registry/component-resolver.ts +159 -67
  58. package/src/registry/index.ts +1 -1
  59. package/src/runtime/component-hierarchy.ts +124 -13
  60. package/src/types/index.ts +2 -0
  61. package/src/utilities/library-loader.ts +133 -22
@@ -68,9 +68,16 @@ export class ComponentResolver {
68
68
  namespace: string = 'Global',
69
69
  contextUser?: UserInfo
70
70
  ): Promise<ResolvedComponents> {
71
- if (this.debug) {
72
- console.log(`🚀 [ComponentResolver] Starting component resolution for: ${spec.name}`);
73
- }
71
+ console.log(`🚀 [ComponentResolver] Starting component resolution for: ${spec.name}`);
72
+ console.log(`📋 [ComponentResolver] Root component spec:`, {
73
+ name: spec.name,
74
+ location: spec.location,
75
+ registry: spec.registry,
76
+ namespace: spec.namespace,
77
+ hasCode: !!spec.code,
78
+ hasDependencies: !!(spec.dependencies && spec.dependencies.length > 0)
79
+ });
80
+
74
81
  if (this.debug) {
75
82
  console.log(`📋 [ComponentResolver] Dependencies to resolve:`, (spec.dependencies || []).map(d => ({
76
83
  name: d.name,
@@ -93,7 +100,15 @@ export class ComponentResolver {
93
100
  }
94
101
 
95
102
  // Resolve the component hierarchy
103
+ console.log(`🔄 [ComponentResolver] About to call resolveComponentHierarchy for root: ${spec.name}`);
96
104
  await this.resolveComponentHierarchy(spec, resolved, namespace, new Set(), contextUser);
105
+ console.log(`✅ [ComponentResolver] Returned from resolveComponentHierarchy`);
106
+ console.log(`🔍 [ComponentResolver] Looking for root component '${spec.name}' in resolved:`, !!resolved[spec.name]);
107
+
108
+ if (!resolved[spec.name]) {
109
+ console.error(`❌ [ComponentResolver] Root component '${spec.name}' was NOT added to resolved map!`);
110
+ console.log(`📦 [ComponentResolver] What IS in resolved map:`, Object.keys(resolved));
111
+ }
97
112
 
98
113
  if (this.debug) {
99
114
  console.log(`📊 [ComponentResolver] Resolved components before unwrapping:`, Object.keys(resolved));
@@ -196,62 +211,96 @@ export class ComponentResolver {
196
211
  // Registry component - need to load from database or external source
197
212
  if (this.debug) {
198
213
  console.log(`🔍 [ComponentResolver] Looking for registry component: ${spec.name} in namespace: ${spec.namespace || namespace}`);
214
+ if (spec.registry) {
215
+ console.log(` 📍 [ComponentResolver] External registry specified: ${spec.registry}`);
216
+ } else {
217
+ console.log(` 📍 [ComponentResolver] Local registry (no registry field specified)`);
218
+ }
199
219
  }
200
220
 
201
221
  try {
202
- // First, try to find the component in the metadata engine
203
- const allComponents = this.componentEngine.Components || [];
204
- if (this.debug) {
205
- console.log(`📊 [ComponentResolver] Total components in engine: ${allComponents.length}`);
206
- }
207
-
208
- // Log all matching names to see duplicates
209
- const matchingNames = allComponents.filter((c: any) => c.Name === spec.name);
210
- if (matchingNames.length > 0 && this.debug) {
211
- console.log(`🔎 [ComponentResolver] Found ${matchingNames.length} components with name "${spec.name}":`,
212
- matchingNames.map((c: any) => ({
213
- ID: c.ID,
214
- Name: c.Name,
215
- Namespace: c.Namespace,
216
- Version: c.Version,
217
- Status: c.Status
218
- }))
219
- );
220
- }
221
-
222
- const component = this.componentEngine.Components?.find(
223
- (c: any) => c.Name === spec.name &&
224
- c.Namespace === (spec.namespace || namespace)
225
- );
226
-
227
- if (component) {
222
+ // If spec.registry is populated, this is an external registry component
223
+ // If spec.registry is blank/undefined, this is a local registry component
224
+ if (spec.registry) {
225
+ // EXTERNAL REGISTRY: Need to fetch from external registry via GraphQL
228
226
  if (this.debug) {
229
- console.log(`✅ [ComponentResolver] Found component in DB:`, {
230
- ID: component.ID,
231
- Name: component.Name,
232
- Namespace: component.Namespace,
233
- Version: component.Version
234
- });
227
+ console.log(`🌐 [ComponentResolver] Fetching from external registry: ${spec.registry}`);
235
228
  }
236
229
 
237
- // Get compiled component from registry service
238
- const compiledComponent = await this.registryService.getCompiledComponent(
239
- component.ID,
230
+ // Get compiled component from registry service (which will handle the external fetch)
231
+ const compiledComponent = await this.registryService.getCompiledComponentFromRegistry(
232
+ spec.registry, // Registry name
233
+ spec.namespace || namespace,
234
+ spec.name,
235
+ spec.version || 'latest',
240
236
  this.resolverInstanceId,
241
237
  contextUser
242
238
  );
243
- resolved[spec.name] = compiledComponent;
239
+
240
+ if (compiledComponent) {
241
+ resolved[spec.name] = compiledComponent;
242
+ if (this.debug) {
243
+ console.log(`✅ [ComponentResolver] Successfully fetched and compiled from external registry: ${spec.name}`);
244
+ }
245
+ } else {
246
+ console.error(`❌ [ComponentResolver] Failed to fetch from external registry: ${spec.name} from ${spec.registry}`);
247
+ }
248
+ } else {
249
+ // LOCAL REGISTRY: Get from local database
244
250
  if (this.debug) {
245
- console.log(`📦 [ComponentResolver] Successfully compiled and resolved: ${spec.name}, type: ${typeof compiledComponent}`);
251
+ console.log(`💾 [ComponentResolver] Looking for locally registered component`);
246
252
  }
247
253
 
254
+ // First, try to find the component in the metadata engine
255
+ const allComponents = this.componentEngine.Components || [];
248
256
  if (this.debug) {
249
- console.log(`📦 Resolved registry component: ${spec.name} from ${componentId}`);
257
+ console.log(`📊 [ComponentResolver] Total components in engine: ${allComponents.length}`);
250
258
  }
251
- } else {
252
- console.error(`❌❌❌❌❌ [ComponentResolver] Registry component NOT found in database: ${spec.name} with namespace: ${spec.namespace || namespace} ❌❌❌❌`);
253
- if (this.debug) {
254
- console.warn(`Registry component not found in database: ${spec.name}`);
259
+
260
+ // Log all matching names to see duplicates
261
+ const matchingNames = allComponents.filter((c: any) => c.Name === spec.name);
262
+ if (matchingNames.length > 0 && this.debug) {
263
+ console.log(`🔎 [ComponentResolver] Found ${matchingNames.length} components with name "${spec.name}":`,
264
+ matchingNames.map((c: any) => ({
265
+ ID: c.ID,
266
+ Name: c.Name,
267
+ Namespace: c.Namespace,
268
+ Version: c.Version,
269
+ Status: c.Status
270
+ }))
271
+ );
272
+ }
273
+
274
+ const component = this.componentEngine.Components?.find(
275
+ (c: any) => c.Name === spec.name &&
276
+ c.Namespace === (spec.namespace || namespace)
277
+ );
278
+
279
+ if (component) {
280
+ if (this.debug) {
281
+ console.log(`✅ [ComponentResolver] Found component in local DB:`, {
282
+ ID: component.ID,
283
+ Name: component.Name,
284
+ Namespace: component.Namespace,
285
+ Version: component.Version
286
+ });
287
+ }
288
+
289
+ // Get compiled component from registry service (local compilation)
290
+ const compiledComponent = await this.registryService.getCompiledComponent(
291
+ component.ID,
292
+ this.resolverInstanceId,
293
+ contextUser
294
+ );
295
+ resolved[spec.name] = compiledComponent;
296
+ if (this.debug) {
297
+ console.log(`📦 [ComponentResolver] Successfully compiled and resolved local component: ${spec.name}, type: ${typeof compiledComponent}`);
298
+ }
299
+ } else {
300
+ console.error(`❌ [ComponentResolver] Local registry component NOT found in database: ${spec.name} with namespace: ${spec.namespace || namespace}`);
301
+ if (this.debug) {
302
+ console.warn(`Local registry component not found in database: ${spec.name}`);
303
+ }
255
304
  }
256
305
  }
257
306
  } catch (error) {
@@ -260,44 +309,87 @@ export class ComponentResolver {
260
309
  }
261
310
  }
262
311
  } else {
263
- // Embedded component - get from local registry
312
+ // Embedded/Local component
264
313
  // Use the component's specified namespace if it has one, otherwise use parent's namespace
265
314
  const componentNamespace = spec.namespace || namespace;
266
- if (this.debug) {
267
- console.log(`🔍 [ComponentResolver] Looking for embedded component: ${spec.name} in namespace: ${componentNamespace}`);
268
- }
269
315
 
270
- const component = this.registry.get(spec.name, componentNamespace);
271
- if (component) {
272
- resolved[spec.name] = component;
316
+ // First check if component has inline code that needs compilation
317
+ if (spec.code && this.compiler) {
273
318
  if (this.debug) {
274
- console.log(`✅ [ComponentResolver] Found embedded component: ${spec.name}, type: ${typeof component}`);
319
+ console.log(`🔨 [ComponentResolver] Component ${spec.name} has inline code, compiling...`);
275
320
  }
276
- if (this.debug) {
277
- console.log(`📄 Resolved embedded component: ${spec.name} from namespace ${componentNamespace}, type:`, typeof component);
321
+
322
+ try {
323
+ // Compile the component with its code
324
+ const compilationResult = await this.compiler.compile({
325
+ componentName: spec.name,
326
+ componentCode: spec.code,
327
+ libraries: spec.libraries,
328
+ dependencies: spec.dependencies,
329
+ allLibraries: [] // TODO: Get from ComponentMetadataEngine if needed
330
+ });
331
+
332
+ if (compilationResult.success && compilationResult.component) {
333
+ // Get the component object from the factory (only if we have runtimeContext)
334
+ if (!this.runtimeContext) {
335
+ console.error(`❌ [ComponentResolver] Cannot compile without runtime context`);
336
+ return;
337
+ }
338
+ const componentObject = compilationResult.component.factory(this.runtimeContext);
339
+
340
+ // Register it in the local registry for future use
341
+ this.registry.register(spec.name, componentObject, componentNamespace, spec.version || 'latest');
342
+
343
+ // Add to resolved
344
+ resolved[spec.name] = componentObject;
345
+
346
+ if (this.debug) {
347
+ console.log(`✅ [ComponentResolver] Successfully compiled and registered inline component: ${spec.name}`);
348
+ }
349
+ } else {
350
+ console.error(`❌ [ComponentResolver] Failed to compile inline component ${spec.name}:`, compilationResult.error);
351
+ }
352
+ } catch (error) {
353
+ console.error(`❌ [ComponentResolver] Error compiling inline component ${spec.name}:`, error);
278
354
  }
279
355
  } else {
280
- // If not found with specified namespace, try the parent namespace as fallback
356
+ // No inline code, try to get from local registry
281
357
  if (this.debug) {
282
- console.log(`⚠️ [ComponentResolver] Not found in namespace ${componentNamespace}, trying fallback namespace: ${namespace}`);
358
+ console.log(`🔍 [ComponentResolver] Looking for embedded component: ${spec.name} in namespace: ${componentNamespace}`);
283
359
  }
284
- const fallbackComponent = this.registry.get(spec.name, namespace);
285
- if (fallbackComponent) {
286
- resolved[spec.name] = fallbackComponent;
360
+
361
+ const component = this.registry.get(spec.name, componentNamespace);
362
+ if (component) {
363
+ resolved[spec.name] = component;
287
364
  if (this.debug) {
288
- console.log(`✅ [ComponentResolver] Found embedded component in fallback namespace: ${spec.name}, type: ${typeof fallbackComponent}`);
365
+ console.log(`✅ [ComponentResolver] Found embedded component: ${spec.name}, type: ${typeof component}`);
289
366
  }
290
367
  if (this.debug) {
291
- console.log(`📄 Resolved embedded component: ${spec.name} from fallback namespace ${namespace}, type:`, typeof fallbackComponent);
368
+ console.log(`📄 Resolved embedded component: ${spec.name} from namespace ${componentNamespace}, type:`, typeof component);
292
369
  }
293
370
  } else {
294
- // Component not found - this might cause issues later
295
- console.error(`❌ [ComponentResolver] Could not resolve embedded component: ${spec.name} in namespace ${componentNamespace} or ${namespace}`);
371
+ // If not found with specified namespace, try the parent namespace as fallback
296
372
  if (this.debug) {
297
- console.warn(`⚠️ Could not resolve embedded component: ${spec.name} in namespace ${componentNamespace} or ${namespace}`);
373
+ console.log(`⚠️ [ComponentResolver] Not found in namespace ${componentNamespace}, trying fallback namespace: ${namespace}`);
374
+ }
375
+ const fallbackComponent = this.registry.get(spec.name, namespace);
376
+ if (fallbackComponent) {
377
+ resolved[spec.name] = fallbackComponent;
378
+ if (this.debug) {
379
+ console.log(`✅ [ComponentResolver] Found embedded component in fallback namespace: ${spec.name}, type: ${typeof fallbackComponent}`);
380
+ }
381
+ if (this.debug) {
382
+ console.log(`📄 Resolved embedded component: ${spec.name} from fallback namespace ${namespace}, type:`, typeof fallbackComponent);
383
+ }
384
+ } else {
385
+ // Component not found - this might cause issues later
386
+ console.error(`❌ [ComponentResolver] Could not resolve embedded component: ${spec.name} in namespace ${componentNamespace} or ${namespace}`);
387
+ if (this.debug) {
388
+ console.warn(`⚠️ Could not resolve embedded component: ${spec.name} in namespace ${componentNamespace} or ${namespace}`);
389
+ }
390
+ // Store undefined explicitly so we know it failed to resolve
391
+ resolved[spec.name] = undefined;
298
392
  }
299
- // Store undefined explicitly so we know it failed to resolve
300
- resolved[spec.name] = undefined;
301
393
  }
302
394
  }
303
395
  }
@@ -5,7 +5,7 @@
5
5
 
6
6
  export { ComponentRegistry } from './component-registry';
7
7
  export { ComponentResolver, ResolvedComponents } from './component-resolver';
8
- export { ComponentRegistryService } from './component-registry-service';
8
+ export { ComponentRegistryService, IComponentRegistryClient } from './component-registry-service';
9
9
  export {
10
10
  RegistryProvider,
11
11
  RegistryComponentMetadata,
@@ -14,7 +14,7 @@ import { ComponentCompiler } from '../compiler';
14
14
  import { ComponentRegistry } from '../registry';
15
15
 
16
16
  import { ComponentSpec, ComponentStyles } from '@memberjunction/interactive-component-types';
17
- import { UserInfo } from '@memberjunction/core';
17
+ import { UserInfo, Metadata } from '@memberjunction/core';
18
18
  import { ComponentLibraryEntity } from '@memberjunction/core-entities';
19
19
 
20
20
  /**
@@ -25,6 +25,8 @@ export interface HierarchyRegistrationResult {
25
25
  registeredComponents: string[];
26
26
  errors: ComponentRegistrationError[];
27
27
  warnings: string[];
28
+ /** The fully resolved component specification with all dependencies and libraries */
29
+ resolvedSpec?: ComponentSpec;
28
30
  }
29
31
 
30
32
  /**
@@ -55,6 +57,8 @@ export interface HierarchyRegistrationOptions {
55
57
  */
56
58
  allLibraries: ComponentLibraryEntity[];
57
59
  debug?: boolean;
60
+ /** Optional user context for fetching from external registries */
61
+ contextUser?: UserInfo;
58
62
  }
59
63
 
60
64
  /**
@@ -66,6 +70,44 @@ export class ComponentHierarchyRegistrar {
66
70
  private registry: ComponentRegistry,
67
71
  private runtimeContext: RuntimeContext
68
72
  ) {}
73
+
74
+ /**
75
+ * Fetches a component specification from an external registry
76
+ */
77
+ private async fetchExternalComponent(
78
+ spec: ComponentSpec,
79
+ contextUser?: UserInfo
80
+ ): Promise<ComponentSpec | null> {
81
+ try {
82
+ const provider = Metadata?.Provider;
83
+ if (!provider || !(provider as any).ExecuteGQL) {
84
+ console.warn('⚠️ [ComponentHierarchyRegistrar] No GraphQL provider available for external registry fetch');
85
+ return null;
86
+ }
87
+
88
+ // Dynamically import the GraphQL client to avoid circular dependencies
89
+ const { GraphQLComponentRegistryClient } = await import('@memberjunction/graphql-dataprovider');
90
+ const graphQLClient = new GraphQLComponentRegistryClient(provider as any);
91
+
92
+ const fullSpec = await graphQLClient.GetRegistryComponent({
93
+ registryName: spec.registry!,
94
+ namespace: spec.namespace || 'Global',
95
+ name: spec.name,
96
+ version: spec.version || 'latest'
97
+ });
98
+
99
+ if (fullSpec && fullSpec.code) {
100
+ console.log(`✅ [ComponentHierarchyRegistrar] Fetched external component ${spec.name} with code (${fullSpec.code.length} chars)`);
101
+ return fullSpec;
102
+ } else {
103
+ console.warn(`⚠️ [ComponentHierarchyRegistrar] Failed to fetch external component ${spec.name} or no code`);
104
+ return null;
105
+ }
106
+ } catch (error) {
107
+ console.error(`❌ [ComponentHierarchyRegistrar] Error fetching external component ${spec.name}:`, error);
108
+ return null;
109
+ }
110
+ }
69
111
 
70
112
  /**
71
113
  * Registers a complete component hierarchy from a root specification
@@ -77,6 +119,12 @@ export class ComponentHierarchyRegistrar {
77
119
  rootSpec: ComponentSpec,
78
120
  options: HierarchyRegistrationOptions
79
121
  ): Promise<HierarchyRegistrationResult> {
122
+ // If this is an external registry component without code, fetch it first
123
+ let resolvedRootSpec = rootSpec;
124
+ if (rootSpec.location === 'registry' && rootSpec.registry && !rootSpec.code) {
125
+ console.log(`🌐 [ComponentHierarchyRegistrar] Fetching external registry component: ${rootSpec.registry}/${rootSpec.name}`);
126
+ resolvedRootSpec = await this.fetchExternalComponent(rootSpec, options.contextUser) || rootSpec;
127
+ }
80
128
  const {
81
129
  styles,
82
130
  namespace = 'Global',
@@ -86,10 +134,10 @@ export class ComponentHierarchyRegistrar {
86
134
  } = options;
87
135
 
88
136
  console.log('🌳 ComponentHierarchyRegistrar.registerHierarchy:', {
89
- rootComponent: rootSpec.name,
90
- hasLibraries: !!(rootSpec.libraries && rootSpec.libraries.length > 0),
91
- libraryCount: rootSpec.libraries?.length || 0,
92
- libraries: rootSpec.libraries?.map(l => l.name)
137
+ rootComponent: resolvedRootSpec.name,
138
+ hasLibraries: !!(resolvedRootSpec.libraries && resolvedRootSpec.libraries.length > 0),
139
+ libraryCount: resolvedRootSpec.libraries?.length || 0,
140
+ libraries: resolvedRootSpec.libraries?.map(l => l.name)
93
141
  });
94
142
 
95
143
  const registeredComponents: string[] = [];
@@ -99,17 +147,26 @@ export class ComponentHierarchyRegistrar {
99
147
  // PHASE 1: Compile all components first (but defer factory execution)
100
148
  const compiledMap = new Map<string, CompiledComponent>();
101
149
  const specMap = new Map<string, ComponentSpec>();
150
+ const allLoadedLibraries = new Map<string, any>(); // Track all loaded libraries
102
151
 
103
152
  // Helper to compile a component without calling its factory
104
153
  const compileOnly = async (spec: ComponentSpec): Promise<{ success: boolean; error?: ComponentRegistrationError }> => {
105
154
  if (!spec.code) return { success: true };
106
155
 
107
156
  try {
157
+ // Filter out invalid library entries before compilation
158
+ const validLibraries = spec.libraries?.filter(lib => {
159
+ if (!lib || typeof lib !== 'object') return false;
160
+ if (!lib.name || lib.name === 'unknown' || lib.name === 'null' || lib.name === 'undefined') return false;
161
+ if (!lib.globalVariable || lib.globalVariable === 'undefined' || lib.globalVariable === 'null') return false;
162
+ return true;
163
+ });
164
+
108
165
  const compileOptions: CompileOptions = {
109
166
  componentName: spec.name,
110
167
  componentCode: spec.code,
111
168
  styles,
112
- libraries: spec.libraries,
169
+ libraries: validLibraries,
113
170
  dependencies: spec.dependencies,
114
171
  allLibraries: options.allLibraries
115
172
  };
@@ -118,6 +175,17 @@ export class ComponentHierarchyRegistrar {
118
175
  if (result.success && result.component) {
119
176
  compiledMap.set(spec.name, result.component);
120
177
  specMap.set(spec.name, spec);
178
+
179
+ // Extract and accumulate loaded libraries from the compilation
180
+ if (result.loadedLibraries) {
181
+ result.loadedLibraries.forEach((value, key) => {
182
+ if (!allLoadedLibraries.has(key)) {
183
+ allLoadedLibraries.set(key, value);
184
+ console.log(`📚 [registerHierarchy] Added library ${key} to accumulated libraries`);
185
+ }
186
+ });
187
+ }
188
+
121
189
  return { success: true };
122
190
  } else {
123
191
  return {
@@ -142,19 +210,30 @@ export class ComponentHierarchyRegistrar {
142
210
  };
143
211
 
144
212
  // Compile all components in hierarchy
145
- const compileQueue = [rootSpec];
213
+ const compileQueue = [resolvedRootSpec];
146
214
  const visited = new Set<string>();
147
215
 
148
216
  while (compileQueue.length > 0) {
149
- const spec = compileQueue.shift()!;
217
+ let spec = compileQueue.shift()!;
150
218
  if (visited.has(spec.name)) continue;
151
219
  visited.add(spec.name);
152
220
 
221
+ // If this is an external registry component without code, fetch it first
222
+ if (spec.location === 'registry' && spec.registry && !spec.code) {
223
+ const fetched = await this.fetchExternalComponent(spec, options.contextUser);
224
+ if (fetched) {
225
+ spec = fetched;
226
+ } else {
227
+ console.warn(`⚠️ [ComponentHierarchyRegistrar] Could not fetch external component ${spec.name}, skipping`);
228
+ continue;
229
+ }
230
+ }
231
+
153
232
  const result = await compileOnly(spec);
154
233
  if (!result.success) {
155
234
  errors.push(result.error!);
156
235
  if (!continueOnError) {
157
- return { success: false, registeredComponents, errors, warnings };
236
+ return { success: false, registeredComponents, errors, warnings, resolvedSpec: resolvedRootSpec };
158
237
  }
159
238
  }
160
239
 
@@ -163,6 +242,17 @@ export class ComponentHierarchyRegistrar {
163
242
  }
164
243
  }
165
244
 
245
+ // Add all accumulated libraries to runtime context
246
+ if (allLoadedLibraries.size > 0) {
247
+ if (!this.runtimeContext.libraries) {
248
+ this.runtimeContext.libraries = {};
249
+ }
250
+ allLoadedLibraries.forEach((value, key) => {
251
+ this.runtimeContext.libraries![key] = value;
252
+ console.log(`✅ [registerHierarchy] Added ${key} to runtime context libraries`);
253
+ });
254
+ }
255
+
166
256
  // PHASE 2: Execute all factories with components available
167
257
  for (const [name, compiled] of compiledMap) {
168
258
  const spec = specMap.get(name)!;
@@ -193,7 +283,8 @@ export class ComponentHierarchyRegistrar {
193
283
  success: errors.length === 0,
194
284
  registeredComponents,
195
285
  errors,
196
- warnings
286
+ warnings,
287
+ resolvedSpec: resolvedRootSpec
197
288
  };
198
289
  }
199
290
 
@@ -237,19 +328,28 @@ export class ComponentHierarchyRegistrar {
237
328
  };
238
329
  }
239
330
 
331
+ // Filter out invalid library entries before compilation
332
+ const validLibraries = spec.libraries?.filter(lib => {
333
+ if (!lib || typeof lib !== 'object') return false;
334
+ if (!lib.name || lib.name === 'unknown' || lib.name === 'null' || lib.name === 'undefined') return false;
335
+ if (!lib.globalVariable || lib.globalVariable === 'undefined' || lib.globalVariable === 'null') return false;
336
+ return true;
337
+ });
338
+
240
339
  // Compile the component
241
340
  const compileOptions: CompileOptions = {
242
341
  componentName: spec.name,
243
342
  componentCode: spec.code,
244
343
  styles,
245
- libraries: spec.libraries, // Pass along library dependencies from the spec
344
+ libraries: validLibraries, // Pass along filtered library dependencies
246
345
  dependencies: spec.dependencies, // Pass along child component dependencies
247
346
  allLibraries: options.allLibraries
248
347
  };
249
348
 
250
349
  console.log(`🔧 Compiling component ${spec.name} with libraries:`, {
251
- libraryCount: spec.libraries?.length || 0,
252
- libraries: spec.libraries?.map(l => ({ name: l.name, globalVariable: l.globalVariable }))
350
+ originalCount: spec.libraries?.length || 0,
351
+ filteredCount: validLibraries?.length || 0,
352
+ validLibraries: validLibraries?.map(l => ({ name: l.name, globalVariable: l.globalVariable }))
253
353
  });
254
354
 
255
355
  const compilationResult = await this.compiler.compile(compileOptions);
@@ -265,6 +365,17 @@ export class ComponentHierarchyRegistrar {
265
365
  };
266
366
  }
267
367
 
368
+ // Add loaded libraries to runtime context
369
+ if (compilationResult.loadedLibraries && compilationResult.loadedLibraries.size > 0) {
370
+ if (!this.runtimeContext.libraries) {
371
+ this.runtimeContext.libraries = {};
372
+ }
373
+ compilationResult.loadedLibraries.forEach((value, key) => {
374
+ this.runtimeContext.libraries![key] = value;
375
+ console.log(`✅ [registerSingleComponent] Added ${key} to runtime context libraries`);
376
+ });
377
+ }
378
+
268
379
  // Call the factory to create the ComponentObject
269
380
  // IMPORTANT: We don't pass components here because child components may not be registered yet
270
381
  // Components are resolved later when the component is actually rendered
@@ -175,6 +175,8 @@ export interface CompilationResult {
175
175
  duration: number;
176
176
  /** Size of compiled code in bytes */
177
177
  size?: number;
178
+ /** Libraries loaded during compilation */
179
+ loadedLibraries?: Map<string, any>;
178
180
  }
179
181
 
180
182
  /**