@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.
- package/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +28 -0
- package/README.md +180 -2
- package/dist/compiler/component-compiler.d.ts +1 -0
- package/dist/compiler/component-compiler.d.ts.map +1 -1
- package/dist/compiler/component-compiler.js +253 -61
- package/dist/compiler/component-compiler.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -5
- package/dist/index.js.map +1 -1
- package/dist/registry/component-registry-service.d.ts +6 -3
- package/dist/registry/component-registry-service.d.ts.map +1 -1
- package/dist/registry/component-registry-service.js +38 -11
- package/dist/registry/component-registry-service.js.map +1 -1
- package/dist/registry/component-registry.d.ts +6 -3
- package/dist/registry/component-registry.d.ts.map +1 -1
- package/dist/registry/component-registry.js +17 -0
- package/dist/registry/component-registry.js.map +1 -1
- package/dist/registry/component-resolver.d.ts +2 -1
- package/dist/registry/component-resolver.d.ts.map +1 -1
- package/dist/registry/component-resolver.js +101 -14
- package/dist/registry/component-resolver.js.map +1 -1
- package/dist/runtime/component-hierarchy.d.ts.map +1 -1
- package/dist/runtime/component-hierarchy.js +75 -13
- package/dist/runtime/component-hierarchy.js.map +1 -1
- package/dist/runtime/prop-builder.d.ts +2 -2
- package/dist/runtime/prop-builder.d.ts.map +1 -1
- package/dist/runtime/prop-builder.js +32 -14
- package/dist/runtime/prop-builder.js.map +1 -1
- package/dist/runtime.umd.js +1 -1
- package/dist/types/dependency-types.d.ts +62 -0
- package/dist/types/dependency-types.d.ts.map +1 -0
- package/dist/types/dependency-types.js +3 -0
- package/dist/types/dependency-types.js.map +1 -0
- package/dist/types/index.d.ts +8 -10
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utilities/index.d.ts +1 -0
- package/dist/utilities/index.d.ts.map +1 -1
- package/dist/utilities/index.js +1 -0
- package/dist/utilities/index.js.map +1 -1
- package/dist/utilities/library-dependency-resolver.d.ts +19 -0
- package/dist/utilities/library-dependency-resolver.d.ts.map +1 -0
- package/dist/utilities/library-dependency-resolver.js +419 -0
- package/dist/utilities/library-dependency-resolver.js.map +1 -0
- package/dist/utilities/library-loader.d.ts +9 -0
- package/dist/utilities/library-loader.d.ts.map +1 -1
- package/dist/utilities/library-loader.js +164 -0
- package/dist/utilities/library-loader.js.map +1 -1
- package/package.json +5 -5
- package/src/compiler/component-compiler.ts +280 -82
- package/src/index.ts +20 -5
- package/src/registry/component-registry-service.ts +53 -14
- package/src/registry/component-registry.ts +36 -7
- package/src/registry/component-resolver.ts +130 -16
- package/src/runtime/component-hierarchy.ts +101 -27
- package/src/runtime/prop-builder.ts +38 -18
- package/src/types/dependency-types.ts +110 -0
- package/src/types/index.ts +17 -21
- package/src/utilities/index.ts +1 -0
- package/src/utilities/library-dependency-resolver.ts +613 -0
- 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:
|
|
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<
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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):
|
|
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
|
|
175
|
+
* @returns Object mapping component names to component objects
|
|
175
176
|
*/
|
|
176
|
-
getAll(namespace: string = 'Global', version: string = 'v1'): Record<string,
|
|
177
|
-
const components: Record<string,
|
|
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
|
-
|
|
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
|
-
|
|
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(`📦
|
|
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.
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
//
|
|
151
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
//
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
//
|
|
201
|
-
|
|
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
|
-
|
|
280
|
+
componentObject,
|
|
207
281
|
spec.namespace || namespace,
|
|
208
282
|
version
|
|
209
283
|
);
|