@bugspotter/sdk 0.1.0-alpha.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/CHANGELOG.md +69 -0
- package/LICENSE +21 -0
- package/README.md +639 -0
- package/dist/bugspotter.min.js +2 -0
- package/dist/bugspotter.min.js.LICENSE.txt +14 -0
- package/dist/capture/base-capture.d.ts +34 -0
- package/dist/capture/base-capture.js +23 -0
- package/dist/capture/capture-lifecycle.d.ts +24 -0
- package/dist/capture/capture-lifecycle.js +2 -0
- package/dist/capture/console.d.ts +29 -0
- package/dist/capture/console.js +107 -0
- package/dist/capture/metadata.d.ts +21 -0
- package/dist/capture/metadata.js +76 -0
- package/dist/capture/network.d.ts +32 -0
- package/dist/capture/network.js +135 -0
- package/dist/capture/screenshot.d.ts +19 -0
- package/dist/capture/screenshot.js +52 -0
- package/dist/collectors/dom.d.ts +67 -0
- package/dist/collectors/dom.js +164 -0
- package/dist/collectors/index.d.ts +2 -0
- package/dist/collectors/index.js +5 -0
- package/dist/core/buffer.d.ts +50 -0
- package/dist/core/buffer.js +88 -0
- package/dist/core/circular-buffer.d.ts +42 -0
- package/dist/core/circular-buffer.js +77 -0
- package/dist/core/compress.d.ts +49 -0
- package/dist/core/compress.js +245 -0
- package/dist/core/offline-queue.d.ts +76 -0
- package/dist/core/offline-queue.js +301 -0
- package/dist/core/transport.d.ts +73 -0
- package/dist/core/transport.js +352 -0
- package/dist/core/upload-helpers.d.ts +32 -0
- package/dist/core/upload-helpers.js +79 -0
- package/dist/core/uploader.d.ts +70 -0
- package/dist/core/uploader.js +185 -0
- package/dist/index.d.ts +140 -0
- package/dist/index.esm.js +205 -0
- package/dist/index.js +244 -0
- package/dist/utils/logger.d.ts +28 -0
- package/dist/utils/logger.js +84 -0
- package/dist/utils/sanitize-patterns.d.ts +103 -0
- package/dist/utils/sanitize-patterns.js +282 -0
- package/dist/utils/sanitize.d.ts +73 -0
- package/dist/utils/sanitize.js +254 -0
- package/dist/widget/button.d.ts +33 -0
- package/dist/widget/button.js +143 -0
- package/dist/widget/components/dom-element-cache.d.ts +62 -0
- package/dist/widget/components/dom-element-cache.js +105 -0
- package/dist/widget/components/form-validator.d.ts +66 -0
- package/dist/widget/components/form-validator.js +115 -0
- package/dist/widget/components/pii-detection-display.d.ts +64 -0
- package/dist/widget/components/pii-detection-display.js +142 -0
- package/dist/widget/components/redaction-canvas.d.ts +95 -0
- package/dist/widget/components/redaction-canvas.js +230 -0
- package/dist/widget/components/screenshot-processor.d.ts +44 -0
- package/dist/widget/components/screenshot-processor.js +191 -0
- package/dist/widget/components/style-manager.d.ts +37 -0
- package/dist/widget/components/style-manager.js +296 -0
- package/dist/widget/components/template-manager.d.ts +66 -0
- package/dist/widget/components/template-manager.js +198 -0
- package/dist/widget/modal.d.ts +62 -0
- package/dist/widget/modal.js +299 -0
- package/docs/CDN.md +213 -0
- package/docs/FRAMEWORK_INTEGRATION.md +1104 -0
- package/docs/PUBLISHING.md +550 -0
- package/docs/SESSION_REPLAY.md +381 -0
- package/package.json +90 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { BaseCapture, type CaptureOptions } from './base-capture';
|
|
2
|
+
type ConsoleLevel = 'log' | 'warn' | 'error' | 'info' | 'debug';
|
|
3
|
+
export interface ConsoleCaptureOptions extends CaptureOptions {
|
|
4
|
+
maxLogs?: number;
|
|
5
|
+
captureStackTrace?: boolean;
|
|
6
|
+
levels?: readonly ConsoleLevel[];
|
|
7
|
+
}
|
|
8
|
+
export declare class ConsoleCapture extends BaseCapture<LogEntry[], ConsoleCaptureOptions> {
|
|
9
|
+
private buffer;
|
|
10
|
+
private captureStackTrace;
|
|
11
|
+
private originalMethods;
|
|
12
|
+
constructor(options?: ConsoleCaptureOptions);
|
|
13
|
+
capture(): LogEntry[];
|
|
14
|
+
private formatMessage;
|
|
15
|
+
private createLogEntry;
|
|
16
|
+
private captureStack;
|
|
17
|
+
private addLog;
|
|
18
|
+
private interceptConsole;
|
|
19
|
+
getLogs(): LogEntry[];
|
|
20
|
+
clear(): void;
|
|
21
|
+
destroy(): void;
|
|
22
|
+
}
|
|
23
|
+
export interface LogEntry {
|
|
24
|
+
level: ConsoleLevel;
|
|
25
|
+
message: string;
|
|
26
|
+
timestamp: number;
|
|
27
|
+
stack?: string;
|
|
28
|
+
}
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConsoleCapture = void 0;
|
|
4
|
+
const base_capture_1 = require("./base-capture");
|
|
5
|
+
const circular_buffer_1 = require("../core/circular-buffer");
|
|
6
|
+
const CONSOLE_METHODS = ['log', 'warn', 'error', 'info', 'debug'];
|
|
7
|
+
class ConsoleCapture extends base_capture_1.BaseCapture {
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
var _a, _b, _c;
|
|
10
|
+
super(options);
|
|
11
|
+
this.originalMethods = new Map();
|
|
12
|
+
const maxLogs = (_a = options.maxLogs) !== null && _a !== void 0 ? _a : 100;
|
|
13
|
+
this.buffer = new circular_buffer_1.CircularBuffer(maxLogs);
|
|
14
|
+
this.captureStackTrace = (_b = options.captureStackTrace) !== null && _b !== void 0 ? _b : true;
|
|
15
|
+
this.interceptConsole((_c = options.levels) !== null && _c !== void 0 ? _c : CONSOLE_METHODS);
|
|
16
|
+
}
|
|
17
|
+
capture() {
|
|
18
|
+
return this.getLogs();
|
|
19
|
+
}
|
|
20
|
+
formatMessage(args) {
|
|
21
|
+
if (!args || args.length === 0) {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
// Sanitize args if sanitizer is enabled
|
|
25
|
+
const sanitizedArgs = this.sanitizer ? this.sanitizer.sanitizeConsoleArgs(args) : args;
|
|
26
|
+
return sanitizedArgs
|
|
27
|
+
.map((arg) => {
|
|
28
|
+
var _a;
|
|
29
|
+
if (arg === null) {
|
|
30
|
+
return 'null';
|
|
31
|
+
}
|
|
32
|
+
if (arg === undefined) {
|
|
33
|
+
return 'undefined';
|
|
34
|
+
}
|
|
35
|
+
if (typeof arg === 'object') {
|
|
36
|
+
try {
|
|
37
|
+
return JSON.stringify(arg);
|
|
38
|
+
}
|
|
39
|
+
catch (_b) {
|
|
40
|
+
return `[${((_a = arg.constructor) === null || _a === void 0 ? void 0 : _a.name) || 'Object'}]`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return String(arg);
|
|
44
|
+
})
|
|
45
|
+
.join(' ');
|
|
46
|
+
}
|
|
47
|
+
createLogEntry(method, args) {
|
|
48
|
+
const log = {
|
|
49
|
+
level: method,
|
|
50
|
+
message: this.formatMessage(args),
|
|
51
|
+
timestamp: Date.now(),
|
|
52
|
+
};
|
|
53
|
+
if (this.captureStackTrace && method === 'error') {
|
|
54
|
+
const stack = this.captureStack();
|
|
55
|
+
log.stack = this.sanitizer && stack ? this.sanitizer.sanitize(stack) : stack;
|
|
56
|
+
}
|
|
57
|
+
return log;
|
|
58
|
+
}
|
|
59
|
+
captureStack() {
|
|
60
|
+
const stack = new Error().stack;
|
|
61
|
+
// Remove first 3 lines (Error, captureStack, createLogEntry)
|
|
62
|
+
return stack === null || stack === void 0 ? void 0 : stack.split('\n').slice(3).join('\n');
|
|
63
|
+
}
|
|
64
|
+
addLog(log) {
|
|
65
|
+
this.buffer.add(log);
|
|
66
|
+
}
|
|
67
|
+
interceptConsole(levels = CONSOLE_METHODS) {
|
|
68
|
+
levels.forEach((method) => {
|
|
69
|
+
try {
|
|
70
|
+
const original = console[method];
|
|
71
|
+
this.originalMethods.set(method, original);
|
|
72
|
+
console[method] = (...args) => {
|
|
73
|
+
try {
|
|
74
|
+
const log = this.createLogEntry(method, args);
|
|
75
|
+
this.addLog(log);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
this.handleError('creating log entry', error);
|
|
79
|
+
}
|
|
80
|
+
original.apply(console, args);
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
this.handleError(`intercepting console.${method}`, error);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
getLogs() {
|
|
89
|
+
return this.buffer.getAll();
|
|
90
|
+
}
|
|
91
|
+
clear() {
|
|
92
|
+
this.buffer.clear();
|
|
93
|
+
}
|
|
94
|
+
destroy() {
|
|
95
|
+
try {
|
|
96
|
+
this.originalMethods.forEach((original, method) => {
|
|
97
|
+
const consoleMethod = method;
|
|
98
|
+
console[consoleMethod] = original;
|
|
99
|
+
});
|
|
100
|
+
this.originalMethods.clear();
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
this.handleError('destroying console capture', error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.ConsoleCapture = ConsoleCapture;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BaseCapture, type CaptureOptions } from './base-capture';
|
|
2
|
+
export interface BrowserMetadata {
|
|
3
|
+
userAgent: string;
|
|
4
|
+
viewport: {
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
};
|
|
8
|
+
browser: string;
|
|
9
|
+
os: string;
|
|
10
|
+
url: string;
|
|
11
|
+
timestamp: number;
|
|
12
|
+
}
|
|
13
|
+
export type MetadataCaptureOptions = CaptureOptions;
|
|
14
|
+
export declare class MetadataCapture extends BaseCapture<BrowserMetadata, MetadataCaptureOptions> {
|
|
15
|
+
private readonly browserPatterns;
|
|
16
|
+
private readonly osPatterns;
|
|
17
|
+
constructor(options?: MetadataCaptureOptions);
|
|
18
|
+
capture(): BrowserMetadata;
|
|
19
|
+
private detectBrowser;
|
|
20
|
+
private detectOS;
|
|
21
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MetadataCapture = void 0;
|
|
4
|
+
const base_capture_1 = require("./base-capture");
|
|
5
|
+
class MetadataCapture extends base_capture_1.BaseCapture {
|
|
6
|
+
constructor(options = {}) {
|
|
7
|
+
super(options);
|
|
8
|
+
this.browserPatterns = [
|
|
9
|
+
{ pattern: 'Edg', name: 'Edge' }, // Check Edge before Chrome
|
|
10
|
+
{ pattern: 'Chrome', exclude: 'Edge', name: 'Chrome' },
|
|
11
|
+
{ pattern: 'Firefox', name: 'Firefox' },
|
|
12
|
+
{ pattern: 'Safari', exclude: 'Chrome', name: 'Safari' },
|
|
13
|
+
];
|
|
14
|
+
this.osPatterns = [
|
|
15
|
+
{ patterns: ['iPhone', 'iPad'], name: 'iOS' }, // Check iOS before Mac
|
|
16
|
+
{ patterns: ['Android'], name: 'Android' }, // Check Android before Linux
|
|
17
|
+
{ patterns: ['Win'], name: 'Windows' },
|
|
18
|
+
{ patterns: ['Mac'], name: 'macOS' },
|
|
19
|
+
{ patterns: ['Linux'], name: 'Linux' },
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
capture() {
|
|
23
|
+
try {
|
|
24
|
+
const metadata = {
|
|
25
|
+
userAgent: navigator.userAgent,
|
|
26
|
+
viewport: {
|
|
27
|
+
width: window.innerWidth,
|
|
28
|
+
height: window.innerHeight,
|
|
29
|
+
},
|
|
30
|
+
browser: this.detectBrowser(),
|
|
31
|
+
os: this.detectOS(),
|
|
32
|
+
url: window.location.href,
|
|
33
|
+
timestamp: Date.now(),
|
|
34
|
+
};
|
|
35
|
+
// Sanitize sensitive data if sanitizer is enabled
|
|
36
|
+
if (this.sanitizer) {
|
|
37
|
+
metadata.url = this.sanitizer.sanitize(metadata.url);
|
|
38
|
+
metadata.userAgent = this.sanitizer.sanitize(metadata.userAgent);
|
|
39
|
+
}
|
|
40
|
+
return metadata;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
this.handleError('capturing metadata', error);
|
|
44
|
+
// Return fallback metadata
|
|
45
|
+
return {
|
|
46
|
+
userAgent: 'Unknown',
|
|
47
|
+
viewport: { width: 0, height: 0 },
|
|
48
|
+
browser: 'Unknown',
|
|
49
|
+
os: 'Unknown',
|
|
50
|
+
url: 'Unknown',
|
|
51
|
+
timestamp: Date.now(),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
detectBrowser() {
|
|
56
|
+
const ua = navigator.userAgent;
|
|
57
|
+
for (const { pattern, exclude, name } of this.browserPatterns) {
|
|
58
|
+
if (ua.includes(pattern) && (!exclude || !ua.includes(exclude))) {
|
|
59
|
+
return name;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return 'Unknown';
|
|
63
|
+
}
|
|
64
|
+
detectOS() {
|
|
65
|
+
const ua = navigator.userAgent;
|
|
66
|
+
for (const { patterns, name } of this.osPatterns) {
|
|
67
|
+
if (patterns.some((pattern) => {
|
|
68
|
+
return ua.includes(pattern);
|
|
69
|
+
})) {
|
|
70
|
+
return name;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return 'Unknown';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.MetadataCapture = MetadataCapture;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { BaseCapture, type CaptureOptions } from './base-capture';
|
|
2
|
+
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
|
|
3
|
+
export interface NetworkCaptureOptions extends CaptureOptions {
|
|
4
|
+
maxRequests?: number;
|
|
5
|
+
filterUrls?: (url: string) => boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare class NetworkCapture extends BaseCapture<NetworkRequest[], NetworkCaptureOptions> {
|
|
8
|
+
private buffer;
|
|
9
|
+
private filterUrls?;
|
|
10
|
+
private originalFetch;
|
|
11
|
+
private originalXHR;
|
|
12
|
+
private isIntercepting;
|
|
13
|
+
constructor(options?: NetworkCaptureOptions);
|
|
14
|
+
capture(): NetworkRequest[];
|
|
15
|
+
private parseFetchArgs;
|
|
16
|
+
private createNetworkRequest;
|
|
17
|
+
private addRequest;
|
|
18
|
+
private interceptFetch;
|
|
19
|
+
private interceptXHR;
|
|
20
|
+
getRequests(): NetworkRequest[];
|
|
21
|
+
clear(): void;
|
|
22
|
+
destroy(): void;
|
|
23
|
+
}
|
|
24
|
+
export interface NetworkRequest {
|
|
25
|
+
url: string;
|
|
26
|
+
method: HttpMethod;
|
|
27
|
+
status: number;
|
|
28
|
+
duration: number;
|
|
29
|
+
timestamp: number;
|
|
30
|
+
error?: string;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NetworkCapture = void 0;
|
|
4
|
+
const base_capture_1 = require("./base-capture");
|
|
5
|
+
const circular_buffer_1 = require("../core/circular-buffer");
|
|
6
|
+
class NetworkCapture extends base_capture_1.BaseCapture {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
var _a;
|
|
9
|
+
super(options);
|
|
10
|
+
this.isIntercepting = false;
|
|
11
|
+
const maxRequests = (_a = options.maxRequests) !== null && _a !== void 0 ? _a : 50;
|
|
12
|
+
this.buffer = new circular_buffer_1.CircularBuffer(maxRequests);
|
|
13
|
+
this.filterUrls = options.filterUrls;
|
|
14
|
+
this.originalFetch = window.fetch;
|
|
15
|
+
this.originalXHR = {
|
|
16
|
+
open: XMLHttpRequest.prototype.open,
|
|
17
|
+
send: XMLHttpRequest.prototype.send,
|
|
18
|
+
};
|
|
19
|
+
this.interceptFetch();
|
|
20
|
+
this.interceptXHR();
|
|
21
|
+
this.isIntercepting = true;
|
|
22
|
+
}
|
|
23
|
+
capture() {
|
|
24
|
+
return this.getRequests();
|
|
25
|
+
}
|
|
26
|
+
parseFetchArgs(args) {
|
|
27
|
+
const [input, init] = args;
|
|
28
|
+
let url;
|
|
29
|
+
let method = 'GET';
|
|
30
|
+
if (typeof input === 'string') {
|
|
31
|
+
url = input;
|
|
32
|
+
}
|
|
33
|
+
else if (input instanceof Request) {
|
|
34
|
+
url = input.url;
|
|
35
|
+
method = input.method.toUpperCase() || 'GET';
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
url = input.toString();
|
|
39
|
+
}
|
|
40
|
+
if (init === null || init === void 0 ? void 0 : init.method) {
|
|
41
|
+
method = init.method.toUpperCase();
|
|
42
|
+
}
|
|
43
|
+
return { url, method };
|
|
44
|
+
}
|
|
45
|
+
createNetworkRequest(url, method, status, startTime, error) {
|
|
46
|
+
const request = Object.assign({ url,
|
|
47
|
+
method,
|
|
48
|
+
status, duration: Date.now() - startTime, timestamp: startTime }, (error && { error }));
|
|
49
|
+
// Sanitize network data if sanitizer is enabled
|
|
50
|
+
if (this.sanitizer) {
|
|
51
|
+
const sanitized = this.sanitizer.sanitizeNetworkData(Object.assign({ url: request.url, method: request.method, status: request.status }, (request.error && { error: request.error })));
|
|
52
|
+
return Object.assign(Object.assign({}, request), { url: sanitized.url || request.url, error: sanitized.error });
|
|
53
|
+
}
|
|
54
|
+
return request;
|
|
55
|
+
}
|
|
56
|
+
addRequest(request) {
|
|
57
|
+
if (this.filterUrls && !this.filterUrls(request.url)) {
|
|
58
|
+
return; // Skip filtered URLs
|
|
59
|
+
}
|
|
60
|
+
this.buffer.add(request);
|
|
61
|
+
}
|
|
62
|
+
interceptFetch() {
|
|
63
|
+
const originalFetch = this.originalFetch;
|
|
64
|
+
window.fetch = async (...args) => {
|
|
65
|
+
const startTime = Date.now();
|
|
66
|
+
let url = '';
|
|
67
|
+
let method = 'GET';
|
|
68
|
+
try {
|
|
69
|
+
({ url, method } = this.parseFetchArgs(args));
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
this.handleError('parsing fetch arguments', error);
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const response = await originalFetch(...args);
|
|
76
|
+
const request = this.createNetworkRequest(url, method, response.status, startTime);
|
|
77
|
+
this.addRequest(request);
|
|
78
|
+
return response;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
const request = this.createNetworkRequest(url, method, 0, startTime, error.message);
|
|
82
|
+
this.addRequest(request);
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
interceptXHR() {
|
|
88
|
+
const originalOpen = this.originalXHR.open;
|
|
89
|
+
const originalSend = this.originalXHR.send;
|
|
90
|
+
const createRequest = this.createNetworkRequest.bind(this);
|
|
91
|
+
const addRequest = this.addRequest.bind(this);
|
|
92
|
+
XMLHttpRequest.prototype.open = function (method, url, ...args) {
|
|
93
|
+
this._method = method.toUpperCase();
|
|
94
|
+
this._url = url.toString();
|
|
95
|
+
this._startTime = Date.now();
|
|
96
|
+
// Type assertion needed for rest params compatibility
|
|
97
|
+
return originalOpen.apply(this, [method, url, ...args]);
|
|
98
|
+
};
|
|
99
|
+
XMLHttpRequest.prototype.send = function (...args) {
|
|
100
|
+
const onLoad = () => {
|
|
101
|
+
const request = createRequest(this._url || '', this._method || 'GET', this.status, this._startTime || Date.now());
|
|
102
|
+
addRequest(request);
|
|
103
|
+
};
|
|
104
|
+
const onError = () => {
|
|
105
|
+
const request = createRequest(this._url || '', this._method || 'GET', 0, this._startTime || Date.now(), 'XMLHttpRequest failed');
|
|
106
|
+
addRequest(request);
|
|
107
|
+
};
|
|
108
|
+
this.addEventListener('load', onLoad);
|
|
109
|
+
this.addEventListener('error', onError);
|
|
110
|
+
// Type assertion needed for rest params compatibility
|
|
111
|
+
return originalSend.apply(this, args);
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
getRequests() {
|
|
115
|
+
return this.buffer.getAll();
|
|
116
|
+
}
|
|
117
|
+
clear() {
|
|
118
|
+
this.buffer.clear();
|
|
119
|
+
}
|
|
120
|
+
destroy() {
|
|
121
|
+
if (!this.isIntercepting) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
window.fetch = this.originalFetch;
|
|
126
|
+
XMLHttpRequest.prototype.open = this.originalXHR.open;
|
|
127
|
+
XMLHttpRequest.prototype.send = this.originalXHR.send;
|
|
128
|
+
this.isIntercepting = false;
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
this.handleError('destroying network capture', error);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
exports.NetworkCapture = NetworkCapture;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { BaseCapture, type CaptureOptions } from './base-capture';
|
|
2
|
+
export interface ScreenshotCaptureOptions extends CaptureOptions {
|
|
3
|
+
quality?: number;
|
|
4
|
+
pixelRatio?: number;
|
|
5
|
+
backgroundColor?: string;
|
|
6
|
+
cacheBust?: boolean;
|
|
7
|
+
targetElement?: HTMLElement;
|
|
8
|
+
excludeAttribute?: string;
|
|
9
|
+
width?: number;
|
|
10
|
+
height?: number;
|
|
11
|
+
errorPlaceholder?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare class ScreenshotCapture extends BaseCapture<Promise<string>, ScreenshotCaptureOptions> {
|
|
14
|
+
constructor(options?: ScreenshotCaptureOptions);
|
|
15
|
+
protected getErrorPlaceholder(): string;
|
|
16
|
+
private shouldIncludeNode;
|
|
17
|
+
private buildCaptureOptions;
|
|
18
|
+
capture(targetElement?: HTMLElement): Promise<string>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ScreenshotCapture = void 0;
|
|
4
|
+
const html_to_image_1 = require("html-to-image");
|
|
5
|
+
const base_capture_1 = require("./base-capture");
|
|
6
|
+
const compress_1 = require("../core/compress");
|
|
7
|
+
const DEFAULT_SCREENSHOT_OPTIONS = {
|
|
8
|
+
quality: 0.8,
|
|
9
|
+
cacheBust: true,
|
|
10
|
+
backgroundColor: '#ffffff',
|
|
11
|
+
excludeAttribute: 'data-bugspotter-exclude',
|
|
12
|
+
errorPlaceholder: 'SCREENSHOT_FAILED',
|
|
13
|
+
};
|
|
14
|
+
class ScreenshotCapture extends base_capture_1.BaseCapture {
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
super(options);
|
|
17
|
+
}
|
|
18
|
+
getErrorPlaceholder() {
|
|
19
|
+
var _a;
|
|
20
|
+
return (_a = this.options.errorPlaceholder) !== null && _a !== void 0 ? _a : DEFAULT_SCREENSHOT_OPTIONS.errorPlaceholder;
|
|
21
|
+
}
|
|
22
|
+
shouldIncludeNode(node) {
|
|
23
|
+
if (!('hasAttribute' in node)) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
const element = node;
|
|
27
|
+
const excludeAttr = this.options.excludeAttribute || DEFAULT_SCREENSHOT_OPTIONS.excludeAttribute;
|
|
28
|
+
return !element.hasAttribute(excludeAttr);
|
|
29
|
+
}
|
|
30
|
+
buildCaptureOptions() {
|
|
31
|
+
var _a, _b, _c, _d;
|
|
32
|
+
return Object.assign(Object.assign(Object.assign({ quality: (_a = this.options.quality) !== null && _a !== void 0 ? _a : DEFAULT_SCREENSHOT_OPTIONS.quality, cacheBust: (_b = this.options.cacheBust) !== null && _b !== void 0 ? _b : DEFAULT_SCREENSHOT_OPTIONS.cacheBust, pixelRatio: (_c = this.options.pixelRatio) !== null && _c !== void 0 ? _c : window.devicePixelRatio, backgroundColor: (_d = this.options.backgroundColor) !== null && _d !== void 0 ? _d : DEFAULT_SCREENSHOT_OPTIONS.backgroundColor }, (this.options.width && { width: this.options.width })), (this.options.height && { height: this.options.height })), { filter: (node) => {
|
|
33
|
+
return this.shouldIncludeNode(node);
|
|
34
|
+
} });
|
|
35
|
+
}
|
|
36
|
+
async capture(targetElement) {
|
|
37
|
+
try {
|
|
38
|
+
const element = targetElement || this.options.targetElement || document.body;
|
|
39
|
+
const options = this.buildCaptureOptions();
|
|
40
|
+
const dataUrl = await (0, html_to_image_1.toPng)(element, options);
|
|
41
|
+
// Compress the screenshot to reduce payload size
|
|
42
|
+
// Converts to WebP if supported, resizes if too large
|
|
43
|
+
const compressed = await (0, compress_1.compressImage)(dataUrl);
|
|
44
|
+
return compressed;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
this.handleError('capturing screenshot', error);
|
|
48
|
+
return this.getErrorPlaceholder();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.ScreenshotCapture = ScreenshotCapture;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { eventWithTime } from '@rrweb/types';
|
|
2
|
+
import type { Sanitizer } from '../utils/sanitize';
|
|
3
|
+
export interface DOMCollectorConfig {
|
|
4
|
+
/** Duration in seconds to keep replay events (default: 15) */
|
|
5
|
+
duration?: number;
|
|
6
|
+
/** Sampling configuration for performance optimization */
|
|
7
|
+
sampling?: {
|
|
8
|
+
/** Throttle mousemove events (ms, default: 50) */
|
|
9
|
+
mousemove?: number;
|
|
10
|
+
/** Throttle scroll events (ms, default: 100) */
|
|
11
|
+
scroll?: number;
|
|
12
|
+
};
|
|
13
|
+
/** Whether to record canvas elements (default: false) */
|
|
14
|
+
recordCanvas?: boolean;
|
|
15
|
+
/** Whether to record cross-origin iframes (default: false) */
|
|
16
|
+
recordCrossOriginIframes?: boolean;
|
|
17
|
+
/** Sanitizer for PII protection */
|
|
18
|
+
sanitizer?: Sanitizer;
|
|
19
|
+
}
|
|
20
|
+
export declare class DOMCollector {
|
|
21
|
+
private buffer;
|
|
22
|
+
private stopRecordingFn?;
|
|
23
|
+
private isRecording;
|
|
24
|
+
private config;
|
|
25
|
+
private sanitizer?;
|
|
26
|
+
constructor(config?: DOMCollectorConfig);
|
|
27
|
+
/**
|
|
28
|
+
* Start recording DOM events
|
|
29
|
+
*/
|
|
30
|
+
startRecording(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Stop recording DOM events
|
|
33
|
+
*/
|
|
34
|
+
stopRecording(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Get all events from the buffer
|
|
37
|
+
*/
|
|
38
|
+
getEvents(): eventWithTime[];
|
|
39
|
+
/**
|
|
40
|
+
* Get compressed events from the buffer
|
|
41
|
+
*/
|
|
42
|
+
getCompressedEvents(): eventWithTime[];
|
|
43
|
+
/**
|
|
44
|
+
* Clear all events from the buffer
|
|
45
|
+
*/
|
|
46
|
+
clearBuffer(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Check if currently recording
|
|
49
|
+
*/
|
|
50
|
+
isCurrentlyRecording(): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Get the current buffer size
|
|
53
|
+
*/
|
|
54
|
+
getBufferSize(): number;
|
|
55
|
+
/**
|
|
56
|
+
* Update the buffer duration
|
|
57
|
+
*/
|
|
58
|
+
setDuration(seconds: number): void;
|
|
59
|
+
/**
|
|
60
|
+
* Get the buffer duration
|
|
61
|
+
*/
|
|
62
|
+
getDuration(): number;
|
|
63
|
+
/**
|
|
64
|
+
* Destroy the collector and clean up resources
|
|
65
|
+
*/
|
|
66
|
+
destroy(): void;
|
|
67
|
+
}
|