crawlforge-mcp-server 3.0.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 (75) hide show
  1. package/CLAUDE.md +315 -0
  2. package/LICENSE +21 -0
  3. package/README.md +181 -0
  4. package/package.json +115 -0
  5. package/server.js +1963 -0
  6. package/setup.js +112 -0
  7. package/src/constants/config.js +615 -0
  8. package/src/core/ActionExecutor.js +1104 -0
  9. package/src/core/AlertNotificationSystem.js +601 -0
  10. package/src/core/AuthManager.js +315 -0
  11. package/src/core/ChangeTracker.js +2306 -0
  12. package/src/core/JobManager.js +687 -0
  13. package/src/core/LLMsTxtAnalyzer.js +753 -0
  14. package/src/core/LocalizationManager.js +1615 -0
  15. package/src/core/PerformanceManager.js +828 -0
  16. package/src/core/ResearchOrchestrator.js +1327 -0
  17. package/src/core/SnapshotManager.js +1037 -0
  18. package/src/core/StealthBrowserManager.js +1795 -0
  19. package/src/core/WebhookDispatcher.js +745 -0
  20. package/src/core/analysis/ContentAnalyzer.js +749 -0
  21. package/src/core/analysis/LinkAnalyzer.js +972 -0
  22. package/src/core/cache/CacheManager.js +821 -0
  23. package/src/core/connections/ConnectionPool.js +553 -0
  24. package/src/core/crawlers/BFSCrawler.js +845 -0
  25. package/src/core/integrations/PerformanceIntegration.js +377 -0
  26. package/src/core/llm/AnthropicProvider.js +135 -0
  27. package/src/core/llm/LLMManager.js +415 -0
  28. package/src/core/llm/LLMProvider.js +97 -0
  29. package/src/core/llm/OpenAIProvider.js +127 -0
  30. package/src/core/processing/BrowserProcessor.js +986 -0
  31. package/src/core/processing/ContentProcessor.js +505 -0
  32. package/src/core/processing/PDFProcessor.js +448 -0
  33. package/src/core/processing/StreamProcessor.js +673 -0
  34. package/src/core/queue/QueueManager.js +98 -0
  35. package/src/core/workers/WorkerPool.js +585 -0
  36. package/src/core/workers/worker.js +743 -0
  37. package/src/monitoring/healthCheck.js +600 -0
  38. package/src/monitoring/metrics.js +761 -0
  39. package/src/optimization/wave3-optimizations.js +932 -0
  40. package/src/security/security-patches.js +120 -0
  41. package/src/security/security-tests.js +355 -0
  42. package/src/security/wave3-security.js +652 -0
  43. package/src/tools/advanced/BatchScrapeTool.js +1089 -0
  44. package/src/tools/advanced/ScrapeWithActionsTool.js +669 -0
  45. package/src/tools/crawl/crawlDeep.js +449 -0
  46. package/src/tools/crawl/mapSite.js +400 -0
  47. package/src/tools/extract/analyzeContent.js +624 -0
  48. package/src/tools/extract/extractContent.js +329 -0
  49. package/src/tools/extract/processDocument.js +503 -0
  50. package/src/tools/extract/summarizeContent.js +376 -0
  51. package/src/tools/llmstxt/generateLLMsTxt.js +570 -0
  52. package/src/tools/research/deepResearch.js +706 -0
  53. package/src/tools/search/adapters/duckduckgoSearch.js +398 -0
  54. package/src/tools/search/adapters/googleSearch.js +236 -0
  55. package/src/tools/search/adapters/searchProviderFactory.js +96 -0
  56. package/src/tools/search/queryExpander.js +543 -0
  57. package/src/tools/search/ranking/ResultDeduplicator.js +676 -0
  58. package/src/tools/search/ranking/ResultRanker.js +497 -0
  59. package/src/tools/search/searchWeb.js +482 -0
  60. package/src/tools/tracking/trackChanges.js +1355 -0
  61. package/src/utils/CircuitBreaker.js +515 -0
  62. package/src/utils/ErrorHandlingConfig.js +342 -0
  63. package/src/utils/HumanBehaviorSimulator.js +569 -0
  64. package/src/utils/Logger.js +568 -0
  65. package/src/utils/MemoryMonitor.js +173 -0
  66. package/src/utils/RetryManager.js +386 -0
  67. package/src/utils/contentUtils.js +588 -0
  68. package/src/utils/domainFilter.js +612 -0
  69. package/src/utils/inputValidation.js +766 -0
  70. package/src/utils/rateLimiter.js +196 -0
  71. package/src/utils/robotsChecker.js +91 -0
  72. package/src/utils/securityMiddleware.js +416 -0
  73. package/src/utils/sitemapParser.js +678 -0
  74. package/src/utils/ssrfProtection.js +640 -0
  75. package/src/utils/urlNormalizer.js +168 -0
