@memberjunction/react-runtime 2.112.0 → 2.113.1

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 (43) hide show
  1. package/.turbo/turbo-build.log +11 -10
  2. package/CHANGELOG.md +14 -1
  3. package/dist/compiler/component-compiler.d.ts.map +1 -1
  4. package/dist/compiler/component-compiler.js +56 -66
  5. package/dist/compiler/component-compiler.js.map +1 -1
  6. package/dist/component-manager/component-manager.d.ts.map +1 -1
  7. package/dist/component-manager/component-manager.js +42 -48
  8. package/dist/component-manager/component-manager.js.map +1 -1
  9. package/dist/component-manager/types.d.ts +1 -1
  10. package/dist/component-manager/types.d.ts.map +1 -1
  11. package/dist/component-manager/types.js.map +1 -1
  12. package/dist/registry/component-registry-service.d.ts +1 -1
  13. package/dist/registry/component-registry-service.d.ts.map +1 -1
  14. package/dist/registry/component-registry-service.js +34 -34
  15. package/dist/registry/component-registry-service.js.map +1 -1
  16. package/dist/registry/component-resolver.d.ts +1 -1
  17. package/dist/registry/component-resolver.d.ts.map +1 -1
  18. package/dist/registry/component-resolver.js +11 -10
  19. package/dist/registry/component-resolver.js.map +1 -1
  20. package/dist/runtime/component-hierarchy.d.ts +1 -1
  21. package/dist/runtime/component-hierarchy.d.ts.map +1 -1
  22. package/dist/runtime/component-hierarchy.js +43 -43
  23. package/dist/runtime/component-hierarchy.js.map +1 -1
  24. package/dist/runtime/prop-builder.d.ts.map +1 -1
  25. package/dist/runtime/prop-builder.js +24 -22
  26. package/dist/runtime/prop-builder.js.map +1 -1
  27. package/dist/runtime.umd.js +511 -494
  28. package/dist/types/index.d.ts.map +1 -1
  29. package/dist/types/index.js.map +1 -1
  30. package/dist/utilities/library-registry.d.ts +1 -1
  31. package/dist/utilities/library-registry.d.ts.map +1 -1
  32. package/dist/utilities/library-registry.js +10 -10
  33. package/dist/utilities/library-registry.js.map +1 -1
  34. package/package.json +6 -5
  35. package/src/compiler/component-compiler.ts +164 -186
  36. package/src/component-manager/component-manager.ts +216 -162
  37. package/src/component-manager/types.ts +27 -27
  38. package/src/registry/component-registry-service.ts +218 -190
  39. package/src/registry/component-resolver.ts +98 -87
  40. package/src/runtime/component-hierarchy.ts +94 -57
  41. package/src/runtime/prop-builder.ts +33 -28
  42. package/src/types/index.ts +4 -3
  43. package/src/utilities/library-registry.ts +19 -14
@@ -4,13 +4,20 @@
4
4
  */
5
5
 
6
6
  import { ComponentSpec, ComponentLibraryDependency } from '@memberjunction/interactive-component-types';
7
- import { UserInfo, Metadata, LogError } from '@memberjunction/global';
7
+ import { UserInfo, Metadata, LogError } from '@memberjunction/core';
8
8
  import { ComponentMetadataEngine, ComponentLibraryEntity, ComponentEntityExtended } from '@memberjunction/core-entities';
9
9
 
10
10
  import { ComponentCompiler } from '../compiler';
11
11
  import { ComponentRegistry } from '../registry';
12
12
  import { RuntimeContext, ComponentObject } from '../types';
13
- import { LoadOptions, LoadResult, HierarchyResult, ComponentManagerConfig, CacheEntry, ResolutionMode } from './types';
13
+ import {
14
+ LoadOptions,
15
+ LoadResult,
16
+ HierarchyResult,
17
+ ComponentManagerConfig,
18
+ CacheEntry,
19
+ ResolutionMode
20
+ } from './types';
14
21
 
