@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,143 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FloatingButton = void 0;
4
+ const DEFAULT_BUTTON_OPTIONS = {
5
+ position: 'bottom-right',
6
+ icon: '🐛',
7
+ backgroundColor: '#ef4444',
8
+ size: 60,
9
+ offset: { x: 20, y: 20 },
10
+ zIndex: 999999,
11
+ };
12
+ const BUTTON_STYLES = {
13
+ transition: 'transform 0.2s ease, box-shadow 0.2s ease',
14
+ boxShadow: {
15
+ default: '0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06)',
16
+ hover: '0 10px 15px rgba(0, 0, 0, 0.2), 0 4px 6px rgba(0, 0, 0, 0.1)',
17
+ },
18
+ transform: {
19
+ default: 'scale(1)',
20
+ hover: 'scale(1.1)',
21
+ active: 'scale(0.95)',
22
+ },
23
+ };
24
+ class FloatingButton {
25
+ constructor(options = {}) {
26
+ var _a, _b, _c, _d, _e, _f;
27
+ this.eventHandlers = new Map();
28
+ this.handleMouseEnter = () => {
29
+ this.button.style.transform = BUTTON_STYLES.transform.hover;
30
+ this.button.style.boxShadow = BUTTON_STYLES.boxShadow.hover;
31
+ };
32
+ this.handleMouseLeave = () => {
33
+ this.button.style.transform = BUTTON_STYLES.transform.default;
34
+ this.button.style.boxShadow = BUTTON_STYLES.boxShadow.default;
35
+ };
36
+ this.handleMouseDown = () => {
37
+ this.button.style.transform = BUTTON_STYLES.transform.active;
38
+ };
39
+ this.handleMouseUp = () => {
40
+ this.button.style.transform = BUTTON_STYLES.transform.hover;
41
+ };
42
+ this.options = {
43
+ position: (_a = options.position) !== null && _a !== void 0 ? _a : DEFAULT_BUTTON_OPTIONS.position,
44
+ icon: (_b = options.icon) !== null && _b !== void 0 ? _b : DEFAULT_BUTTON_OPTIONS.icon,
45
+ backgroundColor: (_c = options.backgroundColor) !== null && _c !== void 0 ? _c : DEFAULT_BUTTON_OPTIONS.backgroundColor,
46
+ size: (_d = options.size) !== null && _d !== void 0 ? _d : DEFAULT_BUTTON_OPTIONS.size,
47
+ offset: (_e = options.offset) !== null && _e !== void 0 ? _e : DEFAULT_BUTTON_OPTIONS.offset,
48
+ zIndex: (_f = options.zIndex) !== null && _f !== void 0 ? _f : DEFAULT_BUTTON_OPTIONS.zIndex,
49
+ };
50
+ this.button = this.createButton();
51
+ // Ensure DOM is ready before appending
52
+ if (document.body) {
53
+ document.body.appendChild(this.button);
54
+ }
55
+ else {
56
+ document.addEventListener('DOMContentLoaded', () => {
57
+ document.body.appendChild(this.button);
58
+ });
59
+ }
60
+ }
61
+ createButton() {
62
+ const btn = document.createElement('button');
63
+ btn.textContent = this.options.icon;
64
+ btn.setAttribute('aria-label', 'Report Bug');
65
+ btn.setAttribute('data-bugspotter-exclude', 'true');
66
+ btn.style.cssText = this.getButtonStyles();
67
+ this.addHoverEffects(btn);
68
+ return btn;
69
+ }
70
+ getButtonStyles() {
71
+ const { position, size, offset, backgroundColor, zIndex } = this.options;
72
+ const positionStyles = this.getPositionStyles(position, offset);
73
+ return `
74
+ position: fixed;
75
+ ${positionStyles}
76
+ width: ${size}px;
77
+ height: ${size}px;
78
+ border-radius: 50%;
79
+ background: ${backgroundColor};
80
+ color: white;
81
+ border: none;
82
+ cursor: pointer;
83
+ font-size: ${size * 0.5}px;
84
+ display: flex;
85
+ align-items: center;
86
+ justify-content: center;
87
+ box-shadow: ${BUTTON_STYLES.boxShadow.default};
88
+ transition: ${BUTTON_STYLES.transition};
89
+ z-index: ${zIndex};
90
+ `;
91
+ }
92
+ getPositionStyles(position, offset) {
93
+ switch (position) {
94
+ case 'bottom-right':
95
+ return `bottom: ${offset.y}px; right: ${offset.x}px;`;
96
+ case 'bottom-left':
97
+ return `bottom: ${offset.y}px; left: ${offset.x}px;`;
98
+ case 'top-right':
99
+ return `top: ${offset.y}px; right: ${offset.x}px;`;
100
+ case 'top-left':
101
+ return `top: ${offset.y}px; left: ${offset.x}px;`;
102
+ default:
103
+ return `bottom: ${offset.y}px; right: ${offset.x}px;`;
104
+ }
105
+ }
106
+ addHoverEffects(btn) {
107
+ const handlers = {
108
+ mouseenter: this.handleMouseEnter,
109
+ mouseleave: this.handleMouseLeave,
110
+ mousedown: this.handleMouseDown,
111
+ mouseup: this.handleMouseUp,
112
+ };
113
+ Object.entries(handlers).forEach(([event, handler]) => {
114
+ btn.addEventListener(event, handler);
115
+ this.eventHandlers.set(event, handler);
116
+ });
117
+ }
118
+ onClick(handler) {
119
+ this.button.addEventListener('click', handler);
120
+ }
121
+ show() {
122
+ this.button.style.display = 'flex';
123
+ }
124
+ hide() {
125
+ this.button.style.display = 'none';
126
+ }
127
+ setIcon(icon) {
128
+ this.button.textContent = icon;
129
+ }
130
+ setBackgroundColor(color) {
131
+ this.button.style.backgroundColor = color;
132
+ }
133
+ destroy() {
134
+ // Remove all event listeners
135
+ this.eventHandlers.forEach((handler, event) => {
136
+ this.button.removeEventListener(event, handler);
137
+ });
138
+ this.eventHandlers.clear();
139
+ // Remove button from DOM
140
+ this.button.remove();
141
+ }
142
+ }
143
+ exports.FloatingButton = FloatingButton;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * DOMElementCache
3
+ *
4
+ * Responsibility: Cache DOM element references to avoid repeated querySelector calls
5
+ * Follows SRP: Only handles element reference management
6
+ * Improves Performance: Single querySelector per element
7
+ */
8
+ export interface ModalElements {
9
+ overlay: HTMLElement;
10
+ modal: HTMLElement;
11
+ closeButton: HTMLElement;
12
+ form: HTMLFormElement;
13
+ titleInput: HTMLInputElement;
14
+ titleError: HTMLElement;
15
+ descriptionTextarea: HTMLTextAreaElement;
16
+ descriptionError: HTMLElement;
17
+ screenshotImg?: HTMLImageElement;
18
+ redactionCanvas?: HTMLCanvasElement;
19
+ redactButton?: HTMLButtonElement;
20
+ clearButton?: HTMLButtonElement;
21
+ piiSection: HTMLElement;
22
+ piiContent: HTMLElement;
23
+ piiConfirmCheckbox: HTMLInputElement;
24
+ cancelButton: HTMLButtonElement;
25
+ submitButton: HTMLButtonElement;
26
+ }
27
+ export declare class DOMElementCache {
28
+ private elements;
29
+ private container;
30
+ /**
31
+ * Initialize cache from a container element or shadow root
32
+ */
33
+ initialize(container: HTMLElement | ShadowRoot): ModalElements;
34
+ /**
35
+ * Get cached elements (throws if not initialized)
36
+ */
37
+ get(): ModalElements;
38
+ /**
39
+ * Check if cache is initialized
40
+ */
41
+ isInitialized(): boolean;
42
+ /**
43
+ * Clear the cache
44
+ */
45
+ clear(): void;
46
+ /**
47
+ * Get a required element (throws if not found)
48
+ */
49
+ private getRequiredElement;
50
+ /**
51
+ * Get an optional element (returns undefined if not found)
52
+ */
53
+ private getOptionalElement;
54
+ /**
55
+ * Refresh a specific element in the cache
56
+ */
57
+ refreshElement(key: keyof ModalElements, selector: string): void;
58
+ /**
59
+ * Get container element
60
+ */
61
+ getContainer(): HTMLElement | ShadowRoot | null;
62
+ }
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ /**
3
+ * DOMElementCache
4
+ *
5
+ * Responsibility: Cache DOM element references to avoid repeated querySelector calls
6
+ * Follows SRP: Only handles element reference management
7
+ * Improves Performance: Single querySelector per element
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.DOMElementCache = void 0;
11
+ class DOMElementCache {
12
+ constructor() {
13
+ this.elements = null;
14
+ this.container = null;
15
+ }
16
+ /**
17
+ * Initialize cache from a container element or shadow root
18
+ */
19
+ initialize(container) {
20
+ this.container = container;
21
+ const overlay = this.getRequiredElement('.overlay', container);
22
+ const modal = this.getRequiredElement('.modal', overlay);
23
+ const form = this.getRequiredElement('.form', modal);
24
+ this.elements = {
25
+ overlay,
26
+ modal,
27
+ closeButton: this.getRequiredElement('.close', modal),
28
+ form,
29
+ titleInput: this.getRequiredElement('#title', form),
30
+ titleError: this.getRequiredElement('#title-error', form),
31
+ descriptionTextarea: this.getRequiredElement('#description', form),
32
+ descriptionError: this.getRequiredElement('#description-error', form),
33
+ screenshotImg: this.getOptionalElement('#screenshot', modal),
34
+ redactionCanvas: this.getOptionalElement('#redaction-canvas', modal),
35
+ redactButton: this.getOptionalElement('#btn-redact', modal),
36
+ clearButton: this.getOptionalElement('#btn-clear', modal),
37
+ piiSection: this.getRequiredElement('#pii-section', modal),
38
+ piiContent: this.getRequiredElement('#pii-content', modal),
39
+ piiConfirmCheckbox: this.getRequiredElement('#pii-confirm', modal),
40
+ cancelButton: this.getRequiredElement('#btn-cancel', modal),
41
+ submitButton: this.getRequiredElement('#btn-submit', modal),
42
+ };
43
+ return this.elements;
44
+ }
45
+ /**
46
+ * Get cached elements (throws if not initialized)
47
+ */
48
+ get() {
49
+ if (!this.elements) {
50
+ throw new Error('DOMElementCache not initialized. Call initialize() first.');
51
+ }
52
+ return this.elements;
53
+ }
54
+ /**
55
+ * Check if cache is initialized
56
+ */
57
+ isInitialized() {
58
+ return this.elements !== null;
59
+ }
60
+ /**
61
+ * Clear the cache
62
+ */
63
+ clear() {
64
+ this.elements = null;
65
+ this.container = null;
66
+ }
67
+ /**
68
+ * Get a required element (throws if not found)
69
+ */
70
+ getRequiredElement(selector, parent) {
71
+ const searchParent = parent || this.container || document;
72
+ const element = searchParent.querySelector(selector);
73
+ if (!element) {
74
+ throw new Error(`Required element not found: ${selector}`);
75
+ }
76
+ return element;
77
+ }
78
+ /**
79
+ * Get an optional element (returns undefined if not found)
80
+ */
81
+ getOptionalElement(selector, parent) {
82
+ const searchParent = parent || this.container || document;
83
+ const element = searchParent.querySelector(selector);
84
+ return element || undefined;
85
+ }
86
+ /**
87
+ * Refresh a specific element in the cache
88
+ */
89
+ refreshElement(key, selector) {
90
+ if (!this.elements || !this.container) {
91
+ throw new Error('DOMElementCache not initialized');
92
+ }
93
+ const element = this.container.querySelector(selector);
94
+ if (element) {
95
+ this.elements[key] = element;
96
+ }
97
+ }
98
+ /**
99
+ * Get container element
100
+ */
101
+ getContainer() {
102
+ return this.container;
103
+ }
104
+ }
105
+ exports.DOMElementCache = DOMElementCache;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * FormValidator
3
+ *
4
+ * Responsibility: Validate form input and manage error states
5
+ * Follows SRP: Only handles validation logic (pure functions)
6
+ */
7
+ export interface ValidationResult {
8
+ isValid: boolean;
9
+ errors: ValidationErrors;
10
+ }
11
+ export interface ValidationErrors {
12
+ title?: string;
13
+ description?: string;
14
+ piiConfirmation?: string;
15
+ }
16
+ export interface FormData {
17
+ title: string;
18
+ description: string;
19
+ piiDetected: boolean;
20
+ piiConfirmed: boolean;
21
+ }
22
+ export declare class FormValidator {
23
+ private minTitleLength;
24
+ private maxTitleLength;
25
+ private minDescriptionLength;
26
+ private maxDescriptionLength;
27
+ constructor(config?: {
28
+ minTitleLength?: number;
29
+ maxTitleLength?: number;
30
+ minDescriptionLength?: number;
31
+ maxDescriptionLength?: number;
32
+ });
33
+ /**
34
+ * Validate complete form data
35
+ */
36
+ validate(data: FormData): ValidationResult;
37
+ /**
38
+ * Validate title field
39
+ */
40
+ validateTitle(title: string): string | null;
41
+ /**
42
+ * Validate description field
43
+ */
44
+ validateDescription(description: string): string | null;
45
+ /**
46
+ * Validate single field by name
47
+ */
48
+ validateField(fieldName: keyof FormData, value: unknown, formData?: Partial<FormData>): string | null;
49
+ /**
50
+ * Check if a string is empty or whitespace only
51
+ */
52
+ isEmpty(value: string): boolean;
53
+ /**
54
+ * Sanitize input (trim whitespace)
55
+ */
56
+ sanitizeInput(value: string): string;
57
+ /**
58
+ * Get validation configuration
59
+ */
60
+ getConfig(): {
61
+ minTitleLength: number;
62
+ maxTitleLength: number;
63
+ minDescriptionLength: number;
64
+ maxDescriptionLength: number;
65
+ };
66
+ }
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ /**
3
+ * FormValidator
4
+ *
5
+ * Responsibility: Validate form input and manage error states
6
+ * Follows SRP: Only handles validation logic (pure functions)
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.FormValidator = void 0;
10
+ class FormValidator {
11
+ constructor(config = {}) {
12
+ this.minTitleLength = config.minTitleLength || 3;
13
+ this.maxTitleLength = config.maxTitleLength || 200;
14
+ this.minDescriptionLength = config.minDescriptionLength || 10;
15
+ this.maxDescriptionLength = config.maxDescriptionLength || 5000;
16
+ }
17
+ /**
18
+ * Validate complete form data
19
+ */
20
+ validate(data) {
21
+ const errors = {};
22
+ // Validate title
23
+ const titleError = this.validateTitle(data.title);
24
+ if (titleError) {
25
+ errors.title = titleError;
26
+ }
27
+ // Validate description
28
+ const descriptionError = this.validateDescription(data.description);
29
+ if (descriptionError) {
30
+ errors.description = descriptionError;
31
+ }
32
+ // Validate PII confirmation if PII detected
33
+ if (data.piiDetected && !data.piiConfirmed) {
34
+ errors.piiConfirmation = 'Please confirm you have reviewed sensitive information';
35
+ }
36
+ return {
37
+ isValid: Object.keys(errors).length === 0,
38
+ errors,
39
+ };
40
+ }
41
+ /**
42
+ * Validate title field
43
+ */
44
+ validateTitle(title) {
45
+ const trimmed = title.trim();
46
+ if (!trimmed) {
47
+ return 'Title is required';
48
+ }
49
+ if (trimmed.length < this.minTitleLength) {
50
+ return `Title must be at least ${this.minTitleLength} characters`;
51
+ }
52
+ if (trimmed.length > this.maxTitleLength) {
53
+ return `Title must not exceed ${this.maxTitleLength} characters`;
54
+ }
55
+ return null;
56
+ }
57
+ /**
58
+ * Validate description field
59
+ */
60
+ validateDescription(description) {
61
+ const trimmed = description.trim();
62
+ if (!trimmed) {
63
+ return 'Description is required';
64
+ }
65
+ if (trimmed.length < this.minDescriptionLength) {
66
+ return `Description must be at least ${this.minDescriptionLength} characters`;
67
+ }
68
+ if (trimmed.length > this.maxDescriptionLength) {
69
+ return `Description must not exceed ${this.maxDescriptionLength} characters`;
70
+ }
71
+ return null;
72
+ }
73
+ /**
74
+ * Validate single field by name
75
+ */
76
+ validateField(fieldName, value, formData) {
77
+ switch (fieldName) {
78
+ case 'title':
79
+ return this.validateTitle(value);
80
+ case 'description':
81
+ return this.validateDescription(value);
82
+ case 'piiConfirmed':
83
+ if ((formData === null || formData === void 0 ? void 0 : formData.piiDetected) && !value) {
84
+ return 'Please confirm you have reviewed sensitive information';
85
+ }
86
+ return null;
87
+ default:
88
+ return null;
89
+ }
90
+ }
91
+ /**
92
+ * Check if a string is empty or whitespace only
93
+ */
94
+ isEmpty(value) {
95
+ return !value || value.trim().length === 0;
96
+ }
97
+ /**
98
+ * Sanitize input (trim whitespace)
99
+ */
100
+ sanitizeInput(value) {
101
+ return value.trim();
102
+ }
103
+ /**
104
+ * Get validation configuration
105
+ */
106
+ getConfig() {
107
+ return {
108
+ minTitleLength: this.minTitleLength,
109
+ maxTitleLength: this.maxTitleLength,
110
+ minDescriptionLength: this.minDescriptionLength,
111
+ maxDescriptionLength: this.maxDescriptionLength,
112
+ };
113
+ }
114
+ }
115
+ exports.FormValidator = FormValidator;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * PIIDetectionDisplay
3
+ *
4
+ * Responsibility: Render PII detection UI and manage PII-related display logic
5
+ * Follows SRP: Only handles PII visualization
6
+ */
7
+ import type { PIIDetection } from '../modal';
8
+ export interface PIIDisplayConfig {
9
+ showExamples?: boolean;
10
+ groupByType?: boolean;
11
+ maxExamplesPerType?: number;
12
+ }
13
+ export declare class PIIDetectionDisplay {
14
+ private config;
15
+ constructor(config?: PIIDisplayConfig);
16
+ /**
17
+ * Render PII detection information into a container
18
+ */
19
+ render(piiDetections: PIIDetection[], container: HTMLElement): void;
20
+ /**
21
+ * Render PII grouped by type with badges
22
+ */
23
+ private renderGroupedPII;
24
+ /**
25
+ * Render PII as a simple list
26
+ */
27
+ private renderListPII;
28
+ /**
29
+ * Create a PII type badge
30
+ */
31
+ private createBadge;
32
+ /**
33
+ * Group PII detections by type
34
+ */
35
+ private groupByType;
36
+ /**
37
+ * Get summary statistics about PII detections
38
+ */
39
+ getSummary(piiDetections: PIIDetection[]): {
40
+ totalCount: number;
41
+ typeCount: number;
42
+ types: string[];
43
+ };
44
+ /**
45
+ * Check if PII detections contain a specific type
46
+ */
47
+ hasType(piiDetections: PIIDetection[], type: string): boolean;
48
+ /**
49
+ * Filter PII detections by type
50
+ */
51
+ filterByType(piiDetections: PIIDetection[], type: string): PIIDetection[];
52
+ /**
53
+ * Escape HTML to prevent XSS
54
+ */
55
+ private escapeHtml;
56
+ /**
57
+ * Update configuration
58
+ */
59
+ updateConfig(config: Partial<PIIDisplayConfig>): void;
60
+ /**
61
+ * Get current configuration
62
+ */
63
+ getConfig(): Required<PIIDisplayConfig>;
64
+ }
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ /**
3
+ * PIIDetectionDisplay
4
+ *
5
+ * Responsibility: Render PII detection UI and manage PII-related display logic
6
+ * Follows SRP: Only handles PII visualization
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.PIIDetectionDisplay = void 0;
10
+ class PIIDetectionDisplay {
11
+ constructor(config = {}) {
12
+ this.config = {
13
+ showExamples: config.showExamples !== false,
14
+ groupByType: config.groupByType !== false,
15
+ maxExamplesPerType: config.maxExamplesPerType || 3,
16
+ };
17
+ }
18
+ /**
19
+ * Render PII detection information into a container
20
+ */
21
+ render(piiDetections, container) {
22
+ if (!piiDetections || piiDetections.length === 0) {
23
+ container.innerHTML = '';
24
+ return;
25
+ }
26
+ if (this.config.groupByType) {
27
+ container.innerHTML = this.renderGroupedPII(piiDetections);
28
+ }
29
+ else {
30
+ container.innerHTML = this.renderListPII(piiDetections);
31
+ }
32
+ }
33
+ /**
34
+ * Render PII grouped by type with badges
35
+ */
36
+ renderGroupedPII(piiDetections) {
37
+ const grouped = this.groupByType(piiDetections);
38
+ const badges = [];
39
+ for (const [type, items] of Object.entries(grouped)) {
40
+ const totalCount = items.reduce((sum, item) => {
41
+ return sum + item.count;
42
+ }, 0);
43
+ badges.push(this.createBadge(type, totalCount));
44
+ }
45
+ let html = `<div class="bugspotter-pii-badges">${badges.join('')}</div>`;
46
+ if (this.config.showExamples) {
47
+ html += '<div class="bugspotter-pii-details">';
48
+ html +=
49
+ '<p style="margin: 10px 0 5px 0; font-size: 13px; color: #856404;">Detected types:</p>';
50
+ html += '<ul class="bugspotter-pii-list">';
51
+ for (const [type, items] of Object.entries(grouped)) {
52
+ const totalCount = items.reduce((sum, item) => {
53
+ return sum + item.count;
54
+ }, 0);
55
+ html += `<li><strong>${this.escapeHtml(type)}:</strong> ${totalCount} occurrence${totalCount !== 1 ? 's' : ''}</li>`;
56
+ }
57
+ html += '</ul></div>';
58
+ }
59
+ return html;
60
+ }
61
+ /**
62
+ * Render PII as a simple list
63
+ */
64
+ renderListPII(piiDetections) {
65
+ let html = '<ul class="bugspotter-pii-list">';
66
+ for (const detection of piiDetections) {
67
+ html += `<li>${this.escapeHtml(detection.type)}: ${detection.count} occurrence${detection.count !== 1 ? 's' : ''}</li>`;
68
+ }
69
+ html += '</ul>';
70
+ return html;
71
+ }
72
+ /**
73
+ * Create a PII type badge
74
+ */
75
+ createBadge(type, count) {
76
+ return `<span class="bugspotter-pii-badge">${this.escapeHtml(type)}: ${count}</span>`;
77
+ }
78
+ /**
79
+ * Group PII detections by type
80
+ */
81
+ groupByType(piiDetections) {
82
+ const grouped = {};
83
+ for (const detection of piiDetections) {
84
+ if (!grouped[detection.type]) {
85
+ grouped[detection.type] = [];
86
+ }
87
+ grouped[detection.type].push(detection);
88
+ }
89
+ return grouped;
90
+ }
91
+ /**
92
+ * Get summary statistics about PII detections
93
+ */
94
+ getSummary(piiDetections) {
95
+ const types = new Set();
96
+ for (const detection of piiDetections) {
97
+ types.add(detection.type);
98
+ }
99
+ return {
100
+ totalCount: piiDetections.length,
101
+ typeCount: types.size,
102
+ types: Array.from(types),
103
+ };
104
+ }
105
+ /**
106
+ * Check if PII detections contain a specific type
107
+ */
108
+ hasType(piiDetections, type) {
109
+ return piiDetections.some((d) => {
110
+ return d.type === type;
111
+ });
112
+ }
113
+ /**
114
+ * Filter PII detections by type
115
+ */
116
+ filterByType(piiDetections, type) {
117
+ return piiDetections.filter((d) => {
118
+ return d.type === type;
119
+ });
120
+ }
121
+ /**
122
+ * Escape HTML to prevent XSS
123
+ */
124
+ escapeHtml(text) {
125
+ const div = document.createElement('div');
126
+ div.textContent = text;
127
+ return div.innerHTML;
128
+ }
129
+ /**
130
+ * Update configuration
131
+ */
132
+ updateConfig(config) {
133
+ this.config = Object.assign(Object.assign({}, this.config), config);
134
+ }
135
+ /**
136
+ * Get current configuration
137
+ */
138
+ getConfig() {
139
+ return Object.assign({}, this.config);
140
+ }
141
+ }
142
+ exports.PIIDetectionDisplay = PIIDetectionDisplay;