@tindalabs/shield 0.1.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.
Files changed (118) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +357 -0
  3. package/dist/assess.d.ts +16 -0
  4. package/dist/assess.js +220 -0
  5. package/dist/config/default-extensions-config.json +103 -0
  6. package/dist/core/ContentProtector.d.ts +63 -0
  7. package/dist/core/ContentProtector.js +281 -0
  8. package/dist/core/index.d.ts +1 -0
  9. package/dist/core/index.js +2 -0
  10. package/dist/core/mediator/ContentProtectionMediator.d.ts +86 -0
  11. package/dist/core/mediator/ContentProtectionMediator.js +238 -0
  12. package/dist/core/mediator/eventDataTypes.d.ts +112 -0
  13. package/dist/core/mediator/eventDataTypes.js +23 -0
  14. package/dist/core/mediator/handlers/abstractEventHandler.d.ts +41 -0
  15. package/dist/core/mediator/handlers/abstractEventHandler.js +59 -0
  16. package/dist/core/mediator/handlers/devToolsEventHandler.d.ts +9 -0
  17. package/dist/core/mediator/handlers/devToolsEventHandler.js +95 -0
  18. package/dist/core/mediator/handlers/eventHandlerRegistry.d.ts +9 -0
  19. package/dist/core/mediator/handlers/eventHandlerRegistry.js +34 -0
  20. package/dist/core/mediator/handlers/extensionEventHandlers.d.ts +40 -0
  21. package/dist/core/mediator/handlers/extensionEventHandlers.js +140 -0
  22. package/dist/core/mediator/handlers/iFrameEventHandlers.d.ts +27 -0
  23. package/dist/core/mediator/handlers/iFrameEventHandlers.js +93 -0
  24. package/dist/core/mediator/handlers/screenShotEventHandlers.d.ts +34 -0
  25. package/dist/core/mediator/handlers/screenShotEventHandlers.js +111 -0
  26. package/dist/core/mediator/protection-event.d.ts +77 -0
  27. package/dist/core/mediator/protection-event.js +32 -0
  28. package/dist/core/mediator/types.d.ts +105 -0
  29. package/dist/core/mediator/types.js +1 -0
  30. package/dist/index.d.ts +10 -0
  31. package/dist/index.js +7 -0
  32. package/dist/otel.d.ts +24 -0
  33. package/dist/otel.js +83 -0
  34. package/dist/policy.d.ts +98 -0
  35. package/dist/policy.js +97 -0
  36. package/dist/strategies/AbstractStrategy.d.ts +124 -0
  37. package/dist/strategies/AbstractStrategy.js +256 -0
  38. package/dist/strategies/ClipboardStrategy.d.ts +67 -0
  39. package/dist/strategies/ClipboardStrategy.js +291 -0
  40. package/dist/strategies/ContextMenuStrategy.d.ts +60 -0
  41. package/dist/strategies/ContextMenuStrategy.js +454 -0
  42. package/dist/strategies/DevToolsStrategy.d.ts +55 -0
  43. package/dist/strategies/DevToolsStrategy.js +314 -0
  44. package/dist/strategies/ExtensionStrategy.d.ts +66 -0
  45. package/dist/strategies/ExtensionStrategy.js +486 -0
  46. package/dist/strategies/IFrameStrategy.d.ts +49 -0
  47. package/dist/strategies/IFrameStrategy.js +255 -0
  48. package/dist/strategies/KeyboardStrategy.d.ts +35 -0
  49. package/dist/strategies/KeyboardStrategy.js +130 -0
  50. package/dist/strategies/PrintStrategy.d.ts +47 -0
  51. package/dist/strategies/PrintStrategy.js +201 -0
  52. package/dist/strategies/ScreenshotStrategy.d.ts +90 -0
  53. package/dist/strategies/ScreenshotStrategy.js +502 -0
  54. package/dist/strategies/SelectionStrategy.d.ts +49 -0
  55. package/dist/strategies/SelectionStrategy.js +216 -0
  56. package/dist/strategies/WatermarkStrategy.d.ts +56 -0
  57. package/dist/strategies/WatermarkStrategy.js +287 -0
  58. package/dist/strategies/index.d.ts +10 -0
  59. package/dist/strategies/index.js +11 -0
  60. package/dist/types/assessment.d.ts +62 -0
  61. package/dist/types/assessment.js +1 -0
  62. package/dist/types/index.d.ts +278 -0
  63. package/dist/types/index.js +17 -0
  64. package/dist/utils/DOMObserver.d.ts +68 -0
  65. package/dist/utils/DOMObserver.js +134 -0
  66. package/dist/utils/base/LoggableComponent.d.ts +44 -0
  67. package/dist/utils/base/LoggableComponent.js +56 -0
  68. package/dist/utils/detectors/AbstractDevToolsDetector.d.ts +98 -0
  69. package/dist/utils/detectors/AbstractDevToolsDetector.js +127 -0
  70. package/dist/utils/detectors/dateToStringDetector.d.ts +43 -0
  71. package/dist/utils/detectors/dateToStringDetector.js +96 -0
  72. package/dist/utils/detectors/debugLibDetector.d.ts +64 -0
  73. package/dist/utils/detectors/debugLibDetector.js +195 -0
  74. package/dist/utils/detectors/debuggerDetector.d.ts +51 -0
  75. package/dist/utils/detectors/debuggerDetector.js +211 -0
  76. package/dist/utils/detectors/defineGetterDetector.d.ts +48 -0
  77. package/dist/utils/detectors/defineGetterDetector.js +150 -0
  78. package/dist/utils/detectors/detectorInterface.d.ts +36 -0
  79. package/dist/utils/detectors/detectorInterface.js +1 -0
  80. package/dist/utils/detectors/devToolsDetectorManager.d.ts +88 -0
  81. package/dist/utils/detectors/devToolsDetectorManager.js +243 -0
  82. package/dist/utils/detectors/funcToStringDetector.d.ts +43 -0
  83. package/dist/utils/detectors/funcToStringDetector.js +90 -0
  84. package/dist/utils/detectors/regToStringDetector.d.ts +43 -0
  85. package/dist/utils/detectors/regToStringDetector.js +129 -0
  86. package/dist/utils/detectors/sizeDetector.d.ts +54 -0
  87. package/dist/utils/detectors/sizeDetector.js +134 -0
  88. package/dist/utils/detectors/timingDetector.d.ts +55 -0
  89. package/dist/utils/detectors/timingDetector.js +143 -0
  90. package/dist/utils/dom.d.ts +20 -0
  91. package/dist/utils/dom.js +83 -0
  92. package/dist/utils/environment.d.ts +29 -0
  93. package/dist/utils/environment.js +267 -0
  94. package/dist/utils/eventManager.d.ts +162 -0
  95. package/dist/utils/eventManager.js +548 -0
  96. package/dist/utils/index.d.ts +2 -0
  97. package/dist/utils/index.js +3 -0
  98. package/dist/utils/intervalManager.d.ts +91 -0
  99. package/dist/utils/intervalManager.js +221 -0
  100. package/dist/utils/keyboardShortcutManager/keyboardShortcutManager.d.ts +41 -0
  101. package/dist/utils/keyboardShortcutManager/keyboardShortcutManager.js +135 -0
  102. package/dist/utils/keyboardShortcutManager/keyboardShortcuts.d.ts +18 -0
  103. package/dist/utils/keyboardShortcutManager/keyboardShortcuts.js +195 -0
  104. package/dist/utils/logging/simple/Loggable.d.ts +33 -0
  105. package/dist/utils/logging/simple/Loggable.js +1 -0
  106. package/dist/utils/logging/simple/LoggingDelegate.d.ts +42 -0
  107. package/dist/utils/logging/simple/LoggingDelegate.js +53 -0
  108. package/dist/utils/logging/simple/SimpleLoggingService.d.ts +39 -0
  109. package/dist/utils/logging/simple/SimpleLoggingService.js +58 -0
  110. package/dist/utils/orientation.d.ts +15 -0
  111. package/dist/utils/orientation.js +32 -0
  112. package/dist/utils/protectedContentManager.d.ts +155 -0
  113. package/dist/utils/protectedContentManager.js +424 -0
  114. package/dist/utils/securityOverlayManager.d.ts +253 -0
  115. package/dist/utils/securityOverlayManager.js +786 -0
  116. package/dist/utils/timeoutManager.d.ts +50 -0
  117. package/dist/utils/timeoutManager.js +113 -0
  118. package/package.json +61 -0
