@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.
Files changed (67) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/LICENSE +21 -0
  3. package/README.md +639 -0
  4. package/dist/bugspotter.min.js +2 -0
  5. package/dist/bugspotter.min.js.LICENSE.txt +14 -0
  6. package/dist/capture/base-capture.d.ts +34 -0
  7. package/dist/capture/base-capture.js +23 -0
  8. package/dist/capture/capture-lifecycle.d.ts +24 -0
  9. package/dist/capture/capture-lifecycle.js +2 -0
  10. package/dist/capture/console.d.ts +29 -0
  11. package/dist/capture/console.js +107 -0
  12. package/dist/capture/metadata.d.ts +21 -0
  13. package/dist/capture/metadata.js +76 -0
  14. package/dist/capture/network.d.ts +32 -0
  15. package/dist/capture/network.js +135 -0
  16. package/dist/capture/screenshot.d.ts +19 -0
  17. package/dist/capture/screenshot.js +52 -0
  18. package/dist/collectors/dom.d.ts +67 -0
  19. package/dist/collectors/dom.js +164 -0
  20. package/dist/collectors/index.d.ts +2 -0
  21. package/dist/collectors/index.js +5 -0
  22. package/dist/core/buffer.d.ts +50 -0
  23. package/dist/core/buffer.js +88 -0
  24. package/dist/core/circular-buffer.d.ts +42 -0
  25. package/dist/core/circular-buffer.js +77 -0
  26. package/dist/core/compress.d.ts +49 -0
  27. package/dist/core/compress.js +245 -0
  28. package/dist/core/offline-queue.d.ts +76 -0
  29. package/dist/core/offline-queue.js +301 -0
  30. package/dist/core/transport.d.ts +73 -0
  31. package/dist/core/transport.js +352 -0
  32. package/dist/core/upload-helpers.d.ts +32 -0
  33. package/dist/core/upload-helpers.js +79 -0
  34. package/dist/core/uploader.d.ts +70 -0
  35. package/dist/core/uploader.js +185 -0
  36. package/dist/index.d.ts +140 -0
  37. package/dist/index.esm.js +205 -0
  38. package/dist/index.js +244 -0
  39. package/dist/utils/logger.d.ts +28 -0
  40. package/dist/utils/logger.js +84 -0
  41. package/dist/utils/sanitize-patterns.d.ts +103 -0
  42. package/dist/utils/sanitize-patterns.js +282 -0
  43. package/dist/utils/sanitize.d.ts +73 -0
  44. package/dist/utils/sanitize.js +254 -0
  45. package/dist/widget/button.d.ts +33 -0
  46. package/dist/widget/button.js +143 -0
  47. package/dist/widget/components/dom-element-cache.d.ts +62 -0
  48. package/dist/widget/components/dom-element-cache.js +105 -0
  49. package/dist/widget/components/form-validator.d.ts +66 -0
  50. package/dist/widget/components/form-validator.js +115 -0
  51. package/dist/widget/components/pii-detection-display.d.ts +64 -0
  52. package/dist/widget/components/pii-detection-display.js +142 -0
  53. package/dist/widget/components/redaction-canvas.d.ts +95 -0
  54. package/dist/widget/components/redaction-canvas.js +230 -0
  55. package/dist/widget/components/screenshot-processor.d.ts +44 -0
  56. package/dist/widget/components/screenshot-processor.js +191 -0
  57. package/dist/widget/components/style-manager.d.ts +37 -0
  58. package/dist/widget/components/style-manager.js +296 -0
  59. package/dist/widget/components/template-manager.d.ts +66 -0
  60. package/dist/widget/components/template-manager.js +198 -0
  61. package/dist/widget/modal.d.ts +62 -0
  62. package/dist/widget/modal.js +299 -0
  63. package/docs/CDN.md +213 -0
  64. package/docs/FRAMEWORK_INTEGRATION.md +1104 -0
  65. package/docs/PUBLISHING.md +550 -0
  66. package/docs/SESSION_REPLAY.md +381 -0
  67. package/package.json +90 -0
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ /**
3
+ * DirectUploader
4
+ * Handles direct client-to-storage uploads using presigned URLs
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.DirectUploader = void 0;
8
+ /**
9
+ * DirectUploader handles uploading files directly to storage using presigned URLs
10
+ * This bypasses the API server for file data, reducing memory usage and improving performance
11
+ */
12
+ class DirectUploader {
13
+ constructor(config) {
14
+ this.config = config;
15
+ }
16
+ /**
17
+ * Upload a screenshot file directly to storage
18
+ * @param file - Screenshot file or Blob
19
+ * @param onProgress - Optional progress callback
20
+ * @returns Upload result with storage key
21
+ */
22
+ async uploadScreenshot(file, onProgress) {
23
+ return this.uploadFile(file, 'screenshot', 'screenshot.png', onProgress);
24
+ }
25
+ /**
26
+ * Upload a compressed session replay directly to storage
27
+ * @param compressedData - Gzip-compressed replay data
28
+ * @param onProgress - Optional progress callback
29
+ * @returns Upload result with storage key
30
+ */
31
+ async uploadReplay(compressedData, onProgress) {
32
+ return this.uploadFile(compressedData, 'replay', 'replay.gz', onProgress);
33
+ }
34
+ /**
35
+ * Upload an attachment file directly to storage
36
+ * @param file - Attachment file
37
+ * @param onProgress - Optional progress callback
38
+ * @returns Upload result with storage key
39
+ */
40
+ async uploadAttachment(file, onProgress) {
41
+ return this.uploadFile(file, 'attachment', file.name, onProgress);
42
+ }
43
+ /**
44
+ * Generic file upload method
45
+ * 1. Request presigned URL from API
46
+ * 2. Upload file directly to storage using presigned URL
47
+ * 3. Confirm upload with API
48
+ */
49
+ async uploadFile(file, fileType, filename, onProgress) {
50
+ try {
51
+ // Step 1: Get presigned upload URL
52
+ const presignedUrlResponse = await this.requestPresignedUrl(fileType, filename);
53
+ if (!presignedUrlResponse.success) {
54
+ return {
55
+ success: false,
56
+ error: presignedUrlResponse.error || 'Failed to get presigned URL',
57
+ };
58
+ }
59
+ const { uploadUrl, storageKey } = presignedUrlResponse.data;
60
+ // Step 2: Upload file to storage using presigned URL
61
+ const uploadSuccess = await this.uploadToStorage(uploadUrl, file, onProgress);
62
+ if (!uploadSuccess) {
63
+ return {
64
+ success: false,
65
+ error: 'Failed to upload file to storage',
66
+ };
67
+ }
68
+ // Step 3: Confirm upload with API
69
+ const confirmSuccess = await this.confirmUpload(fileType);
70
+ if (!confirmSuccess) {
71
+ return {
72
+ success: false,
73
+ error: 'Failed to confirm upload',
74
+ };
75
+ }
76
+ return {
77
+ success: true,
78
+ storageKey,
79
+ };
80
+ }
81
+ catch (error) {
82
+ return {
83
+ success: false,
84
+ error: error instanceof Error ? error.message : 'Unknown error',
85
+ };
86
+ }
87
+ }
88
+ /**
89
+ * Request a presigned URL from the API
90
+ */
91
+ async requestPresignedUrl(fileType, filename) {
92
+ try {
93
+ const response = await fetch(`${this.config.apiEndpoint}/api/v1/uploads/presigned-url`, {
94
+ method: 'POST',
95
+ headers: {
96
+ 'Content-Type': 'application/json',
97
+ 'x-api-key': this.config.apiKey,
98
+ },
99
+ body: JSON.stringify({
100
+ projectId: this.config.projectId,
101
+ bugId: this.config.bugId,
102
+ fileType,
103
+ filename,
104
+ }),
105
+ });
106
+ if (!response.ok) {
107
+ const errorText = await response.text();
108
+ return {
109
+ success: false,
110
+ error: `HTTP ${response.status}: ${errorText}`,
111
+ };
112
+ }
113
+ const result = await response.json();
114
+ return {
115
+ success: result.success,
116
+ data: result.data,
117
+ error: result.error,
118
+ };
119
+ }
120
+ catch (error) {
121
+ return {
122
+ success: false,
123
+ error: error instanceof Error ? error.message : 'Network error',
124
+ };
125
+ }
126
+ }
127
+ /**
128
+ * Upload file to storage using presigned URL
129
+ * Uses XMLHttpRequest for progress tracking
130
+ */
131
+ uploadToStorage(uploadUrl, file, onProgress) {
132
+ return new Promise((resolve) => {
133
+ const xhr = new XMLHttpRequest();
134
+ // Track upload progress
135
+ if (onProgress) {
136
+ xhr.upload.addEventListener('progress', (event) => {
137
+ if (event.lengthComputable) {
138
+ onProgress({
139
+ loaded: event.loaded,
140
+ total: event.total,
141
+ percentage: Math.round((event.loaded / event.total) * 100),
142
+ });
143
+ }
144
+ });
145
+ }
146
+ // Handle completion
147
+ xhr.addEventListener('load', () => {
148
+ resolve(xhr.status >= 200 && xhr.status < 300);
149
+ });
150
+ // Handle errors
151
+ xhr.addEventListener('error', () => {
152
+ resolve(false);
153
+ });
154
+ xhr.addEventListener('abort', () => {
155
+ resolve(false);
156
+ });
157
+ // Send file
158
+ xhr.open('PUT', uploadUrl);
159
+ xhr.setRequestHeader('Content-Type', file.type || 'application/octet-stream');
160
+ xhr.send(file);
161
+ });
162
+ }
163
+ /**
164
+ * Confirm successful upload with the API
165
+ */
166
+ async confirmUpload(fileType) {
167
+ try {
168
+ const response = await fetch(`${this.config.apiEndpoint}/api/v1/reports/${this.config.bugId}/confirm-upload`, {
169
+ method: 'POST',
170
+ headers: {
171
+ 'Content-Type': 'application/json',
172
+ 'x-api-key': this.config.apiKey,
173
+ },
174
+ body: JSON.stringify({
175
+ fileType,
176
+ }),
177
+ });
178
+ return response.ok;
179
+ }
180
+ catch (_a) {
181
+ return false;
182
+ }
183
+ }
184
+ }
185
+ exports.DirectUploader = DirectUploader;
@@ -0,0 +1,140 @@
1
+ import type { BrowserMetadata } from './capture/metadata';
2
+ import { type FloatingButtonOptions } from './widget/button';
3
+ import type { eventWithTime } from '@rrweb/types';
4
+ import { type AuthConfig, type RetryConfig } from './core/transport';
5
+ import type { OfflineConfig } from './core/offline-queue';
6
+ export declare class BugSpotter {
7
+ private static instance;
8
+ private config;
9
+ private screenshot;
10
+ private console;
11
+ private network;
12
+ private metadata;
13
+ private domCollector?;
14
+ private widget?;
15
+ private sanitizer?;
16
+ constructor(config: BugSpotterConfig);
17
+ static init(config: BugSpotterConfig): BugSpotter;
18
+ static getInstance(): BugSpotter | null;
19
+ /**
20
+ * Capture bug report data
21
+ * Note: Screenshot is captured for modal preview only (_screenshotPreview)
22
+ * Actual file uploads use presigned URLs (screenshotKey/replayKey set after upload)
23
+ */
24
+ capture(): Promise<BugReport>;
25
+ private handleBugReport;
26
+ private submitBugReport;
27
+ getConfig(): Readonly<BugSpotterConfig>;
28
+ destroy(): void;
29
+ }
30
+ export interface BugSpotterConfig {
31
+ endpoint?: string;
32
+ showWidget?: boolean;
33
+ widgetOptions?: FloatingButtonOptions;
34
+ /** Authentication configuration */
35
+ auth?: AuthConfig;
36
+ /** Retry configuration for failed requests */
37
+ retry?: RetryConfig;
38
+ /** Offline queue configuration */
39
+ offline?: OfflineConfig;
40
+ replay?: {
41
+ /** Enable session replay recording (default: true) */
42
+ enabled?: boolean;
43
+ /** Duration in seconds to keep replay events (default: 15, max recommended: 30) */
44
+ duration?: number;
45
+ /** Sampling configuration for performance optimization */
46
+ sampling?: {
47
+ /** Throttle mousemove events in milliseconds (default: 50) */
48
+ mousemove?: number;
49
+ /** Throttle scroll events in milliseconds (default: 100) */
50
+ scroll?: number;
51
+ };
52
+ };
53
+ sanitize?: {
54
+ /** Enable PII sanitization (default: true) */
55
+ enabled?: boolean;
56
+ /**
57
+ * PII patterns to detect and mask
58
+ * - Can be a preset name: 'all', 'minimal', 'financial', 'contact', 'gdpr', 'pci', etc.
59
+ * - Or an array of pattern names: ['email', 'phone', 'ip']
60
+ */
61
+ patterns?: 'all' | 'minimal' | 'financial' | 'contact' | 'identification' | 'kazakhstan' | 'gdpr' | 'pci' | Array<'email' | 'phone' | 'creditcard' | 'ssn' | 'iin' | 'ip' | 'custom'>;
62
+ /** Custom regex patterns for PII detection */
63
+ customPatterns?: Array<{
64
+ name: string;
65
+ regex: RegExp;
66
+ description?: string;
67
+ examples?: string[];
68
+ priority?: number;
69
+ }>;
70
+ /** CSS selectors to exclude from sanitization */
71
+ excludeSelectors?: string[];
72
+ };
73
+ }
74
+ export interface BugReportPayload {
75
+ title: string;
76
+ description: string;
77
+ report: BugReport;
78
+ }
79
+ export interface BugReport {
80
+ screenshotKey?: string;
81
+ console: Array<{
82
+ level: string;
83
+ message: string;
84
+ timestamp: number;
85
+ stack?: string;
86
+ }>;
87
+ network: Array<{
88
+ url: string;
89
+ method: string;
90
+ status: number;
91
+ duration: number;
92
+ timestamp: number;
93
+ error?: string;
94
+ }>;
95
+ metadata: BrowserMetadata;
96
+ replay?: eventWithTime[];
97
+ replayKey?: string;
98
+ _screenshotPreview?: string;
99
+ }
100
+ export type { BrowserMetadata } from './capture/metadata';
101
+ export { ScreenshotCapture } from './capture/screenshot';
102
+ export { ConsoleCapture } from './capture/console';
103
+ export { NetworkCapture } from './capture/network';
104
+ export { MetadataCapture } from './capture/metadata';
105
+ export { DOMCollector } from './collectors';
106
+ export type { DOMCollectorConfig } from './collectors';
107
+ export { CircularBuffer } from './core/buffer';
108
+ export type { CircularBufferConfig } from './core/buffer';
109
+ export { compressData, decompressData, compressImage, estimateSize, getCompressionRatio, } from './core/compress';
110
+ export { submitWithAuth, getAuthHeaders, clearOfflineQueue } from './core/transport';
111
+ export type { AuthConfig, TransportOptions, RetryConfig } from './core/transport';
112
+ export type { OfflineConfig } from './core/offline-queue';
113
+ export type { Logger, LogLevel, LoggerConfig } from './utils/logger';
114
+ export { getLogger, configureLogger, createLogger } from './utils/logger';
115
+ export { DirectUploader } from './core/uploader';
116
+ export type { UploadResult } from './core/uploader';
117
+ export { compressReplayEvents, canvasToBlob, estimateCompressedReplaySize, isWithinSizeLimit, } from './core/upload-helpers';
118
+ export { createSanitizer, Sanitizer } from './utils/sanitize';
119
+ export type { PIIPattern, CustomPattern, SanitizeConfig } from './utils/sanitize';
120
+ export { DEFAULT_PATTERNS, PATTERN_PRESETS, PATTERN_CATEGORIES, PatternBuilder, createPatternConfig, getPattern, getPatternsByCategory, validatePattern, } from './utils/sanitize';
121
+ export type { PIIPatternName, PatternDefinition } from './utils/sanitize';
122
+ export { FloatingButton } from './widget/button';
123
+ export type { FloatingButtonOptions } from './widget/button';
124
+ export { BugReportModal } from './widget/modal';
125
+ export type { BugReportData, BugReportModalOptions, PIIDetection } from './widget/modal';
126
+ export type { eventWithTime } from '@rrweb/types';
127
+ /**
128
+ * Convenience function to sanitize text with default PII patterns
129
+ * Useful for quick sanitization without creating a Sanitizer instance
130
+ *
131
+ * @param text - Text to sanitize
132
+ * @returns Sanitized text with PII redacted
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * const sanitized = sanitize('Email: user@example.com');
137
+ * // Returns: 'Email: [REDACTED]'
138
+ * ```
139
+ */
140
+ export declare function sanitize(text: string): string;
@@ -0,0 +1,205 @@
1
+ import { ScreenshotCapture } from './capture/screenshot';
2
+ import { ConsoleCapture } from './capture/console';
3
+ import { NetworkCapture } from './capture/network';
4
+ import { MetadataCapture } from './capture/metadata';
5
+ import { compressData, estimateSize, getCompressionRatio } from './core/compress';
6
+ import { FloatingButton } from './widget/button';
7
+ import { BugReportModal } from './widget/modal';
8
+ import { DOMCollector } from './collectors';
9
+ import { createSanitizer } from './utils/sanitize';
10
+ import { getLogger } from './utils/logger';
11
+ import { submitWithAuth } from './core/transport';
12
+ const logger = getLogger();
13
+ export class BugSpotter {
14
+ constructor(config) {
15
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
16
+ this.config = config;
17
+ // Initialize sanitizer if enabled
18
+ if (((_a = config.sanitize) === null || _a === void 0 ? void 0 : _a.enabled) !== false) {
19
+ this.sanitizer = createSanitizer({
20
+ enabled: (_c = (_b = config.sanitize) === null || _b === void 0 ? void 0 : _b.enabled) !== null && _c !== void 0 ? _c : true,
21
+ patterns: (_d = config.sanitize) === null || _d === void 0 ? void 0 : _d.patterns,
22
+ customPatterns: (_e = config.sanitize) === null || _e === void 0 ? void 0 : _e.customPatterns,
23
+ excludeSelectors: (_f = config.sanitize) === null || _f === void 0 ? void 0 : _f.excludeSelectors,
24
+ });
25
+ }
26
+ this.screenshot = new ScreenshotCapture();
27
+ this.console = new ConsoleCapture({ sanitizer: this.sanitizer });
28
+ this.network = new NetworkCapture({ sanitizer: this.sanitizer });
29
+ this.metadata = new MetadataCapture({ sanitizer: this.sanitizer });
30
+ // Note: DirectUploader is created per-report since it needs bugId
31
+ // See submitBugReport() for initialization
32
+ // Initialize DOM collector if replay is enabled
33
+ if (((_g = config.replay) === null || _g === void 0 ? void 0 : _g.enabled) !== false) {
34
+ this.domCollector = new DOMCollector({
35
+ duration: (_j = (_h = config.replay) === null || _h === void 0 ? void 0 : _h.duration) !== null && _j !== void 0 ? _j : 15,
36
+ sampling: (_k = config.replay) === null || _k === void 0 ? void 0 : _k.sampling,
37
+ sanitizer: this.sanitizer,
38
+ });
39
+ this.domCollector.startRecording();
40
+ }
41
+ // Initialize widget if enabled
42
+ if (config.showWidget !== false) {
43
+ this.widget = new FloatingButton(config.widgetOptions);
44
+ this.widget.onClick(async () => {
45
+ await this.handleBugReport();
46
+ });
47
+ }
48
+ }
49
+ static init(config) {
50
+ if (!BugSpotter.instance) {
51
+ BugSpotter.instance = new BugSpotter(config);
52
+ }
53
+ return BugSpotter.instance;
54
+ }
55
+ static getInstance() {
56
+ return BugSpotter.instance || null;
57
+ }
58
+ /**
59
+ * Capture bug report data
60
+ * Note: Screenshot is captured for modal preview only (_screenshotPreview)
61
+ * Actual file uploads use presigned URLs (screenshotKey/replayKey set after upload)
62
+ */
63
+ async capture() {
64
+ var _a, _b;
65
+ const screenshotPreview = await this.screenshot.capture();
66
+ const replayEvents = (_b = (_a = this.domCollector) === null || _a === void 0 ? void 0 : _a.getEvents()) !== null && _b !== void 0 ? _b : [];
67
+ return {
68
+ console: this.console.getLogs(),
69
+ network: this.network.getRequests(),
70
+ metadata: this.metadata.capture(),
71
+ replay: replayEvents,
72
+ // Internal: screenshot preview for modal (not sent to API)
73
+ _screenshotPreview: screenshotPreview,
74
+ };
75
+ }
76
+ async handleBugReport() {
77
+ const report = await this.capture();
78
+ const modal = new BugReportModal({
79
+ onSubmit: async (data) => {
80
+ logger.log('Submitting bug:', Object.assign(Object.assign({}, data), { report }));
81
+ // Send to endpoint if configured
82
+ if (this.config.endpoint) {
83
+ try {
84
+ await this.submitBugReport(Object.assign(Object.assign({}, data), { report }));
85
+ logger.log('Bug report submitted successfully');
86
+ }
87
+ catch (error) {
88
+ logger.error('Failed to submit bug report:', error);
89
+ // Re-throw to allow UI to handle errors if needed
90
+ throw error;
91
+ }
92
+ }
93
+ },
94
+ });
95
+ modal.show(report._screenshotPreview || '');
96
+ }
97
+ async submitBugReport(payload) {
98
+ if (!this.config.endpoint) {
99
+ throw new Error('No endpoint configured for bug report submission');
100
+ }
101
+ const contentHeaders = {
102
+ 'Content-Type': 'application/json',
103
+ };
104
+ logger.warn(`Submitting bug report to ${this.config.endpoint}`);
105
+ let body;
106
+ try {
107
+ // Try to compress the payload
108
+ const originalSize = estimateSize(payload);
109
+ const compressed = await compressData(payload);
110
+ const compressedSize = compressed.byteLength;
111
+ const ratio = getCompressionRatio(originalSize, compressedSize);
112
+ logger.log(`Payload compression: ${(originalSize / 1024).toFixed(1)}KB → ${(compressedSize / 1024).toFixed(1)}KB (${ratio}% reduction)`);
113
+ // Use compression if it actually reduces size
114
+ if (compressedSize < originalSize) {
115
+ // Create a Blob from the compressed Uint8Array for proper binary upload
116
+ // Use Uint8Array constructor to ensure clean ArrayBuffer (no extra padding bytes)
117
+ body = new Blob([new Uint8Array(compressed)], { type: 'application/gzip' });
118
+ contentHeaders['Content-Encoding'] = 'gzip';
119
+ contentHeaders['Content-Type'] = 'application/gzip';
120
+ }
121
+ else {
122
+ body = JSON.stringify(payload);
123
+ }
124
+ }
125
+ catch (error) {
126
+ // Fallback to uncompressed if compression fails
127
+ logger.warn('Compression failed, sending uncompressed payload:', error);
128
+ body = JSON.stringify(payload);
129
+ }
130
+ // Determine auth configuration
131
+ const auth = this.config.auth;
132
+ // Submit with authentication, retry logic, and offline queue
133
+ const response = await submitWithAuth(this.config.endpoint, body, contentHeaders, {
134
+ auth,
135
+ retry: this.config.retry,
136
+ offline: this.config.offline,
137
+ });
138
+ logger.warn(`${JSON.stringify(response)}`);
139
+ if (!response.ok) {
140
+ const errorText = await response.text().catch(() => {
141
+ return 'Unknown error';
142
+ });
143
+ throw new Error(`Failed to submit bug report: ${response.status} ${response.statusText}. ${errorText}`);
144
+ }
145
+ return response.json().catch(() => {
146
+ return undefined;
147
+ });
148
+ }
149
+ getConfig() {
150
+ return Object.assign({}, this.config);
151
+ }
152
+ destroy() {
153
+ var _a, _b;
154
+ this.console.destroy();
155
+ this.network.destroy();
156
+ (_a = this.domCollector) === null || _a === void 0 ? void 0 : _a.destroy();
157
+ (_b = this.widget) === null || _b === void 0 ? void 0 : _b.destroy();
158
+ BugSpotter.instance = undefined;
159
+ }
160
+ }
161
+ export { ScreenshotCapture } from './capture/screenshot';
162
+ export { ConsoleCapture } from './capture/console';
163
+ export { NetworkCapture } from './capture/network';
164
+ export { MetadataCapture } from './capture/metadata';
165
+ // Export collector modules
166
+ export { DOMCollector } from './collectors';
167
+ // Export core utilities
168
+ export { CircularBuffer } from './core/buffer';
169
+ // Export compression utilities
170
+ export { compressData, decompressData, compressImage, estimateSize, getCompressionRatio, } from './core/compress';
171
+ // Export transport and authentication
172
+ export { submitWithAuth, getAuthHeaders, clearOfflineQueue } from './core/transport';
173
+ export { getLogger, configureLogger, createLogger } from './utils/logger';
174
+ // Export upload utilities
175
+ export { DirectUploader } from './core/uploader';
176
+ export { compressReplayEvents, canvasToBlob, estimateCompressedReplaySize, isWithinSizeLimit, } from './core/upload-helpers';
177
+ // Export sanitization utilities
178
+ export { createSanitizer, Sanitizer } from './utils/sanitize';
179
+ // Export pattern configuration utilities
180
+ export { DEFAULT_PATTERNS, PATTERN_PRESETS, PATTERN_CATEGORIES, PatternBuilder, createPatternConfig, getPattern, getPatternsByCategory, validatePattern, } from './utils/sanitize';
181
+ // Export widget components
182
+ export { FloatingButton } from './widget/button';
183
+ export { BugReportModal } from './widget/modal';
184
+ /**
185
+ * Convenience function to sanitize text with default PII patterns
186
+ * Useful for quick sanitization without creating a Sanitizer instance
187
+ *
188
+ * @param text - Text to sanitize
189
+ * @returns Sanitized text with PII redacted
190
+ *
191
+ * @example
192
+ * ```typescript
193
+ * const sanitized = sanitize('Email: user@example.com');
194
+ * // Returns: 'Email: [REDACTED]'
195
+ * ```
196
+ */
197
+ export function sanitize(text) {
198
+ const sanitizer = createSanitizer({
199
+ enabled: true,
200
+ patterns: 'all',
201
+ customPatterns: [],
202
+ excludeSelectors: [],
203
+ });
204
+ return sanitizer.sanitize(text);
205
+ }