@memberjunction/react-runtime 2.112.0 → 2.113.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 +11 -10
- package/CHANGELOG.md +15 -1
- package/dist/compiler/component-compiler.d.ts.map +1 -1
- package/dist/compiler/component-compiler.js +56 -66
- package/dist/compiler/component-compiler.js.map +1 -1
- package/dist/component-manager/component-manager.d.ts.map +1 -1
- package/dist/component-manager/component-manager.js +42 -48
- package/dist/component-manager/component-manager.js.map +1 -1
- package/dist/component-manager/types.d.ts +1 -1
- package/dist/component-manager/types.d.ts.map +1 -1
- package/dist/component-manager/types.js.map +1 -1
- package/dist/registry/component-registry-service.d.ts +1 -1
- package/dist/registry/component-registry-service.d.ts.map +1 -1
- package/dist/registry/component-registry-service.js +34 -34
- package/dist/registry/component-registry-service.js.map +1 -1
- package/dist/registry/component-resolver.d.ts +1 -1
- package/dist/registry/component-resolver.d.ts.map +1 -1
- package/dist/registry/component-resolver.js +11 -10
- package/dist/registry/component-resolver.js.map +1 -1
- package/dist/runtime/component-hierarchy.d.ts +1 -1
- package/dist/runtime/component-hierarchy.d.ts.map +1 -1
- package/dist/runtime/component-hierarchy.js +43 -43
- package/dist/runtime/component-hierarchy.js.map +1 -1
- package/dist/runtime/prop-builder.d.ts.map +1 -1
- package/dist/runtime/prop-builder.js +24 -22
- package/dist/runtime/prop-builder.js.map +1 -1
- package/dist/runtime.umd.js +511 -494
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utilities/library-registry.d.ts +1 -1
- package/dist/utilities/library-registry.d.ts.map +1 -1
- package/dist/utilities/library-registry.js +10 -10
- package/dist/utilities/library-registry.js.map +1 -1
- package/package.json +6 -5
- package/src/compiler/component-compiler.ts +164 -186
- package/src/component-manager/component-manager.ts +216 -162
- package/src/component-manager/types.ts +27 -27
- package/src/registry/component-registry-service.ts +218 -190
- package/src/registry/component-resolver.ts +98 -87
- package/src/runtime/component-hierarchy.ts +94 -57
- package/src/runtime/prop-builder.ts +33 -28
- package/src/types/index.ts +4 -3
- package/src/utilities/library-registry.ts +19 -14
|
@@ -4,12 +4,17 @@
|
|
|
4
4
|
* @module @memberjunction/react-runtime/hierarchy
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
CompilationResult,
|
|
9
|
+
CompileOptions,
|
|
10
|
+
RuntimeContext,
|
|
11
|
+
CompiledComponent
|
|
12
|
+
} from '../types';
|
|
8
13
|
import { ComponentCompiler } from '../compiler';
|
|
9
14
|
import { ComponentRegistry } from '../registry';
|
|
10
15
|
|
|
11
16
|
import { ComponentSpec, ComponentStyles } from '@memberjunction/interactive-component-types';
|
|
12
|
-
import { UserInfo, Metadata, LogStatus, GetProductionStatus } from '@memberjunction/
|
|
17
|
+
import { UserInfo, Metadata, LogStatus, GetProductionStatus } from '@memberjunction/core';
|
|
13
18
|
import { ComponentLibraryEntity } from '@memberjunction/core-entities';
|
|
14
19
|
|
|
15
20
|
/**
|
|
@@ -65,27 +70,30 @@ export class ComponentHierarchyRegistrar {
|
|
|
65
70
|
private registry: ComponentRegistry,
|
|
66
71
|
private runtimeContext: RuntimeContext
|
|
67
72
|
) {}
|
|
68
|
-
|
|
73
|
+
|
|
69
74
|
/**
|
|
70
75
|
* Fetches a component specification from an external registry
|
|
71
76
|
*/
|
|
72
|
-
private async fetchExternalComponent(
|
|
77
|
+
private async fetchExternalComponent(
|
|
78
|
+
spec: ComponentSpec,
|
|
79
|
+
contextUser?: UserInfo
|
|
80
|
+
): Promise<ComponentSpec | null> {
|
|
73
81
|
try {
|
|
74
82
|
const provider = Metadata?.Provider;
|
|
75
83
|
if (!provider || !(provider as any).ExecuteGQL) {
|
|
76
84
|
console.warn('⚠️ [ComponentHierarchyRegistrar] No GraphQL provider available for external registry fetch');
|
|
77
85
|
return null;
|
|
78
86
|
}
|
|
79
|
-
|
|
87
|
+
|
|
80
88
|
// Dynamically import the GraphQL client to avoid circular dependencies
|
|
81
89
|
const { GraphQLComponentRegistryClient } = await import('@memberjunction/graphql-dataprovider');
|
|
82
90
|
const graphQLClient = new GraphQLComponentRegistryClient(provider as any);
|
|
83
|
-
|
|
91
|
+
|
|
84
92
|
const fullSpec = await graphQLClient.GetRegistryComponent({
|
|
85
93
|
registryName: spec.registry!,
|
|
86
94
|
namespace: spec.namespace || 'Global',
|
|
87
95
|
name: spec.name,
|
|
88
|
-
version: spec.version || 'latest'
|
|
96
|
+
version: spec.version || 'latest'
|
|
89
97
|
});
|
|
90
98
|
|
|
91
99
|
if (fullSpec && fullSpec.code) {
|
|
@@ -109,16 +117,25 @@ export class ComponentHierarchyRegistrar {
|
|
|
109
117
|
* @param options - Registration options
|
|
110
118
|
* @returns Registration result with details about success/failures
|
|
111
119
|
*/
|
|
112
|
-
async registerHierarchy(
|
|
120
|
+
async registerHierarchy(
|
|
121
|
+
rootSpec: ComponentSpec,
|
|
122
|
+
options: HierarchyRegistrationOptions
|
|
123
|
+
): Promise<HierarchyRegistrationResult> {
|
|
113
124
|
// If this is an external registry component without code, fetch it first
|
|
114
125
|
let resolvedRootSpec = rootSpec;
|
|
115
126
|
if (rootSpec.location === 'registry' && rootSpec.registry && !rootSpec.code) {
|
|
116
127
|
if (!GetProductionStatus()) {
|
|
117
128
|
LogStatus(`🌐 [ComponentHierarchyRegistrar] Fetching external registry component: ${rootSpec.registry}/${rootSpec.name}`);
|
|
118
129
|
}
|
|
119
|
-
resolvedRootSpec =
|
|
130
|
+
resolvedRootSpec = await this.fetchExternalComponent(rootSpec, options.contextUser) || rootSpec;
|
|
120
131
|
}
|
|
121
|
-
const {
|
|
132
|
+
const {
|
|
133
|
+
styles,
|
|
134
|
+
namespace = 'Global',
|
|
135
|
+
version = 'v1',
|
|
136
|
+
continueOnError = true,
|
|
137
|
+
allowOverride = true
|
|
138
|
+
} = options;
|
|
122
139
|
|
|
123
140
|
const registeredComponents: string[] = [];
|
|
124
141
|
const errors: ComponentRegistrationError[] = [];
|
|
@@ -128,7 +145,7 @@ export class ComponentHierarchyRegistrar {
|
|
|
128
145
|
LogStatus('🌳 ComponentHierarchyRegistrar.registerHierarchy:', undefined, {
|
|
129
146
|
rootComponent: resolvedRootSpec.name,
|
|
130
147
|
hasLibraries: !!(resolvedRootSpec.libraries && resolvedRootSpec.libraries.length > 0),
|
|
131
|
-
libraryCount: resolvedRootSpec.libraries?.length || 0
|
|
148
|
+
libraryCount: resolvedRootSpec.libraries?.length || 0
|
|
132
149
|
});
|
|
133
150
|
}
|
|
134
151
|
|
|
@@ -136,34 +153,34 @@ export class ComponentHierarchyRegistrar {
|
|
|
136
153
|
const compiledMap = new Map<string, CompiledComponent>();
|
|
137
154
|
const specMap = new Map<string, ComponentSpec>();
|
|
138
155
|
const allLoadedLibraries = new Map<string, any>(); // Track all loaded libraries
|
|
139
|
-
|
|
156
|
+
|
|
140
157
|
// Helper to compile a component without calling its factory
|
|
141
158
|
const compileOnly = async (spec: ComponentSpec): Promise<{ success: boolean; error?: ComponentRegistrationError }> => {
|
|
142
159
|
if (!spec.code) return { success: true };
|
|
143
|
-
|
|
160
|
+
|
|
144
161
|
try {
|
|
145
162
|
// Filter out invalid library entries before compilation
|
|
146
|
-
const validLibraries = spec.libraries?.filter(
|
|
163
|
+
const validLibraries = spec.libraries?.filter(lib => {
|
|
147
164
|
if (!lib || typeof lib !== 'object') return false;
|
|
148
165
|
if (!lib.name || lib.name === 'unknown' || lib.name === 'null' || lib.name === 'undefined') return false;
|
|
149
166
|
if (!lib.globalVariable || lib.globalVariable === 'undefined' || lib.globalVariable === 'null') return false;
|
|
150
167
|
return true;
|
|
151
168
|
});
|
|
152
|
-
|
|
169
|
+
|
|
153
170
|
const compileOptions: CompileOptions = {
|
|
154
171
|
componentName: spec.name,
|
|
155
172
|
componentCode: spec.code,
|
|
156
173
|
styles,
|
|
157
174
|
libraries: validLibraries,
|
|
158
175
|
dependencies: spec.dependencies,
|
|
159
|
-
allLibraries: options.allLibraries
|
|
176
|
+
allLibraries: options.allLibraries
|
|
160
177
|
};
|
|
161
|
-
|
|
178
|
+
|
|
162
179
|
const result = await this.compiler.compile(compileOptions);
|
|
163
180
|
if (result.success && result.component) {
|
|
164
181
|
compiledMap.set(spec.name, result.component);
|
|
165
182
|
specMap.set(spec.name, spec);
|
|
166
|
-
|
|
183
|
+
|
|
167
184
|
// Extract and accumulate loaded libraries from the compilation
|
|
168
185
|
if (result.loadedLibraries) {
|
|
169
186
|
result.loadedLibraries.forEach((value, key) => {
|
|
@@ -175,7 +192,7 @@ export class ComponentHierarchyRegistrar {
|
|
|
175
192
|
}
|
|
176
193
|
});
|
|
177
194
|
}
|
|
178
|
-
|
|
195
|
+
|
|
179
196
|
return { success: true };
|
|
180
197
|
} else {
|
|
181
198
|
return {
|
|
@@ -183,8 +200,8 @@ export class ComponentHierarchyRegistrar {
|
|
|
183
200
|
error: {
|
|
184
201
|
componentName: spec.name,
|
|
185
202
|
error: result.error?.message || 'Unknown compilation error',
|
|
186
|
-
phase: 'compilation'
|
|
187
|
-
}
|
|
203
|
+
phase: 'compilation'
|
|
204
|
+
}
|
|
188
205
|
};
|
|
189
206
|
}
|
|
190
207
|
} catch (error) {
|
|
@@ -193,21 +210,21 @@ export class ComponentHierarchyRegistrar {
|
|
|
193
210
|
error: {
|
|
194
211
|
componentName: spec.name,
|
|
195
212
|
error: error instanceof Error ? error.message : String(error),
|
|
196
|
-
phase: 'compilation'
|
|
197
|
-
}
|
|
213
|
+
phase: 'compilation'
|
|
214
|
+
}
|
|
198
215
|
};
|
|
199
216
|
}
|
|
200
217
|
};
|
|
201
|
-
|
|
218
|
+
|
|
202
219
|
// Compile all components in hierarchy
|
|
203
220
|
const compileQueue = [resolvedRootSpec];
|
|
204
221
|
const visited = new Set<string>();
|
|
205
|
-
|
|
222
|
+
|
|
206
223
|
while (compileQueue.length > 0) {
|
|
207
224
|
let spec = compileQueue.shift()!;
|
|
208
225
|
if (visited.has(spec.name)) continue;
|
|
209
226
|
visited.add(spec.name);
|
|
210
|
-
|
|
227
|
+
|
|
211
228
|
// If this is an external registry component without code, fetch it first
|
|
212
229
|
if (spec.location === 'registry' && spec.registry && !spec.code) {
|
|
213
230
|
const fetched = await this.fetchExternalComponent(spec, options.contextUser);
|
|
@@ -218,7 +235,7 @@ export class ComponentHierarchyRegistrar {
|
|
|
218
235
|
continue;
|
|
219
236
|
}
|
|
220
237
|
}
|
|
221
|
-
|
|
238
|
+
|
|
222
239
|
const result = await compileOnly(spec);
|
|
223
240
|
if (!result.success) {
|
|
224
241
|
errors.push(result.error!);
|
|
@@ -226,12 +243,12 @@ export class ComponentHierarchyRegistrar {
|
|
|
226
243
|
return { success: false, registeredComponents, errors, warnings, resolvedSpec: resolvedRootSpec };
|
|
227
244
|
}
|
|
228
245
|
}
|
|
229
|
-
|
|
246
|
+
|
|
230
247
|
if (spec.dependencies) {
|
|
231
248
|
compileQueue.push(...spec.dependencies);
|
|
232
249
|
}
|
|
233
250
|
}
|
|
234
|
-
|
|
251
|
+
|
|
235
252
|
// Add all accumulated libraries to runtime context
|
|
236
253
|
if (allLoadedLibraries.size > 0) {
|
|
237
254
|
if (!this.runtimeContext.libraries) {
|
|
@@ -244,11 +261,11 @@ export class ComponentHierarchyRegistrar {
|
|
|
244
261
|
}
|
|
245
262
|
});
|
|
246
263
|
}
|
|
247
|
-
|
|
264
|
+
|
|
248
265
|
// PHASE 2: Execute all factories with components available
|
|
249
266
|
for (const [name, compiled] of compiledMap) {
|
|
250
267
|
const spec = specMap.get(name)!;
|
|
251
|
-
|
|
268
|
+
|
|
252
269
|
// Build components object from all registered components
|
|
253
270
|
const components: Record<string, any> = {};
|
|
254
271
|
for (const [depName, depCompiled] of compiledMap) {
|
|
@@ -256,13 +273,18 @@ export class ComponentHierarchyRegistrar {
|
|
|
256
273
|
const depObject = depCompiled.factory(this.runtimeContext, styles);
|
|
257
274
|
components[depName] = depObject.component;
|
|
258
275
|
}
|
|
259
|
-
|
|
276
|
+
|
|
260
277
|
// Now call factory with components available
|
|
261
278
|
const componentObject = compiled.factory(this.runtimeContext, styles, components);
|
|
262
|
-
|
|
279
|
+
|
|
263
280
|
// Register in registry
|
|
264
|
-
this.registry.register(
|
|
265
|
-
|
|
281
|
+
this.registry.register(
|
|
282
|
+
spec.name,
|
|
283
|
+
componentObject,
|
|
284
|
+
spec.namespace || namespace,
|
|
285
|
+
version
|
|
286
|
+
);
|
|
287
|
+
|
|
266
288
|
registeredComponents.push(spec.name);
|
|
267
289
|
}
|
|
268
290
|
|
|
@@ -271,7 +293,7 @@ export class ComponentHierarchyRegistrar {
|
|
|
271
293
|
registeredComponents,
|
|
272
294
|
errors,
|
|
273
295
|
warnings,
|
|
274
|
-
resolvedSpec: resolvedRootSpec
|
|
296
|
+
resolvedSpec: resolvedRootSpec
|
|
275
297
|
};
|
|
276
298
|
}
|
|
277
299
|
|
|
@@ -298,7 +320,7 @@ export class ComponentHierarchyRegistrar {
|
|
|
298
320
|
if (!spec.code) {
|
|
299
321
|
return {
|
|
300
322
|
success: true,
|
|
301
|
-
error: undefined
|
|
323
|
+
error: undefined
|
|
302
324
|
};
|
|
303
325
|
}
|
|
304
326
|
|
|
@@ -310,13 +332,13 @@ export class ComponentHierarchyRegistrar {
|
|
|
310
332
|
error: {
|
|
311
333
|
componentName: spec.name,
|
|
312
334
|
error: `Component already registered in ${namespace}/${version}`,
|
|
313
|
-
phase: 'registration'
|
|
314
|
-
}
|
|
335
|
+
phase: 'registration'
|
|
336
|
+
}
|
|
315
337
|
};
|
|
316
338
|
}
|
|
317
339
|
|
|
318
340
|
// Filter out invalid library entries before compilation
|
|
319
|
-
const validLibraries = spec.libraries?.filter(
|
|
341
|
+
const validLibraries = spec.libraries?.filter(lib => {
|
|
320
342
|
if (!lib || typeof lib !== 'object') return false;
|
|
321
343
|
if (!lib.name || lib.name === 'unknown' || lib.name === 'null' || lib.name === 'undefined') return false;
|
|
322
344
|
if (!lib.globalVariable || lib.globalVariable === 'undefined' || lib.globalVariable === 'null') return false;
|
|
@@ -327,7 +349,7 @@ export class ComponentHierarchyRegistrar {
|
|
|
327
349
|
LogStatus(`🔧 Compiling component ${spec.name} with libraries:`, undefined, {
|
|
328
350
|
originalCount: spec.libraries?.length || 0,
|
|
329
351
|
filteredCount: validLibraries?.length || 0,
|
|
330
|
-
libraries: validLibraries?.map(
|
|
352
|
+
libraries: validLibraries?.map(l => l.name) || []
|
|
331
353
|
});
|
|
332
354
|
}
|
|
333
355
|
|
|
@@ -338,7 +360,7 @@ export class ComponentHierarchyRegistrar {
|
|
|
338
360
|
styles,
|
|
339
361
|
libraries: validLibraries, // Pass along filtered library dependencies
|
|
340
362
|
dependencies: spec.dependencies, // Pass along child component dependencies
|
|
341
|
-
allLibraries: options.allLibraries
|
|
363
|
+
allLibraries: options.allLibraries
|
|
342
364
|
};
|
|
343
365
|
|
|
344
366
|
const compilationResult = await this.compiler.compile(compileOptions);
|
|
@@ -349,8 +371,8 @@ export class ComponentHierarchyRegistrar {
|
|
|
349
371
|
error: {
|
|
350
372
|
componentName: spec.name,
|
|
351
373
|
error: compilationResult.error?.message || 'Unknown compilation error',
|
|
352
|
-
phase: 'compilation'
|
|
353
|
-
}
|
|
374
|
+
phase: 'compilation'
|
|
375
|
+
}
|
|
354
376
|
};
|
|
355
377
|
}
|
|
356
378
|
|
|
@@ -374,23 +396,29 @@ export class ComponentHierarchyRegistrar {
|
|
|
374
396
|
LogStatus(`🏭 Calling factory for ${spec.name} with runtime context:`, undefined, {
|
|
375
397
|
hasReact: !!this.runtimeContext.React,
|
|
376
398
|
hasReactDOM: !!this.runtimeContext.ReactDOM,
|
|
377
|
-
libraryCount: Object.keys(this.runtimeContext.libraries || {}).length
|
|
399
|
+
libraryCount: Object.keys(this.runtimeContext.libraries || {}).length
|
|
378
400
|
});
|
|
379
401
|
}
|
|
380
402
|
const componentObject = compilationResult.component!.factory(this.runtimeContext, styles);
|
|
381
403
|
|
|
382
404
|
// Register the full ComponentObject (not just the React component)
|
|
383
|
-
this.registry.register(
|
|
405
|
+
this.registry.register(
|
|
406
|
+
spec.name,
|
|
407
|
+
componentObject,
|
|
408
|
+
spec.namespace || namespace,
|
|
409
|
+
version
|
|
410
|
+
);
|
|
384
411
|
|
|
385
412
|
return { success: true };
|
|
413
|
+
|
|
386
414
|
} catch (error) {
|
|
387
415
|
return {
|
|
388
416
|
success: false,
|
|
389
417
|
error: {
|
|
390
418
|
componentName: spec.name,
|
|
391
419
|
error: error instanceof Error ? error.message : String(error),
|
|
392
|
-
phase: 'registration'
|
|
393
|
-
}
|
|
420
|
+
phase: 'registration'
|
|
421
|
+
}
|
|
394
422
|
};
|
|
395
423
|
}
|
|
396
424
|
}
|
|
@@ -417,7 +445,7 @@ export class ComponentHierarchyRegistrar {
|
|
|
417
445
|
namespace: options.namespace,
|
|
418
446
|
version: options.version,
|
|
419
447
|
allowOverride: options.allowOverride,
|
|
420
|
-
allLibraries: options.allLibraries
|
|
448
|
+
allLibraries: options.allLibraries
|
|
421
449
|
});
|
|
422
450
|
|
|
423
451
|
if (childResult.success) {
|
|
@@ -434,7 +462,13 @@ export class ComponentHierarchyRegistrar {
|
|
|
434
462
|
// Register nested children recursively
|
|
435
463
|
const nestedChildren = child.dependencies || [];
|
|
436
464
|
if (nestedChildren.length > 0) {
|
|
437
|
-
await this.registerChildComponents(
|
|
465
|
+
await this.registerChildComponents(
|
|
466
|
+
nestedChildren,
|
|
467
|
+
options,
|
|
468
|
+
registeredComponents,
|
|
469
|
+
errors,
|
|
470
|
+
warnings
|
|
471
|
+
);
|
|
438
472
|
}
|
|
439
473
|
}
|
|
440
474
|
}
|
|
@@ -486,7 +520,7 @@ export function validateComponentSpec(spec: ComponentSpec): string[] {
|
|
|
486
520
|
const children = spec.dependencies || [];
|
|
487
521
|
children.forEach((child, index) => {
|
|
488
522
|
const childErrors = validateComponentSpec(child);
|
|
489
|
-
childErrors.forEach(
|
|
523
|
+
childErrors.forEach(error => {
|
|
490
524
|
errors.push(`Child ${index} (${child.name || 'unnamed'}): ${error}`);
|
|
491
525
|
});
|
|
492
526
|
});
|
|
@@ -501,9 +535,9 @@ export function validateComponentSpec(spec: ComponentSpec): string[] {
|
|
|
501
535
|
*/
|
|
502
536
|
export function flattenComponentHierarchy(rootSpec: ComponentSpec): ComponentSpec[] {
|
|
503
537
|
const components: ComponentSpec[] = [rootSpec];
|
|
504
|
-
|
|
538
|
+
|
|
505
539
|
const children = rootSpec.dependencies || [];
|
|
506
|
-
children.forEach(
|
|
540
|
+
children.forEach(child => {
|
|
507
541
|
components.push(...flattenComponentHierarchy(child));
|
|
508
542
|
});
|
|
509
543
|
|
|
@@ -516,17 +550,20 @@ export function flattenComponentHierarchy(rootSpec: ComponentSpec): ComponentSpe
|
|
|
516
550
|
* @param includeEmpty - Whether to include components without code
|
|
517
551
|
* @returns Total component count
|
|
518
552
|
*/
|
|
519
|
-
export function countComponentsInHierarchy(
|
|
553
|
+
export function countComponentsInHierarchy(
|
|
554
|
+
rootSpec: ComponentSpec,
|
|
555
|
+
includeEmpty: boolean = false
|
|
556
|
+
): number {
|
|
520
557
|
let count = 0;
|
|
521
|
-
|
|
558
|
+
|
|
522
559
|
if (includeEmpty || rootSpec.code) {
|
|
523
560
|
count = 1;
|
|
524
561
|
}
|
|
525
562
|
|
|
526
563
|
const children = rootSpec.dependencies || [];
|
|
527
|
-
children.forEach(
|
|
564
|
+
children.forEach(child => {
|
|
528
565
|
count += countComponentsInHierarchy(child, includeEmpty);
|
|
529
566
|
});
|
|
530
567
|
|
|
531
568
|
return count;
|
|
532
|
-
}
|
|
569
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { ComponentStyles, ComponentCallbacks } from '@memberjunction/interactive-component-types';
|
|
8
8
|
import { ComponentProps } from '../types';
|
|
9
9
|
import { Subject, debounceTime, Subscription } from 'rxjs';
|
|
10
|
-
import { LogStatus, GetProductionStatus } from '@memberjunction/
|
|
10
|
+
import { LogStatus, GetProductionStatus } from '@memberjunction/core';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Options for building component props
|
|
@@ -43,7 +43,7 @@ export function buildComponentProps(
|
|
|
43
43
|
callbacks: ComponentCallbacks = {
|
|
44
44
|
OpenEntityRecord: () => {},
|
|
45
45
|
RegisterMethod: () => {},
|
|
46
|
-
CreateSimpleNotification: () => {}
|
|
46
|
+
CreateSimpleNotification: () => {}
|
|
47
47
|
},
|
|
48
48
|
components: Record<string, any> = {},
|
|
49
49
|
styles?: ComponentStyles,
|
|
@@ -54,7 +54,7 @@ export function buildComponentProps(
|
|
|
54
54
|
validate = true,
|
|
55
55
|
transformData,
|
|
56
56
|
transformState,
|
|
57
|
-
debounceUpdateUserState = 3000
|
|
57
|
+
debounceUpdateUserState = 3000 // Default 3 seconds
|
|
58
58
|
} = options;
|
|
59
59
|
|
|
60
60
|
// Transform data if transformer provided
|
|
@@ -69,7 +69,7 @@ export function buildComponentProps(
|
|
|
69
69
|
callbacks: normalizeCallbacks(callbacks, debounceUpdateUserState),
|
|
70
70
|
components,
|
|
71
71
|
styles: normalizeStyles(styles),
|
|
72
|
-
onStateChanged
|
|
72
|
+
onStateChanged
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
// Validate if enabled
|
|
@@ -97,20 +97,20 @@ const loopDetectionStates = new WeakMap<Function, LoopDetectionState>();
|
|
|
97
97
|
// Deep equality check for objects
|
|
98
98
|
function deepEqual(obj1: any, obj2: any): boolean {
|
|
99
99
|
if (obj1 === obj2) return true;
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
if (!obj1 || !obj2) return false;
|
|
102
102
|
if (typeof obj1 !== 'object' || typeof obj2 !== 'object') return false;
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
const keys1 = Object.keys(obj1);
|
|
105
105
|
const keys2 = Object.keys(obj2);
|
|
106
|
-
|
|
106
|
+
|
|
107
107
|
if (keys1.length !== keys2.length) return false;
|
|
108
|
-
|
|
108
|
+
|
|
109
109
|
for (const key of keys1) {
|
|
110
110
|
if (!keys2.includes(key)) return false;
|
|
111
111
|
if (!deepEqual(obj1[key], obj2[key])) return false;
|
|
112
112
|
}
|
|
113
|
-
|
|
113
|
+
|
|
114
114
|
return true;
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -125,12 +125,12 @@ export function normalizeCallbacks(callbacks: any, debounceMs: number = 3000): C
|
|
|
125
125
|
const normalized: ComponentCallbacks = {
|
|
126
126
|
OpenEntityRecord: callbacks?.OpenEntityRecord || (() => {}),
|
|
127
127
|
RegisterMethod: callbacks?.RegisterMethod || (() => {}),
|
|
128
|
-
CreateSimpleNotification: callbacks?.CreateSimpleNotification || (() => {})
|
|
128
|
+
CreateSimpleNotification: callbacks?.CreateSimpleNotification || (() => {})
|
|
129
129
|
};
|
|
130
130
|
|
|
131
131
|
// Copy any additional callbacks that might exist
|
|
132
132
|
if (callbacks) {
|
|
133
|
-
Object.keys(callbacks).forEach(
|
|
133
|
+
Object.keys(callbacks).forEach(key => {
|
|
134
134
|
if (typeof callbacks[key] === 'function' && !normalized.hasOwnProperty(key)) {
|
|
135
135
|
(normalized as any)[key] = callbacks[key];
|
|
136
136
|
}
|
|
@@ -198,7 +198,7 @@ export function mergeProps(...propsList: Partial<ComponentProps>[]): ComponentPr
|
|
|
198
198
|
utilities: {},
|
|
199
199
|
callbacks: {},
|
|
200
200
|
components: {},
|
|
201
|
-
styles: {} as ComponentStyles
|
|
201
|
+
styles: {} as ComponentStyles
|
|
202
202
|
};
|
|
203
203
|
|
|
204
204
|
for (const props of propsList) {
|
|
@@ -229,20 +229,22 @@ export function mergeProps(...propsList: Partial<ComponentProps>[]): ComponentPr
|
|
|
229
229
|
|
|
230
230
|
return merged;
|
|
231
231
|
}
|
|
232
|
-
|
|
232
|
+
|
|
233
233
|
/**
|
|
234
234
|
* Creates a props transformer function
|
|
235
235
|
* @param transformations - Map of prop paths to transformer functions
|
|
236
236
|
* @returns Props transformer
|
|
237
237
|
*/
|
|
238
|
-
export function createPropsTransformer(
|
|
238
|
+
export function createPropsTransformer(
|
|
239
|
+
transformations: Record<string, (value: any) => any>
|
|
240
|
+
): (props: ComponentProps) => ComponentProps {
|
|
239
241
|
return (props: ComponentProps) => {
|
|
240
242
|
const transformed = { ...props };
|
|
241
243
|
|
|
242
244
|
for (const [path, transformer] of Object.entries(transformations)) {
|
|
243
245
|
const pathParts = path.split('.');
|
|
244
246
|
let current: any = transformed;
|
|
245
|
-
|
|
247
|
+
|
|
246
248
|
// Navigate to the parent of the target property
|
|
247
249
|
for (let i = 0; i < pathParts.length - 1; i++) {
|
|
248
250
|
if (!current[pathParts[i]]) {
|
|
@@ -268,21 +270,19 @@ export function createPropsTransformer(transformations: Record<string, (value: a
|
|
|
268
270
|
* @param componentName - Component name for logging
|
|
269
271
|
* @returns Wrapped callbacks
|
|
270
272
|
*/
|
|
271
|
-
export function wrapCallbacksWithLogging(
|
|
273
|
+
export function wrapCallbacksWithLogging(
|
|
274
|
+
callbacks: ComponentCallbacks,
|
|
275
|
+
componentName: string
|
|
276
|
+
): ComponentCallbacks {
|
|
272
277
|
const wrapped: ComponentCallbacks = {
|
|
273
278
|
OpenEntityRecord: callbacks?.OpenEntityRecord || (() => {}),
|
|
274
279
|
RegisterMethod: callbacks?.RegisterMethod || (() => {}),
|
|
275
|
-
CreateSimpleNotification: callbacks?.CreateSimpleNotification || (() => {})
|
|
280
|
+
CreateSimpleNotification: callbacks?.CreateSimpleNotification || (() => {})
|
|
276
281
|
};
|
|
277
282
|
|
|
278
283
|
// Wrap any additional callbacks that might exist
|
|
279
|
-
Object.keys(callbacks).forEach(
|
|
280
|
-
if (
|
|
281
|
-
key !== 'OpenEntityRecord' &&
|
|
282
|
-
key !== 'RegisterMethod' &&
|
|
283
|
-
key !== 'CreateSimpleNotification' &&
|
|
284
|
-
typeof (callbacks as any)[key] === 'function'
|
|
285
|
-
) {
|
|
284
|
+
Object.keys(callbacks).forEach(key => {
|
|
285
|
+
if (key !== 'OpenEntityRecord' && key !== 'RegisterMethod' && key !== 'CreateSimpleNotification' && typeof (callbacks as any)[key] === 'function') {
|
|
286
286
|
(wrapped as any)[key] = (...args: any[]) => {
|
|
287
287
|
if (!GetProductionStatus()) {
|
|
288
288
|
LogStatus(`[${componentName}] ${key} called with args:`, undefined, args);
|
|
@@ -311,7 +311,7 @@ export function wrapCallbacksWithLogging(callbacks: ComponentCallbacks, componen
|
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
if (callbacks.CreateSimpleNotification) {
|
|
314
|
-
wrapped.CreateSimpleNotification = (message: string, style:
|
|
314
|
+
wrapped.CreateSimpleNotification = (message: string, style: "none" | "success" | "error" | "warning" | "info", hideAfter?: number) => {
|
|
315
315
|
if (!GetProductionStatus()) {
|
|
316
316
|
LogStatus(`[${componentName}] CreateSimpleNotification called:`, undefined, { message, style, hideAfter });
|
|
317
317
|
}
|
|
@@ -329,9 +329,14 @@ export function wrapCallbacksWithLogging(callbacks: ComponentCallbacks, componen
|
|
|
329
329
|
*/
|
|
330
330
|
export function extractPropPaths(componentCode: string): string[] {
|
|
331
331
|
const paths: string[] = [];
|
|
332
|
-
|
|
332
|
+
|
|
333
333
|
// Simple regex patterns to find prop access
|
|
334
|
-
const patterns = [
|
|
334
|
+
const patterns = [
|
|
335
|
+
/props\.data\.(\w+)/g,
|
|
336
|
+
/props\.userState\.(\w+)/g,
|
|
337
|
+
/props\.utilities\.(\w+)/g,
|
|
338
|
+
/props\.callbacks\.(\w+)/g
|
|
339
|
+
];
|
|
335
340
|
|
|
336
341
|
for (const pattern of patterns) {
|
|
337
342
|
let match;
|
|
@@ -341,4 +346,4 @@ export function extractPropPaths(componentCode: string): string[] {
|
|
|
341
346
|
}
|
|
342
347
|
|
|
343
348
|
return [...new Set(paths)]; // Remove duplicates
|
|
344
|
-
}
|
|
349
|
+
}
|
package/src/types/index.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @module @memberjunction/react-runtime/types
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { UserInfo } from '@memberjunction/
|
|
7
|
+
import { UserInfo } from '@memberjunction/core';
|
|
8
8
|
import { ComponentLibraryEntity } from '@memberjunction/core-entities';
|
|
9
9
|
import { ComponentLibraryDependency, ComponentStyles, ComponentObject } from '@memberjunction/interactive-component-types';
|
|
10
10
|
|
|
@@ -43,7 +43,7 @@ export interface CompileOptions {
|
|
|
43
43
|
|
|
44
44
|
/** Library dependencies that the component requires */
|
|
45
45
|
libraries?: ComponentLibraryDependency[];
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
/** Child component dependencies that the component requires */
|
|
48
48
|
dependencies?: Array<{ name: string; code?: string }>;
|
|
49
49
|
|
|
@@ -53,6 +53,7 @@ export interface CompileOptions {
|
|
|
53
53
|
allLibraries: ComponentLibraryEntity[];
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
|
|
56
57
|
/**
|
|
57
58
|
* Registry entry for a compiled component
|
|
58
59
|
*/
|
|
@@ -229,4 +230,4 @@ export * from './library-config';
|
|
|
229
230
|
export * from './dependency-types';
|
|
230
231
|
|
|
231
232
|
// Re-export ComponentObject for convenience
|
|
232
|
-
export { ComponentObject } from '@memberjunction/interactive-component-types';
|
|
233
|
+
export { ComponentObject } from '@memberjunction/interactive-component-types';
|