@@ -0,0 +1,278 @@
1
+ import { OverlayOptions } from "@/utils/securityOverlayManager";
2
+ /**
3
+ * Configuration options for content protection
4
+ */
5
+ export interface ContentProtectionOptions {
6
+ /** Enable/disable keyboard shortcuts protection */
7
+ preventKeyboardShortcuts?: boolean;
8
+ /** Enable/disable context menu protection */
9
+ preventContextMenu?: boolean;
10
+ /** Context menu protection options */
11
+ contextMenuOptions?: ContextMenuOptions;
12
+ /** Enable/disable print protection */
13
+ preventPrinting?: boolean;
14
+ /** Enable/disable selection protection */
15
+ preventSelection?: boolean;
16
+ /** Enable/disable watermarking */
17
+ enableWatermark?: boolean;
18
+ /** Enable/disable DevTools detection */
19
+ preventDevTools?: boolean;
20
+ /** Dev tools protection options */
21
+ devToolsOptions?: DevToolsOptions;
22
+ /** Enable/disable screenshot protection */
23
+ preventScreenshots?: boolean;
24
+ /** Screenshot protection options */
25
+ screenshotOptions?: ScreenshotOptions;
26
+ /** Watermark options */
27
+ watermarkOptions?: WatermarkOptions;
28
+ /** Enable/disable scraping extension protection */
29
+ preventExtensions?: boolean;
30
+ /** Extension protection options */
31
+ extensionOptions?: BrowserExtensionOptions;
32
+ /** Enable/disable iFrame protection */
33
+ preventEmbedding?: boolean;
34
+ /** iFrame protection options */
35
+ frameEmbeddingOptions?: FrameEmbeddingOptions;
36
+ /** Enable/disable clipboard protection (copy/cut/paste events + Clipboard API)
37
+ * @default false
38
+ */
39
+ preventClipboard?: boolean;
40
+ /** Clipboard protection options */
41
+ clipboardOptions?: ClipboardOptions;
42
+ /** Target element (defaults to document.body) */
43
+ targetElement?: HTMLElement | null;
44
+ /** Custom event handlers */
45
+ customHandlers?: CustomEventHandlers;
46
+ /** Enable debug mode for all strategies
47
+ * @default false
48
+ */
49
+ debugMode?: boolean;
50
+ }
51
+ /**
52
+ * Watermark configuration options
53
+ */
54
+ export interface WatermarkOptions {
55
+ /** Text to display in watermark */
56
+ text: string;
57
+ /** User identifier to include in watermark */
58
+ userId?: string;
59
+ /** Opacity of watermark (0-1) */
60
+ opacity?: number;
61
+ /** Density of watermark pattern (1-10) */
62
+ density?: number;
63
+ /** Custom styling for watermark */
64
+ style?: Partial<CSSStyleDeclaration>;
65
+ }
66
+ export interface ContextMenuOptions {
67
+ /**
68
+ * Whether to observe and protect iframes that are dynamically added to the DOM
69
+ * @default false
70
+ */
71
+ observeForIframes?: boolean;
72
+ }
73
+ /**
74
+ * Clipboard protection options
75
+ */
76
+ export interface ClipboardOptions {
77
+ /**
78
+ * Whether to prevent copy operations
79
+ * @default true
80
+ */
81
+ preventCopy?: boolean;
82
+ /**
83
+ * Whether to prevent cut operations
84
+ * @default true
85
+ */
86
+ preventCut?: boolean;
87
+ /**
88
+ * Whether to prevent paste operations
89
+ * @default false
90
+ */
91
+ preventPaste?: boolean;
92
+ /**
93
+ * Text to replace copied content with
94
+ * @default "Content copying is disabled for security reasons."
95
+ */
96
+ replacementText?: string;
97
+ }
98
+ /**
99
+ * Screenshot protection overlay options
100
+ */
101
+ export interface ScreenshotOptions {
102
+ /**
103
+ * Whether to show overlay when triggered
104
+ * @default true
105
+ */
106
+ showOverlay?: boolean;
107
+ overlayOptions?: OverlayOptions;
108
+ /**
109
+ * Whether to hide sensitive content when triggered
110
+ * @default true
111
+ */
112
+ hideContent?: boolean;
113
+ /**
114
+ * Whether to prevent fullscreen mode
115
+ * @default true
116
+ */
117
+ preventFullscreen?: boolean;
118
+ /**
119
+ * Message to display when fullscreen is attempted
120
+ */
121
+ fullscreenMessage?: string;
122
+ }
123
+ /**
124
+ * Options for the DevTools protection overlay
125
+ */
126
+ export interface DevToolsOptions {
127
+ /**
128
+ * Check frequency in milliseconds
129
+ */
130
+ checkFrequency?: number;
131
+ /**
132
+ * Whether to show overlay when triggered
133
+ * @default true
134
+ */
135
+ showOverlay?: boolean;
136
+ overlayOptions?: OverlayOptions;
137
+ /**
138
+ * Whether to hide sensitive content when triggered
139
+ * @default true
140
+ */
141
+ hideContent?: boolean;
142
+ /**
143
+ * Specific detector types to use
144
+ * If empty, will use the optimal detectors for the current browser
145
+ */
146
+ detectorTypes?: string[];
147
+ }
148
+ export interface BrowserExtensionOptions {
149
+ configPath?: string;
150
+ extensionsConfig?: Record<string, ExtensionConfig>;
151
+ detectionInterval?: number;
152
+ /**
153
+ * Whether to show overlay when triggered
154
+ * @default true
155
+ */
156
+ showOverlay?: boolean;
157
+ overlayOptions?: OverlayOptions;
158
+ /**
159
+ * Whether to hide sensitive content when triggered
160
+ * @default true
161
+ */
162
+ hideContent?: boolean;
163
+ }
164
+ /**
165
+ * Configuration for a specific browser extension to detect
166
+ */
167
+ export interface ExtensionConfig {
168
+ name: string;
169
+ description?: string;
170
+ risk: string;
171
+ detectionMethods: {
172
+ domSelectors?: string[];
173
+ jsSignatures?: string[];
174
+ };
175
+ }
176
+ /**
177
+ * Options for the FrameEmbeddingProtectionStrategy
178
+ */
179
+ export interface FrameEmbeddingOptions {
180
+ /**
181
+ * Whether to show overlay when triggered
182
+ * @default true
183
+ */
184
+ showOverlay?: boolean;
185
+ overlayOptions?: OverlayOptions;
186
+ /**
187
+ * Whether to hide sensitive content when triggered
188
+ * @default true
189
+ */
190
+ hideContent?: boolean;
191
+ /**
192
+ * Allowed domains that can embed the content (empty array means only same-origin is allowed)
193
+ */
194
+ allowedDomains?: string[];
195
+ /**
196
+ * Whether to completely block the content from loading in any iframe (even same-origin)
197
+ */
198
+ blockAllFrames?: boolean;
199
+ }
200
+ /**
201
+ * Custom event handlers
202
+ */
203
+ export interface CustomEventHandlers {
204
+ /** Called when protection is bypassed */
205
+ onProtectionBypassed?: (method: string, event: Event) => void;
206
+ /** Called when print is attempted */
207
+ onPrintAttempt?: (event: Event) => void;
208
+ /** Called when keyboard shortcut is blocked */
209
+ onKeyboardShortcutBlocked?: (event: KeyboardEvent) => void;
210
+ /** Called when DevTools is opened or closed */
211
+ onDevToolsOpen?: (isOpen: boolean) => void;
212
+ /** Called when a clipboard operation is detected */
213
+ onClipboardAttempt?: (event: Event, action: "copy" | "cut" | "paste") => void;
214
+ onContextMenuAttempt?: (event: Event) => void;
215
+ onSelectionAttempt?: (event: Event) => void;
216
+ onScreenshotAttempt?: (event: Event) => void;
217
+ onExtensionDetected?: (extensionId: string, extensionName: string, riskLevel: "low" | "medium" | "high") => void;
218
+ onFrameEmbeddingDetected?: (isEmbedded: boolean, isExternalFrame: boolean) => void;
219
+ /**
220
+ * Called when content is hidden due to a security event (e.g., DevTools opened)
221
+ * @param reason The reason why content was hidden
222
+ * @param targetElement The element whose content was hidden
223
+ */
224
+ onContentHidden?: (reason: string, targetElement: HTMLElement | null) => void;
225
+ /**
226
+ * Called when content is restored after a security event clears (e.g., DevTools closed)
227
+ * Useful for frameworks like Vue to re-mount components after innerHTML restoration
228
+ * @param targetElement The element whose content was restored
229
+ */
230
+ onContentRestored?: (targetElement: HTMLElement | null) => void;
231
+ }
232
+ /**
233
+ * Protection strategy interface
234
+ */
235
+ export interface ProtectionStrategy {
236
+ /**
237
+ * Unique identifier for the strategy
238
+ * Used for logging and event management
239
+ */
240
+ readonly STRATEGY_NAME: string;
241
+ /** Apply the protection strategy */
242
+ apply(options?: unknown): void;
243
+ /** Remove the protection strategy */
244
+ remove(): void;
245
+ /** Check if strategy is currently applied */
246
+ isApplied(): boolean;
247
+ /** Update strategy options */
248
+ updateOptions(options: Record<string, unknown>): void;
249
+ /** Get the debug mode status
250
+ * @returns True if debug mode is enabled
251
+ */
252
+ isDebugEnabled(): boolean;
253
+ /** Set debug mode
254
+ * @param enabled Whether debug mode should be enabled
255
+ */
256
+ setDebugMode(enabled: boolean): void;
257
+ }
258
+ /**
259
+ * Protection strategy names
260
+ */
261
+ export declare enum StrategyName {
262
+ KEYBOARD = "KeyboardStrategy",
263
+ CONTEXT_MENU = "ContextMenuStrategy",
264
+ PRINT = "PrintStrategy",
265
+ SELECTION = "SelectionStrategy",
266
+ WATERMARK = "WatermarkStrategy",
267
+ DEV_TOOLS = "DevToolsStrategy",
268
+ SCREENSHOT = "ScreenshotStrategy",
269
+ BROWSER_EXTENSION = "BrowserExtensionStrategy",
270
+ FRAME_EMBEDDING = "FrameEmbeddingStrategy",
271
+ CLIPBOARD = "ClipboardStrategy"
272
+ }
273
+ declare global {
274
+ interface Console {
275
+ exception?: (...data: unknown[]) => void;
276
+ }
277
+ }
278
+ export * from './assessment.js';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Protection strategy names
3
+ */
4
+ export var StrategyName;
5
+ (function (StrategyName) {
6
+ StrategyName["KEYBOARD"] = "KeyboardStrategy";
7
+ StrategyName["CONTEXT_MENU"] = "ContextMenuStrategy";
8
+ StrategyName["PRINT"] = "PrintStrategy";
9
+ StrategyName["SELECTION"] = "SelectionStrategy";
10
+ StrategyName["WATERMARK"] = "WatermarkStrategy";
11
+ StrategyName["DEV_TOOLS"] = "DevToolsStrategy";
12
+ StrategyName["SCREENSHOT"] = "ScreenshotStrategy";
13
+ StrategyName["BROWSER_EXTENSION"] = "BrowserExtensionStrategy";
14
+ StrategyName["FRAME_EMBEDDING"] = "FrameEmbeddingStrategy";
15
+ StrategyName["CLIPBOARD"] = "ClipboardStrategy";
16
+ })(StrategyName || (StrategyName = {}));
17
+ export * from './assessment.js';
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Options for the DOM observer
3
+ */
4
+ export interface DomObserverOptions {
5
+ /**
6
+ * Target element to observe
7
+ */
8
+ targetElement: HTMLElement;
9
+ /**
10
+ * Callback function when elements are removed
11
+ */
12
+ onElementsRemoved?: (removedElements: HTMLElement[]) => void;
13
+ /**
14
+ * Callback function when elements are added
15
+ */
16
+ onElementsAdded?: (addedElements: HTMLElement[]) => void;
17
+ /**
18
+ * Elements to watch for removal
19
+ */
20
+ elementsToWatch: HTMLElement[];
21
+ /**
22
+ * Whether to observe child elements as well
23
+ */
24
+ observeSubtree?: boolean;
25
+ /**
26
+ * Enable debug mode for troubleshooting
27
+ */
28
+ debugMode?: boolean;
29
+ /**
30
+ * Custom name for logging
31
+ */
32
+ name?: string;
33
+ }
34
+ /**
35
+ * Helper class to observe DOM changes and detect when specific elements are added or removed
36
+ */
37
+ export declare class DomObserver {
38
+ private observer;
39
+ private options;
40
+ private isObserving;
41
+ /**
42
+ * Create a new DOM observer
43
+ * @param options Observer options
44
+ */
45
+ constructor(options: DomObserverOptions);
46
+ /**
47
+ * Start observing the DOM for changes
48
+ */
49
+ startObserving(): void;
50
+ /**
51
+ * Stop observing the DOM
52
+ */
53
+ stopObserving(): void;
54
+ /**
55
+ * Update the elements to watch
56
+ * @param elements New elements to watch
57
+ */
58
+ updateElementsToWatch(elements: HTMLElement[]): void;
59
+ /**
60
+ * Update the target element
61
+ * @param element New target element
62
+ */
63
+ updateTargetElement(element: HTMLElement): void;
64
+ /**
65
+ * Check if the observer is currently observing
66
+ */
67
+ isActive(): boolean;
68
+ }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Helper class to observe DOM changes and detect when specific elements are added or removed
3
+ */
4
+ export class DomObserver {
5
+ /**
6
+ * Create a new DOM observer
7
+ * @param options Observer options
8
+ */
9
+ constructor(options) {
10
+ this.observer = null;
11
+ this.isObserving = false;
12
+ this.options = {
13
+ observeSubtree: true,
14
+ debugMode: false,
15
+ name: 'DomObserver',
16
+ ...options,
17
+ };
18
+ }
19
+ /**
20
+ * Start observing the DOM for changes
21
+ */
22
+ startObserving() {
23
+ if (this.isObserving || typeof MutationObserver === 'undefined' || !this.options.targetElement) {
24
+ return;
25
+ }
26
+ // Remove any existing observer
27
+ this.stopObserving();
28
+ // Create a new observer
29
+ this.observer = new MutationObserver((mutations) => {
30
+ const removedElements = [];
31
+ const addedElements = [];
32
+ for (const mutation of mutations) {
33
+ if (mutation.type === 'childList') {
34
+ // Check if any of our watched elements were removed
35
+ for (const node of Array.from(mutation.removedNodes)) {
36
+ if (node instanceof HTMLElement) {
37
+ if (this.options.elementsToWatch.includes(node)) {
38
+ removedElements.push(node);
39
+ if (this.options.debugMode) {
40
+ console.log(`${this.options.name}: Watched element was removed from DOM`, node);
41
+ }
42
+ }
43
+ }
44
+ }
45
+ // Check for added nodes if we have an onElementsAdded callback
46
+ if (this.options.onElementsAdded) {
47
+ for (const node of Array.from(mutation.addedNodes)) {
48
+ if (node instanceof HTMLElement) {
49
+ addedElements.push(node);
50
+ // If we're observing the subtree, also check for child elements
51
+ if (this.options.observeSubtree) {
52
+ const childElements = node.querySelectorAll("*");
53
+ childElements.forEach((child) => {
54
+ if (child instanceof HTMLElement) {
55
+ addedElements.push(child);
56
+ }
57
+ });
58
+ }
59
+ if (this.options.debugMode) {
60
+ console.log(`${this.options.name}: Element was added to DOM`, node);
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }
67
+ // Call callbacks if we have elements to report
68
+ if (removedElements.length > 0 && this.options.onElementsRemoved) {
69
+ this.options.onElementsRemoved(removedElements);
70
+ }
71
+ if (addedElements.length > 0 && this.options.onElementsAdded) {
72
+ this.options.onElementsAdded(addedElements);
73
+ }
74
+ });
75
+ // Start observing the target element
76
+ this.observer.observe(this.options.targetElement, {
77
+ childList: true,
78
+ subtree: this.options.observeSubtree,
79
+ });
80
+ this.isObserving = true;
81
+ if (this.options.debugMode) {
82
+ console.log(`${this.options.name}: Started observing`, {
83
+ target: this.options.targetElement,
84
+ elementsCount: this.options.elementsToWatch.length,
85
+ subtree: this.options.observeSubtree,
86
+ });
87
+ }
88
+ }
89
+ /**
90
+ * Stop observing the DOM
91
+ */
92
+ stopObserving() {
93
+ if (this.observer) {
94
+ this.observer.disconnect();
95
+ this.observer = null;
96
+ this.isObserving = false;
97
+ if (this.options.debugMode) {
98
+ console.log(`${this.options.name}: Stopped observing`);
99
+ }
100
+ }
101
+ }
102
+ /**
103
+ * Update the elements to watch
104
+ * @param elements New elements to watch
105
+ */
106
+ updateElementsToWatch(elements) {
107
+ this.options.elementsToWatch = elements;
108
+ if (this.isObserving && this.options.debugMode) {
109
+ console.log(`${this.options.name}: Updated elements to watch`, {
110
+ elementsCount: elements.length,
111
+ });
112
+ }
113
+ }
114
+ /**
115
+ * Update the target element
116
+ * @param element New target element
117
+ */
118
+ updateTargetElement(element) {
119
+ if (this.options.targetElement !== element) {
120
+ this.options.targetElement = element;
121
+ // Restart observing with the new target
122
+ if (this.isObserving) {
123
+ this.stopObserving();
124
+ this.startObserving();
125
+ }
126
+ }
127
+ }
128
+ /**
129
+ * Check if the observer is currently observing
130
+ */
131
+ isActive() {
132
+ return this.isObserving;
133
+ }
134
+ }
@@ -0,0 +1,44 @@
1
+ import { SimpleLoggingService } from "../logging/simple/SimpleLoggingService";
2
+ /**
3
+ * Abstract base for any component that owns a named, debug-toggleable logger.
4
+ *
5
+ * Consolidates the logger + debug-mode plumbing that was previously duplicated
6
+ * across {@link import("../../strategies/AbstractStrategy").AbstractStrategy},
7
+ * {@link import("../detectors/AbstractDevToolsDetector").AbstractDevToolsDetector},
8
+ * and {@link import("../../core/mediator/handlers/abstractEventHandler").AbstractEventHandler}.
9
+ *
10
+ * What lives here:
11
+ * - `COMPONENT_NAME` (used as the log prefix and as an owner key in event-
12
+ * manager / mediator subscriptions by subclasses).
13
+ * - `debugMode` + `logger`, kept in sync.
14
+ * - `log` / `warn` / `error` — thin pass-throughs to `SimpleLoggingService`,
15
+ * which already debug-gates `log` and applies the brief-vs-verbose split on
16
+ * `warn`. Subclasses' previous re-implementations of those gates were
17
+ * redundant.
18
+ *
19
+ * What does NOT live here: error-classification helpers like `handleError` and
20
+ * `safeExecute` — `AbstractStrategy` and `AbstractDevToolsDetector` use
21
+ * different error-type enums (`StrategyErrorType` vs `DetectorErrorType`) and
22
+ * different reporting strategies, so hoisting them up would require generics
23
+ * that obscure the call sites for little gain. They stay in the subclasses.
24
+ */
25
+ export declare abstract class LoggableComponent {
26
+ /** Used as the log prefix and as the owner key for event/mediator registrations. */
27
+ readonly COMPONENT_NAME: string;
28
+ protected debugMode: boolean;
29
+ protected logger: SimpleLoggingService;
30
+ constructor(componentName: string, debugMode?: boolean);
31
+ /**
32
+ * Toggle debug mode on both this component and its logger. Emits a
33
+ * confirmation line on enable (the disable case is silent because the logger
34
+ * is gated by the post-update flag).
35
+ */
36
+ setDebugMode(enabled: boolean): void;
37
+ isDebugEnabled(): boolean;
38
+ /** Debug log; suppressed in non-debug mode by `SimpleLoggingService`. */
39
+ protected log(message: string, ...args: unknown[]): void;
40
+ /** Warn log; brief form in non-debug mode (no args), full form with debug on. */
41
+ protected warn(message: string, ...args: unknown[]): void;
42
+ /** Error log; always printed. */
43
+ protected error(message: string, ...args: unknown[]): void;
44
+ }
@@ -0,0 +1,56 @@
1
+ import { SimpleLoggingService } from "../logging/simple/SimpleLoggingService";
2
+ /**
3
+ * Abstract base for any component that owns a named, debug-toggleable logger.
4
+ *
5
+ * Consolidates the logger + debug-mode plumbing that was previously duplicated
6
+ * across {@link import("../../strategies/AbstractStrategy").AbstractStrategy},
7
+ * {@link import("../detectors/AbstractDevToolsDetector").AbstractDevToolsDetector},
8
+ * and {@link import("../../core/mediator/handlers/abstractEventHandler").AbstractEventHandler}.
9
+ *
10
+ * What lives here:
11
+ * - `COMPONENT_NAME` (used as the log prefix and as an owner key in event-
12
+ * manager / mediator subscriptions by subclasses).
13
+ * - `debugMode` + `logger`, kept in sync.
14
+ * - `log` / `warn` / `error` — thin pass-throughs to `SimpleLoggingService`,
15
+ * which already debug-gates `log` and applies the brief-vs-verbose split on
16
+ * `warn`. Subclasses' previous re-implementations of those gates were
17
+ * redundant.
18
+ *
19
+ * What does NOT live here: error-classification helpers like `handleError` and
20
+ * `safeExecute` — `AbstractStrategy` and `AbstractDevToolsDetector` use
21
+ * different error-type enums (`StrategyErrorType` vs `DetectorErrorType`) and
22
+ * different reporting strategies, so hoisting them up would require generics
23
+ * that obscure the call sites for little gain. They stay in the subclasses.
24
+ */
25
+ export class LoggableComponent {
26
+ constructor(componentName, debugMode = false) {
27
+ this.COMPONENT_NAME = componentName;
28
+ this.debugMode = debugMode;
29
+ this.logger = new SimpleLoggingService(componentName, debugMode);
30
+ }
31
+ /**
32
+ * Toggle debug mode on both this component and its logger. Emits a
33
+ * confirmation line on enable (the disable case is silent because the logger
34
+ * is gated by the post-update flag).
35
+ */
36
+ setDebugMode(enabled) {
37
+ this.debugMode = enabled;
38
+ this.logger.setDebugMode(enabled);
39
+ this.logger.log(`Debug mode ${enabled ? "enabled" : "disabled"}`);
40
+ }
41
+ isDebugEnabled() {
42
+ return this.debugMode;
43
+ }
44
+ /** Debug log; suppressed in non-debug mode by `SimpleLoggingService`. */
45
+ log(message, ...args) {
46
+ this.logger.log(message, ...args);
47
+ }
48
+ /** Warn log; brief form in non-debug mode (no args), full form with debug on. */
49
+ warn(message, ...args) {
50
+ this.logger.warn(message, ...args);
51
+ }
52
+ /** Error log; always printed. */
53
+ error(message, ...args) {
54
+ this.logger.error(message, ...args);
55
+ }
56
+ }
@@ -0,0 +1,98 @@
1
+ import type { DevToolsDetector, DevToolsDetectorOptions } from "./detectorInterface";
2
+ import { LoggableComponent } from "../base/LoggableComponent";
3
+ /**
4
+ * Enum representing different types of errors that can occur in detectors
5
+ */
6
+ export declare enum DetectorErrorType {
7
+ /**
8
+ * Error during initialization of a detector
9
+ */
10
+ INITIALIZATION_ERROR = "initialization_error",
11
+ /**
12
+ * Error during detection check
13
+ */
14
+ DETECTION_ERROR = "detection_error",
15
+ /**
16
+ * Error related to browser compatibility
17
+ */
18
+ COMPATIBILITY_ERROR = "compatibility_error",
19
+ /**
20
+ * Error during cleanup/disposal
21
+ */
22
+ DISPOSAL_ERROR = "disposal_error",
23
+ /**
24
+ * Error in worker communication
25
+ */
26
+ WORKER_ERROR = "worker_error",
27
+ /**
28
+ * Error in DOM manipulation
29
+ */
30
+ DOM_ERROR = "dom_error",
31
+ /**
32
+ * Error in event handling
33
+ */
34
+ EVENT_ERROR = "event_error",
35
+ /**
36
+ * Unexpected or unknown error
37
+ */
38
+ UNKNOWN_ERROR = "unknown_error"
39
+ }
40
+ /**
41
+ * Abstract base class for DevTools detectors
42
+ * Implements common functionality to reduce duplication across detector implementations
43
+ */
44
+ export declare abstract class AbstractDevToolsDetector extends LoggableComponent implements DevToolsDetector {
45
+ protected readonly detectorName: string;
46
+ protected isDevToolsOpen: boolean;
47
+ protected isChecking: boolean;
48
+ protected onDevToolsChange: (isOpen: boolean) => void;
49
+ /**
50
+ * Create a new detector instance
51
+ * @param detectorName Name of the detector for logging
52
+ * @param options Configuration options
53
+ */
54
+ constructor(detectorName: string, options?: DevToolsDetectorOptions);
55
+ /**
56
+ * Check if DevTools is open
57
+ * This method must be implemented by subclasses
58
+ */
59
+ abstract checkDevTools(): void;
60
+ /**
61
+ * Get the current DevTools state
62
+ * @returns True if DevTools is open
63
+ */
64
+ isOpen(): boolean;
65
+ /**
66
+ * Update the DevTools state and notify listeners if changed
67
+ * @param isOpen New DevTools state
68
+ */
69
+ protected updateDevToolsState(isOpen: boolean): void;
70
+ /**
71
+ * Handle an error with appropriate logging
72
+ * @param errorType Type of error that occurred
73
+ * @param message Error message
74
+ * @param error The error object
75
+ */
76
+ protected handleError(errorType: DetectorErrorType, message: string, error: unknown): void;
77
+ /**
78
+ * Execute a function with error handling
79
+ * @param operation Name of the operation for error reporting
80
+ * @param errorType Type of error to report if the operation fails
81
+ * @param fn Function to execute
82
+ * @returns The result of the function or undefined if an error occurred
83
+ */
84
+ protected safeExecute<T>(operation: string, errorType: DetectorErrorType, fn: () => T): T | undefined;
85
+ /**
86
+ * Execute an async function with error handling
87
+ * @param operation Name of the operation for error reporting
88
+ * @param errorType Type of error to report if the operation fails
89
+ * @param fn Async function to execute
90
+ * @returns Promise resolving to the result of the function or undefined if an error occurred
91
+ */
92
+ protected safeExecuteAsync<T>(operation: string, errorType: DetectorErrorType, fn: () => Promise<T>): Promise<T | undefined>;
93
+ /**
94
+ * Clean up resources
95
+ * Override in subclasses to perform detector-specific cleanup
96
+ */
97
+ dispose(): void;
98
+ }