@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,548 @@
1
+ import { isBrowser } from "./environment";
2
+ import { LoggableComponent } from "./base/LoggableComponent";
3
+ /**
4
+ * EventManager centralizes event handling across protection strategies
5
+ * It provides a unified API for registering and removing event listeners
6
+ * and ensures proper cleanup when strategies are removed
7
+ */
8
+ export class EventManager extends LoggableComponent {
9
+ /**
10
+ * Create a new EventManager
11
+ * @param debugMode Enable debug mode for troubleshooting
12
+ */
13
+ constructor(debugMode = false) {
14
+ super("EventManager", debugMode);
15
+ // Main storage: Map<TargetId, Map<EventId, StoredEvent>>
16
+ this.events = new Map();
17
+ // WeakMap to associate DOM elements with their target IDs
18
+ this.targetMap = new WeakMap();
19
+ // Special symbols for document and window
20
+ this.DOCUMENT_SYMBOL = Symbol("document");
21
+ this.WINDOW_SYMBOL = Symbol("window");
22
+ this.logger.log("Initialized");
23
+ }
24
+ /**
25
+ * Get the EventManager instance (singleton)
26
+ * @param debugMode Enable debug mode for troubleshooting
27
+ */
28
+ static getInstance(debugMode = false) {
29
+ if (!EventManager.instance) {
30
+ EventManager.instance = new EventManager(debugMode);
31
+ }
32
+ // Update debug mode if it's explicitly passed
33
+ if (arguments.length > 0) {
34
+ EventManager.instance.setDebugMode(debugMode);
35
+ }
36
+ return EventManager.instance;
37
+ }
38
+ /**
39
+ * Register an event listener
40
+ * @param target The target element, document, or window
41
+ * @param eventType The type of event (e.g., "click", "keydown")
42
+ * @param handler The event handler function
43
+ * @param owner The component or strategy that owns this event
44
+ * @param options Additional options for the event listener
45
+ * @returns The ID of the registered event
46
+ */
47
+ addEventListener(target, eventType, handler, owner, options) {
48
+ if (!isBrowser() || !target) {
49
+ this.logger.log(`Cannot add event, ${!isBrowser() ? "not in browser" : "target is null"}`);
50
+ return "";
51
+ }
52
+ // Get or create target ID
53
+ const targetId = this.getTargetId(target);
54
+ if (!targetId)
55
+ return "";
56
+ // Get or create event map for this target
57
+ if (!this.events.has(targetId)) {
58
+ this.events.set(targetId, new Map());
59
+ }
60
+ const targetEvents = this.events.get(targetId);
61
+ // Generate event ID if not provided
62
+ const eventId = options?.id || `${owner}-${eventType}-${Date.now()}`;
63
+ // Set default priority if not specified
64
+ const priority = options?.priority !== undefined ? options.priority : 0;
65
+ // Create a wrapped handler with error handling
66
+ const wrappedHandler = (e) => {
67
+ try {
68
+ return handler(e);
69
+ }
70
+ catch (error) {
71
+ this.logger.error(`Error in ${eventType} event handler for ${owner}:`, error);
72
+ // Continue event propagation by not returning false
73
+ }
74
+ };
75
+ // Store event information
76
+ const storedEvent = {
77
+ eventType,
78
+ handler,
79
+ wrappedHandler,
80
+ options,
81
+ owner,
82
+ priority,
83
+ };
84
+ // Add event listener to the target
85
+ try {
86
+ target.addEventListener(eventType, wrappedHandler, options);
87
+ // Store the event information
88
+ targetEvents.set(eventId, storedEvent);
89
+ this.logger.log(`Added ${eventType} event for ${owner} with ID ${eventId} and priority ${priority}`);
90
+ return eventId;
91
+ }
92
+ catch (error) {
93
+ this.logger.error(`Error adding ${eventType} event:`, error);
94
+ return "";
95
+ }
96
+ }
97
+ /**
98
+ * Remove a specific event listener by ID
99
+ * @param target The target element, document, or window
100
+ * @param eventId The ID of the event to remove
101
+ * @returns True if the event was removed, false otherwise
102
+ */
103
+ removeEventListener(target, eventId) {
104
+ if (!isBrowser() || !target) {
105
+ return false;
106
+ }
107
+ const targetId = this.getTargetId(target, false);
108
+ if (!targetId)
109
+ return false;
110
+ const targetEvents = this.events.get(targetId);
111
+ if (!targetEvents || !targetEvents.has(eventId)) {
112
+ return false;
113
+ }
114
+ // Get the stored event information
115
+ const storedEvent = targetEvents.get(eventId);
116
+ // Remove the event listener
117
+ try {
118
+ target.removeEventListener(storedEvent.eventType, storedEvent.wrappedHandler, storedEvent.options);
119
+ // Remove from our map
120
+ targetEvents.delete(eventId);
121
+ // Clean up the target map if no more events
122
+ if (targetEvents.size === 0) {
123
+ this.events.delete(targetId);
124
+ // Only remove from targetMap if it's not document or window
125
+ if (targetId !== this.DOCUMENT_SYMBOL && targetId !== this.WINDOW_SYMBOL) {
126
+ this.targetMap.delete(target);
127
+ }
128
+ }
129
+ this.logger.log(`Removed event ${eventId} (${storedEvent.eventType}) for ${storedEvent.owner}`);
130
+ return true;
131
+ }
132
+ catch (error) {
133
+ this.logger.error(`Error removing event ${eventId}:`, error);
134
+ return false;
135
+ }
136
+ }
137
+ /**
138
+ * Remove all event listeners for a specific owner (strategy/component)
139
+ * @param owner The owner to remove events for
140
+ * @returns The number of events removed
141
+ */
142
+ removeEventsByOwner(owner) {
143
+ if (!isBrowser())
144
+ return 0;
145
+ let removedCount = 0;
146
+ // We need to track which target IDs to clean up after removal
147
+ const emptyTargetIds = [];
148
+ // Iterate through all targets
149
+ for (const [targetId, targetEvents] of this.events.entries()) {
150
+ // Find all events owned by this owner
151
+ const eventsToRemove = [];
152
+ for (const [eventId, storedEvent] of targetEvents.entries()) {
153
+ if (storedEvent.owner === owner) {
154
+ eventsToRemove.push(eventId);
155
+ }
156
+ }
157
+ // If we have events to remove, try to get the target
158
+ if (eventsToRemove.length > 0) {
159
+ const target = this.getTargetFromId(targetId);
160
+ if (target) {
161
+ // For document and window targets, we can reliably remove events
162
+ if (targetId === this.DOCUMENT_SYMBOL || targetId === this.WINDOW_SYMBOL) {
163
+ // Remove each event
164
+ for (const eventId of eventsToRemove) {
165
+ if (this.removeEventListener(target, eventId)) {
166
+ removedCount++;
167
+ }
168
+ }
169
+ }
170
+ else {
171
+ // For DOM elements that might be recreated (like in Vue),
172
+ // we need to be more cautious and remove handlers directly
173
+ for (const eventId of eventsToRemove) {
174
+ const storedEvent = targetEvents.get(eventId);
175
+ if (storedEvent) {
176
+ try {
177
+ target.removeEventListener(storedEvent.eventType, storedEvent.wrappedHandler, storedEvent.options);
178
+ targetEvents.delete(eventId);
179
+ removedCount++;
180
+ this.logger.log(`Removed event ${eventId} (${storedEvent.eventType}) for ${storedEvent.owner}`);
181
+ }
182
+ catch (e) {
183
+ // If we can't remove the listener, just remove from our maps
184
+ targetEvents.delete(eventId);
185
+ removedCount++;
186
+ this.logger.log(`Removed event ${eventId} from maps only - could not remove listener directly (${String(e)})`);
187
+ }
188
+ }
189
+ }
190
+ }
191
+ }
192
+ else {
193
+ // For targets we can't retrieve (due to WeakMap limitations),
194
+ // we'll just remove the event entries from our maps
195
+ for (const eventId of eventsToRemove) {
196
+ const storedEvent = targetEvents.get(eventId);
197
+ if (storedEvent) {
198
+ targetEvents.delete(eventId);
199
+ removedCount++;
200
+ this.logger.log(`Removed event ${eventId} (${storedEvent.eventType}) for ${storedEvent.owner} (target unavailable)`);
201
+ }
202
+ }
203
+ }
204
+ // Check if the target events map is now empty
205
+ if (targetEvents.size === 0) {
206
+ emptyTargetIds.push(targetId);
207
+ }
208
+ }
209
+ }
210
+ // Clean up empty target maps
211
+ for (const targetId of emptyTargetIds) {
212
+ this.events.delete(targetId);
213
+ }
214
+ if (removedCount > 0) {
215
+ this.logger.log(`Removed ${removedCount} events for owner ${owner}`);
216
+ }
217
+ return removedCount;
218
+ }
219
+ /**
220
+ * Remove all event listeners for a specific target
221
+ * @param target The target to remove events from
222
+ * @returns The number of events removed
223
+ */
224
+ removeAllEventsForTarget(target) {
225
+ if (!isBrowser() || !target)
226
+ return 0;
227
+ const targetId = this.getTargetId(target, false);
228
+ if (!targetId)
229
+ return 0;
230
+ const targetEvents = this.events.get(targetId);
231
+ if (!targetEvents)
232
+ return 0;
233
+ let removedCount = 0;
234
+ // Create a copy of the event IDs to avoid modification during iteration
235
+ const eventIds = Array.from(targetEvents.keys());
236
+ for (const eventId of eventIds) {
237
+ try {
238
+ const storedEvent = targetEvents.get(eventId);
239
+ if (storedEvent) {
240
+ // Try to properly remove the event listener
241
+ target.removeEventListener(storedEvent.eventType, storedEvent.wrappedHandler, storedEvent.options);
242
+ // Remove from our map
243
+ targetEvents.delete(eventId);
244
+ removedCount++;
245
+ this.logger.log(`Removed event ${eventId} (${storedEvent.eventType}) from target`);
246
+ }
247
+ }
248
+ catch (e) {
249
+ // If removal fails, still remove from our maps
250
+ targetEvents.delete(eventId);
251
+ removedCount++;
252
+ this.logger.log(`Removed event ${eventId} from maps (${String(e)})`);
253
+ }
254
+ }
255
+ if (removedCount > 0) {
256
+ this.logger.log(`Removed ${removedCount} events for target`);
257
+ }
258
+ return removedCount;
259
+ }
260
+ /**
261
+ * Get all event IDs for a specific owner
262
+ * @param owner The owner to get events for
263
+ * @returns Array of event IDs
264
+ */
265
+ getEventsByOwner(owner) {
266
+ if (!isBrowser())
267
+ return [];
268
+ const eventIds = [];
269
+ for (const targetEvents of this.events.values()) {
270
+ for (const [eventId, storedEvent] of targetEvents.entries()) {
271
+ if (storedEvent.owner === owner) {
272
+ eventIds.push(eventId);
273
+ }
274
+ }
275
+ }
276
+ return eventIds;
277
+ }
278
+ /**
279
+ * Check if an event exists
280
+ * @param target The target element
281
+ * @param eventId The event ID
282
+ * @returns True if the event exists
283
+ */
284
+ hasEvent(target, eventId) {
285
+ if (!isBrowser() || !target)
286
+ return false;
287
+ const targetId = this.getTargetId(target, false);
288
+ if (!targetId)
289
+ return false;
290
+ const targetEvents = this.events.get(targetId);
291
+ return targetEvents ? targetEvents.has(eventId) : false;
292
+ }
293
+ /**
294
+ * Get a unique identifier for a target
295
+ * @param target The target element, document, or window
296
+ * @param create Whether to create a new ID if one doesn't exist
297
+ * @returns The target ID
298
+ */
299
+ getTargetId(target, create = true) {
300
+ // Special cases for document and window
301
+ if (target === document) {
302
+ return this.DOCUMENT_SYMBOL;
303
+ }
304
+ if (target === window) {
305
+ return this.WINDOW_SYMBOL;
306
+ }
307
+ // Check if we already have an ID for this target
308
+ let targetId = this.targetMap.get(target);
309
+ // Create a new ID if needed
310
+ if (!targetId && create) {
311
+ targetId = `target-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
312
+ this.targetMap.set(target, targetId);
313
+ }
314
+ return targetId || null;
315
+ }
316
+ /**
317
+ * Get a target from its ID
318
+ * @param targetId The target ID
319
+ * @returns The target element or null
320
+ */
321
+ getTargetFromId(targetId) {
322
+ if (!isBrowser())
323
+ return null;
324
+ // Special cases for document and window
325
+ if (targetId === this.DOCUMENT_SYMBOL) {
326
+ return document;
327
+ }
328
+ if (targetId === this.WINDOW_SYMBOL) {
329
+ return window;
330
+ }
331
+ // For other targets, we can't easily look up by ID since WeakMap doesn't support iteration
332
+ // This is a limitation of WeakMap, but in practice, most events will be on document/window
333
+ // For element-specific events, we'll need to rely on the caller having a reference to the element
334
+ // Return null for other target IDs - this means some operations like removeEventsByOwner
335
+ // will only work fully for document and window events
336
+ this.logger.warn(`Cannot retrieve target for ID ${String(targetId)}. This is a limitation for element-specific events.`);
337
+ return null;
338
+ }
339
+ /**
340
+ * Get the number of registered events
341
+ * @returns The total number of registered events
342
+ */
343
+ getEventCount() {
344
+ let count = 0;
345
+ for (const targetEvents of this.events.values()) {
346
+ count += targetEvents.size;
347
+ }
348
+ return count;
349
+ }
350
+ /**
351
+ * Get debug information about registered events
352
+ * @returns Object with debug information
353
+ */
354
+ getDebugInfo() {
355
+ const eventsByOwner = {};
356
+ const eventsByType = {};
357
+ let totalEvents = 0;
358
+ const eventDetails = [];
359
+ for (const [targetId, targetEvents] of this.events.entries()) {
360
+ for (const [eventId, storedEvent] of targetEvents.entries()) {
361
+ totalEvents++;
362
+ // Count by owner
363
+ eventsByOwner[storedEvent.owner] = (eventsByOwner[storedEvent.owner] || 0) + 1;
364
+ // Count by event type
365
+ eventsByType[storedEvent.eventType] = (eventsByType[storedEvent.eventType] || 0) + 1;
366
+ // Add detailed event info
367
+ eventDetails.push({
368
+ targetId: targetId,
369
+ eventId,
370
+ eventType: storedEvent.eventType,
371
+ owner: storedEvent.owner,
372
+ priority: storedEvent.priority,
373
+ });
374
+ }
375
+ }
376
+ return {
377
+ totalEvents,
378
+ eventsByOwner,
379
+ eventsByType,
380
+ eventDetails,
381
+ };
382
+ }
383
+ /**
384
+ * Clear all registered events
385
+ * @returns The number of events removed
386
+ */
387
+ clearAllEvents() {
388
+ if (!isBrowser())
389
+ return 0;
390
+ let removedCount = 0;
391
+ // Handle document and window events first
392
+ if (this.events.has(this.DOCUMENT_SYMBOL)) {
393
+ removedCount += this.removeAllEventsForTarget(document);
394
+ }
395
+ if (this.events.has(this.WINDOW_SYMBOL)) {
396
+ removedCount += this.removeAllEventsForTarget(window);
397
+ }
398
+ // For other targets, we'll have to just clear our maps
399
+ // Create a copy of the entries to avoid modification during iteration
400
+ const entries = Array.from(this.events.entries());
401
+ for (const [targetId, targetEvents] of entries) {
402
+ // Skip document and window as we already handled them
403
+ if (targetId === this.DOCUMENT_SYMBOL || targetId === this.WINDOW_SYMBOL) {
404
+ continue;
405
+ }
406
+ // For other targets, just clear the events from our maps
407
+ removedCount += targetEvents.size;
408
+ this.events.delete(targetId);
409
+ this.logger.log(`Cleared ${targetEvents.size} events for target ID ${String(targetId)} (target unavailable)`);
410
+ }
411
+ // Clear the target map
412
+ this.targetMap = new WeakMap();
413
+ if (removedCount > 0) {
414
+ this.logger.log(`Cleared all ${removedCount} events`);
415
+ }
416
+ return removedCount;
417
+ }
418
+ /**
419
+ * Check if an event handler is already registered for this target, event type, and owner
420
+ * This helps prevent duplicate registrations
421
+ * @param target The target element
422
+ * @param eventType The event type (e.g., "click", "contextmenu")
423
+ * @param owner The owner of the event handler
424
+ * @returns True if an event handler is already registered
425
+ */
426
+ hasRegisteredEventType(target, eventType, owner) {
427
+ if (!isBrowser() || !target)
428
+ return false;
429
+ const targetId = this.getTargetId(target, false);
430
+ if (!targetId)
431
+ return false;
432
+ const targetEvents = this.events.get(targetId);
433
+ if (!targetEvents)
434
+ return false;
435
+ // Check if any event of this type is already registered for this owner
436
+ for (const storedEvent of targetEvents.values()) {
437
+ if (storedEvent.eventType === eventType && storedEvent.owner === owner) {
438
+ return true;
439
+ }
440
+ }
441
+ return false;
442
+ }
443
+ /**
444
+ * Check for potential conflicts with existing event listeners
445
+ * @param target The target element
446
+ * @param eventType The event type (e.g., "click", "contextmenu")
447
+ * @param owner The owner of the event handler
448
+ * @returns Object with conflict information
449
+ */
450
+ checkForConflicts(target, eventType, owner) {
451
+ const result = {
452
+ hasConflicts: false,
453
+ conflictsWith: [],
454
+ };
455
+ if (!isBrowser() || !target) {
456
+ return result;
457
+ }
458
+ const targetId = this.getTargetId(target, false);
459
+ if (!targetId)
460
+ return result;
461
+ const targetEvents = this.events.get(targetId);
462
+ if (!targetEvents)
463
+ return result;
464
+ for (const [eventId, storedEvent] of targetEvents.entries()) {
465
+ if (storedEvent.eventType === eventType && storedEvent.owner !== owner) {
466
+ result.hasConflicts = true;
467
+ result.conflictsWith.push({
468
+ owner: storedEvent.owner,
469
+ eventId,
470
+ });
471
+ }
472
+ }
473
+ return result;
474
+ }
475
+ /**
476
+ * Get events of a specific type for a target
477
+ * @param target The target element
478
+ * @param eventType The event type (e.g., "click", "contextmenu")
479
+ * @returns Array of event information
480
+ */
481
+ getEventsByType(target, eventType) {
482
+ const events = [];
483
+ if (!isBrowser() || !target) {
484
+ return events;
485
+ }
486
+ const targetId = this.getTargetId(target, false);
487
+ if (!targetId)
488
+ return events;
489
+ const targetEvents = this.events.get(targetId);
490
+ if (!targetEvents)
491
+ return events;
492
+ for (const [eventId, storedEvent] of targetEvents.entries()) {
493
+ if (storedEvent.eventType === eventType) {
494
+ events.push({
495
+ eventId,
496
+ owner: storedEvent.owner,
497
+ priority: storedEvent.priority,
498
+ });
499
+ }
500
+ }
501
+ // Sort by priority (higher first)
502
+ return events.sort((a, b) => b.priority - a.priority);
503
+ }
504
+ /**
505
+ * Remove event listeners by selector
506
+ * Useful for Vue components and other dynamically created elements
507
+ * @param selector CSS selector to match elements
508
+ * @param eventType The event type (e.g., "click", "contextmenu")
509
+ * @param owner The owner of the event handler
510
+ * @returns The number of events removed
511
+ */
512
+ removeEventsBySelector(selector, eventType, owner) {
513
+ if (!isBrowser())
514
+ return 0;
515
+ let removedCount = 0;
516
+ try {
517
+ // Find all matching elements
518
+ const elements = document.querySelectorAll(selector);
519
+ if (elements.length > 0) {
520
+ this.logger.log(`Attempting to remove ${eventType} events for ${owner} on ${elements.length} matched elements`);
521
+ // For each element, find and remove matching events
522
+ elements.forEach((element) => {
523
+ const targetId = this.getTargetId(element, false);
524
+ if (targetId) {
525
+ const targetEvents = this.events.get(targetId);
526
+ if (targetEvents) {
527
+ for (const [eventId, storedEvent] of targetEvents.entries()) {
528
+ if (storedEvent.owner === owner && storedEvent.eventType === eventType) {
529
+ element.removeEventListener(eventType, storedEvent.wrappedHandler, storedEvent.options);
530
+ targetEvents.delete(eventId);
531
+ removedCount++;
532
+ this.logger.log(`Removed ${eventType} event for ${owner} via selector`);
533
+ }
534
+ }
535
+ }
536
+ }
537
+ });
538
+ }
539
+ }
540
+ catch (error) {
541
+ this.logger.error("Error removing events by selector:", error);
542
+ }
543
+ return removedCount;
544
+ }
545
+ }
546
+ EventManager.instance = null;
547
+ // Export a singleton instance
548
+ export const eventManager = EventManager.getInstance();
@@ -0,0 +1,2 @@
1
+ export * from './environment';
2
+ export * from './dom';
@@ -0,0 +1,3 @@
1
+ // BARREL FILE
2
+ export * from './environment';
3
+ export * from './dom';
@@ -0,0 +1,91 @@
1
+ import { LoggableComponent } from "./base/LoggableComponent";
2
+ export type IntervalTask = {
3
+ id: string;
4
+ callback: () => void;
5
+ frequency: number;
6
+ lastRun?: number;
7
+ isActive: boolean;
8
+ };
9
+ /**
10
+ * Manages periodic tasks for the content protection toolkit
11
+ * Consolidates multiple interval timers into a single efficient timer
12
+ */
13
+ export declare class IntervalManager extends LoggableComponent {
14
+ private tasks;
15
+ private intervalId;
16
+ private intervalFrequency;
17
+ private isRunning;
18
+ /**
19
+ * Create a new IntervalManager
20
+ * @param debugMode Enable debug mode for troubleshooting
21
+ */
22
+ constructor(debugMode?: boolean);
23
+ /**
24
+ * Register a new task to be executed periodically
25
+ * @param id Unique identifier for the task
26
+ * @param callback Function to execute
27
+ * @param frequency How often to run the task in milliseconds
28
+ * @returns The task ID for later reference
29
+ */
30
+ registerTask(id: string, callback: () => void, frequency: number): string;
31
+ /**
32
+ * Unregister a task by ID
33
+ * @param id Task ID to remove
34
+ * @returns True if the task was found and removed
35
+ */
36
+ unregisterTask(id: string): boolean;
37
+ /**
38
+ * Pause a specific task without removing it
39
+ * @param id Task ID to pause
40
+ * @returns True if the task was found and paused
41
+ */
42
+ pauseTask(id: string): boolean;
43
+ /**
44
+ * Resume a paused task
45
+ * @param id Task ID to resume
46
+ * @returns True if the task was found and resumed
47
+ */
48
+ resumeTask(id: string): boolean;
49
+ /**
50
+ * Start the interval timer
51
+ */
52
+ private startInterval;
53
+ /**
54
+ * Stop the interval timer
55
+ */
56
+ private stopInterval;
57
+ /**
58
+ * Execute all tasks that are due to run
59
+ */
60
+ private executeEligibleTasks;
61
+ /**
62
+ * Force execution of a specific task immediately
63
+ * @param id Task ID to execute
64
+ * @returns True if the task was found and executed
65
+ */
66
+ executeTaskNow(id: string): boolean;
67
+ /**
68
+ * Get the current status of all registered tasks
69
+ * @returns Array of task status objects
70
+ */
71
+ getTasksStatus(): Array<{
72
+ id: string;
73
+ isActive: boolean;
74
+ frequency: number;
75
+ lastRun: number | undefined;
76
+ timeSinceLastRun: number | null;
77
+ }>;
78
+ /**
79
+ * Clean up and stop all intervals
80
+ */
81
+ dispose(): void;
82
+ /**
83
+ * Update the base interval frequency
84
+ * @param frequency New frequency in milliseconds
85
+ */
86
+ setIntervalFrequency(frequency: number): void;
87
+ }
88
+ /**
89
+ * Create a singleton instance for use throughout the application
90
+ */
91
+ export declare const intervalManager: IntervalManager;