15
22
  /**
16
23
  * Unified component management system that handles all component operations
@@ -21,16 +28,16 @@ export class ComponentManager {
21
28
  private registry: ComponentRegistry;
22
29
  private runtimeContext: RuntimeContext;
23
30
  private config: ComponentManagerConfig;
24
-
31
+
25
32
  // Caching
26
33
  private fetchCache: Map<string, CacheEntry> = new Map();
27
34
  private registryNotifications: Set<string> = new Set();
28
35
  private loadingPromises: Map<string, Promise<LoadResult>> = new Map();
29
-
36
+
30
37
  // Metadata engine
31
38
  private componentEngine = ComponentMetadataEngine.Instance;
32
39
  private graphQLClient: any = null;
33
-
40
+
34
41
  constructor(
35
42
  compiler: ComponentCompiler,
36
43
  registry: ComponentRegistry,
@@ -47,41 +54,44 @@ export class ComponentManager {
47
54
  enableUsageTracking: true,
48
55
  dependencyBatchSize: 5,
49
56
  fetchTimeout: 30000,
50
- ...config,
57
+ ...config
51
58
  };
52
-
59
+
53
60
  this.log('ComponentManager initialized', {
54
61
  debug: this.config.debug,
55
62
  cacheTTL: this.config.cacheTTL,
56
- usageTracking: this.config.enableUsageTracking,
63
+ usageTracking: this.config.enableUsageTracking
57
64
  });
58
65
  }
59
-
66
+
60
67
  /**
61
68
  * Main entry point - intelligently handles all component operations
62
69
  */