@@ -0,0 +1,569 @@
1
+ /**
2
+ * HumanBehaviorSimulator - Simulate human-like interactions
3
+ * Features: natural mouse movements, typing patterns, scroll behavior, idle periods
4
+ */
5
+
6
+ import { z } from 'zod';
7
+
8
+ const BehaviorConfigSchema = z.object({
9
+ mouseMovements: z.object({
10
+ enabled: z.boolean().default(true),
11
+ speed: z.enum(['slow', 'normal', 'fast']).default('normal'),
12
+ accuracy: z.number().min(0.1).max(1.0).default(0.8), // 0.1 = very inaccurate, 1.0 = perfect
13
+ naturalCurves: z.boolean().default(true),
14
+ randomMicroMovements: z.boolean().default(true)
15
+ }).default({}),
16
+
17
+ typing: z.object({
18
+ enabled: z.boolean().default(true),
19
+ speed: z.enum(['slow', 'normal', 'fast']).default('normal'),
20
+ variability: z.number().min(0).max(1).default(0.3), // Typing speed variation
21
+ mistakes: z.object({
22
+ enabled: z.boolean().default(true),
23
+ frequency: z.number().min(0).max(0.1).default(0.02), // 2% mistake rate
24
+ correctionDelay: z.number().min(100).max(2000).default(500)
25
+ }).default({})
26
+ }).default({}),
27
+
28
+ scrolling: z.object({
29
+ enabled: z.boolean().default(true),
30
+ naturalAcceleration: z.boolean().default(true),
31
+ randomPauses: z.boolean().default(true),
32
+ scrollBackProbability: z.number().min(0).max(1).default(0.1)
33
+ }).default({}),
34
+
35
+ interactions: z.object({
36
+ hoverBeforeClick: z.boolean().default(true),
37
+ clickDelay: z.object({
38
+ min: z.number().default(100),
39
+ max: z.number().default(300)
40
+ }).default({}),
41
+ focusBlurSimulation: z.boolean().default(true),
42
+ idlePeriods: z.object({
43
+ enabled: z.boolean().default(true),
44
+ frequency: z.number().min(0).max(1).default(0.1), // 10% chance
45
+ minDuration: z.number().default(1000),
46
+ maxDuration: z.number().default(5000)
47
+ }).default({})
48
+ }).default({})
49
+ });
50
+
51
+ export class HumanBehaviorSimulator {
52
+ constructor(config = {}) {
53
+ this.config = BehaviorConfigSchema.parse(config);
54
+
55
+ // Speed multipliers for different speeds
56
+ this.speedMultipliers = {
57
+ slow: 1.5,
58
+ normal: 1.0,
59
+ fast: 0.7
60
+ };
61
+
62
+ // Base typing speeds (characters per minute)
63
+ this.typingSpeeds = {
64
+ slow: 200,
65
+ normal: 350,
66
+ fast: 500
67
+ };
68
+
69
+ // Common typing patterns and mistakes
70
+ this.commonMistakes = {
71
+ 'e': ['r', 'w'],
72
+ 'r': ['e', 't'],
73
+ 't': ['r', 'y'],
74
+ 'y': ['t', 'u'],
75
+ 'u': ['y', 'i'],
76
+ 'i': ['u', 'o'],
77
+ 'o': ['i', 'p'],
78
+ 'a': ['s', 'q'],
79
+ 's': ['a', 'd'],
80
+ 'd': ['s', 'f'],
81
+ 'f': ['d', 'g'],
82
+ 'g': ['f', 'h'],
83
+ 'h': ['g', 'j'],
84
+ 'j': ['h', 'k'],
85
+ 'k': ['j', 'l'],
86
+ 'l': ['k', ';'],
87
+ 'n': ['b', 'm'],
88
+ 'm': ['n', ',']
89
+ };
90
+
91
+ this.stats = {
92
+ mouseMovements: 0,
93
+ typingActions: 0,
94
+ scrollActions: 0,
95
+ idlePeriods: 0,
96
+ mistakesSimulated: 0,
97
+ totalInteractions: 0
98
+ };
99
+ }
100
+
101
+ /**
102
+ * Simulate human-like mouse movement to target coordinates
103
+ */
104
+ async simulateMouseMovement(page, fromX, fromY, toX, toY) {
105
+ if (!this.config.mouseMovements.enabled) {
106
+ await page.mouse.move(toX, toY);
107
+ return;
108
+ }
109
+
110
+ const path = this.generateMousePath(fromX, fromY, toX, toY);
111
+ const speed = this.speedMultipliers[this.config.mouseMovements.speed];
112
+
113
+ for (let i = 0; i < path.length; i++) {
114
+ const point = path[i];
115
+ await page.mouse.move(point.x, point.y);
116
+
117
+ // Add realistic delay between movements
118
+ const delay = (5 + Math.random() * 5) * speed;
119
+ await this.delay(delay);
120
+
121
+ // Occasionally add micro-movements
122
+ if (this.config.mouseMovements.randomMicroMovements && Math.random() < 0.1) {
123
+ const microX = point.x + (Math.random() - 0.5) * 2;
124
+ const microY = point.y + (Math.random() - 0.5) * 2;
125
+ await page.mouse.move(microX, microY);
126
+ await this.delay(10 + Math.random() * 20);
127
+ }
128
+ }
129
+
130
+ this.stats.mouseMovements++;
131
+ }
132
+
133
+ /**
134
+ * Generate natural mouse movement path using Bezier curves
135
+ */
136
+ generateMousePath(fromX, fromY, toX, toY) {
137
+ const points = [];
138
+ const distance = Math.sqrt(Math.pow(toX - fromX, 2) + Math.pow(toY - fromY, 2));
139
+ const steps = Math.max(10, Math.min(100, Math.floor(distance / 5)));
140
+
141
+ if (!this.config.mouseMovements.naturalCurves || distance < 50) {
142
+ // Simple linear movement for short distances
143
+ for (let i = 0; i <= steps; i++) {
144
+ const t = i / steps;
145
+ points.push({
146
+ x: fromX + (toX - fromX) * t,
147
+ y: fromY + (toY - fromY) * t
148
+ });
149
+ }
150
+ } else {
151
+ // Bezier curve for natural movement
152
+ const controlPoints = this.generateBezierControlPoints(fromX, fromY, toX, toY);
153
+
154
+ for (let i = 0; i <= steps; i++) {
155
+ const t = i / steps;
156
+ const point = this.calculateBezierPoint(t,
157
+ { x: fromX, y: fromY },
158
+ controlPoints.cp1,
159
+ controlPoints.cp2,
160
+ { x: toX, y: toY }
161
+ );
162
+
163
+ // Add accuracy variation
164
+ const accuracy = this.config.mouseMovements.accuracy;
165
+ const deviation = (1 - accuracy) * 10;
166
+
167
+ points.push({
168
+ x: point.x + (Math.random() - 0.5) * deviation,
169
+ y: point.y + (Math.random() - 0.5) * deviation
170
+ });
171
+ }
172
+ }
173
+
174
+ return points;
175
+ }
176
+
177
+ /**
178
+ * Generate control points for Bezier curve
179
+ */
180
+ generateBezierControlPoints(x1, y1, x2, y2) {
181
+ const midX = (x1 + x2) / 2;
182
+ const midY = (y1 + y2) / 2;
183
+
184
+ // Add some randomness to make the curve more natural
185
+ const curvature = Math.random() * 100 + 50;
186
+ const direction = Math.random() < 0.5 ? 1 : -1;
187
+
188
+ const cp1 = {
189
+ x: midX + direction * curvature * Math.random(),
190
+ y: midY + direction * curvature * Math.random()
191
+ };
192
+
193
+ const cp2 = {
194
+ x: midX - direction * curvature * Math.random(),
195
+ y: midY - direction * curvature * Math.random()
196
+ };
197
+
198
+ return { cp1, cp2 };
199
+ }
200
+
201
+ /**
202
+ * Calculate point on cubic Bezier curve
203
+ */
204
+ calculateBezierPoint(t, p0, p1, p2, p3) {
205
+ const u = 1 - t;
206
+ const tt = t * t;
207
+ const uu = u * u;
208
+ const uuu = uu * u;
209
+ const ttt = tt * t;
210
+
211
+ return {
212
+ x: uuu * p0.x + 3 * uu * t * p1.x + 3 * u * tt * p2.x + ttt * p3.x,
213
+ y: uuu * p0.y + 3 * uu * t * p1.y + 3 * u * tt * p2.y + ttt * p3.y
214
+ };
215
+ }
216
+
217
+ /**
218
+ * Simulate human-like typing with realistic delays and mistakes
219
+ */
220
+ async simulateTyping(page, selector, text) {
221
+ if (!this.config.typing.enabled) {
222
+ await page.type(selector, text);
223
+ return;
224
+ }
225
+
226
+ const element = await page.waitForSelector(selector);
227
+ await element.focus();
228
+
229
+ // Calculate base typing speed
230
+ const baseSpeed = this.typingSpeeds[this.config.typing.speed];
231
+ const baseDelay = 60000 / baseSpeed; // Convert CPM to milliseconds per character
232
+
233
+ for (let i = 0; i < text.length; i++) {
234
+ const char = text[i];
235
+
236
+ // Simulate typing mistake
237
+ if (this.config.typing.mistakes.enabled && Math.random() < this.config.typing.mistakes.frequency) {
238
+ await this.simulateTypingMistake(element, char);
239
+ continue;
240
+ }
241
+
242
+ // Calculate realistic delay for this character
243
+ const delay = this.calculateTypingDelay(char, i, text, baseDelay);
244
+
245
+ // Type the character
246
+ await element.type(char);
247
+ await this.delay(delay);
248
+
249
+ // Occasionally add brief pauses (like thinking)
250
+ if (Math.random() < 0.05) {
251
+ await this.delay(200 + Math.random() * 800);
252
+ }
253
+ }
254
+
255
+ this.stats.typingActions++;
256
+ }
257
+
258
+ /**
259
+ * Simulate a typing mistake and correction
260
+ */
261
+ async simulateTypingMistake(element, correctChar) {
262
+ // Type wrong character
263
+ const mistakes = this.commonMistakes[correctChar.toLowerCase()] || [correctChar];
264
+ const wrongChar = mistakes[Math.floor(Math.random() * mistakes.length)];
265
+
266
+ await element.type(wrongChar);
267
+ await this.delay(this.config.typing.mistakes.correctionDelay + Math.random() * 500);
268
+
269
+ // Correct the mistake
270
+ await element.press('Backspace');
271
+ await this.delay(100 + Math.random() * 200);
272
+ await element.type(correctChar);
273
+
274
+ this.stats.mistakesSimulated++;
275
+ }
276
+
277
+ /**
278
+ * Calculate realistic typing delay based on character and context
279
+ */
280
+ calculateTypingDelay(char, index, text, baseDelay) {
281
+ let delay = baseDelay;
282
+
283
+ // Add variability
284
+ const variability = this.config.typing.variability;
285
+ delay += (Math.random() - 0.5) * delay * variability;
286
+
287
+ // Longer delays for certain characters
288
+ if (char === ' ') {
289
+ delay *= 1.2; // Space takes slightly longer
290
+ } else if (char.match(/[.!?]/)) {
291
+ delay *= 1.5; // Punctuation takes longer
292
+ } else if (char.match(/[A-Z]/)) {
293
+ delay *= 1.3; // Capital letters take longer (shift key)
294
+ } else if (char.match(/[0-9]/)) {
295
+ delay *= 1.4; // Numbers take longer
296
+ }
297
+
298
+ // Longer delay after punctuation (thinking pause)
299
+ if (index > 0 && text[index - 1].match(/[.!?]/)) {
300
+ delay *= 2;
301
+ }
302
+
303
+ // Shorter delay for repeated characters
304
+ if (index > 0 && text[index - 1] === char) {
305
+ delay *= 0.8;
306
+ }
307
+
308
+ return Math.max(50, delay); // Minimum 50ms delay
309
+ }
310
+
311
+ /**
312
+ * Simulate human-like clicking with hover and delay
313
+ */
314
+ async simulateClick(page, selector, options = {}) {
315
+ const element = await page.waitForSelector(selector);
316
+ const boundingBox = await element.boundingBox();
317
+
318
+ if (!boundingBox) {
319
+ throw new Error('Element not visible for clicking');
320
+ }
321
+
322
+ // Calculate click position with slight randomness
323
+ const clickX = boundingBox.x + boundingBox.width * (0.3 + Math.random() * 0.4);
324
+ const clickY = boundingBox.y + boundingBox.height * (0.3 + Math.random() * 0.4);
325
+
326
+ // Get current mouse position (approximate)
327
+ const currentX = boundingBox.x - 50 + Math.random() * 100;
328
+ const currentY = boundingBox.y - 50 + Math.random() * 100;
329
+
330
+ // Simulate hover before click
331
+ if (this.config.interactions.hoverBeforeClick) {
332
+ await this.simulateMouseMovement(page, currentX, currentY, clickX, clickY);
333
+ await this.delay(100 + Math.random() * 200); // Brief hover
334
+ }
335
+
336
+ // Add click delay
337
+ const clickDelay = this.config.interactions.clickDelay.min +
338
+ Math.random() * (this.config.interactions.clickDelay.max - this.config.interactions.clickDelay.min);
339
+ await this.delay(clickDelay);
340
+
341
+ // Perform the click
342
+ await page.mouse.click(clickX, clickY, options);
343
+
344
+ this.stats.totalInteractions++;
345
+ }
346
+
347
+ /**
348
+ * Simulate human-like scrolling with natural acceleration
349
+ */
350
+ async simulateScroll(page, options = {}) {
351
+ if (!this.config.scrolling.enabled) {
352
+ await page.mouse.wheel(0, options.deltaY || 100);
353
+ return;
354
+ }
355
+
356
+ const {
357
+ direction = 'down',
358
+ distance = 300,
359
+ duration = 1000,
360
+ target = null
361
+ } = options;
362
+
363
+ if (target) {
364
+ // Scroll to specific element
365
+ await page.evaluate((sel) => {
366
+ const element = document.querySelector(sel);
367
+ if (element) {
368
+ element.scrollIntoView({ behavior: 'smooth', block: 'center' });
369
+ }
370
+ }, target);
371
+ return;
372
+ }
373
+
374
+ // Calculate scroll parameters
375
+ const steps = Math.max(10, Math.min(50, duration / 50));
376
+ const stepDistance = distance / steps;
377
+ const stepDelay = duration / steps;
378
+
379
+ let deltaY = direction === 'up' ? -stepDistance : stepDistance;
380
+
381
+ for (let i = 0; i < steps; i++) {
382
+ // Apply natural acceleration curve
383
+ let acceleration = 1;
384
+ if (this.config.scrolling.naturalAcceleration) {
385
+ const progress = i / steps;
386
+ // Ease-in-out curve
387
+ acceleration = progress < 0.5
388
+ ? 2 * progress * progress
389
+ : 1 - Math.pow(-2 * progress + 2, 2) / 2;
390
+ }
391
+
392
+ const currentDelta = deltaY * acceleration;
393
+ await page.mouse.wheel(0, currentDelta);
394
+
395
+ // Variable delay between scroll steps
396
+ const delay = stepDelay * (0.8 + Math.random() * 0.4);
397
+ await this.delay(delay);
398
+
399
+ // Random pauses during scrolling
400
+ if (this.config.scrolling.randomPauses && Math.random() < 0.1) {
401
+ await this.delay(200 + Math.random() * 500);
402
+ }
403
+ }
404
+
405
+ // Occasionally scroll back slightly (human behavior)
406
+ if (this.config.scrolling.scrollBackProbability > 0 &&
407
+ Math.random() < this.config.scrolling.scrollBackProbability) {
408
+ await this.delay(500 + Math.random() * 1000);
409
+ await page.mouse.wheel(0, -deltaY * 0.3);
410
+ }
411
+
412
+ this.stats.scrollActions++;
413
+ }
414
+
415
+ /**
416
+ * Simulate focus and blur events
417
+ */
418
+ async simulateFocusBlur(page, selector) {
419
+ if (!this.config.interactions.focusBlurSimulation) {
420
+ return;
421
+ }
422
+
423
+ const element = await page.waitForSelector(selector);
424
+
425
+ // Brief unfocus/refocus to simulate human attention
426
+ if (Math.random() < 0.3) {
427
+ await page.evaluate(() => document.activeElement?.blur());
428
+ await this.delay(100 + Math.random() * 300);
429
+ await element.focus();
430
+ await this.delay(50 + Math.random() * 150);
431
+ }
432
+ }
433
+
434
+ /**
435
+ * Simulate idle periods (human pauses)
436
+ */
437
+ async simulateIdlePeriod() {
438
+ if (!this.config.interactions.idlePeriods.enabled ||
439
+ Math.random() > this.config.interactions.idlePeriods.frequency) {
440
+ return;
441
+ }
442
+
443
+ const minDuration = this.config.interactions.idlePeriods.minDuration;
444
+ const maxDuration = this.config.interactions.idlePeriods.maxDuration;
445
+ const duration = minDuration + Math.random() * (maxDuration - minDuration);
446
+
447
+ await this.delay(duration);
448
+ this.stats.idlePeriods++;
449
+ }
450
+
451
+ /**
452
+ * Simulate reading time based on content
453
+ */
454
+ async simulateReadingTime(page, selector = null) {
455
+ let textLength = 0;
456
+
457
+ if (selector) {
458
+ textLength = await page.evaluate((sel) => {
459
+ const element = document.querySelector(sel);
460
+ return element ? element.textContent.length : 0;
461
+ }, selector);
462
+ } else {
463
+ textLength = await page.evaluate(() => document.body.textContent.length);
464
+ }
465
+
466
+ // Average reading speed: 250 words per minute
467
+ // Average word length: 5 characters
468
+ const wordsCount = textLength / 5;
469
+ const readingTime = (wordsCount / 250) * 60 * 1000; // Convert to milliseconds
470
+
471
+ // Add randomness (50% to 150% of calculated time)
472
+ const actualReadingTime = readingTime * (0.5 + Math.random());
473
+
474
+ // Cap reading time at 30 seconds for practical purposes
475
+ const finalTime = Math.min(actualReadingTime, 30000);
476
+
477
+ await this.delay(finalTime);
478
+ }
479
+
480
+ /**
481
+ * Simulate form filling behavior
482
+ */
483
+ async simulateFormFilling(page, formData) {
484
+ for (const [selector, value] of Object.entries(formData)) {
485
+ // Focus on field
486
+ await this.simulateFocusBlur(page, selector);
487
+
488
+ // Possible idle period before typing
489
+ await this.simulateIdlePeriod();
490
+
491
+ // Type with human-like behavior
492
+ await this.simulateTyping(page, selector, value);
493
+
494
+ // Brief pause after typing
495
+ await this.delay(200 + Math.random() * 500);
496
+ }
497
+ }
498
+
499
+ /**
500
+ * Generate realistic viewport interactions
501
+ */
502
+ async simulateViewportInteraction(page, action) {
503
+ switch (action.type) {
504
+ case 'resize':
505
+ const newSize = action.size || { width: 1280 + Math.random() * 640, height: 720 + Math.random() * 360 };
506
+ await page.setViewportSize(newSize);
507
+ break;
508
+
509
+ case 'zoom':
510
+ const zoomLevel = action.level || (0.8 + Math.random() * 0.4); // 80% to 120%
511
+ await page.evaluate((zoom) => {
512
+ document.body.style.zoom = zoom;
513
+ }, zoomLevel);
514
+ break;
515
+
516
+ case 'fullscreen':
517
+ // Simulate fullscreen behavior
518
+ await page.keyboard.press('F11');
519
+ await this.delay(1000);
520
+ if (action.exit) {
521
+ await page.keyboard.press('F11');
522
+ }
523
+ break;
524
+ }
525
+ }
526
+
527
+ /**
528
+ * Utility delay function with slight randomness
529
+ */
530
+ delay(ms, randomness = 0.1) {
531
+ const variation = ms * randomness;
532
+ const actualDelay = ms + (Math.random() - 0.5) * variation;
533
+ return new Promise(resolve => setTimeout(resolve, Math.max(0, actualDelay)));
534
+ }
535
+
536
+ /**
537
+ * Get behavior statistics
538
+ */
539
+ getStats() {
540
+ return {
541
+ ...this.stats,
542
+ configuration: this.config,
543
+ timestamp: Date.now()
544
+ };
545
+ }
546
+
547
+ /**
548
+ * Reset statistics
549
+ */
550
+ resetStats() {
551
+ this.stats = {
552
+ mouseMovements: 0,
553
+ typingActions: 0,
554
+ scrollActions: 0,
555
+ idlePeriods: 0,
556
+ mistakesSimulated: 0,
557
+ totalInteractions: 0
558
+ };
559
+ }
560
+
561
+ /**
562
+ * Update configuration
563
+ */
564
+ updateConfig(newConfig) {
565
+ this.config = BehaviorConfigSchema.parse({ ...this.config, ...newConfig });
566
+ }
567
+ }
568
+
569
+ export default HumanBehaviorSimulator;