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