@memberjunction/react-test-harness 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.
- package/dist/lib/component-linter.d.ts +1 -1
- package/dist/lib/component-linter.d.ts.map +1 -1
- package/dist/lib/component-linter.js +935 -1083
- package/dist/lib/component-linter.js.map +1 -1
- package/dist/lib/component-runner.d.ts +1 -1
- package/dist/lib/component-runner.d.ts.map +1 -1
- package/dist/lib/component-runner.js +176 -165
- package/dist/lib/component-runner.js.map +1 -1
- package/dist/lib/library-lint-cache.d.ts +1 -1
- package/dist/lib/library-lint-cache.d.ts.map +1 -1
- package/dist/lib/library-lint-cache.js +7 -5
- package/dist/lib/library-lint-cache.js.map +1 -1
- package/package.json +3 -4
|
@@ -24,7 +24,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.ComponentRunner = void 0;
|
|
27
|
-
const
|
|
27
|
+
const core_1 = require("@memberjunction/core");
|
|
28
28
|
const component_linter_1 = require("./component-linter");
|
|
29
29
|
const core_entities_1 = require("@memberjunction/core-entities");
|
|
30
30
|
const ai_vectors_memory_1 = require("@memberjunction/ai-vectors-memory");
|
|
@@ -63,10 +63,10 @@ class ComponentRunner {
|
|
|
63
63
|
async lintComponent(componentCode, componentName, componentSpec, isRootComponent, contextUser, options) {
|
|
64
64
|
const lintResult = await component_linter_1.ComponentLinter.lintComponent(componentCode, componentName, componentSpec, isRootComponent, contextUser, false, // debugMode
|
|
65
65
|
options);
|
|
66
|
-
const hasErrors = lintResult.violations.some(
|
|
66
|
+
const hasErrors = lintResult.violations.some(v => v.severity === 'critical' || v.severity === 'high');
|
|
67
67
|
return {
|
|
68
68
|
violations: lintResult.violations,
|
|
69
|
-
hasErrors
|
|
69
|
+
hasErrors
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
async executeComponent(options) {
|
|
@@ -89,7 +89,7 @@ class ComponentRunner {
|
|
|
89
89
|
page.setDefaultTimeout(globalTimeout);
|
|
90
90
|
// Load component metadata and libraries first (needed for library loading)
|
|
91
91
|
await core_entities_1.ComponentMetadataEngine.Instance.Config(false, options.contextUser);
|
|
92
|
-
const allLibraries = core_entities_1.ComponentMetadataEngine.Instance.ComponentLibraries.map(
|
|
92
|
+
const allLibraries = core_entities_1.ComponentMetadataEngine.Instance.ComponentLibraries.map(c => c.GetAll());
|
|
93
93
|
try {
|
|
94
94
|
// Navigate to a blank page FIRST, then load runtime
|
|
95
95
|
await page.goto('about:blank');
|
|
@@ -101,12 +101,12 @@ class ComponentRunner {
|
|
|
101
101
|
<meta charset="utf-8">
|
|
102
102
|
<title>React Component Test (V2)</title>
|
|
103
103
|
<style>
|
|
104
|
-
body {
|
|
105
|
-
margin: 0;
|
|
106
|
-
padding: 20px;
|
|
104
|
+
body {
|
|
105
|
+
margin: 0;
|
|
106
|
+
padding: 20px;
|
|
107
107
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
108
108
|
}
|
|
109
|
-
#root {
|
|
109
|
+
#root {
|
|
110
110
|
min-height: 100vh;
|
|
111
111
|
background-color: white;
|
|
112
112
|
padding: 20px;
|
|
@@ -130,7 +130,7 @@ class ComponentRunner {
|
|
|
130
130
|
hasReact: typeof window.React !== 'undefined',
|
|
131
131
|
hasReactDOM: typeof window.ReactDOM !== 'undefined',
|
|
132
132
|
hasBabel: typeof window.Babel !== 'undefined',
|
|
133
|
-
mjRuntimeKeys: window.MJReactRuntime ? Object.keys(window.MJReactRuntime) : []
|
|
133
|
+
mjRuntimeKeys: window.MJReactRuntime ? Object.keys(window.MJReactRuntime) : []
|
|
134
134
|
};
|
|
135
135
|
});
|
|
136
136
|
if (debug) {
|
|
@@ -152,10 +152,10 @@ class ComponentRunner {
|
|
|
152
152
|
console.log(' - props:', JSON.stringify(options.props || {}, null, 2));
|
|
153
153
|
// Show spec-specific libraries, not all available libraries
|
|
154
154
|
if (options.componentSpec.libraries && options.componentSpec.libraries.length > 0) {
|
|
155
|
-
console.log(' - spec requires libraries:', options.componentSpec.libraries.map(
|
|
155
|
+
console.log(' - spec requires libraries:', options.componentSpec.libraries.map(lib => ({
|
|
156
156
|
name: lib.name,
|
|
157
157
|
globalVariable: lib.globalVariable,
|
|
158
|
-
version: lib.version
|
|
158
|
+
version: lib.version
|
|
159
159
|
})));
|
|
160
160
|
}
|
|
161
161
|
else {
|
|
@@ -213,7 +213,7 @@ class ComponentRunner {
|
|
|
213
213
|
// Diagnostic: Check if React is available before creating context
|
|
214
214
|
if (!window.React) {
|
|
215
215
|
console.error('🔴 CRITICAL: React is NULL when creating runtimeContext!');
|
|
216
|
-
console.error('Window keys:', Object.keys(window).filter(
|
|
216
|
+
console.error('Window keys:', Object.keys(window).filter(k => k.toLowerCase().includes('react')));
|
|
217
217
|
throw new Error('React is not available in window context');
|
|
218
218
|
}
|
|
219
219
|
if (debug) {
|
|
@@ -222,13 +222,13 @@ class ComponentRunner {
|
|
|
222
222
|
useState: typeof window.React?.useState,
|
|
223
223
|
useEffect: typeof window.React?.useEffect,
|
|
224
224
|
useRef: typeof window.React?.useRef,
|
|
225
|
-
useMemo: typeof window.React?.useMemo
|
|
225
|
+
useMemo: typeof window.React?.useMemo
|
|
226
226
|
});
|
|
227
227
|
}
|
|
228
228
|
const runtimeContext = {
|
|
229
229
|
React: window.React,
|
|
230
230
|
ReactDOM: window.ReactDOM,
|
|
231
|
-
libraries: {}
|
|
231
|
+
libraries: {} // Libraries are loaded internally by the compiler
|
|
232
232
|
};
|
|
233
233
|
// Create instances with debug mode to see library loading
|
|
234
234
|
const compiler = new ComponentCompiler({ debug: debug });
|
|
@@ -277,7 +277,7 @@ class ComponentRunner {
|
|
|
277
277
|
continue;
|
|
278
278
|
}
|
|
279
279
|
if (!specLib.globalVariable) {
|
|
280
|
-
const libDef = componentLibraries.find(
|
|
280
|
+
const libDef = componentLibraries.find(l => l && l.Name && l.Name.toLowerCase() === specLib.name.toLowerCase());
|
|
281
281
|
if (libDef && libDef.GlobalVariable) {
|
|
282
282
|
specLib.globalVariable = libDef.GlobalVariable;
|
|
283
283
|
if (debug) {
|
|
@@ -289,7 +289,7 @@ class ComponentRunner {
|
|
|
289
289
|
if (debug) {
|
|
290
290
|
console.log('🔍 Spec libraries after enhancement:', spec.libraries.map((l) => ({
|
|
291
291
|
name: l.name,
|
|
292
|
-
globalVariable: l.globalVariable
|
|
292
|
+
globalVariable: l.globalVariable
|
|
293
293
|
})));
|
|
294
294
|
}
|
|
295
295
|
}
|
|
@@ -315,8 +315,8 @@ class ComponentRunner {
|
|
|
315
315
|
dependencies: spec.dependencies?.map((d) => ({
|
|
316
316
|
name: d.name,
|
|
317
317
|
location: d.location,
|
|
318
|
-
hasCode: !!d.code
|
|
319
|
-
}))
|
|
318
|
+
hasCode: !!d.code
|
|
319
|
+
}))
|
|
320
320
|
});
|
|
321
321
|
}
|
|
322
322
|
// NEW: Use ComponentManager.loadHierarchy instead of registrar.registerHierarchy
|
|
@@ -328,23 +328,21 @@ class ComponentRunner {
|
|
|
328
328
|
defaultVersion: 'v1',
|
|
329
329
|
returnType: 'both',
|
|
330
330
|
resolutionMode: 'embed', // Convert to embedded format for browser execution
|
|
331
|
-
allLibraries: componentLibraries || []
|
|
331
|
+
allLibraries: componentLibraries || [] // Pass libraries for compiler
|
|
332
332
|
});
|
|
333
333
|
if (debug) {
|
|
334
334
|
console.log('📋 [BROWSER] LoadHierarchy result:', {
|
|
335
335
|
success: loadResult.success,
|
|
336
336
|
rootComponent: !!loadResult.rootComponent,
|
|
337
|
-
resolvedSpec: loadResult.resolvedSpec
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
: null,
|
|
337
|
+
resolvedSpec: loadResult.resolvedSpec ? {
|
|
338
|
+
name: loadResult.resolvedSpec.name,
|
|
339
|
+
location: loadResult.resolvedSpec.location,
|
|
340
|
+
registry: loadResult.resolvedSpec.registry,
|
|
341
|
+
libraries: loadResult.resolvedSpec.libraries,
|
|
342
|
+
hasCode: !!loadResult.resolvedSpec.code
|
|
343
|
+
} : null,
|
|
346
344
|
loadedComponents: loadResult.loadedComponents,
|
|
347
|
-
errors: loadResult.errors
|
|
345
|
+
errors: loadResult.errors
|
|
348
346
|
});
|
|
349
347
|
}
|
|
350
348
|
// Convert to old format for compatibility
|
|
@@ -353,7 +351,7 @@ class ComponentRunner {
|
|
|
353
351
|
registeredComponents: loadResult.loadedComponents,
|
|
354
352
|
errors: loadResult.errors.map((e) => ({ componentName: e.componentName || '', error: e.message, phase: e.phase })),
|
|
355
353
|
warnings: [],
|
|
356
|
-
resolvedSpec: loadResult.resolvedSpec
|
|
354
|
+
resolvedSpec: loadResult.resolvedSpec
|
|
357
355
|
};
|
|
358
356
|
}
|
|
359
357
|
catch (registrationError) {
|
|
@@ -365,7 +363,7 @@ class ComponentRunner {
|
|
|
365
363
|
stack: registrationError.stack,
|
|
366
364
|
type: 'registration-error',
|
|
367
365
|
phase: 'component-compilation',
|
|
368
|
-
source: 'runtime-wrapper'
|
|
366
|
+
source: 'runtime-wrapper'
|
|
369
367
|
});
|
|
370
368
|
window.__testHarnessTestFailed = true;
|
|
371
369
|
// Don't re-throw - let execution continue to collect this error properly
|
|
@@ -373,7 +371,7 @@ class ComponentRunner {
|
|
|
373
371
|
return {
|
|
374
372
|
success: false,
|
|
375
373
|
error: `Component registration failed: ${registrationError.message || registrationError}`,
|
|
376
|
-
componentCount: 0
|
|
374
|
+
componentCount: 0
|
|
377
375
|
};
|
|
378
376
|
}
|
|
379
377
|
if (debug && !registrationResult.success) {
|
|
@@ -415,7 +413,10 @@ class ComponentRunner {
|
|
|
415
413
|
antd: typeof window.antd,
|
|
416
414
|
React: typeof window.React,
|
|
417
415
|
ReactDOM: typeof window.ReactDOM,
|
|
418
|
-
windowKeys: Object.keys(window).filter(
|
|
416
|
+
windowKeys: Object.keys(window).filter(k => k.toLowerCase().includes('apex') ||
|
|
417
|
+
k.toLowerCase().includes('antd') ||
|
|
418
|
+
k === 'ApexCharts' ||
|
|
419
|
+
k === 'antd')
|
|
419
420
|
});
|
|
420
421
|
// If libraries were supposed to be loaded, check their actual presence
|
|
421
422
|
if (spec.libraries && spec.libraries.length > 0) {
|
|
@@ -478,7 +479,7 @@ class ComponentRunner {
|
|
|
478
479
|
'',
|
|
479
480
|
'Inspect all instances where you are using JSX elements that come from libraries or components to ensure they are properly referenced.',
|
|
480
481
|
'',
|
|
481
|
-
'The exact fix depends on the specific library or component structure.'
|
|
482
|
+
'The exact fix depends on the specific library or component structure.'
|
|
482
483
|
].join('\\n');
|
|
483
484
|
// Log to both console and error tracking
|
|
484
485
|
console.error('🔴 Invalid JSX Element Type Detected:', errorMsg);
|
|
@@ -488,7 +489,7 @@ class ComponentRunner {
|
|
|
488
489
|
type: 'invalid-element-type',
|
|
489
490
|
phase: 'createElement',
|
|
490
491
|
source: 'enhanced-detection',
|
|
491
|
-
elementInfo: objectInfo
|
|
492
|
+
elementInfo: objectInfo
|
|
492
493
|
});
|
|
493
494
|
// Still try to call the original to get React's error too
|
|
494
495
|
// This will provide the component stack trace
|
|
@@ -503,14 +504,14 @@ class ComponentRunner {
|
|
|
503
504
|
'',
|
|
504
505
|
'Inspect how this component is being accessed - it may not exist in the expected location or may have a different name.',
|
|
505
506
|
'',
|
|
506
|
-
'Check that the component exists in your dependencies or libraries and is properly referenced.'
|
|
507
|
+
'Check that the component exists in your dependencies or libraries and is properly referenced.'
|
|
507
508
|
].join('\\n');
|
|
508
509
|
console.error('🔴 Undefined JSX Component:', errorMsg);
|
|
509
510
|
window.__testHarnessRuntimeErrors.push({
|
|
510
511
|
message: errorMsg,
|
|
511
512
|
type: 'undefined-component',
|
|
512
513
|
phase: 'createElement',
|
|
513
|
-
source: 'enhanced-detection'
|
|
514
|
+
source: 'enhanced-detection'
|
|
514
515
|
});
|
|
515
516
|
}
|
|
516
517
|
// Call original createElement
|
|
@@ -539,7 +540,7 @@ class ComponentRunner {
|
|
|
539
540
|
window.__testHarnessRuntimeErrors.push({
|
|
540
541
|
message: `Likely infinite render loop: ${currentRenderCount} createElement calls (max: ${MAX_RENDERS_ALLOWED})`,
|
|
541
542
|
type: 'render-loop',
|
|
542
|
-
source: 'test-harness'
|
|
543
|
+
source: 'test-harness'
|
|
543
544
|
});
|
|
544
545
|
// Try to unmount to stop the madness
|
|
545
546
|
try {
|
|
@@ -573,8 +574,8 @@ class ComponentRunner {
|
|
|
573
574
|
CreateSimpleNotification: (message, style, hideAfter) => {
|
|
574
575
|
console.log('[Test Harness] CreateSimpleNotification called:', { message, style, hideAfter });
|
|
575
576
|
// In test harness, we just log but don't display actual notifications
|
|
576
|
-
}
|
|
577
|
-
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
578
579
|
};
|
|
579
580
|
if (debug) {
|
|
580
581
|
console.log('🎨 Rendering component with props:', Object.keys(componentProps));
|
|
@@ -603,7 +604,7 @@ class ComponentRunner {
|
|
|
603
604
|
stack: error.stack,
|
|
604
605
|
type: 'react-render-error',
|
|
605
606
|
phase: 'component-render',
|
|
606
|
-
source: 'user-component'
|
|
607
|
+
source: 'user-component' // This is the actual error from user's component
|
|
607
608
|
});
|
|
608
609
|
window.__testHarnessTestFailed = true;
|
|
609
610
|
return { hasError: true, error };
|
|
@@ -639,7 +640,7 @@ class ComponentRunner {
|
|
|
639
640
|
}
|
|
640
641
|
return {
|
|
641
642
|
success: true,
|
|
642
|
-
componentCount: registrationResult.registeredComponents.length
|
|
643
|
+
componentCount: registrationResult.registeredComponents.length
|
|
643
644
|
};
|
|
644
645
|
}
|
|
645
646
|
catch (error) {
|
|
@@ -656,19 +657,19 @@ class ComponentRunner {
|
|
|
656
657
|
message: error.message || String(error),
|
|
657
658
|
stack: error.stack,
|
|
658
659
|
type: 'execution-error',
|
|
659
|
-
source: 'runtime-wrapper'
|
|
660
|
+
source: 'runtime-wrapper'
|
|
660
661
|
});
|
|
661
662
|
window.__testHarnessTestFailed = true;
|
|
662
663
|
return {
|
|
663
664
|
success: false,
|
|
664
|
-
error: error.message || String(error)
|
|
665
|
+
error: error.message || String(error)
|
|
665
666
|
};
|
|
666
667
|
}
|
|
667
668
|
}, {
|
|
668
669
|
spec: options.componentSpec,
|
|
669
670
|
props: options.props,
|
|
670
671
|
debug,
|
|
671
|
-
componentLibraries: allLibraries || []
|
|
672
|
+
componentLibraries: allLibraries || []
|
|
672
673
|
});
|
|
673
674
|
// Create timeout promise (Recommendation #1)
|
|
674
675
|
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error(`Component execution timeout after ${globalTimeout}ms`)), globalTimeout));
|
|
@@ -684,21 +685,21 @@ class ComponentRunner {
|
|
|
684
685
|
errors.push(`Component execution timed out after ${globalTimeout}ms`);
|
|
685
686
|
executionResult = {
|
|
686
687
|
success: false,
|
|
687
|
-
error: timeoutError instanceof Error ? timeoutError.message : 'Execution timeout'
|
|
688
|
+
error: timeoutError instanceof Error ? timeoutError.message : 'Execution timeout'
|
|
688
689
|
};
|
|
689
690
|
}
|
|
690
691
|
// Ensure executionResult has proper shape
|
|
691
692
|
if (!executionResult) {
|
|
692
693
|
executionResult = {
|
|
693
694
|
success: false,
|
|
694
|
-
error: 'Component execution returned no result'
|
|
695
|
+
error: 'Component execution returned no result'
|
|
695
696
|
};
|
|
696
697
|
errors.push('Component execution failed to return a result');
|
|
697
698
|
}
|
|
698
699
|
else if (!executionResult.success && executionResult.error) {
|
|
699
700
|
// Add the execution error if it hasn't been captured elsewhere
|
|
700
701
|
const errorMsg = `Component execution failed: ${executionResult.error}`;
|
|
701
|
-
if (!errors.some(
|
|
702
|
+
if (!errors.some(e => e.includes(executionResult.error))) {
|
|
702
703
|
errors.push(errorMsg);
|
|
703
704
|
}
|
|
704
705
|
}
|
|
@@ -714,12 +715,13 @@ class ComponentRunner {
|
|
|
714
715
|
const runtimeErrorsWithSource = await this.collectRuntimeErrors(page);
|
|
715
716
|
// Filter out JSX element type errors when adding to errors array
|
|
716
717
|
errors.push(...runtimeErrorsWithSource
|
|
717
|
-
.filter(
|
|
718
|
+
.filter(e => {
|
|
718
719
|
// Skip JSX element type errors from error count
|
|
719
|
-
const isJSXError = e.type === 'invalid-element-type' ||
|
|
720
|
+
const isJSXError = e.type === 'invalid-element-type' ||
|
|
721
|
+
e.type === 'undefined-component';
|
|
720
722
|
return !isJSXError;
|
|
721
723
|
})
|
|
722
|
-
.map(
|
|
724
|
+
.map(e => e.message)); // Extract messages for backward compat
|
|
723
725
|
// Collect warnings (separate from errors)
|
|
724
726
|
const collectedWarnings = await this.collectWarnings(page);
|
|
725
727
|
warnings.push(...collectedWarnings);
|
|
@@ -728,20 +730,21 @@ class ComponentRunner {
|
|
|
728
730
|
await page.waitForTimeout(asyncWaitTime);
|
|
729
731
|
const asyncErrors = await this.collectRuntimeErrors(page);
|
|
730
732
|
// Only add new errors (excluding JSX element type errors)
|
|
731
|
-
asyncErrors.forEach(
|
|
732
|
-
const isJSXError = err.type === 'invalid-element-type' ||
|
|
733
|
+
asyncErrors.forEach(err => {
|
|
734
|
+
const isJSXError = err.type === 'invalid-element-type' ||
|
|
735
|
+
err.type === 'undefined-component';
|
|
733
736
|
if (!isJSXError && !errors.includes(err.message)) {
|
|
734
737
|
errors.push(err.message);
|
|
735
738
|
runtimeErrorsWithSource.push(err); // Keep the structured version too
|
|
736
739
|
}
|
|
737
|
-
else if (isJSXError && !runtimeErrorsWithSource.some(
|
|
740
|
+
else if (isJSXError && !runtimeErrorsWithSource.some(e => e.message === err.message)) {
|
|
738
741
|
// Still track JSX errors for logging but don't add to errors array
|
|
739
742
|
runtimeErrorsWithSource.push(err);
|
|
740
743
|
}
|
|
741
744
|
});
|
|
742
745
|
// Also check for new warnings
|
|
743
746
|
const asyncWarnings = await this.collectWarnings(page);
|
|
744
|
-
asyncWarnings.forEach(
|
|
747
|
+
asyncWarnings.forEach(warn => {
|
|
745
748
|
if (!warnings.includes(warn)) {
|
|
746
749
|
warnings.push(warn);
|
|
747
750
|
}
|
|
@@ -756,13 +759,16 @@ class ComponentRunner {
|
|
|
756
759
|
errors.push(`Possible render loop: ${renderCount} createElement calls detected (likely infinite loop)`);
|
|
757
760
|
}
|
|
758
761
|
// Determine success
|
|
759
|
-
const success = errors.length === 0 &&
|
|
762
|
+
const success = errors.length === 0 &&
|
|
763
|
+
!hasRenderLoop &&
|
|
764
|
+
!hasTimeout &&
|
|
765
|
+
executionResult.success;
|
|
760
766
|
// Combine runtime errors with data errors
|
|
761
767
|
const allErrors = [...errors, ...dataErrors];
|
|
762
768
|
// Map runtime errors with source info and specific rules
|
|
763
769
|
// Filter out JSX element type errors - they're too noisy and often false positives
|
|
764
770
|
const errorViolations = runtimeErrorsWithSource
|
|
765
|
-
.filter(
|
|
771
|
+
.filter(e => {
|
|
766
772
|
// Skip JSX element type errors - still logged but not reported as violations
|
|
767
773
|
const isJSXError = e.rule === 'invalid-jsx-element' ||
|
|
768
774
|
e.rule === 'undefined-jsx-component' ||
|
|
@@ -773,13 +779,13 @@ class ComponentRunner {
|
|
|
773
779
|
}
|
|
774
780
|
return !isJSXError;
|
|
775
781
|
})
|
|
776
|
-
.map(
|
|
782
|
+
.map(e => ({
|
|
777
783
|
message: e.message,
|
|
778
784
|
severity: 'critical',
|
|
779
785
|
rule: e.rule || 'runtime-error', // Use specific rule from collectRuntimeErrors
|
|
780
786
|
line: 0,
|
|
781
787
|
column: 0,
|
|
782
|
-
source: e.source
|
|
788
|
+
source: e.source
|
|
783
789
|
}));
|
|
784
790
|
// Add timeout error if detected
|
|
785
791
|
if (hasTimeout) {
|
|
@@ -789,7 +795,7 @@ class ComponentRunner {
|
|
|
789
795
|
rule: 'timeout',
|
|
790
796
|
line: 0,
|
|
791
797
|
column: 0,
|
|
792
|
-
source: 'test-harness'
|
|
798
|
+
source: 'test-harness' // This is a test harness timeout
|
|
793
799
|
});
|
|
794
800
|
}
|
|
795
801
|
// Add render loop error if detected
|
|
@@ -800,25 +806,25 @@ class ComponentRunner {
|
|
|
800
806
|
rule: 'render-loop',
|
|
801
807
|
line: 0,
|
|
802
808
|
column: 0,
|
|
803
|
-
source: 'test-harness'
|
|
809
|
+
source: 'test-harness' // This is a test harness detection
|
|
804
810
|
});
|
|
805
811
|
}
|
|
806
812
|
// Add data errors without source
|
|
807
|
-
dataErrors.forEach(
|
|
813
|
+
dataErrors.forEach(e => {
|
|
808
814
|
errorViolations.push({
|
|
809
815
|
message: e,
|
|
810
816
|
severity: 'critical',
|
|
811
817
|
rule: 'runtime-error',
|
|
812
818
|
line: 0,
|
|
813
819
|
column: 0,
|
|
814
|
-
source: 'user-component'
|
|
820
|
+
source: 'user-component' // Data errors are from user's data access code
|
|
815
821
|
});
|
|
816
822
|
});
|
|
817
823
|
// Check warnings for critical patterns and move them to errors
|
|
818
824
|
const criticalWarningViolations = [];
|
|
819
825
|
const regularWarnings = [];
|
|
820
|
-
warnings.forEach(
|
|
821
|
-
if (ComponentRunner.CRITICAL_WARNING_PATTERNS.some(
|
|
826
|
+
warnings.forEach(w => {
|
|
827
|
+
if (ComponentRunner.CRITICAL_WARNING_PATTERNS.some(pattern => pattern.test(w))) {
|
|
822
828
|
// This is a critical warning - add to errors
|
|
823
829
|
criticalWarningViolations.push({
|
|
824
830
|
message: w,
|
|
@@ -826,7 +832,7 @@ class ComponentRunner {
|
|
|
826
832
|
rule: 'critical-react-warning',
|
|
827
833
|
line: 0,
|
|
828
834
|
column: 0,
|
|
829
|
-
source: 'react-framework'
|
|
835
|
+
source: 'react-framework'
|
|
830
836
|
});
|
|
831
837
|
}
|
|
832
838
|
else {
|
|
@@ -840,17 +846,17 @@ class ComponentRunner {
|
|
|
840
846
|
success: success && dataErrors.length === 0 && criticalWarningViolations.length === 0, // Fail on critical warnings too
|
|
841
847
|
html,
|
|
842
848
|
errors: allErrorViolations,
|
|
843
|
-
warnings: regularWarnings.map(
|
|
849
|
+
warnings: regularWarnings.map(w => ({
|
|
844
850
|
message: w,
|
|
845
851
|
severity: 'low',
|
|
846
852
|
rule: 'warning',
|
|
847
853
|
line: 0,
|
|
848
|
-
column: 0
|
|
854
|
+
column: 0
|
|
849
855
|
})),
|
|
850
856
|
console: consoleLogs,
|
|
851
857
|
screenshot,
|
|
852
858
|
executionTime: Date.now() - startTime,
|
|
853
|
-
renderCount
|
|
859
|
+
renderCount
|
|
854
860
|
};
|
|
855
861
|
if (debug) {
|
|
856
862
|
this.dumpDebugInfo(result);
|
|
@@ -861,44 +867,42 @@ class ComponentRunner {
|
|
|
861
867
|
// For catch block errors, we need to handle them specially
|
|
862
868
|
const catchError = {
|
|
863
869
|
message: error instanceof Error ? error.message : String(error),
|
|
864
|
-
source: 'test-harness'
|
|
870
|
+
source: 'test-harness' // Errors caught here are usually test harness issues
|
|
865
871
|
};
|
|
866
872
|
// Create error violations including the catch error
|
|
867
|
-
const errorViolations = [
|
|
868
|
-
{
|
|
873
|
+
const errorViolations = [{
|
|
869
874
|
message: catchError.message,
|
|
870
875
|
severity: 'critical',
|
|
871
876
|
rule: 'runtime-error',
|
|
872
877
|
line: 0,
|
|
873
878
|
column: 0,
|
|
874
|
-
source: catchError.source
|
|
875
|
-
}
|
|
876
|
-
];
|
|
879
|
+
source: catchError.source
|
|
880
|
+
}];
|
|
877
881
|
// Add any data errors
|
|
878
|
-
dataErrors.forEach(
|
|
882
|
+
dataErrors.forEach(e => {
|
|
879
883
|
errorViolations.push({
|
|
880
884
|
message: e,
|
|
881
885
|
severity: 'critical',
|
|
882
886
|
rule: 'runtime-error',
|
|
883
887
|
line: 0,
|
|
884
888
|
column: 0,
|
|
885
|
-
source: 'user-component'
|
|
889
|
+
source: 'user-component'
|
|
886
890
|
});
|
|
887
891
|
});
|
|
888
892
|
const result = {
|
|
889
893
|
success: false,
|
|
890
894
|
html: '',
|
|
891
895
|
errors: errorViolations,
|
|
892
|
-
warnings: warnings.map(
|
|
896
|
+
warnings: warnings.map(w => ({
|
|
893
897
|
message: w,
|
|
894
898
|
severity: 'low',
|
|
895
899
|
rule: 'warning',
|
|
896
900
|
line: 0,
|
|
897
|
-
column: 0
|
|
901
|
+
column: 0
|
|
898
902
|
})),
|
|
899
903
|
console: consoleLogs,
|
|
900
904
|
executionTime: Date.now() - startTime,
|
|
901
|
-
renderCount
|
|
905
|
+
renderCount
|
|
902
906
|
};
|
|
903
907
|
if (debug) {
|
|
904
908
|
console.log('\n❌ Component execution failed with error:', error);
|
|
@@ -936,7 +940,7 @@ class ComponentRunner {
|
|
|
936
940
|
try {
|
|
937
941
|
await Promise.race([
|
|
938
942
|
page.addScriptTag({ url }),
|
|
939
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error(`Script load timeout: ${url}`)), timeout))
|
|
943
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`Script load timeout: ${url}`)), timeout))
|
|
940
944
|
]);
|
|
941
945
|
}
|
|
942
946
|
catch (error) {
|
|
@@ -974,7 +978,7 @@ class ComponentRunner {
|
|
|
974
978
|
React: typeof window.React !== 'undefined',
|
|
975
979
|
ReactDOM: typeof window.ReactDOM !== 'undefined',
|
|
976
980
|
Babel: typeof window.Babel !== 'undefined',
|
|
977
|
-
MJRuntime: typeof window.MJReactRuntime !== 'undefined'
|
|
981
|
+
MJRuntime: typeof window.MJReactRuntime !== 'undefined'
|
|
978
982
|
};
|
|
979
983
|
});
|
|
980
984
|
// All core libraries loaded successfully
|
|
@@ -993,10 +997,10 @@ class ComponentRunner {
|
|
|
993
997
|
async loadComponentLibraries(page, specLibraries, allLibraries, debug = false) {
|
|
994
998
|
if (debug) {
|
|
995
999
|
console.log('📚 Loading component libraries:');
|
|
996
|
-
console.log(' 🔍 Component requires libraries:', specLibraries.map(
|
|
1000
|
+
console.log(' 🔍 Component requires libraries:', specLibraries.map(l => l.name));
|
|
997
1001
|
console.log(' 📦 Total available ComponentLibrary entries:', allLibraries.length);
|
|
998
1002
|
console.log(' 📋 Sample of available libraries (first 10):');
|
|
999
|
-
allLibraries.slice(0, 10).forEach(
|
|
1003
|
+
allLibraries.slice(0, 10).forEach(lib => {
|
|
1000
1004
|
console.log(` - ${lib.Name}@${lib.Version} (${lib.Status})`);
|
|
1001
1005
|
});
|
|
1002
1006
|
}
|
|
@@ -1016,12 +1020,12 @@ class ComponentRunner {
|
|
|
1016
1020
|
console.log(`📦 Loading ${specLib.name}:`, {
|
|
1017
1021
|
cdnUrl: libDef.CDNUrl,
|
|
1018
1022
|
globalVariable: libDef.GlobalVariable,
|
|
1019
|
-
dependencies: libDef.Dependencies ? JSON.parse(libDef.Dependencies) : null
|
|
1023
|
+
dependencies: libDef.Dependencies ? JSON.parse(libDef.Dependencies) : null
|
|
1020
1024
|
});
|
|
1021
1025
|
}
|
|
1022
1026
|
// Load CSS if available
|
|
1023
1027
|
if (libDef.CDNCssUrl) {
|
|
1024
|
-
const cssUrls = libDef.CDNCssUrl.split(',').map(
|
|
1028
|
+
const cssUrls = libDef.CDNCssUrl.split(',').map(url => url.trim());
|
|
1025
1029
|
for (const cssUrl of cssUrls) {
|
|
1026
1030
|
if (cssUrl) {
|
|
1027
1031
|
await page.addStyleTag({ url: cssUrl });
|
|
@@ -1037,7 +1041,7 @@ class ComponentRunner {
|
|
|
1037
1041
|
// Add timeout for library loading (Recommendation #3)
|
|
1038
1042
|
await Promise.race([
|
|
1039
1043
|
page.addScriptTag({ url: libDef.CDNUrl }),
|
|
1040
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error(`Library load timeout: ${libDef.CDNUrl}`)), 10000))
|
|
1044
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`Library load timeout: ${libDef.CDNUrl}`)), 10000))
|
|
1041
1045
|
]);
|
|
1042
1046
|
// Verify the library loaded
|
|
1043
1047
|
const isLoaded = await page.evaluate((globalVar) => {
|
|
@@ -1064,12 +1068,10 @@ class ComponentRunner {
|
|
|
1064
1068
|
if (debug) {
|
|
1065
1069
|
// Log all available global variables that look like libraries
|
|
1066
1070
|
// Get all the global variables we expect from the spec
|
|
1067
|
-
const expectedGlobals = specLibraries
|
|
1068
|
-
.map((lib) => {
|
|
1071
|
+
const expectedGlobals = specLibraries.map(lib => {
|
|
1069
1072
|
const libDef = libraryMap.get(lib.name.toLowerCase());
|
|
1070
1073
|
return libDef?.GlobalVariable;
|
|
1071
|
-
})
|
|
1072
|
-
.filter(Boolean);
|
|
1074
|
+
}).filter(Boolean);
|
|
1073
1075
|
const globals = await page.evaluate((expected) => {
|
|
1074
1076
|
const relevantGlobals = {};
|
|
1075
1077
|
// Check the expected globals from the spec
|
|
@@ -1143,7 +1145,7 @@ class ComponentRunner {
|
|
|
1143
1145
|
'',
|
|
1144
1146
|
'Inspect all instances where you are using JSX elements that come from libraries or components to ensure they are properly referenced.',
|
|
1145
1147
|
'',
|
|
1146
|
-
'The exact fix depends on the specific library or component structure.'
|
|
1148
|
+
'The exact fix depends on the specific library or component structure.'
|
|
1147
1149
|
].join('\\n');
|
|
1148
1150
|
// Log to both console and error tracking
|
|
1149
1151
|
console.error('🔴 Invalid JSX Element Type Detected:', errorMsg);
|
|
@@ -1154,7 +1156,7 @@ class ComponentRunner {
|
|
|
1154
1156
|
type: 'invalid-element-type',
|
|
1155
1157
|
phase: 'createElement',
|
|
1156
1158
|
source: 'enhanced-detection',
|
|
1157
|
-
elementInfo: objectInfo
|
|
1159
|
+
elementInfo: objectInfo
|
|
1158
1160
|
});
|
|
1159
1161
|
// Still try to call the original to get React's error too
|
|
1160
1162
|
// This will provide the component stack trace
|
|
@@ -1169,7 +1171,7 @@ class ComponentRunner {
|
|
|
1169
1171
|
'',
|
|
1170
1172
|
'Inspect how this component is being accessed - it may not exist in the expected location or may have a different name.',
|
|
1171
1173
|
'',
|
|
1172
|
-
'Check that the component exists in your dependencies or libraries and is properly referenced.'
|
|
1174
|
+
'Check that the component exists in your dependencies or libraries and is properly referenced.'
|
|
1173
1175
|
].join('\\n');
|
|
1174
1176
|
console.error('🔴 Undefined JSX Component:', errorMsg);
|
|
1175
1177
|
window.__testHarnessRuntimeErrors = window.__testHarnessRuntimeErrors || [];
|
|
@@ -1177,7 +1179,7 @@ class ComponentRunner {
|
|
|
1177
1179
|
message: errorMsg,
|
|
1178
1180
|
type: 'undefined-component',
|
|
1179
1181
|
phase: 'createElement',
|
|
1180
|
-
source: 'enhanced-detection'
|
|
1182
|
+
source: 'enhanced-detection'
|
|
1181
1183
|
});
|
|
1182
1184
|
}
|
|
1183
1185
|
// Call original createElement
|
|
@@ -1187,7 +1189,7 @@ class ComponentRunner {
|
|
|
1187
1189
|
// Override console.error
|
|
1188
1190
|
const originalConsoleError = console.error;
|
|
1189
1191
|
console.error = function (...args) {
|
|
1190
|
-
const errorText = args.map(
|
|
1192
|
+
const errorText = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join(' ');
|
|
1191
1193
|
// Check if this is a warning rather than an error
|
|
1192
1194
|
// React warnings typically start with "Warning:" or contain warning-related text
|
|
1193
1195
|
const isWarning = errorText.includes('Warning:') ||
|
|
@@ -1218,15 +1220,16 @@ class ComponentRunner {
|
|
|
1218
1220
|
isAvailableLibrary: false,
|
|
1219
1221
|
matchedLibrary: null,
|
|
1220
1222
|
specLibraries: spec?.libraries || [],
|
|
1221
|
-
specDependencies: spec?.dependencies || []
|
|
1223
|
+
specDependencies: spec?.dependencies || []
|
|
1222
1224
|
};
|
|
1223
1225
|
// Check if it's in spec libraries
|
|
1224
1226
|
result.isInSpecLibraries = result.specLibraries.some((lib) => lib.globalVariable === identifier);
|
|
1225
|
-
// Check if it's in spec dependencies
|
|
1227
|
+
// Check if it's in spec dependencies
|
|
1226
1228
|
result.isInSpecDependencies = result.specDependencies.some((dep) => dep.name === identifier);
|
|
1227
1229
|
// Check against ALL available libraries (case-insensitive)
|
|
1228
1230
|
if (availableLibraries) {
|
|
1229
|
-
const availableLib = availableLibraries.find((lib) => lib.GlobalVariable &&
|
|
1231
|
+
const availableLib = availableLibraries.find((lib) => lib.GlobalVariable &&
|
|
1232
|
+
lib.GlobalVariable.toLowerCase() === identifier.toLowerCase());
|
|
1230
1233
|
if (availableLib) {
|
|
1231
1234
|
result.isAvailableLibrary = true;
|
|
1232
1235
|
result.matchedLibrary = availableLib;
|
|
@@ -1239,35 +1242,29 @@ class ComponentRunner {
|
|
|
1239
1242
|
// Case 1: Trying to use a library not in their spec
|
|
1240
1243
|
if (analysis.isAvailableLibrary && !analysis.isInSpecLibraries) {
|
|
1241
1244
|
const libList = analysis.specLibraries.length > 0
|
|
1242
|
-
? analysis.specLibraries
|
|
1243
|
-
.map((l) => l.globalVariable || l.name)
|
|
1244
|
-
.filter(Boolean)
|
|
1245
|
-
.join(', ')
|
|
1245
|
+
? analysis.specLibraries.map((l) => l.globalVariable || l.name).filter(Boolean).join(', ')
|
|
1246
1246
|
: 'no third-party libraries';
|
|
1247
|
-
return
|
|
1247
|
+
return `${identifier} is not defined. It appears you're trying to use the ${analysis.matchedLibrary.Name} library. ` +
|
|
1248
1248
|
`You do NOT have access to this library. ` +
|
|
1249
1249
|
`Your architect gave you access to: ${libList}. ` +
|
|
1250
|
-
`You must work within these constraints and cannot load additional libraries
|
|
1250
|
+
`You must work within these constraints and cannot load additional libraries.`;
|
|
1251
1251
|
}
|
|
1252
1252
|
// Case 2: Should be a component but not properly accessed
|
|
1253
1253
|
if (analysis.isInSpecDependencies) {
|
|
1254
|
-
return
|
|
1254
|
+
return `${identifier} is not defined. This component exists in your dependencies. ` +
|
|
1255
1255
|
`Ensure you've destructured it: const { ${identifier} } = components; ` +
|
|
1256
|
-
`or accessed it as: components.${identifier}
|
|
1256
|
+
`or accessed it as: components.${identifier}`;
|
|
1257
1257
|
}
|
|
1258
1258
|
// Case 3: Not a valid library or component
|
|
1259
1259
|
const libList = analysis.specLibraries.length > 0
|
|
1260
|
-
? `Available libraries: ${analysis.specLibraries
|
|
1261
|
-
.map((l) => l.globalVariable || l.name)
|
|
1262
|
-
.filter(Boolean)
|
|
1263
|
-
.join(', ')}`
|
|
1260
|
+
? `Available libraries: ${analysis.specLibraries.map((l) => l.globalVariable || l.name).filter(Boolean).join(', ')}`
|
|
1264
1261
|
: 'No third-party libraries are available';
|
|
1265
1262
|
const depList = analysis.specDependencies.length > 0
|
|
1266
1263
|
? `Available components: ${analysis.specDependencies.map((d) => d.name).join(', ')}`
|
|
1267
1264
|
: 'No component dependencies are available';
|
|
1268
|
-
return
|
|
1265
|
+
return `${identifier} is not defined. This is not a valid library or component in your specification. ` +
|
|
1269
1266
|
`${libList}. ${depList}. ` +
|
|
1270
|
-
`You must only use the libraries and components specified in your component specification
|
|
1267
|
+
`You must only use the libraries and components specified in your component specification.`;
|
|
1271
1268
|
};
|
|
1272
1269
|
// Global error handler
|
|
1273
1270
|
window.addEventListener('error', (event) => {
|
|
@@ -1285,7 +1282,7 @@ class ComponentRunner {
|
|
|
1285
1282
|
stack: event.error?.stack,
|
|
1286
1283
|
type: 'undefined-identifier',
|
|
1287
1284
|
source: 'user-component',
|
|
1288
|
-
identifier: identifier
|
|
1285
|
+
identifier: identifier
|
|
1289
1286
|
});
|
|
1290
1287
|
window.__testHarnessTestFailed = true;
|
|
1291
1288
|
return;
|
|
@@ -1299,7 +1296,7 @@ class ComponentRunner {
|
|
|
1299
1296
|
message: event.error?.message || event.message,
|
|
1300
1297
|
stack: event.error?.stack,
|
|
1301
1298
|
type: 'runtime',
|
|
1302
|
-
source: source
|
|
1299
|
+
source: source
|
|
1303
1300
|
});
|
|
1304
1301
|
window.__testHarnessTestFailed = true;
|
|
1305
1302
|
});
|
|
@@ -1309,7 +1306,7 @@ class ComponentRunner {
|
|
|
1309
1306
|
message: 'Unhandled Promise Rejection: ' + (event.reason?.message || event.reason),
|
|
1310
1307
|
stack: event.reason?.stack,
|
|
1311
1308
|
type: 'promise-rejection',
|
|
1312
|
-
source: 'user-component'
|
|
1309
|
+
source: 'user-component' // Async errors are likely from user code
|
|
1313
1310
|
});
|
|
1314
1311
|
window.__testHarnessTestFailed = true;
|
|
1315
1312
|
event.preventDefault();
|
|
@@ -1324,17 +1321,20 @@ class ComponentRunner {
|
|
|
1324
1321
|
return {
|
|
1325
1322
|
runtimeErrors: window.__testHarnessRuntimeErrors || [],
|
|
1326
1323
|
consoleErrors: window.__testHarnessConsoleErrors || [],
|
|
1327
|
-
testFailed: window.__testHarnessTestFailed || false
|
|
1324
|
+
testFailed: window.__testHarnessTestFailed || false
|
|
1328
1325
|
};
|
|
1329
1326
|
});
|
|
1330
1327
|
// Track unique errors and their counts
|
|
1331
1328
|
const errorMap = new Map();
|
|
1332
1329
|
// Check if we have any specific React render errors
|
|
1333
|
-
const hasSpecificReactError = errorData.runtimeErrors.some((error) => error.type === 'react-render-error' &&
|
|
1330
|
+
const hasSpecificReactError = errorData.runtimeErrors.some((error) => error.type === 'react-render-error' &&
|
|
1331
|
+
!error.message.includes('Script error'));
|
|
1334
1332
|
// Process runtime errors with their source information
|
|
1335
1333
|
errorData.runtimeErrors.forEach((error) => {
|
|
1336
1334
|
// Skip generic "Script error" messages if we have more specific React errors
|
|
1337
|
-
if (hasSpecificReactError &&
|
|
1335
|
+
if (hasSpecificReactError &&
|
|
1336
|
+
error.type === 'runtime' &&
|
|
1337
|
+
error.message === 'Script error.') {
|
|
1338
1338
|
return; // Skip this generic error
|
|
1339
1339
|
}
|
|
1340
1340
|
// Map error types to specific rule names
|
|
@@ -1381,9 +1381,9 @@ class ComponentRunner {
|
|
|
1381
1381
|
message: error.message,
|
|
1382
1382
|
source: error.source,
|
|
1383
1383
|
type: error.type,
|
|
1384
|
-
rule: rule
|
|
1384
|
+
rule: rule
|
|
1385
1385
|
},
|
|
1386
|
-
count: 1
|
|
1386
|
+
count: 1
|
|
1387
1387
|
});
|
|
1388
1388
|
}
|
|
1389
1389
|
});
|
|
@@ -1399,9 +1399,9 @@ class ComponentRunner {
|
|
|
1399
1399
|
message: error,
|
|
1400
1400
|
source: 'react-framework',
|
|
1401
1401
|
type: 'console-error',
|
|
1402
|
-
rule: 'console-error'
|
|
1402
|
+
rule: 'console-error'
|
|
1403
1403
|
},
|
|
1404
|
-
count: 1
|
|
1404
|
+
count: 1
|
|
1405
1405
|
});
|
|
1406
1406
|
}
|
|
1407
1407
|
});
|
|
@@ -1409,10 +1409,12 @@ class ComponentRunner {
|
|
|
1409
1409
|
const errors = [];
|
|
1410
1410
|
errorMap.forEach(({ error, count }) => {
|
|
1411
1411
|
// Append count if > 1
|
|
1412
|
-
const message = count > 1
|
|
1412
|
+
const message = count > 1
|
|
1413
|
+
? `${error.message} (occurred ${count} times)`
|
|
1414
|
+
: error.message;
|
|
1413
1415
|
errors.push({
|
|
1414
1416
|
...error,
|
|
1415
|
-
message
|
|
1417
|
+
message
|
|
1416
1418
|
});
|
|
1417
1419
|
});
|
|
1418
1420
|
return errors;
|
|
@@ -1423,7 +1425,7 @@ class ComponentRunner {
|
|
|
1423
1425
|
async collectWarnings(page) {
|
|
1424
1426
|
const warningData = await page.evaluate(() => {
|
|
1425
1427
|
return {
|
|
1426
|
-
consoleWarnings: window.__testHarnessConsoleWarnings || []
|
|
1428
|
+
consoleWarnings: window.__testHarnessConsoleWarnings || []
|
|
1427
1429
|
};
|
|
1428
1430
|
});
|
|
1429
1431
|
const warnings = [];
|
|
@@ -1455,23 +1457,23 @@ class ComponentRunner {
|
|
|
1455
1457
|
});
|
|
1456
1458
|
}
|
|
1457
1459
|
async buildLocalMJUtilities(contextUser) {
|
|
1458
|
-
console.log(
|
|
1459
|
-
const rv = new
|
|
1460
|
-
const rq = new
|
|
1461
|
-
const md = new
|
|
1460
|
+
console.log(" Building local MJ utilities");
|
|
1461
|
+
const rv = new core_1.RunView();
|
|
1462
|
+
const rq = new core_1.RunQuery();
|
|
1463
|
+
const md = new core_1.Metadata();
|
|
1462
1464
|
return {
|
|
1463
1465
|
rv: {
|
|
1464
1466
|
RunView: rv.RunView,
|
|
1465
|
-
RunViews: rv.RunViews
|
|
1467
|
+
RunViews: rv.RunViews
|
|
1466
1468
|
},
|
|
1467
1469
|
rq: {
|
|
1468
|
-
RunQuery: rq.RunQuery
|
|
1470
|
+
RunQuery: rq.RunQuery
|
|
1469
1471
|
},
|
|
1470
1472
|
md: {
|
|
1471
1473
|
GetEntityObject: md.GetEntityObject, // return the function
|
|
1472
|
-
Entities: md.Entities
|
|
1474
|
+
Entities: md.Entities // return the function
|
|
1473
1475
|
},
|
|
1474
|
-
ai: await this.BuildLocalSimpleAITools(contextUser)
|
|
1476
|
+
ai: await this.BuildLocalSimpleAITools(contextUser)
|
|
1475
1477
|
};
|
|
1476
1478
|
}
|
|
1477
1479
|
async BuildLocalSimpleAITools(contextUser) {
|
|
@@ -1488,7 +1490,8 @@ class ComponentRunner {
|
|
|
1488
1490
|
await aiEngine.Config(false, params.contextUser);
|
|
1489
1491
|
const models = aiEngine.Models;
|
|
1490
1492
|
for (const preferredModel of params.preferredModels) {
|
|
1491
|
-
model = models.find((m) => m.Name === preferredModel &&
|
|
1493
|
+
model = models.find((m) => m.Name === preferredModel &&
|
|
1494
|
+
m.IsActive === true);
|
|
1492
1495
|
if (model)
|
|
1493
1496
|
break;
|
|
1494
1497
|
}
|
|
@@ -1498,7 +1501,8 @@ class ComponentRunner {
|
|
|
1498
1501
|
if (params.modelPower === 'lowest') {
|
|
1499
1502
|
// Get lowest power model by sorting in reverse
|
|
1500
1503
|
await aiEngine.Config(false, params.contextUser);
|
|
1501
|
-
const llmModels = aiEngine.Models.filter((m) => m.AIModelType === 'LLM' &&
|
|
1504
|
+
const llmModels = aiEngine.Models.filter((m) => m.AIModelType === 'LLM' &&
|
|
1505
|
+
m.IsActive === true);
|
|
1502
1506
|
model = llmModels.sort((a, b) => (a.PowerRank || 0) - (b.PowerRank || 0))[0];
|
|
1503
1507
|
}
|
|
1504
1508
|
else if (params.modelPower === 'highest') {
|
|
@@ -1507,7 +1511,8 @@ class ComponentRunner {
|
|
|
1507
1511
|
else {
|
|
1508
1512
|
// Default to medium - get a model in the middle range
|
|
1509
1513
|
await aiEngine.Config(false, params.contextUser);
|
|
1510
|
-
const llmModels = aiEngine.Models.filter((m) => m.AIModelType === 'LLM' &&
|
|
1514
|
+
const llmModels = aiEngine.Models.filter((m) => m.AIModelType === 'LLM' &&
|
|
1515
|
+
m.IsActive === true);
|
|
1511
1516
|
const sortedModels = llmModels.sort((a, b) => (b.PowerRank || 0) - (a.PowerRank || 0));
|
|
1512
1517
|
const midIndex = Math.floor(sortedModels.length / 2);
|
|
1513
1518
|
model = sortedModels[midIndex] || sortedModels[0];
|
|
@@ -1516,7 +1521,9 @@ class ComponentRunner {
|
|
|
1516
1521
|
// Build full conversation from messages if provided
|
|
1517
1522
|
let fullUserPrompt = '';
|
|
1518
1523
|
if (params.messages && params.messages.length > 0) {
|
|
1519
|
-
fullUserPrompt = params.messages
|
|
1524
|
+
fullUserPrompt = params.messages
|
|
1525
|
+
.map(m => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.message}`)
|
|
1526
|
+
.join('\n');
|
|
1520
1527
|
}
|
|
1521
1528
|
// Execute the prompt using AIEngine
|
|
1522
1529
|
const result = await aiEngine.SimpleLLMCompletion(fullUserPrompt || '', params.contextUser || {}, // Provide empty object if no context user
|
|
@@ -1537,33 +1544,35 @@ class ComponentRunner {
|
|
|
1537
1544
|
success: true,
|
|
1538
1545
|
result: result,
|
|
1539
1546
|
resultObject,
|
|
1540
|
-
modelName: model?.Name || 'Unknown'
|
|
1547
|
+
modelName: model?.Name || 'Unknown'
|
|
1541
1548
|
};
|
|
1542
1549
|
}
|
|
1543
1550
|
catch (error) {
|
|
1544
|
-
(0,
|
|
1551
|
+
(0, core_1.LogError)(error);
|
|
1545
1552
|
return {
|
|
1546
1553
|
success: false,
|
|
1547
1554
|
result: 'Failed to execute prompt: ' + (error instanceof Error ? error.message : String(error)),
|
|
1548
|
-
modelName: ''
|
|
1555
|
+
modelName: ''
|
|
1549
1556
|
};
|
|
1550
1557
|
}
|
|
1551
1558
|
},
|
|
1552
1559
|
EmbedText: async (params) => {
|
|
1553
1560
|
try {
|
|
1554
1561
|
// Handle both single string and array of strings
|
|
1555
|
-
const texts = Array.isArray(params.textToEmbed)
|
|
1562
|
+
const texts = Array.isArray(params.textToEmbed)
|
|
1563
|
+
? params.textToEmbed
|
|
1564
|
+
: [params.textToEmbed];
|
|
1556
1565
|
// Use appropriate embedding model based on size
|
|
1557
1566
|
await aiEngine.Config(false, params.contextUser);
|
|
1558
1567
|
// Get embedding models and filter by size preference
|
|
1559
|
-
const embeddingModels = aiEngine.Models.filter((m) => m.AIModelType === 'Embeddings' &&
|
|
1568
|
+
const embeddingModels = aiEngine.Models.filter((m) => m.AIModelType === 'Embeddings' &&
|
|
1569
|
+
m.IsActive === true);
|
|
1560
1570
|
// Select model based on size preference
|
|
1561
1571
|
let model;
|
|
1562
1572
|
if (params.modelSize === 'small') {
|
|
1563
1573
|
// Prefer local/smaller models for 'small'
|
|
1564
|
-
model =
|
|
1565
|
-
embeddingModels.
|
|
1566
|
-
embeddingModels.sort((a, b) => (a.PowerRank || 0) - (b.PowerRank || 0))[0];
|
|
1574
|
+
model = embeddingModels.find((m) => m.Vendor === 'LocalEmbeddings') ||
|
|
1575
|
+
embeddingModels.sort((a, b) => (a.PowerRank || 0) - (b.PowerRank || 0))[0];
|
|
1567
1576
|
}
|
|
1568
1577
|
else {
|
|
1569
1578
|
// Use more powerful models for 'medium'
|
|
@@ -1584,19 +1593,21 @@ class ComponentRunner {
|
|
|
1584
1593
|
}
|
|
1585
1594
|
}
|
|
1586
1595
|
// Return single embedding or array based on input
|
|
1587
|
-
const returnEmbeddings = Array.isArray(params.textToEmbed)
|
|
1596
|
+
const returnEmbeddings = Array.isArray(params.textToEmbed)
|
|
1597
|
+
? embeddings
|
|
1598
|
+
: embeddings[0];
|
|
1588
1599
|
return {
|
|
1589
1600
|
result: returnEmbeddings,
|
|
1590
1601
|
modelName: model.Name,
|
|
1591
|
-
vectorDimensions: embeddings[0]?.length || 0
|
|
1602
|
+
vectorDimensions: embeddings[0]?.length || 0
|
|
1592
1603
|
};
|
|
1593
1604
|
}
|
|
1594
1605
|
catch (error) {
|
|
1595
|
-
(0,
|
|
1606
|
+
(0, core_1.LogError)(error);
|
|
1596
1607
|
throw error; // Re-throw for embeddings as they're critical
|
|
1597
1608
|
}
|
|
1598
1609
|
},
|
|
1599
|
-
VectorService: new ai_vectors_memory_1.SimpleVectorService()
|
|
1610
|
+
VectorService: new ai_vectors_memory_1.SimpleVectorService()
|
|
1600
1611
|
};
|
|
1601
1612
|
}
|
|
1602
1613
|
/**
|
|
@@ -1609,7 +1620,7 @@ class ComponentRunner {
|
|
|
1609
1620
|
// UserInfo is a simple object that can be serialized
|
|
1610
1621
|
const serializedContextUser = JSON.parse(JSON.stringify(options.contextUser));
|
|
1611
1622
|
// utilities - favor the one passed in by the caller, or fall back to the local ones
|
|
1612
|
-
const util = options.utilities ||
|
|
1623
|
+
const util = options.utilities || await this.buildLocalMJUtilities(options.contextUser);
|
|
1613
1624
|
// Create a lightweight mock metadata object with serializable data
|
|
1614
1625
|
// This avoids authentication/provider issues in the browser context
|
|
1615
1626
|
let entitiesData = [];
|
|
@@ -1644,7 +1655,7 @@ class ComponentRunner {
|
|
|
1644
1655
|
AuditLogTypes: [],
|
|
1645
1656
|
Authorizations: [],
|
|
1646
1657
|
VisibleExplorerNavigationItems: [],
|
|
1647
|
-
AllExplorerNavigationItems: []
|
|
1658
|
+
AllExplorerNavigationItems: []
|
|
1648
1659
|
};
|
|
1649
1660
|
// Inject both the contextUser and mock metadata into the page
|
|
1650
1661
|
// Playwright only accepts a single argument, so wrap in an object
|
|
@@ -1660,7 +1671,7 @@ class ComponentRunner {
|
|
|
1660
1671
|
// This must be available before any component code runs
|
|
1661
1672
|
if (!window.Metadata) {
|
|
1662
1673
|
window.Metadata = {
|
|
1663
|
-
Provider: mockMd
|
|
1674
|
+
Provider: mockMd
|
|
1664
1675
|
};
|
|
1665
1676
|
// Created global Metadata mock with Provider (early)
|
|
1666
1677
|
}
|
|
@@ -1731,7 +1742,7 @@ class ComponentRunner {
|
|
|
1731
1742
|
}
|
|
1732
1743
|
catch (error) {
|
|
1733
1744
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1734
|
-
const entities = params.map(
|
|
1745
|
+
const entities = params.map(p => p.EntityName || 'unknown').join(', ');
|
|
1735
1746
|
// Debug logging for errors
|
|
1736
1747
|
if (debug) {
|
|
1737
1748
|
console.log(`❌ RunViews FAILED: Entities=[${entities}]`);
|
|
@@ -1800,7 +1811,7 @@ class ComponentRunner {
|
|
|
1800
1811
|
return {
|
|
1801
1812
|
success: false,
|
|
1802
1813
|
result: errorMessage,
|
|
1803
|
-
modelName: ''
|
|
1814
|
+
modelName: ''
|
|
1804
1815
|
};
|
|
1805
1816
|
}
|
|
1806
1817
|
});
|
|
@@ -1813,7 +1824,9 @@ class ComponentRunner {
|
|
|
1813
1824
|
const paramsWithUser = { ...params, contextUser: options.contextUser };
|
|
1814
1825
|
const result = await util.ai.EmbedText(paramsWithUser);
|
|
1815
1826
|
if (debug) {
|
|
1816
|
-
const count = Array.isArray(result.result)
|
|
1827
|
+
const count = Array.isArray(result.result)
|
|
1828
|
+
? (Array.isArray(result.result[0]) ? result.result.length : 1)
|
|
1829
|
+
: 1;
|
|
1817
1830
|
console.log(`🤖 EmbedText SUCCESS: Model="${result.modelName}" Count=${count} Dims=${result.vectorDimensions}`);
|
|
1818
1831
|
}
|
|
1819
1832
|
return result;
|
|
@@ -1836,9 +1849,7 @@ class ComponentRunner {
|
|
|
1836
1849
|
const VectorService = window.MJReactRuntime?.SimpleVectorService ||
|
|
1837
1850
|
class {
|
|
1838
1851
|
// Stub implementation if not available
|
|
1839
|
-
cosineSimilarity(_a, _b) {
|
|
1840
|
-
return 0;
|
|
1841
|
-
}
|
|
1852
|
+
cosineSimilarity(_a, _b) { return 0; }
|
|
1842
1853
|
};
|
|
1843
1854
|
window.__mjUtilities = {
|
|
1844
1855
|
md: {
|
|
@@ -1850,25 +1861,25 @@ class ComponentRunner {
|
|
|
1850
1861
|
return mockMd.Entities.find((e) => e.Name === name) || null;
|
|
1851
1862
|
},
|
|
1852
1863
|
// Keep async function for GetEntityObject for compatibility
|
|
1853
|
-
GetEntityObject: async (entityName) => await window.__mjGetEntityObject(entityName)
|
|
1864
|
+
GetEntityObject: async (entityName) => await window.__mjGetEntityObject(entityName)
|
|
1854
1865
|
},
|
|
1855
1866
|
rv: {
|
|
1856
1867
|
RunView: async (params) => await window.__mjRunView(params),
|
|
1857
|
-
RunViews: async (params) => await window.__mjRunViews(params)
|
|
1868
|
+
RunViews: async (params) => await window.__mjRunViews(params)
|
|
1858
1869
|
},
|
|
1859
1870
|
rq: {
|
|
1860
|
-
RunQuery: async (params) => await window.__mjRunQuery(params)
|
|
1871
|
+
RunQuery: async (params) => await window.__mjRunQuery(params)
|
|
1861
1872
|
},
|
|
1862
1873
|
ai: {
|
|
1863
1874
|
ExecutePrompt: async (params) => await window.__mjExecutePrompt(params),
|
|
1864
1875
|
EmbedText: async (params) => await window.__mjEmbedText(params),
|
|
1865
|
-
VectorService: new VectorService()
|
|
1866
|
-
}
|
|
1876
|
+
VectorService: new VectorService()
|
|
1877
|
+
}
|
|
1867
1878
|
};
|
|
1868
1879
|
// Update or create global Metadata mock (in case it wasn't created earlier)
|
|
1869
1880
|
if (!window.Metadata) {
|
|
1870
1881
|
window.Metadata = {
|
|
1871
|
-
Provider: mockMd
|
|
1882
|
+
Provider: mockMd
|
|
1872
1883
|
};
|
|
1873
1884
|
// Created global Metadata mock with Provider (late)
|
|
1874
1885
|
}
|