@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.
- package/LICENSE +9 -0
- package/README.md +357 -0
- package/dist/assess.d.ts +16 -0
- package/dist/assess.js +220 -0
- package/dist/config/default-extensions-config.json +103 -0
- package/dist/core/ContentProtector.d.ts +63 -0
- package/dist/core/ContentProtector.js +281 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +2 -0
- package/dist/core/mediator/ContentProtectionMediator.d.ts +86 -0
- package/dist/core/mediator/ContentProtectionMediator.js +238 -0
- package/dist/core/mediator/eventDataTypes.d.ts +112 -0
- package/dist/core/mediator/eventDataTypes.js +23 -0
- package/dist/core/mediator/handlers/abstractEventHandler.d.ts +41 -0
- package/dist/core/mediator/handlers/abstractEventHandler.js +59 -0
- package/dist/core/mediator/handlers/devToolsEventHandler.d.ts +9 -0
- package/dist/core/mediator/handlers/devToolsEventHandler.js +95 -0
- package/dist/core/mediator/handlers/eventHandlerRegistry.d.ts +9 -0
- package/dist/core/mediator/handlers/eventHandlerRegistry.js +34 -0
- package/dist/core/mediator/handlers/extensionEventHandlers.d.ts +40 -0
- package/dist/core/mediator/handlers/extensionEventHandlers.js +140 -0
- package/dist/core/mediator/handlers/iFrameEventHandlers.d.ts +27 -0
- package/dist/core/mediator/handlers/iFrameEventHandlers.js +93 -0
- package/dist/core/mediator/handlers/screenShotEventHandlers.d.ts +34 -0
- package/dist/core/mediator/handlers/screenShotEventHandlers.js +111 -0
- package/dist/core/mediator/protection-event.d.ts +77 -0
- package/dist/core/mediator/protection-event.js +32 -0
- package/dist/core/mediator/types.d.ts +105 -0
- package/dist/core/mediator/types.js +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +7 -0
- package/dist/otel.d.ts +24 -0
- package/dist/otel.js +83 -0
- package/dist/policy.d.ts +98 -0
- package/dist/policy.js +97 -0
- package/dist/strategies/AbstractStrategy.d.ts +124 -0
- package/dist/strategies/AbstractStrategy.js +256 -0
- package/dist/strategies/ClipboardStrategy.d.ts +67 -0
- package/dist/strategies/ClipboardStrategy.js +291 -0
- package/dist/strategies/ContextMenuStrategy.d.ts +60 -0
- package/dist/strategies/ContextMenuStrategy.js +454 -0
- package/dist/strategies/DevToolsStrategy.d.ts +55 -0
- package/dist/strategies/DevToolsStrategy.js +314 -0
- package/dist/strategies/ExtensionStrategy.d.ts +66 -0
- package/dist/strategies/ExtensionStrategy.js +486 -0
- package/dist/strategies/IFrameStrategy.d.ts +49 -0
- package/dist/strategies/IFrameStrategy.js +255 -0
- package/dist/strategies/KeyboardStrategy.d.ts +35 -0
- package/dist/strategies/KeyboardStrategy.js +130 -0
- package/dist/strategies/PrintStrategy.d.ts +47 -0
- package/dist/strategies/PrintStrategy.js +201 -0
- package/dist/strategies/ScreenshotStrategy.d.ts +90 -0
- package/dist/strategies/ScreenshotStrategy.js +502 -0
- package/dist/strategies/SelectionStrategy.d.ts +49 -0
- package/dist/strategies/SelectionStrategy.js +216 -0
- package/dist/strategies/WatermarkStrategy.d.ts +56 -0
- package/dist/strategies/WatermarkStrategy.js +287 -0
- package/dist/strategies/index.d.ts +10 -0
- package/dist/strategies/index.js +11 -0
- package/dist/types/assessment.d.ts +62 -0
- package/dist/types/assessment.js +1 -0
- package/dist/types/index.d.ts +278 -0
- package/dist/types/index.js +17 -0
- package/dist/utils/DOMObserver.d.ts +68 -0
- package/dist/utils/DOMObserver.js +134 -0
- package/dist/utils/base/LoggableComponent.d.ts +44 -0
- package/dist/utils/base/LoggableComponent.js +56 -0
- package/dist/utils/detectors/AbstractDevToolsDetector.d.ts +98 -0
- package/dist/utils/detectors/AbstractDevToolsDetector.js +127 -0
- package/dist/utils/detectors/dateToStringDetector.d.ts +43 -0
- package/dist/utils/detectors/dateToStringDetector.js +96 -0
- package/dist/utils/detectors/debugLibDetector.d.ts +64 -0
- package/dist/utils/detectors/debugLibDetector.js +195 -0
- package/dist/utils/detectors/debuggerDetector.d.ts +51 -0
- package/dist/utils/detectors/debuggerDetector.js +211 -0
- package/dist/utils/detectors/defineGetterDetector.d.ts +48 -0
- package/dist/utils/detectors/defineGetterDetector.js +150 -0
- package/dist/utils/detectors/detectorInterface.d.ts +36 -0
- package/dist/utils/detectors/detectorInterface.js +1 -0
- package/dist/utils/detectors/devToolsDetectorManager.d.ts +88 -0
- package/dist/utils/detectors/devToolsDetectorManager.js +243 -0
- package/dist/utils/detectors/funcToStringDetector.d.ts +43 -0
- package/dist/utils/detectors/funcToStringDetector.js +90 -0
- package/dist/utils/detectors/regToStringDetector.d.ts +43 -0
- package/dist/utils/detectors/regToStringDetector.js +129 -0
- package/dist/utils/detectors/sizeDetector.d.ts +54 -0
- package/dist/utils/detectors/sizeDetector.js +134 -0
- package/dist/utils/detectors/timingDetector.d.ts +55 -0
- package/dist/utils/detectors/timingDetector.js +143 -0
- package/dist/utils/dom.d.ts +20 -0
- package/dist/utils/dom.js +83 -0
- package/dist/utils/environment.d.ts +29 -0
- package/dist/utils/environment.js +267 -0
- package/dist/utils/eventManager.d.ts +162 -0
- package/dist/utils/eventManager.js +548 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/intervalManager.d.ts +91 -0
- package/dist/utils/intervalManager.js +221 -0
- package/dist/utils/keyboardShortcutManager/keyboardShortcutManager.d.ts +41 -0
- package/dist/utils/keyboardShortcutManager/keyboardShortcutManager.js +135 -0
- package/dist/utils/keyboardShortcutManager/keyboardShortcuts.d.ts +18 -0
- package/dist/utils/keyboardShortcutManager/keyboardShortcuts.js +195 -0
- package/dist/utils/logging/simple/Loggable.d.ts +33 -0
- package/dist/utils/logging/simple/Loggable.js +1 -0
- package/dist/utils/logging/simple/LoggingDelegate.d.ts +42 -0
- package/dist/utils/logging/simple/LoggingDelegate.js +53 -0
- package/dist/utils/logging/simple/SimpleLoggingService.d.ts +39 -0
- package/dist/utils/logging/simple/SimpleLoggingService.js +58 -0
- package/dist/utils/orientation.d.ts +15 -0
- package/dist/utils/orientation.js +32 -0
- package/dist/utils/protectedContentManager.d.ts +155 -0
- package/dist/utils/protectedContentManager.js +424 -0
- package/dist/utils/securityOverlayManager.d.ts +253 -0
- package/dist/utils/securityOverlayManager.js +786 -0
- package/dist/utils/timeoutManager.d.ts +50 -0
- package/dist/utils/timeoutManager.js +113 -0
- 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
|
+
}
|