63
- async loadComponent(spec: ComponentSpec, options: LoadOptions = {}): Promise<LoadResult> {
70
+ async loadComponent(
71
+ spec: ComponentSpec,
72
+ options: LoadOptions = {}
73
+ ): Promise<LoadResult> {
64
74
  const startTime = Date.now();
65
75
  const componentKey = this.getComponentKey(spec, options);
66
-
67
- this.log(`Loading component: ${spec.name}`, {
68
- key: componentKey,
76
+
77
+ this.log(`Loading component: ${spec.name}`, {
78
+ key: componentKey,
69
79
  location: spec.location,
70
80
  registry: spec.registry,
71
- forceRefresh: options.forceRefresh,
81
+ forceRefresh: options.forceRefresh
72
82
  });
73
-
83
+
74
84
  // Check if already loading to prevent duplicate work
75
85
  const existingPromise = this.loadingPromises.get(componentKey);
76
86
  if (existingPromise && !options.forceRefresh) {
77
87
  this.log(`Component already loading: ${spec.name}, waiting...`);
78
88
  return existingPromise;
79
89
  }
80
-
90
+
81
91
  // Create loading promise
82
92
  const loadPromise = this.doLoadComponent(spec, options, componentKey, startTime);
83
93
  this.loadingPromises.set(componentKey, loadPromise);
84
-
94
+
85
95
  try {
86
96
  const result = await loadPromise;
87
97
  return result;
@@ -89,45 +99,50 @@ export class ComponentManager {
89
99
  this.loadingPromises.delete(componentKey);
90
100
  }
91
101
  }
92
-
102
+
93
103
  /**
94
104
  * Internal method that does the actual loading
95
105
  */
96
- private async doLoadComponent(spec: ComponentSpec, options: LoadOptions, componentKey: string, startTime: number): Promise<LoadResult> {
106
+ private async doLoadComponent(
107
+ spec: ComponentSpec,
108
+ options: LoadOptions,
109
+ componentKey: string,
110
+ startTime: number
111
+ ): Promise<LoadResult> {
97
112
  const errors: LoadResult['errors'] = [];
98
-
113
+
99
114
  try {
100
115
  // STEP 1: Check if already loaded in ComponentRegistry
101
116
  const namespace = spec.namespace || options.defaultNamespace || 'Global';
102
117
  const version = spec.version || options.defaultVersion || 'latest';
103
-
118
+
104
119
  const existing = this.registry.get(spec.name, namespace, version);
105
120
  if (existing && !options.forceRefresh && !options.forceRecompile) {
106
121
  this.log(`Component found in registry: ${spec.name}`);
107
-
122
+
108
123
  // Still need to notify registry for usage tracking
109
124
  if (options.trackUsage !== false) {
110
125
  await this.notifyRegistryUsageIfNeeded(spec, componentKey);
111
126
  }
112
-
127
+
113
128
  // Get cached spec if available
114
129
  const cachedEntry = this.fetchCache.get(componentKey);
115
-
130
+
116
131
  return {
117
132
  success: true,
118
133
  component: existing,
119
134
  spec: cachedEntry?.spec || spec,
120
- fromCache: true,
135
+ fromCache: true
121
136
  };
122
137
  }
123
-
138
+
124
139
  // STEP 2: Fetch full spec if needed
125
140
  let fullSpec = spec;
126
141
  if (this.needsFetch(spec)) {
127
142
  this.log(`Fetching component spec: ${spec.name}`);
128
143
  try {
129
144
  fullSpec = await this.fetchComponentSpec(spec, options.contextUser, {
130
- resolutionMode: options.resolutionMode,
145
+ resolutionMode: options.resolutionMode
131
146
  });
132
147
 
133
148
  // Cache the fetched spec
@@ -135,13 +150,13 @@ export class ComponentManager {
135
150
  spec: fullSpec,
136
151
  fetchedAt: new Date(),
137
152
  hash: await this.calculateHash(fullSpec),
138
- usageNotified: false,
153
+ usageNotified: false
139
154
  });
140
155
  } catch (error) {
141
156
  errors.push({
142
157
  message: `Failed to fetch component: ${error instanceof Error ? error.message : String(error)}`,
143
158
  phase: 'fetch',
144
- componentName: spec.name,
159
+ componentName: spec.name
145
160
  });
146
161
  throw error;
147
162
  }
@@ -150,7 +165,7 @@ export class ComponentManager {
150
165
  if (spec.location === 'registry' && spec.code) {
151
166
  this.log(`Skipping fetch for registry component: ${spec.name} (code already provided)`, {
152
167
  location: spec.location,
153
- registry: spec.registry,
168
+ registry: spec.registry
154
169
  });
155
170
  }
156
171
  // Also cache the spec if it has code to avoid re-fetching
@@ -159,16 +174,16 @@ export class ComponentManager {
159
174
  spec: fullSpec,
160
175
  fetchedAt: new Date(),
161
176
  hash: await this.calculateHash(fullSpec),
162
- usageNotified: false,
177
+ usageNotified: false
163
178
  });
164
179
  }
165
180
  }
166
-
181
+
167
182
  // STEP 3: Notify registry of usage (exactly once per session)
168
183
  if (options.trackUsage !== false) {
169
184
  await this.notifyRegistryUsageIfNeeded(fullSpec, componentKey);
170
185
  }
171
-
186
+
172
187
  // STEP 4: Compile if needed
173
188
  let compiledComponent = existing;
174
189
  if (!compiledComponent || options.forceRecompile) {
@@ -179,29 +194,39 @@ export class ComponentManager {
179
194
  errors.push({
180
195
  message: `Failed to compile component: ${error instanceof Error ? error.message : String(error)}`,
181
196
  phase: 'compile',
182
- componentName: spec.name,
197
+ componentName: spec.name
183
198
  });
184
199
  throw error;
185
200
  }
186
201
  }
187
-
202
+
188
203
  // STEP 5: Register in ComponentRegistry
189
204
  if (!existing || options.forceRefresh || options.forceRecompile) {
190
205
  this.log(`Registering component: ${spec.name}`);
191
- this.registry.register(fullSpec.name, compiledComponent, namespace, version);
206
+ this.registry.register(
207
+ fullSpec.name,
208
+ compiledComponent,
209
+ namespace,
210
+ version
211
+ );
192
212
  }
193
-
213
+
194
214
  // STEP 6: Process dependencies recursively
195
215
  const dependencies: Record<string, ComponentObject> = {};
196
216
  if (fullSpec.dependencies && fullSpec.dependencies.length > 0) {
197
217
  this.log(`Loading ${fullSpec.dependencies.length} dependencies for ${spec.name}`);
198
-
218
+
199
219
  // Load dependencies in batches for efficiency
200
- const depResults = await this.loadDependenciesBatched(fullSpec.dependencies, { ...options, isDependent: true });
201
-
220
+ const depResults = await this.loadDependenciesBatched(
221
+ fullSpec.dependencies,
222
+ { ...options, isDependent: true }
223
+ );
224
+
202
225
  for (const result of depResults) {
203
226
  if (result.success && result.component) {
204
- const depSpec = fullSpec.dependencies.find((d) => d.name === (result.spec?.name || ''));
227
+ const depSpec = fullSpec.dependencies.find(d =>
228
+ d.name === (result.spec?.name || '')
229
+ );
205
230
  if (depSpec) {
206
231
  dependencies[depSpec.name] = result.component;
207
232
  }
@@ -210,46 +235,45 @@ export class ComponentManager {
210
235
  }
211
236
  }
212
237
  }
213
-
238
+
214
239
  const elapsed = Date.now() - startTime;
215
240
  this.log(`Component loaded successfully: ${spec.name} (${elapsed}ms)`, {
216
241
  fromCache: false,
217
- dependencyCount: Object.keys(dependencies).length,
242
+ dependencyCount: Object.keys(dependencies).length
218
243
  });
219
-
244
+
220
245
  return {
221
246
  success: errors.length === 0,
222
247
  component: compiledComponent,
223
248
  spec: fullSpec,
224
249
  fromCache: false,
225
250
  dependencies,
226
- errors: errors.length > 0 ? errors : undefined,
251
+ errors: errors.length > 0 ? errors : undefined
227
252
  };
253
+
228
254
  } catch (error) {
229
255
  const elapsed = Date.now() - startTime;
230
256
  this.log(`Failed to load component: ${spec.name} (${elapsed}ms)`, error);
231
-
257
+
232
258
  return {
233
259
  success: false,
234
260
  fromCache: false,
235
- errors:
236
- errors.length > 0
237
- ? errors
238
- : [
239
- {
240
- message: error instanceof Error ? error.message : String(error),
241
- phase: 'fetch',
242
- componentName: spec.name,
243
- },
244
- ],
261
+ errors: errors.length > 0 ? errors : [{
262
+ message: error instanceof Error ? error.message : String(error),
263
+ phase: 'fetch',
264
+ componentName: spec.name
265
+ }]
245
266
  };
246
267
  }
247
268
  }
248
-
269
+
249
270
  /**
250
271
  * Load a complete hierarchy efficiently
251
272
  */
252
- async loadHierarchy(rootSpec: ComponentSpec, options: LoadOptions = {}): Promise<HierarchyResult> {
273
+ async loadHierarchy(
274
+ rootSpec: ComponentSpec,
275
+ options: LoadOptions = {}
276
+ ): Promise<HierarchyResult> {
253
277
  const startTime = Date.now();
254
278
  const loaded: string[] = [];
255
279
  const errors: HierarchyResult['errors'] = [];
@@ -258,32 +282,40 @@ export class ComponentManager {
258
282
  fromCache: 0,
259
283
  fetched: 0,
260
284
  compiled: 0,
261
- totalTime: 0,
285
+ totalTime: 0
262
286
  };
263
-
287
+
264
288
  this.log(`Loading component hierarchy: ${rootSpec.name}`, {
265
289
  location: rootSpec.location,
266
- registry: rootSpec.registry,
290
+ registry: rootSpec.registry
267
291
  });
268
-
292
+
269
293
  try {
270
294
  // Initialize component engine if needed (skip in browser context where it doesn't exist)
271
295
  if (this.componentEngine && typeof this.componentEngine.Config === 'function') {
272
296
  await this.componentEngine.Config(false, options.contextUser);
273
297
  }
274
-
298
+
275
299
  // Load the root component and all its dependencies
276
- const result = await this.loadComponentRecursive(rootSpec, options, loaded, errors, components, stats, new Set());
277
-
300
+ const result = await this.loadComponentRecursive(
301
+ rootSpec,
302
+ options,
303
+ loaded,
304
+ errors,
305
+ components,
306
+ stats,
307
+ new Set()
308
+ );
309
+
278
310
  stats.totalTime = Date.now() - startTime;
279
-
311
+
280
312
  this.log(`Hierarchy loaded: ${rootSpec.name}`, {
281
313
  success: errors.length === 0,
282
314
  loadedCount: loaded.length,
283
315
  errors: errors.length,
284
- ...stats,
316
+ ...stats
285
317
  });
286
-
318
+
287
319
  // Unwrap components before returning
288
320
  // Components are ComponentObject wrappers, but consumers expect just the React components
289
321
  const unwrappedComponents: Record<string, ComponentObject> = {};
@@ -296,7 +328,7 @@ export class ComponentManager {
296
328
  unwrappedComponents[name] = componentObject;
297
329
  }
298
330
  }
299
-
331
+
300
332
  return {
301
333
  success: errors.length === 0,
302
334
  rootComponent: result.component,
@@ -304,29 +336,27 @@ export class ComponentManager {
304
336
  loadedComponents: loaded,
305
337
  errors,
306
338
  components: unwrappedComponents,
307
- stats,
339
+ stats
308
340
  };
341
+
309
342
  } catch (error) {
310
343
  stats.totalTime = Date.now() - startTime;
311
-
344
+
312
345
  this.log(`Failed to load hierarchy: ${rootSpec.name}`, error);
313
-
346
+
314
347
  return {
315
348
  success: false,
316
349
  loadedComponents: loaded,
317
- errors: [
318
- ...errors,
319
- {
320
- message: error instanceof Error ? error.message : String(error),
321
- phase: 'fetch',
322
- componentName: rootSpec.name,
323
- },
324
- ],
325
- stats,
350
+ errors: [...errors, {
351
+ message: error instanceof Error ? error.message : String(error),
352
+ phase: 'fetch',
353
+ componentName: rootSpec.name
354
+ }],
355
+ stats
326
356
  };
327
357
  }
328
358
  }
329
-
359
+
330
360
  /**
331
361
  * Recursively load a component and its dependencies
332
362
  */
@@ -340,7 +370,7 @@ export class ComponentManager {
340
370
  visited: Set<string>
341
371
  ): Promise<LoadResult> {
342
372
  const componentKey = this.getComponentKey(spec, options);
343
-
373
+
344
374
  // Prevent circular dependencies
345
375
  if (visited.has(componentKey)) {
346
376
  this.log(`Circular dependency detected: ${spec.name}`);
@@ -348,18 +378,18 @@ export class ComponentManager {
348
378
  success: true,
349
379
  component: components[spec.name],
350
380
  spec,
351
- fromCache: true,
381
+ fromCache: true
352
382
  };
353
383
  }
354
384
  visited.add(componentKey);
355
-
385
+
356
386
  // Load this component
357
387
  const result = await this.loadComponent(spec, options);
358
-
388
+
359
389
  if (result.success && result.component) {
360
390
  loaded.push(spec.name);
361
391
  components[spec.name] = result.component;
362
-
392
+
363
393
  // Update stats
364
394
  if (stats) {
365
395
  if (result.fromCache) stats.fromCache++;
@@ -368,7 +398,7 @@ export class ComponentManager {
368
398
  stats.compiled++;
369
399
  }
370
400
  }
371
-
401
+
372
402
  // Load dependencies
373
403
  if (result.spec?.dependencies) {
374
404
  for (const dep of result.spec.dependencies) {
@@ -391,33 +421,44 @@ export class ComponentManager {
391
421
  this.log(`Dependency ${depSpec.name} is a local registry component (registry=undefined)`);
392
422
  }
393
423
 
394
- await this.loadComponentRecursive(depSpec, { ...options, isDependent: true }, loaded, errors, components, stats, visited);
424
+ await this.loadComponentRecursive(
425
+ depSpec,
426
+ { ...options, isDependent: true },
427
+ loaded,
428
+ errors,
429
+ components,
430
+ stats,
431
+ visited
432
+ );
395
433
  }
396
434
  }
397
435
  } else if (result.errors) {
398
436
  errors.push(...result.errors);
399
437
  }
400
-
438
+
401
439
  return result;
402
440
  }
403
-
441
+
404
442
  /**
405
443
  * Load dependencies in batches for efficiency
406
444
  */
407
- private async loadDependenciesBatched(dependencies: ComponentSpec[], options: LoadOptions): Promise<LoadResult[]> {
445
+ private async loadDependenciesBatched(
446
+ dependencies: ComponentSpec[],
447
+ options: LoadOptions
448
+ ): Promise<LoadResult[]> {
408
449
  const batchSize = this.config.dependencyBatchSize || 5;
409
450
  const results: LoadResult[] = [];
410
-
451
+
411
452
  for (let i = 0; i < dependencies.length; i += batchSize) {
412
453
  const batch = dependencies.slice(i, i + batchSize);
413
- const batchPromises = batch.map((dep) => this.loadComponent(dep, options));
454
+ const batchPromises = batch.map(dep => this.loadComponent(dep, options));
414
455
  const batchResults = await Promise.all(batchPromises);
415
456
  results.push(...batchResults);
416
457
  }
417
-
458
+
418
459
  return results;
419
460
  }
420
-
461
+
421
462
  /**
422
463
  * Check if a component needs to be fetched from a registry
423
464
  */
@@ -427,10 +468,10 @@ export class ComponentManager {
427
468
  // 2. It's missing required fields
428
469
  return spec.location === 'registry' && !spec.code;
429
470
  }
430
-
471
+
431
472
  /**
432
473
  * Fetch a component specification from a registry (local or external)
433
- *
474
+ *
434
475
  * Convention: When location === 'registry' and registry === undefined,
435
476
  * the component is looked up in the local ComponentMetadataEngine.
436
477
  * This allows components to reference local registry components without
@@ -444,94 +485,100 @@ export class ComponentManager {
444
485
  // Check cache first
445
486
  const cacheKey = this.getComponentKey(spec, {});
446
487
  const cached = this.fetchCache.get(cacheKey);
447
-
488
+
448
489
  if (cached && this.isCacheValid(cached)) {
449
490
  this.log(`Using cached spec for: ${spec.name}`);
450
491
  return cached.spec;
451
492
  }
452
-
493
+
453
494
  // Handle LOCAL registry components (registry is null/undefined)
454
495
  if (!spec.registry) {
455
496
  this.log(`Fetching from local registry: ${spec.name}`);
456
-
497
+
457
498
  // Find component in local ComponentMetadataEngine
458
- const localComponent = this.componentEngine.Components?.find((c: ComponentEntityExtended) => {
459
- // Match by name (case-insensitive for better compatibility)
460
- const nameMatch = c.Name?.toLowerCase() === spec.name?.toLowerCase();
461
-
462
- // Match by namespace if provided (handle different formats)
463
- const namespaceMatch = !spec.namespace || c.Namespace?.toLowerCase() === spec.namespace?.toLowerCase();
464
-
465
- if (nameMatch && !namespaceMatch) {
499
+ const localComponent = this.componentEngine.Components?.find(
500
+ (c: ComponentEntityExtended) => {
501
+ // Match by name (case-insensitive for better compatibility)
502
+ const nameMatch = c.Name?.toLowerCase() === spec.name?.toLowerCase();
503
+
504
+ // Match by namespace if provided (handle different formats)
505
+ const namespaceMatch = !spec.namespace || c.Namespace?.toLowerCase() === spec.namespace?.toLowerCase();
506
+
507
+ if (nameMatch && !namespaceMatch) {
508
+ }
509
+
510
+ return nameMatch && namespaceMatch;
466
511
  }
467
-
468
- return nameMatch && namespaceMatch;
469
- });
470
-
512
+ );
513
+
471
514
  if (!localComponent) {
472
515
  throw new Error(`Local component not found: ${spec.name}`);
473
516
  }
474
-
517
+
475
518
  // Parse specification from local component
476
519
  if (!localComponent.Specification) {
477
520
  throw new Error(`Local component ${spec.name} has no specification`);
478
521
  }
479
-
522
+
480
523
  const fullSpec = JSON.parse(localComponent.Specification);
481
-
524
+
482
525
  // Cache it
483
526
  this.fetchCache.set(cacheKey, {
484
527
  spec: fullSpec,
485
528
  fetchedAt: new Date(),
486
- usageNotified: false,
529
+ usageNotified: false
487
530
  });
488
-
531
+
489
532
  return fullSpec;
490
533
  }
491
-
534
+
492
535
  // Handle EXTERNAL registry components (registry has a name)
493
536
  // Initialize GraphQL client if needed
494
537
  if (!this.graphQLClient) {
495
538
  await this.initializeGraphQLClient();
496
539
  }
497
-
540
+
498
541
  if (!this.graphQLClient) {
499
542
  throw new Error('GraphQL client not available for registry fetching');
500
543
  }
501
-
544
+
502
545
  // Fetch from external registry
503
546
  this.log(`Fetching from external registry: ${spec.registry}/${spec.name}`);
504
-
547
+
505
548
  const fullSpec = await this.graphQLClient.GetRegistryComponent({
506
549
  registryName: spec.registry,
507
550
  namespace: spec.namespace || 'Global',
508
551
  name: spec.name,
509
- version: spec.version || 'latest',
552
+ version: spec.version || 'latest'
510
553
  });
511
-
554
+
512
555
  if (!fullSpec) {
513
556
  throw new Error(`Component not found in registry: ${spec.registry}/${spec.name}`);
514
557
  }
515
-
558
+
516
559
  // Apply resolution mode if specified
517
560
  const processedSpec = this.applyResolutionMode(fullSpec, spec, options?.resolutionMode);
518
-
561
+
519
562
  // Cache it
520
563
  this.fetchCache.set(cacheKey, {
521
564
  spec: processedSpec,
522
565
  fetchedAt: new Date(),
523
- usageNotified: false,
566
+ usageNotified: false
524
567
  });
525
-
568
+
526
569
  return processedSpec;
527
570
  }
528
-
571
+
529
572
  /**
530
573
  * Apply resolution mode to a fetched spec (recursively including dependencies)
531
574
  */
532
- private applyResolutionMode(fullSpec: ComponentSpec, originalSpec: ComponentSpec, resolutionMode?: ResolutionMode): ComponentSpec {
575
+ private applyResolutionMode(
576
+ fullSpec: ComponentSpec,
577
+ originalSpec: ComponentSpec,
578
+ resolutionMode?: ResolutionMode
579
+ ): ComponentSpec {
533
580
  let processedSpec: ComponentSpec;
534
-
581
+
535
582
  if (resolutionMode === 'embed') {
536
583
  // Convert to embedded format for test harness
537
584
  processedSpec = {
@@ -548,47 +595,51 @@ export class ComponentManager {
548
595
  location: originalSpec.location,
549
596
  registry: originalSpec.registry,
550
597
  namespace: originalSpec.namespace || fullSpec.namespace,
551
- name: originalSpec.name || fullSpec.name,
598
+ name: originalSpec.name || fullSpec.name
552
599
  };
553
600
  }
554
-
601
+
555
602
  // Recursively apply resolution mode to dependencies
556
603
  if (processedSpec.dependencies && processedSpec.dependencies.length > 0) {
557
- processedSpec.dependencies = processedSpec.dependencies.map((dep) => {
604
+ processedSpec.dependencies = processedSpec.dependencies.map(dep => {
558
605
  // For dependencies, use the dep itself as both full and original spec
559
606
  // since they've already been fetched and processed
560
607
  return this.applyResolutionMode(dep, dep, resolutionMode);
561
608
  });
562
609
  }
563
-
610
+
564
611
  return processedSpec;
565
612
  }
566
613
 
567
614
  /**
568
615
  * Compile a component specification
569
616
  */
570
- private async compileComponent(spec: ComponentSpec, options: LoadOptions): Promise<ComponentObject> {
617
+ private async compileComponent(
618
+ spec: ComponentSpec,
619
+ options: LoadOptions
620
+ ): Promise<ComponentObject> {
571
621
  // Get all available libraries - use passed libraries or fall back to ComponentMetadataEngine
572
622
  const allLibraries = options.allLibraries || this.componentEngine.ComponentLibraries || [];
573
-
623
+
574
624
  // Filter valid libraries
575
- const validLibraries = spec.libraries?.filter(
576
- (lib) => lib && lib.name && lib.globalVariable && lib.name !== 'unknown' && lib.globalVariable !== 'undefined'
625
+ const validLibraries = spec.libraries?.filter(lib =>
626
+ lib && lib.name && lib.globalVariable &&
627
+ lib.name !== 'unknown' && lib.globalVariable !== 'undefined'
577
628
  );
578
-
629
+
579
630
  // Compile the component
580
631
  const result = await this.compiler.compile({
581
632
  componentName: spec.name,
582
633
  componentCode: spec.code || '',
583
634
  libraries: validLibraries,
584
635
  dependencies: spec.dependencies,
585
- allLibraries,
636
+ allLibraries
586
637
  });
587
-
638
+
588
639
  if (!result.success || !result.component) {
589
640
  throw new Error(result.error?.message || 'Compilation failed');
590
641
  }
591
-
642
+
592
643
  // Add loaded libraries to runtime context
593
644
  if (result.loadedLibraries && result.loadedLibraries.size > 0) {
594
645
  if (!this.runtimeContext.libraries) {
@@ -598,38 +649,41 @@ export class ComponentManager {
598
649
  this.runtimeContext.libraries![key] = value;
599
650
  });
600
651
  }
601
-
652
+
602
653
  // Get the component object from the factory
603
654
  const componentObject = result.component.factory(
604
655
  this.runtimeContext,
605
656
  undefined, // styles
606
657
  {} // components - will be injected by parent
607
658
  );
608
-
659
+
609
660
  return componentObject;
610
661
  }
611
-
662
+
612
663
  /**
613
664
  * Notify registry of component usage for licensing
614
665
  * Only happens once per component per session
615
666
  */
616
- private async notifyRegistryUsageIfNeeded(spec: ComponentSpec, componentKey: string): Promise<void> {
667
+ private async notifyRegistryUsageIfNeeded(
668
+ spec: ComponentSpec,
669
+ componentKey: string
670
+ ): Promise<void> {
617
671
  if (!spec.registry || !this.config.enableUsageTracking) {
618
672
  return; // Only for external registry components with tracking enabled
619
673
  }
620
-
674
+
621
675
  const notificationKey = `${spec.registry}:${componentKey}`;
622
676
  if (this.registryNotifications.has(notificationKey)) {
623
677
  this.log(`Usage already notified for: ${spec.name}`);
624
678
  return; // Already notified this session
625
679
  }
626
-
680
+
627
681
  try {
628
682
  // In the future, make lightweight usage notification call to registry
629
683
  // For now, the fetch itself serves as the notification
630
684
  this.log(`Notifying registry usage for: ${spec.name}`);
631
685
  this.registryNotifications.add(notificationKey);
632
-
686
+
633
687
  // Update cache entry
634
688
  const cached = this.fetchCache.get(componentKey);
635
689
  if (cached) {
@@ -640,7 +694,7 @@ export class ComponentManager {
640
694
  console.warn(`Failed to notify registry usage for ${componentKey}:`, error);
641
695
  }
642
696
  }
643
-
697
+
644
698
  /**
645
699
  * Initialize GraphQL client for registry operations
646
700
  */
@@ -656,7 +710,7 @@ export class ComponentManager {
656
710
  LogError(`Failed to initialize GraphQL client: ${error instanceof Error ? error.message : String(error)}`);
657
711
  }
658
712
  }
659
-
713
+
660
714
  /**
661
715
  * Check if a cache entry is still valid
662
716
  */
@@ -664,7 +718,7 @@ export class ComponentManager {
664
718
  const age = Date.now() - entry.fetchedAt.getTime();
665
719
  return age < (this.config.cacheTTL || 3600000);
666
720
  }
667
-
721
+
668
722
  /**
669
723
  * Calculate a hash for a component spec (for cache validation)
670
724
  */
@@ -674,19 +728,19 @@ export class ComponentManager {
674
728
  name: spec.name,
675
729
  version: spec.version,
676
730
  code: spec.code,
677
- libraries: spec.libraries,
731
+ libraries: spec.libraries
678
732
  });
679
-
733
+
680
734
  // Simple hash function (in production, use crypto)
681
735
  let hash = 0;
682
736
  for (let i = 0; i < content.length; i++) {
683
737
  const char = content.charCodeAt(i);
684
- hash = (hash << 5) - hash + char;
738
+ hash = ((hash << 5) - hash) + char;
685
739
  hash = hash & hash; // Convert to 32bit integer
686
740
  }
687
741
  return hash.toString(16);
688
742
  }
689
-
743
+
690
744
  /**
691
745
  * Generate a unique key for a component
692
746
  */
@@ -696,7 +750,7 @@ export class ComponentManager {
696
750
  const version = spec.version || options.defaultVersion || 'latest';
697
751
  return `${registry}:${namespace}:${spec.name}:${version}`;
698
752
  }
699
-
753
+
700
754
  /**
701
755
  * Clear all caches
702
756
  */
@@ -706,7 +760,7 @@ export class ComponentManager {
706
760
  this.loadingPromises.clear();
707
761
  this.log('All caches cleared');
708
762
  }
709
-
763
+
710
764
  /**
711
765
  * Get cache statistics
712
766
  */
@@ -718,10 +772,10 @@ export class ComponentManager {
718
772
  return {
719
773
  fetchCacheSize: this.fetchCache.size,
720
774
  notificationsCount: this.registryNotifications.size,
721
- loadingCount: this.loadingPromises.size,
775
+ loadingCount: this.loadingPromises.size
722
776
  };
723
777
  }
724
-
778
+
725
779
  /**
726
780
  * Log a message if debug is enabled
727
781
  */
@@ -730,4 +784,4 @@ export class ComponentManager {
730
784
  console.log(`🎯 [ComponentManager] ${message}`, data || '');
731
785
  }
732
786
  }
733
- }
787
+ }