@capillarytech/cap-ui-dev-tools 1.5.0 → 1.6.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/package.json +1 -1
- package/src/capvision-recorder/adapters/WebdriverIOAdapter.js +94 -2
- package/src/capvision-recorder/core/CapVisionRecorder.js +7 -275
- package/src/capvision-recorder/core/ReportEnhancer.js +0 -57
- package/src/capvision-recorder/index.js +0 -3
- package/src/capvision-recorder/assets/capvision-plugins/network-record.min.js +0 -542
- package/src/capvision-recorder/assets/capvision-plugins/network-replay.min.js +0 -434
package/package.json
CHANGED
|
@@ -51,12 +51,21 @@ class WebdriverIOCapVisionRecorder {
|
|
|
51
51
|
constructor(recorderConfig = {}, enhancerConfig = {}) {
|
|
52
52
|
/** @type {import('../core/CapVisionRecorder').CapVisionRecorder} */
|
|
53
53
|
this.recorder = new CapVisionRecorder(recorderConfig);
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
/** @type {import('../core/ReportEnhancer').ReportEnhancer} */
|
|
56
56
|
this.enhancer = new ReportEnhancer(enhancerConfig);
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
/** @type {WebdriverIOBrowserExecutor|null} */
|
|
59
59
|
this.browserExecutor = null;
|
|
60
|
+
|
|
61
|
+
/** @type {Object|null} */
|
|
62
|
+
this.browser = null;
|
|
63
|
+
|
|
64
|
+
/** @type {boolean} */
|
|
65
|
+
this.cdpNetworkLoggingSetup = false;
|
|
66
|
+
|
|
67
|
+
/** @type {Object<string, string>} */
|
|
68
|
+
this.requestMap = {};
|
|
60
69
|
}
|
|
61
70
|
|
|
62
71
|
/**
|
|
@@ -67,6 +76,8 @@ class WebdriverIOCapVisionRecorder {
|
|
|
67
76
|
init(browser) {
|
|
68
77
|
this.browserExecutor = new WebdriverIOBrowserExecutor(browser);
|
|
69
78
|
this.recorder.setBrowserExecutor(this.browserExecutor);
|
|
79
|
+
/** @type {Object} */
|
|
80
|
+
this.browser = browser;
|
|
70
81
|
}
|
|
71
82
|
|
|
72
83
|
/**
|
|
@@ -127,6 +138,81 @@ class WebdriverIOCapVisionRecorder {
|
|
|
127
138
|
this.recorder.clearRecordingsFolder();
|
|
128
139
|
}
|
|
129
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Setup CDP network listeners to log network requests to browser console
|
|
143
|
+
* @private
|
|
144
|
+
* @async
|
|
145
|
+
* @param {boolean} [force=false] - Force re-setup even if already configured
|
|
146
|
+
* @returns {Promise<void>}
|
|
147
|
+
*/
|
|
148
|
+
async setupCDPNetworkLogging(force = false) {
|
|
149
|
+
if (!this.browser) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Skip if already setup (unless forced)
|
|
154
|
+
if (this.cdpNetworkLoggingSetup && !force) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
await this.browser.cdp('Network', 'enable');
|
|
160
|
+
|
|
161
|
+
if (force && this.cdpNetworkLoggingSetup) {
|
|
162
|
+
try {
|
|
163
|
+
this.browser.removeAllListeners('Network.requestWillBeSent');
|
|
164
|
+
this.browser.removeAllListeners('Network.responseReceived');
|
|
165
|
+
this.browser.removeAllListeners('Network.loadingFailed');
|
|
166
|
+
this.requestMap = {};
|
|
167
|
+
} catch (e) {
|
|
168
|
+
// Ignore errors when removing listeners
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Track requests to map requestId to URL
|
|
173
|
+
this.browser.on('Network.requestWillBeSent', (params) => {
|
|
174
|
+
const { requestId, request } = params;
|
|
175
|
+
this.requestMap[requestId] = request.url;
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
this.browser.on('Network.responseReceived', async (params) => {
|
|
179
|
+
const { response } = params;
|
|
180
|
+
const url = response.url;
|
|
181
|
+
const requestId = response.headers['x-cap-request-id'] || 'N/A';
|
|
182
|
+
|
|
183
|
+
// Log to browser console so it gets captured by CapVision recorder
|
|
184
|
+
await this.browser.execute((url, requestId) => {
|
|
185
|
+
console.log(
|
|
186
|
+
'%c🌐 Network Request',
|
|
187
|
+
'color: #4CAF50; font-weight: bold; font-size: 12px;',
|
|
188
|
+
'\n 📍 URL:',
|
|
189
|
+
url,
|
|
190
|
+
'\n 🆔 Request ID:',
|
|
191
|
+
requestId,
|
|
192
|
+
'\n'
|
|
193
|
+
);
|
|
194
|
+
}, url, requestId);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Log failed requests
|
|
198
|
+
this.browser.on('Network.loadingFailed', async (params) => {
|
|
199
|
+
const { requestId } = params;
|
|
200
|
+
const url = this.requestMap[requestId] || 'unknown';
|
|
201
|
+
const cap_requestId = params?.response?.headers['x-cap-request-id'] || 'N/A';
|
|
202
|
+
|
|
203
|
+
await this.browser.execute((url, cap_requestId) => {
|
|
204
|
+
console.log(`❌ Request failed (ID: ${cap_requestId})`, url);
|
|
205
|
+
}, url, cap_requestId);
|
|
206
|
+
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
this.cdpNetworkLoggingSetup = true;
|
|
210
|
+
} catch (error) {
|
|
211
|
+
console.warn('⚠️ Failed to setup CDP network logging:', error.message);
|
|
212
|
+
// Don't throw - CDP might not be available in all browsers/configurations
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
130
216
|
/**
|
|
131
217
|
* Start recording
|
|
132
218
|
* @async
|
|
@@ -137,6 +223,9 @@ class WebdriverIOCapVisionRecorder {
|
|
|
137
223
|
if (!this.browserExecutor) {
|
|
138
224
|
throw new Error('Browser executor not initialized. Call init(browser) first.');
|
|
139
225
|
}
|
|
226
|
+
|
|
227
|
+
await this.setupCDPNetworkLogging();
|
|
228
|
+
|
|
140
229
|
return this.recorder.startRecording();
|
|
141
230
|
}
|
|
142
231
|
|
|
@@ -165,6 +254,9 @@ class WebdriverIOCapVisionRecorder {
|
|
|
165
254
|
if (!this.browserExecutor) {
|
|
166
255
|
throw new Error('Browser executor not initialized. Call init(browser) first.');
|
|
167
256
|
}
|
|
257
|
+
|
|
258
|
+
await this.setupCDPNetworkLogging(true);
|
|
259
|
+
|
|
168
260
|
return this.recorder.ensureCapVisionIsActive();
|
|
169
261
|
}
|
|
170
262
|
|
|
@@ -29,16 +29,6 @@ const os = require('os');
|
|
|
29
29
|
* @property {Object} [consoleRecordOptions.stringifyOptions] - Stringification options
|
|
30
30
|
* @property {number} [consoleRecordOptions.stringifyOptions.stringLengthLimit] - String length limit
|
|
31
31
|
* @property {number} [consoleRecordOptions.stringifyOptions.numOfKeysLimit] - Number of keys limit
|
|
32
|
-
* @property {string} [networkRecordPluginPath] - Path to network record plugin
|
|
33
|
-
* @property {string} [networkReplayPluginPath] - Path to network replay plugin
|
|
34
|
-
* @property {boolean} [recordNetwork] - Enable network recording
|
|
35
|
-
* @property {Object} [networkRecordOptions] - Network recording options
|
|
36
|
-
* @property {boolean} [networkRecordOptions.recordBody] - Record request/response bodies
|
|
37
|
-
* @property {boolean} [networkRecordOptions.recordHeaders] - Record request/response headers
|
|
38
|
-
* @property {boolean} [networkRecordOptions.recordInitiator] - Record stack traces
|
|
39
|
-
* @property {boolean} [networkRecordOptions.recordPerformance] - Record performance metrics
|
|
40
|
-
* @property {number} [networkRecordOptions.maxBodyLength] - Max body length before truncation
|
|
41
|
-
* @property {Function} [networkRecordOptions.ignoreRequestFn] - Filter function for requests
|
|
42
32
|
*/
|
|
43
33
|
|
|
44
34
|
/**
|
|
@@ -54,8 +44,6 @@ const DEFAULT_CONFIG = {
|
|
|
54
44
|
capVisionScriptPath: path.join(__dirname, '../assets/capvision.min.js'),
|
|
55
45
|
consoleRecordPluginPath: path.join(__dirname, '../assets/capvision-plugins/console-record.min.js'),
|
|
56
46
|
consoleReplayPluginPath: path.join(__dirname, '../assets/capvision-plugins/console-replay.min.js'),
|
|
57
|
-
networkRecordPluginPath: path.join(__dirname, '../assets/capvision-plugins/network-record.min.js'),
|
|
58
|
-
networkReplayPluginPath: path.join(__dirname, '../assets/capvision-plugins/network-replay.min.js'),
|
|
59
47
|
recordingsOutputDir: path.join(process.cwd(), 'reports', 'recordings'),
|
|
60
48
|
|
|
61
49
|
sessionStorageKey: 'capvision_events',
|
|
@@ -77,23 +65,6 @@ const DEFAULT_CONFIG = {
|
|
|
77
65
|
stringLengthLimit: 10000,
|
|
78
66
|
numOfKeysLimit: 100
|
|
79
67
|
}
|
|
80
|
-
},
|
|
81
|
-
// Network recording options
|
|
82
|
-
recordNetwork: true,
|
|
83
|
-
networkRecordOptions: {
|
|
84
|
-
recordBody: true,
|
|
85
|
-
recordHeaders: true,
|
|
86
|
-
recordInitiator: true,
|
|
87
|
-
recordPerformance: true,
|
|
88
|
-
maxBodyLength: 10000,
|
|
89
|
-
ignoreRequestFn: (url, type) => {
|
|
90
|
-
// Ignore CapVision internal requests and common tracking/analytics
|
|
91
|
-
if (!url) return true;
|
|
92
|
-
if (url.includes('capvision')) return true;
|
|
93
|
-
if (url.includes('google-analytics')) return true;
|
|
94
|
-
if (url.includes('googletagmanager')) return true;
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
68
|
}
|
|
98
69
|
};
|
|
99
70
|
|
|
@@ -499,25 +470,7 @@ class CapVisionRecorder {
|
|
|
499
470
|
}
|
|
500
471
|
}
|
|
501
472
|
|
|
502
|
-
|
|
503
|
-
let networkPluginCode = '';
|
|
504
|
-
if (this.config.recordNetwork) {
|
|
505
|
-
try {
|
|
506
|
-
console.log('🟡 Loading network plugin from:', this.config.networkRecordPluginPath);
|
|
507
|
-
networkPluginCode = fs.readFileSync(this.config.networkRecordPluginPath, 'utf8');
|
|
508
|
-
console.log(`🟢 Network plugin code loaded: ${networkPluginCode.length} characters`);
|
|
509
|
-
if (networkPluginCode.length === 0) {
|
|
510
|
-
console.warn('⚠️ Network plugin file is empty!');
|
|
511
|
-
}
|
|
512
|
-
} catch (error) {
|
|
513
|
-
console.error('🔴 Failed to load network plugin file:', error.message);
|
|
514
|
-
console.error('🔴 File path:', this.config.networkRecordPluginPath);
|
|
515
|
-
}
|
|
516
|
-
} else {
|
|
517
|
-
console.log('🟡 Network recording disabled in config');
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
await this.browserExecutor.execute((config, capVisionScript, pluginCode, networkPluginCode) => {
|
|
473
|
+
await this.browserExecutor.execute((config, capVisionScript, pluginCode) => {
|
|
521
474
|
// Initialize global state
|
|
522
475
|
window.capVisionInitialized = false;
|
|
523
476
|
window.capVisionLastCheckTime = Date.now();
|
|
@@ -572,71 +525,6 @@ class CapVisionRecorder {
|
|
|
572
525
|
});
|
|
573
526
|
};
|
|
574
527
|
|
|
575
|
-
// Inject Network Record Plugin script
|
|
576
|
-
const injectNetworkPlugin = (pluginCode) => {
|
|
577
|
-
return new Promise((resolve) => {
|
|
578
|
-
if (!config.recordNetwork || !pluginCode) {
|
|
579
|
-
console.log('🟡 Network recording disabled or plugin code not available');
|
|
580
|
-
resolve();
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
if (window.rrwebPluginNetworkRecord) {
|
|
585
|
-
console.log('🟢 Network plugin already loaded');
|
|
586
|
-
resolve();
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
try {
|
|
591
|
-
console.log('🟡 Injecting network plugin script...');
|
|
592
|
-
console.log('🟡 Plugin code length:', pluginCode.length);
|
|
593
|
-
|
|
594
|
-
// Try direct execution first (for immediate availability)
|
|
595
|
-
try {
|
|
596
|
-
eval(pluginCode);
|
|
597
|
-
if (window.rrwebPluginNetworkRecord) {
|
|
598
|
-
console.log('🟢 Network plugin loaded via eval');
|
|
599
|
-
resolve();
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
} catch (evalError) {
|
|
603
|
-
console.warn('⚠️ Eval failed, trying script injection:', evalError);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
// Fallback: inject as script element
|
|
607
|
-
const script = document.createElement('script');
|
|
608
|
-
script.textContent = pluginCode;
|
|
609
|
-
script.onerror = (error) => {
|
|
610
|
-
console.error('🔴 Script onerror:', error);
|
|
611
|
-
};
|
|
612
|
-
document.head.appendChild(script);
|
|
613
|
-
|
|
614
|
-
// Wait for script to execute and set window.rrwebPluginNetworkRecord
|
|
615
|
-
let checkCount = 0;
|
|
616
|
-
const checkInterval = setInterval(() => {
|
|
617
|
-
checkCount++;
|
|
618
|
-
if (window.rrwebPluginNetworkRecord) {
|
|
619
|
-
clearInterval(checkInterval);
|
|
620
|
-
console.log(`🟢 Network plugin script loaded successfully after ${checkCount * 50}ms`);
|
|
621
|
-
resolve();
|
|
622
|
-
} else if (checkCount > 20) {
|
|
623
|
-
// Stop checking after 1 second
|
|
624
|
-
clearInterval(checkInterval);
|
|
625
|
-
console.warn('⚠️ Network plugin script injected but function not found after 1s');
|
|
626
|
-
console.warn('⚠️ Script element:', script);
|
|
627
|
-
console.warn('⚠️ Script parent:', script.parentElement);
|
|
628
|
-
console.warn('⚠️ Window keys:', Object.keys(window).filter(k => k.includes('capvision') || k.includes('rrweb')));
|
|
629
|
-
resolve();
|
|
630
|
-
}
|
|
631
|
-
}, 50);
|
|
632
|
-
} catch (error) {
|
|
633
|
-
console.error('🔴 Failed to inject network plugin:', error.message);
|
|
634
|
-
console.error('🔴 Error stack:', error.stack);
|
|
635
|
-
resolve();
|
|
636
|
-
}
|
|
637
|
-
});
|
|
638
|
-
};
|
|
639
|
-
|
|
640
528
|
// Restore events from sessionStorage (fallback)
|
|
641
529
|
const restoreEventsFromStorage = () => {
|
|
642
530
|
if (!config.useTempFile) {
|
|
@@ -703,54 +591,6 @@ class CapVisionRecorder {
|
|
|
703
591
|
return null;
|
|
704
592
|
};
|
|
705
593
|
|
|
706
|
-
// Get network plugin if available
|
|
707
|
-
const getNetworkPlugin = () => {
|
|
708
|
-
try {
|
|
709
|
-
if (!config.recordNetwork) {
|
|
710
|
-
console.log('🟡 Network recording disabled in config');
|
|
711
|
-
return null;
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
if (!window.rrwebPluginNetworkRecord) {
|
|
715
|
-
console.warn('⚠️ Network plugin function not found on window object');
|
|
716
|
-
console.log('🟡 Available window properties:', Object.keys(window).filter(k => k.includes('capvision') || k.includes('rrweb')));
|
|
717
|
-
return null;
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
console.log('🟡 Initializing network plugin with options:', config.networkRecordOptions);
|
|
721
|
-
|
|
722
|
-
// Handle different export patterns
|
|
723
|
-
if (typeof window.rrwebPluginNetworkRecord === 'function') {
|
|
724
|
-
const plugin = window.rrwebPluginNetworkRecord(config.networkRecordOptions);
|
|
725
|
-
console.log('🟢 Network plugin initialized successfully (function pattern)');
|
|
726
|
-
console.log('🟡 Plugin structure:', {
|
|
727
|
-
name: plugin?.name,
|
|
728
|
-
hasObserver: typeof plugin?.observer === 'function',
|
|
729
|
-
keys: Object.keys(plugin || {})
|
|
730
|
-
});
|
|
731
|
-
return plugin;
|
|
732
|
-
}
|
|
733
|
-
if (window.rrwebPluginNetworkRecord.getRecordNetworkPlugin) {
|
|
734
|
-
const plugin = window.rrwebPluginNetworkRecord.getRecordNetworkPlugin(config.networkRecordOptions);
|
|
735
|
-
console.log('🟢 Network plugin initialized successfully (getRecordNetworkPlugin pattern)');
|
|
736
|
-
return plugin;
|
|
737
|
-
}
|
|
738
|
-
if (window.rrwebPluginNetworkRecord.default) {
|
|
739
|
-
const plugin = window.rrwebPluginNetworkRecord.default(config.networkRecordOptions);
|
|
740
|
-
console.log('🟢 Network plugin initialized successfully (default pattern)');
|
|
741
|
-
return plugin;
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
console.warn('⚠️ Network plugin function found but no matching export pattern');
|
|
745
|
-
console.log('🟡 Plugin type:', typeof window.rrwebPluginNetworkRecord);
|
|
746
|
-
console.log('🟡 Plugin keys:', Object.keys(window.rrwebPluginNetworkRecord || {}));
|
|
747
|
-
} catch (error) {
|
|
748
|
-
console.error('🔴 Failed to initialize network plugin:', error.message);
|
|
749
|
-
console.error('🔴 Error stack:', error.stack);
|
|
750
|
-
}
|
|
751
|
-
return null;
|
|
752
|
-
};
|
|
753
|
-
|
|
754
594
|
// Start CapVision recorder
|
|
755
595
|
const startRecorder = () => {
|
|
756
596
|
waitForCapVision().then(() => {
|
|
@@ -764,11 +604,6 @@ class CapVisionRecorder {
|
|
|
764
604
|
plugins.push(consolePlugin);
|
|
765
605
|
console.log('🟢 Console recording enabled');
|
|
766
606
|
}
|
|
767
|
-
const networkPlugin = getNetworkPlugin();
|
|
768
|
-
if (networkPlugin) {
|
|
769
|
-
plugins.push(networkPlugin);
|
|
770
|
-
console.log('🟢 Network recording enabled');
|
|
771
|
-
}
|
|
772
607
|
|
|
773
608
|
window.rrwebRecorder = window.rrweb.record({
|
|
774
609
|
emit: (event) => {
|
|
@@ -826,7 +661,6 @@ class CapVisionRecorder {
|
|
|
826
661
|
if (!document.querySelector('script[src*="capvision"]')) {
|
|
827
662
|
injectScript()
|
|
828
663
|
.then(() => injectConsolePlugin(pluginCode))
|
|
829
|
-
.then(() => injectNetworkPlugin(networkPluginCode))
|
|
830
664
|
.then(() => startRecorder())
|
|
831
665
|
.catch(console.error);
|
|
832
666
|
} else {
|
|
@@ -844,7 +678,6 @@ class CapVisionRecorder {
|
|
|
844
678
|
setupPersistence();
|
|
845
679
|
await injectScript();
|
|
846
680
|
await injectConsolePlugin(pluginCode);
|
|
847
|
-
await injectNetworkPlugin(networkPluginCode);
|
|
848
681
|
restoreEventsFromStorage();
|
|
849
682
|
startRecorder();
|
|
850
683
|
} catch (error) {
|
|
@@ -853,7 +686,7 @@ class CapVisionRecorder {
|
|
|
853
686
|
};
|
|
854
687
|
|
|
855
688
|
initialize();
|
|
856
|
-
}, this.config, capVisionScriptCode, consolePluginCode
|
|
689
|
+
}, this.config, capVisionScriptCode, consolePluginCode);
|
|
857
690
|
|
|
858
691
|
// Setup Node.js-side periodic file save
|
|
859
692
|
if (this.config.useTempFile) {
|
|
@@ -1035,16 +868,6 @@ class CapVisionRecorder {
|
|
|
1035
868
|
}
|
|
1036
869
|
}
|
|
1037
870
|
|
|
1038
|
-
// Read network plugin code
|
|
1039
|
-
let networkPluginCode = '';
|
|
1040
|
-
if (this.config.recordNetwork) {
|
|
1041
|
-
try {
|
|
1042
|
-
networkPluginCode = fs.readFileSync(this.config.networkRecordPluginPath, 'utf8');
|
|
1043
|
-
} catch (error) {
|
|
1044
|
-
console.warn('⚠️ Failed to load network plugin file:', error.message);
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
871
|
// Get event count
|
|
1049
872
|
let eventCount = 0;
|
|
1050
873
|
if (this.config.useTempFile) {
|
|
@@ -1057,7 +880,7 @@ class CapVisionRecorder {
|
|
|
1057
880
|
}
|
|
1058
881
|
}
|
|
1059
882
|
|
|
1060
|
-
await this.browserExecutor.execute((config, count, capVisionScript, pluginCode
|
|
883
|
+
await this.browserExecutor.execute((config, count, capVisionScript, pluginCode) => {
|
|
1061
884
|
// Re-inject CapVision
|
|
1062
885
|
const injectScript = () => {
|
|
1063
886
|
return new Promise((resolve) => {
|
|
@@ -1104,70 +927,6 @@ class CapVisionRecorder {
|
|
|
1104
927
|
});
|
|
1105
928
|
};
|
|
1106
929
|
|
|
1107
|
-
const injectNetworkPlugin = (pluginCode) => {
|
|
1108
|
-
return new Promise((resolve) => {
|
|
1109
|
-
if (!config.recordNetwork || !pluginCode) {
|
|
1110
|
-
console.log('🟡 Network recording disabled or plugin code not available');
|
|
1111
|
-
resolve();
|
|
1112
|
-
return;
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
if (window.rrwebPluginNetworkRecord) {
|
|
1116
|
-
console.log('🟢 Network plugin already loaded');
|
|
1117
|
-
resolve();
|
|
1118
|
-
return;
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
try {
|
|
1122
|
-
console.log('🟡 Injecting network plugin script (re-init)...');
|
|
1123
|
-
console.log('🟡 Plugin code length:', pluginCode.length);
|
|
1124
|
-
|
|
1125
|
-
// Try direct execution first (for immediate availability)
|
|
1126
|
-
try {
|
|
1127
|
-
eval(pluginCode);
|
|
1128
|
-
if (window.rrwebPluginNetworkRecord) {
|
|
1129
|
-
console.log('🟢 Network plugin loaded via eval (re-init)');
|
|
1130
|
-
resolve();
|
|
1131
|
-
return;
|
|
1132
|
-
}
|
|
1133
|
-
} catch (evalError) {
|
|
1134
|
-
console.warn('⚠️ Eval failed, trying script injection:', evalError);
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
// Fallback: inject as script element
|
|
1138
|
-
const script = document.createElement('script');
|
|
1139
|
-
script.textContent = pluginCode;
|
|
1140
|
-
script.onerror = (error) => {
|
|
1141
|
-
console.error('🔴 Script onerror:', error);
|
|
1142
|
-
};
|
|
1143
|
-
document.head.appendChild(script);
|
|
1144
|
-
|
|
1145
|
-
// Wait for script to execute and set window.rrwebPluginNetworkRecord
|
|
1146
|
-
let checkCount = 0;
|
|
1147
|
-
const checkInterval = setInterval(() => {
|
|
1148
|
-
checkCount++;
|
|
1149
|
-
if (window.rrwebPluginNetworkRecord) {
|
|
1150
|
-
clearInterval(checkInterval);
|
|
1151
|
-
console.log(`🟢 Network plugin script loaded successfully after ${checkCount * 50}ms (re-init)`);
|
|
1152
|
-
resolve();
|
|
1153
|
-
} else if (checkCount > 20) {
|
|
1154
|
-
// Stop checking after 1 second
|
|
1155
|
-
clearInterval(checkInterval);
|
|
1156
|
-
console.warn('⚠️ Network plugin script injected but function not found after 1s (re-init)');
|
|
1157
|
-
console.warn('⚠️ Script element:', script);
|
|
1158
|
-
console.warn('⚠️ Script parent:', script.parentElement);
|
|
1159
|
-
console.warn('⚠️ Window keys:', Object.keys(window).filter(k => k.includes('capvision') || k.includes('rrweb')));
|
|
1160
|
-
resolve();
|
|
1161
|
-
}
|
|
1162
|
-
}, 50);
|
|
1163
|
-
} catch (error) {
|
|
1164
|
-
console.error('🔴 Failed to inject network plugin:', error.message);
|
|
1165
|
-
console.error('🔴 Error stack:', error.stack);
|
|
1166
|
-
resolve();
|
|
1167
|
-
}
|
|
1168
|
-
});
|
|
1169
|
-
};
|
|
1170
|
-
|
|
1171
930
|
const restoreEventsFromStorage = () => {
|
|
1172
931
|
if (!window.capVisionEvents) {
|
|
1173
932
|
window.capVisionEvents = [];
|
|
@@ -1233,28 +992,6 @@ class CapVisionRecorder {
|
|
|
1233
992
|
return null;
|
|
1234
993
|
};
|
|
1235
994
|
|
|
1236
|
-
const getNetworkPlugin = () => {
|
|
1237
|
-
try {
|
|
1238
|
-
if (!config.recordNetwork || !window.rrwebPluginNetworkRecord) {
|
|
1239
|
-
return null;
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
// Handle different export patterns
|
|
1243
|
-
if (typeof window.rrwebPluginNetworkRecord === 'function') {
|
|
1244
|
-
return window.rrwebPluginNetworkRecord(config.networkRecordOptions);
|
|
1245
|
-
}
|
|
1246
|
-
if (window.rrwebPluginNetworkRecord.getRecordNetworkPlugin) {
|
|
1247
|
-
return window.rrwebPluginNetworkRecord.getRecordNetworkPlugin(config.networkRecordOptions);
|
|
1248
|
-
}
|
|
1249
|
-
if (window.rrwebPluginNetworkRecord.default) {
|
|
1250
|
-
return window.rrwebPluginNetworkRecord.default(config.networkRecordOptions);
|
|
1251
|
-
}
|
|
1252
|
-
} catch (error) {
|
|
1253
|
-
console.warn('⚠️ Failed to initialize network plugin:', error.message);
|
|
1254
|
-
}
|
|
1255
|
-
return null;
|
|
1256
|
-
};
|
|
1257
|
-
|
|
1258
995
|
const startRecorder = () => {
|
|
1259
996
|
if (window.rrwebRecorder) {
|
|
1260
997
|
try {
|
|
@@ -1270,10 +1007,6 @@ class CapVisionRecorder {
|
|
|
1270
1007
|
if (consolePlugin) {
|
|
1271
1008
|
plugins.push(consolePlugin);
|
|
1272
1009
|
}
|
|
1273
|
-
const networkPlugin = getNetworkPlugin();
|
|
1274
|
-
if (networkPlugin) {
|
|
1275
|
-
plugins.push(networkPlugin);
|
|
1276
|
-
}
|
|
1277
1010
|
|
|
1278
1011
|
window.rrwebRecorder = window.rrweb.record({
|
|
1279
1012
|
emit: (event) => {
|
|
@@ -1287,9 +1020,9 @@ class CapVisionRecorder {
|
|
|
1287
1020
|
}
|
|
1288
1021
|
}
|
|
1289
1022
|
|
|
1290
|
-
if (window.capVisionEvents.length % 10 === 0) {
|
|
1291
|
-
|
|
1292
|
-
}
|
|
1023
|
+
// if (window.capVisionEvents.length % 10 === 0) {
|
|
1024
|
+
// console.log(`🟡 Captured ${window.capVisionEvents.length} events`);
|
|
1025
|
+
// }
|
|
1293
1026
|
},
|
|
1294
1027
|
checkoutEveryNms: config.checkoutIntervalMs,
|
|
1295
1028
|
recordCanvas: config.recordCanvas,
|
|
@@ -1314,7 +1047,6 @@ class CapVisionRecorder {
|
|
|
1314
1047
|
console.log('🟡 Re-initializing...');
|
|
1315
1048
|
await injectScript();
|
|
1316
1049
|
await injectConsolePlugin(pluginCode);
|
|
1317
|
-
await injectNetworkPlugin(networkPluginCode);
|
|
1318
1050
|
restoreEventsFromStorage();
|
|
1319
1051
|
initializeEventsForFileMode();
|
|
1320
1052
|
startRecorder();
|
|
@@ -1324,7 +1056,7 @@ class CapVisionRecorder {
|
|
|
1324
1056
|
};
|
|
1325
1057
|
|
|
1326
1058
|
reinitialize();
|
|
1327
|
-
}, this.config, eventCount, capVisionScriptCode, consolePluginCode
|
|
1059
|
+
}, this.config, eventCount, capVisionScriptCode, consolePluginCode);
|
|
1328
1060
|
|
|
1329
1061
|
console.log("🟢 Re-initialization complete");
|
|
1330
1062
|
}
|
|
@@ -10,7 +10,6 @@ const path = require('path');
|
|
|
10
10
|
* @property {string} [playerCSSPath] - Path to CapVision player CSS file
|
|
11
11
|
* @property {string} [playerScriptPath] - Path to CapVision player script file
|
|
12
12
|
* @property {string} [consoleReplayPluginPath] - Path to console replay plugin
|
|
13
|
-
* @property {string} [networkReplayPluginPath] - Path to network replay plugin
|
|
14
13
|
* @property {number} [playerWidth] - Player width in pixels
|
|
15
14
|
* @property {number} [playerHeight] - Player height in pixels
|
|
16
15
|
* @property {boolean} [autoPlay] - Auto-play recordings on load
|
|
@@ -41,7 +40,6 @@ const DEFAULT_ENHANCER_CONFIG = {
|
|
|
41
40
|
playerCSSPath: path.join(__dirname, '../assets/capvision-player.css'),
|
|
42
41
|
playerScriptPath: path.join(__dirname, '../assets/capvision-player.min.js'),
|
|
43
42
|
consoleReplayPluginPath: path.join(__dirname, '../assets/capvision-plugins/console-replay.min.js'),
|
|
44
|
-
networkReplayPluginPath: path.join(__dirname, '../assets/capvision-plugins/network-replay.min.js'),
|
|
45
43
|
playerWidth: 800,
|
|
46
44
|
playerHeight: 460,
|
|
47
45
|
autoPlay: false,
|
|
@@ -381,14 +379,6 @@ ${recordingPlayersHTML}
|
|
|
381
379
|
console.warn('⚠️ Failed to load console replay plugin:', error.message);
|
|
382
380
|
}
|
|
383
381
|
|
|
384
|
-
// Read bundled network replay plugin
|
|
385
|
-
let networkReplayPlugin = '';
|
|
386
|
-
try {
|
|
387
|
-
networkReplayPlugin = fs.readFileSync(this.config.networkReplayPluginPath, 'utf8');
|
|
388
|
-
} catch (error) {
|
|
389
|
-
console.warn('⚠️ Failed to load network replay plugin:', error.message);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
382
|
return `
|
|
393
383
|
<!-- CapVision Player CSS (Bundled) -->
|
|
394
384
|
<style>
|
|
@@ -405,11 +395,6 @@ ${recordingPlayersHTML}
|
|
|
405
395
|
${consoleReplayPlugin}
|
|
406
396
|
</script>
|
|
407
397
|
|
|
408
|
-
<!-- Network Replay Plugin (Bundled) -->
|
|
409
|
-
<script>
|
|
410
|
-
${networkReplayPlugin}
|
|
411
|
-
</script>
|
|
412
|
-
|
|
413
398
|
<script>
|
|
414
399
|
// CapVision Player Integration Script for Inline Players
|
|
415
400
|
(function() {
|
|
@@ -453,48 +438,6 @@ ${recordingPlayersHTML}
|
|
|
453
438
|
}
|
|
454
439
|
}
|
|
455
440
|
|
|
456
|
-
// Add network replay plugin if available
|
|
457
|
-
if (window.rrwebPluginNetworkReplay) {
|
|
458
|
-
try {
|
|
459
|
-
console.log('🟡 Initializing network replay plugin...');
|
|
460
|
-
const networkReplayOptions = {
|
|
461
|
-
showPanel: true,
|
|
462
|
-
maxEntries: 100,
|
|
463
|
-
position: 'bottom-right'
|
|
464
|
-
};
|
|
465
|
-
let networkPlugin;
|
|
466
|
-
|
|
467
|
-
// Handle different export patterns
|
|
468
|
-
if (typeof window.rrwebPluginNetworkReplay === 'function') {
|
|
469
|
-
networkPlugin = window.rrwebPluginNetworkReplay(networkReplayOptions);
|
|
470
|
-
console.log('🟢 Network replay plugin initialized (function pattern)');
|
|
471
|
-
} else if (window.rrwebPluginNetworkReplay.getReplayNetworkPlugin) {
|
|
472
|
-
networkPlugin = window.rrwebPluginNetworkReplay.getReplayNetworkPlugin(networkReplayOptions);
|
|
473
|
-
console.log('🟢 Network replay plugin initialized (getReplayNetworkPlugin pattern)');
|
|
474
|
-
} else if (window.rrwebPluginNetworkReplay.default) {
|
|
475
|
-
networkPlugin = window.rrwebPluginNetworkReplay.default(networkReplayOptions);
|
|
476
|
-
console.log('🟢 Network replay plugin initialized (default pattern)');
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
if (networkPlugin) {
|
|
480
|
-
console.log('🟡 Network plugin structure:', {
|
|
481
|
-
hasHandler: typeof networkPlugin.handler === 'function',
|
|
482
|
-
hasOnBuild: typeof networkPlugin.onBuild === 'function',
|
|
483
|
-
keys: Object.keys(networkPlugin)
|
|
484
|
-
});
|
|
485
|
-
plugins.push(networkPlugin);
|
|
486
|
-
console.log('🟢 Network replay plugin added to plugins array');
|
|
487
|
-
} else {
|
|
488
|
-
console.warn('⚠️ Network replay plugin is null or undefined');
|
|
489
|
-
}
|
|
490
|
-
} catch (error) {
|
|
491
|
-
console.error('🔴 Failed to initialize network replay plugin:', error);
|
|
492
|
-
console.error('🔴 Error stack:', error.stack);
|
|
493
|
-
}
|
|
494
|
-
} else {
|
|
495
|
-
console.warn('⚠️ window.rrwebPluginNetworkReplay not found');
|
|
496
|
-
}
|
|
497
|
-
|
|
498
441
|
// Create RRWeb player
|
|
499
442
|
const player = new rrwebPlayer({
|
|
500
443
|
target: playerContainer,
|
|
@@ -23,13 +23,10 @@ const ASSET_BASE_PATH = path.join(__dirname, 'assets');
|
|
|
23
23
|
CAPVISION_DEFAULT_CONFIG.capVisionScriptPath = path.join(ASSET_BASE_PATH, 'capvision.min.js');
|
|
24
24
|
CAPVISION_DEFAULT_CONFIG.consoleRecordPluginPath = path.join(ASSET_BASE_PATH, 'capvision-plugins/console-record.min.js');
|
|
25
25
|
CAPVISION_DEFAULT_CONFIG.consoleReplayPluginPath = path.join(ASSET_BASE_PATH, 'capvision-plugins/console-replay.min.js');
|
|
26
|
-
CAPVISION_DEFAULT_CONFIG.networkRecordPluginPath = path.join(ASSET_BASE_PATH, 'capvision-plugins/network-record.min.js');
|
|
27
|
-
CAPVISION_DEFAULT_CONFIG.networkReplayPluginPath = path.join(ASSET_BASE_PATH, 'capvision-plugins/network-replay.min.js');
|
|
28
26
|
|
|
29
27
|
DEFAULT_ENHANCER_CONFIG.playerCSSPath = path.join(ASSET_BASE_PATH, 'capvision-player.css');
|
|
30
28
|
DEFAULT_ENHANCER_CONFIG.playerScriptPath = path.join(ASSET_BASE_PATH, 'capvision-player.min.js');
|
|
31
29
|
DEFAULT_ENHANCER_CONFIG.consoleReplayPluginPath = path.join(ASSET_BASE_PATH, 'capvision-plugins/console-replay.min.js');
|
|
32
|
-
DEFAULT_ENHANCER_CONFIG.networkReplayPluginPath = path.join(ASSET_BASE_PATH, 'capvision-plugins/network-replay.min.js');
|
|
33
30
|
|
|
34
31
|
/**
|
|
35
32
|
* Export all CapVision recorder components
|