@axeptio/behavior-detection 1.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 (76) hide show
  1. package/README.md +828 -0
  2. package/dist/behavior-detection.esm.min.js +2 -0
  3. package/dist/behavior-detection.esm.min.js.map +7 -0
  4. package/dist/behavior-detection.min.js +2 -0
  5. package/dist/behavior-detection.min.js.map +7 -0
  6. package/dist/behavior-detector.d.ts +102 -0
  7. package/dist/browser.d.ts +33 -0
  8. package/dist/cjs/behavior-detector.d.ts +102 -0
  9. package/dist/cjs/behavior-detector.js +315 -0
  10. package/dist/cjs/browser.d.ts +33 -0
  11. package/dist/cjs/browser.js +226 -0
  12. package/dist/cjs/index.d.ts +38 -0
  13. package/dist/cjs/index.js +55 -0
  14. package/dist/cjs/math-utils.d.ts +84 -0
  15. package/dist/cjs/math-utils.js +141 -0
  16. package/dist/cjs/strategies/click.d.ts +39 -0
  17. package/dist/cjs/strategies/click.js +173 -0
  18. package/dist/cjs/strategies/environment.d.ts +52 -0
  19. package/dist/cjs/strategies/environment.js +148 -0
  20. package/dist/cjs/strategies/index.d.ts +18 -0
  21. package/dist/cjs/strategies/index.js +36 -0
  22. package/dist/cjs/strategies/keyboard.d.ts +43 -0
  23. package/dist/cjs/strategies/keyboard.js +233 -0
  24. package/dist/cjs/strategies/mouse.d.ts +39 -0
  25. package/dist/cjs/strategies/mouse.js +159 -0
  26. package/dist/cjs/strategies/resize.d.ts +21 -0
  27. package/dist/cjs/strategies/resize.js +97 -0
  28. package/dist/cjs/strategies/scroll.d.ts +37 -0
  29. package/dist/cjs/strategies/scroll.js +149 -0
  30. package/dist/cjs/strategies/tap.d.ts +38 -0
  31. package/dist/cjs/strategies/tap.js +214 -0
  32. package/dist/cjs/strategy.d.ts +107 -0
  33. package/dist/cjs/strategy.js +33 -0
  34. package/dist/cjs/types.d.ts +168 -0
  35. package/dist/cjs/types.js +26 -0
  36. package/dist/esm/behavior-detector.d.ts +102 -0
  37. package/dist/esm/behavior-detector.js +311 -0
  38. package/dist/esm/browser.d.ts +33 -0
  39. package/dist/esm/browser.js +224 -0
  40. package/dist/esm/index.d.ts +38 -0
  41. package/dist/esm/index.js +36 -0
  42. package/dist/esm/math-utils.d.ts +84 -0
  43. package/dist/esm/math-utils.js +127 -0
  44. package/dist/esm/strategies/click.d.ts +39 -0
  45. package/dist/esm/strategies/click.js +169 -0
  46. package/dist/esm/strategies/environment.d.ts +52 -0
  47. package/dist/esm/strategies/environment.js +144 -0
  48. package/dist/esm/strategies/index.d.ts +18 -0
  49. package/dist/esm/strategies/index.js +19 -0
  50. package/dist/esm/strategies/keyboard.d.ts +43 -0
  51. package/dist/esm/strategies/keyboard.js +229 -0
  52. package/dist/esm/strategies/mouse.d.ts +39 -0
  53. package/dist/esm/strategies/mouse.js +155 -0
  54. package/dist/esm/strategies/resize.d.ts +21 -0
  55. package/dist/esm/strategies/resize.js +93 -0
  56. package/dist/esm/strategies/scroll.d.ts +37 -0
  57. package/dist/esm/strategies/scroll.js +145 -0
  58. package/dist/esm/strategies/tap.d.ts +38 -0
  59. package/dist/esm/strategies/tap.js +210 -0
  60. package/dist/esm/strategy.d.ts +107 -0
  61. package/dist/esm/strategy.js +29 -0
  62. package/dist/esm/types.d.ts +168 -0
  63. package/dist/esm/types.js +23 -0
  64. package/dist/index.d.ts +38 -0
  65. package/dist/math-utils.d.ts +84 -0
  66. package/dist/strategies/click.d.ts +39 -0
  67. package/dist/strategies/environment.d.ts +52 -0
  68. package/dist/strategies/index.d.ts +18 -0
  69. package/dist/strategies/keyboard.d.ts +43 -0
  70. package/dist/strategies/mouse.d.ts +39 -0
  71. package/dist/strategies/resize.d.ts +21 -0
  72. package/dist/strategies/scroll.d.ts +37 -0
  73. package/dist/strategies/tap.d.ts +38 -0
  74. package/dist/strategy.d.ts +107 -0
  75. package/dist/types.d.ts +168 -0
  76. package/package.json +60 -0
@@ -0,0 +1,168 @@
1
+ export type EventType = 'tab-visibility' | 'scroll' | 'resize' | 'mouse-move' | 'click' | 'keypress' | 'environment';
2
+ export interface BaseEvent {
3
+ type: EventType;
4
+ timestamp: number;
5
+ }
6
+ export interface TabVisibilityEvent extends BaseEvent {
7
+ type: 'tab-visibility';
8
+ hidden: boolean;
9
+ }
10
+ export interface ScrollEvent extends BaseEvent {
11
+ type: 'scroll';
12
+ x: number;
13
+ y: number;
14
+ elementId?: string;
15
+ deltaY?: number;
16
+ deltaTime?: number;
17
+ velocity?: number;
18
+ isProgrammatic?: boolean;
19
+ }
20
+ export interface ResizeEvent extends BaseEvent {
21
+ type: 'resize';
22
+ width: number;
23
+ height: number;
24
+ mouseX?: number;
25
+ mouseY?: number;
26
+ mouseNearEdge?: boolean;
27
+ screenWidth: number;
28
+ screenHeight: number;
29
+ screenAvailWidth: number;
30
+ screenAvailHeight: number;
31
+ devicePixelRatio: number;
32
+ isFullscreen?: boolean;
33
+ }
34
+ export interface MouseMoveEvent extends BaseEvent {
35
+ type: 'mouse-move';
36
+ x: number;
37
+ y: number;
38
+ velocityX?: number;
39
+ velocityY?: number;
40
+ directionChange?: boolean;
41
+ }
42
+ export interface ClickEvent extends BaseEvent {
43
+ type: 'click';
44
+ x: number;
45
+ y: number;
46
+ targetX: number;
47
+ targetY: number;
48
+ targetWidth: number;
49
+ targetHeight: number;
50
+ distanceFromTarget: number;
51
+ mousePositionBeforeClick?: {
52
+ x: number;
53
+ y: number;
54
+ timestamp: number;
55
+ };
56
+ timeSinceLastMouseMove?: number;
57
+ targetInViewport: boolean;
58
+ targetVisibleArea: number;
59
+ }
60
+ export interface KeypressEvent extends BaseEvent {
61
+ type: 'keypress';
62
+ key: string;
63
+ timeSinceLastKey?: number;
64
+ }
65
+ export interface EnvironmentEvent extends BaseEvent {
66
+ type: 'environment';
67
+ screenWidth: number;
68
+ screenHeight: number;
69
+ screenAvailWidth: number;
70
+ screenAvailHeight: number;
71
+ windowWidth: number;
72
+ windowHeight: number;
73
+ devicePixelRatio: number;
74
+ colorDepth: number;
75
+ pixelDepth: number;
76
+ userAgent: string;
77
+ platform: string;
78
+ language: string;
79
+ languages: string[];
80
+ hardwareConcurrency?: number;
81
+ maxTouchPoints: number;
82
+ vendor: string;
83
+ hasWebGL: boolean;
84
+ hasWebRTC: boolean;
85
+ hasNotifications: boolean;
86
+ hasGeolocation: boolean;
87
+ hasIndexedDB: boolean;
88
+ hasLocalStorage: boolean;
89
+ hasSessionStorage: boolean;
90
+ plugins: number;
91
+ mimeTypes: number;
92
+ suspiciousRatio?: boolean;
93
+ suspiciousDimensions?: boolean;
94
+ featureInconsistency?: boolean;
95
+ }
96
+ export type TrackedEvent = TabVisibilityEvent | ScrollEvent | ResizeEvent | MouseMoveEvent | ClickEvent | KeypressEvent | EnvironmentEvent;
97
+ export interface EventStorage {
98
+ 'tab-visibility': TabVisibilityEvent[];
99
+ 'scroll': ScrollEvent[];
100
+ 'resize': ResizeEvent[];
101
+ 'mouse-move': MouseMoveEvent[];
102
+ 'click': ClickEvent[];
103
+ 'keypress': KeypressEvent[];
104
+ 'environment': EnvironmentEvent[];
105
+ }
106
+ export interface ScoreBreakdown {
107
+ overall: number;
108
+ factors: {
109
+ mouseMovement?: number;
110
+ clickAccuracy?: number;
111
+ scrollBehavior?: number;
112
+ keyboardTiming?: number;
113
+ tabActivity?: number;
114
+ resizeBehavior?: number;
115
+ environmentFingerprint?: number;
116
+ };
117
+ weights: {
118
+ mouseMovement: number;
119
+ clickAccuracy: number;
120
+ scrollBehavior: number;
121
+ keyboardTiming: number;
122
+ tabActivity: number;
123
+ resizeBehavior: number;
124
+ environmentFingerprint: number;
125
+ };
126
+ }
127
+ export interface ScoreOptions {
128
+ breakdown?: boolean;
129
+ auditTrail?: boolean;
130
+ }
131
+ export interface ScoreResult {
132
+ score: number;
133
+ breakdown?: ScoreBreakdown;
134
+ auditTrail?: TrackedEvent[];
135
+ }
136
+ export type ScoringFunction = (events: TrackedEvent[]) => number | undefined;
137
+ export interface BehaviorSettings {
138
+ sampleRates?: {
139
+ mouseMove?: number;
140
+ scroll?: number;
141
+ keypress?: number;
142
+ };
143
+ rollingWindows?: {
144
+ mouseMove?: number;
145
+ scroll?: number;
146
+ };
147
+ weights?: {
148
+ mouseMovement?: number;
149
+ clickAccuracy?: number;
150
+ scrollBehavior?: number;
151
+ keyboardTiming?: number;
152
+ tabActivity?: number;
153
+ resizeBehavior?: number;
154
+ environmentFingerprint?: number;
155
+ };
156
+ customScorers?: {
157
+ mouseMovement?: ScoringFunction;
158
+ clickAccuracy?: ScoringFunction;
159
+ scrollBehavior?: ScoringFunction;
160
+ keyboardTiming?: ScoringFunction;
161
+ tabActivity?: ScoringFunction;
162
+ resizeBehavior?: ScoringFunction;
163
+ environmentFingerprint?: ScoringFunction;
164
+ };
165
+ clickMouseHistoryWindow?: number;
166
+ useWebWorker?: boolean;
167
+ }
168
+ export declare const DEFAULT_SETTINGS: Required<BehaviorSettings>;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_SETTINGS = void 0;
4
+ exports.DEFAULT_SETTINGS = {
5
+ sampleRates: {
6
+ mouseMove: 0.1, // Track 10% of mouse moves
7
+ scroll: 1.0, // Track ALL scrolls - critical for detection
8
+ keypress: 1.0, // Track all keypresses
9
+ },
10
+ rollingWindows: {
11
+ mouseMove: 30000,
12
+ scroll: 30000,
13
+ },
14
+ weights: {
15
+ mouseMovement: 0.30, // Increased - continuous behavioral signal
16
+ clickAccuracy: 0.30, // Increased - critical behavioral signal
17
+ scrollBehavior: 0.15, // Unchanged
18
+ keyboardTiming: 0.10, // Slightly reduced
19
+ tabActivity: 0.05, // Unchanged
20
+ resizeBehavior: 0.02, // Reduced - less reliable
21
+ environmentFingerprint: 0.08, // Reduced - static signal, one-time check
22
+ },
23
+ customScorers: {},
24
+ clickMouseHistoryWindow: 1000,
25
+ useWebWorker: false, // Will implement Web Worker support in phase 2
26
+ };
@@ -0,0 +1,102 @@
1
+ import type { ScoreOptions, ScoreResult } from './types';
2
+ import type { DetectionStrategy, StrategyConfig, TickOptions } from './strategy';
3
+ /**
4
+ * Main class for behavior detection
5
+ * Uses modular strategy pattern for tree-shakeable, autonomous detection modules
6
+ */
7
+ export declare class BehaviorDetector {
8
+ private strategies;
9
+ private isTracking;
10
+ private isPausedByVisibility;
11
+ private tickInterval;
12
+ private tickIntervalMs;
13
+ private pauseOnHidden;
14
+ private visibilityChangeHandler;
15
+ private confidenceScore;
16
+ private readonly CONFIDENCE_TARGET;
17
+ private readonly CONFIDENCE_DECAY;
18
+ constructor(tickOptions?: TickOptions);
19
+ /**
20
+ * Add a detection strategy
21
+ */
22
+ addStrategy(strategy: DetectionStrategy, weight?: number): this;
23
+ /**
24
+ * Handle event from strategy - update confidence
25
+ */
26
+ private onStrategyEvent;
27
+ /**
28
+ * Remove a detection strategy
29
+ */
30
+ removeStrategy(name: string): this;
31
+ /**
32
+ * Enable/disable a strategy
33
+ */
34
+ setStrategyEnabled(name: string, enabled: boolean): this;
35
+ /**
36
+ * Get all registered strategies
37
+ */
38
+ getStrategies(): ReadonlyMap<string, StrategyConfig>;
39
+ /**
40
+ * Start tracking
41
+ */
42
+ start(): void;
43
+ /**
44
+ * Stop tracking
45
+ */
46
+ stop(): void;
47
+ /**
48
+ * Reset all data
49
+ */
50
+ reset(): void;
51
+ /**
52
+ * Calculate human likelihood score
53
+ */
54
+ score(options?: ScoreOptions): Promise<ScoreResult>;
55
+ /**
56
+ * Check if currently tracking
57
+ */
58
+ isActive(): boolean;
59
+ /**
60
+ * Check if currently paused due to tab visibility
61
+ */
62
+ isPaused(): boolean;
63
+ /**
64
+ * Get event count from all strategies
65
+ */
66
+ getEventCount(): Record<string, number>;
67
+ /**
68
+ * Get debug info from all strategies
69
+ */
70
+ getStrategyDebugInfo(): Record<string, any>;
71
+ /**
72
+ * Get current confidence score (0-1)
73
+ * Represents how much interaction data we've collected
74
+ * Higher confidence = more reliable classification
75
+ */
76
+ getConfidence(): number;
77
+ /**
78
+ * Check if confidence is above threshold for reliable classification
79
+ * @param threshold - Minimum confidence (0-1), default 0.3
80
+ */
81
+ hasConfidentData(threshold?: number): boolean;
82
+ /**
83
+ * Cleanup resources
84
+ */
85
+ destroy(): void;
86
+ /**
87
+ * Handle visibility change events
88
+ */
89
+ private handleVisibilityChange;
90
+ /**
91
+ * Start tick mechanism for strategies
92
+ */
93
+ private startTick;
94
+ /**
95
+ * Stop tick mechanism
96
+ */
97
+ private stopTick;
98
+ /**
99
+ * Calculate score using strategies
100
+ */
101
+ private calculateStrategyScore;
102
+ }
@@ -0,0 +1,311 @@
1
+ /**
2
+ * Main class for behavior detection
3
+ * Uses modular strategy pattern for tree-shakeable, autonomous detection modules
4
+ */
5
+ export class BehaviorDetector {
6
+ constructor(tickOptions) {
7
+ // Strategy mode
8
+ this.strategies = new Map();
9
+ this.isTracking = false;
10
+ this.isPausedByVisibility = false;
11
+ this.tickInterval = null;
12
+ this.tickIntervalMs = 1000; // Default: 1 second
13
+ this.pauseOnHidden = true;
14
+ this.visibilityChangeHandler = null;
15
+ // Confidence tracking
16
+ this.confidenceScore = 0;
17
+ this.CONFIDENCE_TARGET = 1.0; // Target confidence for reliable classification
18
+ this.CONFIDENCE_DECAY = 0.95; // Per event decay to prevent infinite growth
19
+ if (tickOptions === null || tickOptions === void 0 ? void 0 : tickOptions.interval) {
20
+ this.tickIntervalMs = tickOptions.interval;
21
+ }
22
+ if ((tickOptions === null || tickOptions === void 0 ? void 0 : tickOptions.pauseOnHidden) !== undefined) {
23
+ this.pauseOnHidden = tickOptions.pauseOnHidden;
24
+ }
25
+ }
26
+ /**
27
+ * Add a detection strategy
28
+ */
29
+ addStrategy(strategy, weight) {
30
+ const config = {
31
+ strategy,
32
+ weight: weight !== null && weight !== void 0 ? weight : strategy.defaultWeight,
33
+ enabled: true,
34
+ };
35
+ this.strategies.set(strategy.name, config);
36
+ // Set up event callback for confidence tracking
37
+ if (strategy.setEventCallback) {
38
+ strategy.setEventCallback((event) => {
39
+ this.onStrategyEvent(event, config.weight);
40
+ });
41
+ }
42
+ // If already tracking, start this strategy immediately
43
+ if (this.isTracking) {
44
+ strategy.start();
45
+ }
46
+ return this;
47
+ }
48
+ /**
49
+ * Handle event from strategy - update confidence
50
+ */
51
+ onStrategyEvent(event, strategyWeight) {
52
+ // Apply decay to existing confidence to prevent infinite growth
53
+ this.confidenceScore *= this.CONFIDENCE_DECAY;
54
+ // Add weighted contribution from this event
55
+ // eventWeight (0-1) * strategyWeight (e.g. 0.3 for mouse) = contribution
56
+ const contribution = event.weight * strategyWeight;
57
+ this.confidenceScore = Math.min(this.CONFIDENCE_TARGET, this.confidenceScore + contribution);
58
+ }
59
+ /**
60
+ * Remove a detection strategy
61
+ */
62
+ removeStrategy(name) {
63
+ const config = this.strategies.get(name);
64
+ if (config) {
65
+ if (this.isTracking) {
66
+ config.strategy.stop();
67
+ }
68
+ this.strategies.delete(name);
69
+ }
70
+ return this;
71
+ }
72
+ /**
73
+ * Enable/disable a strategy
74
+ */
75
+ setStrategyEnabled(name, enabled) {
76
+ const config = this.strategies.get(name);
77
+ if (config) {
78
+ config.enabled = enabled;
79
+ // If tracking and being disabled, stop it
80
+ if (!enabled && this.isTracking) {
81
+ config.strategy.stop();
82
+ }
83
+ // If tracking and being enabled, start it
84
+ if (enabled && this.isTracking) {
85
+ config.strategy.start();
86
+ }
87
+ }
88
+ return this;
89
+ }
90
+ /**
91
+ * Get all registered strategies
92
+ */
93
+ getStrategies() {
94
+ return this.strategies;
95
+ }
96
+ /**
97
+ * Start tracking
98
+ */
99
+ start() {
100
+ if (this.isTracking)
101
+ return;
102
+ this.isTracking = true;
103
+ // Set up visibility change listener
104
+ if (this.pauseOnHidden && typeof document !== 'undefined') {
105
+ this.visibilityChangeHandler = this.handleVisibilityChange.bind(this);
106
+ document.addEventListener('visibilitychange', this.visibilityChangeHandler);
107
+ // Check initial visibility state
108
+ if (document.hidden) {
109
+ this.isPausedByVisibility = true;
110
+ return; // Don't start strategies if already hidden
111
+ }
112
+ }
113
+ // Start all enabled strategies
114
+ for (const [_, config] of this.strategies) {
115
+ if (config.enabled) {
116
+ config.strategy.start();
117
+ }
118
+ }
119
+ // Start tick mechanism
120
+ this.startTick();
121
+ }
122
+ /**
123
+ * Stop tracking
124
+ */
125
+ stop() {
126
+ if (!this.isTracking)
127
+ return;
128
+ this.isTracking = false;
129
+ this.isPausedByVisibility = false;
130
+ // Remove visibility change listener
131
+ if (this.visibilityChangeHandler && typeof document !== 'undefined') {
132
+ document.removeEventListener('visibilitychange', this.visibilityChangeHandler);
133
+ this.visibilityChangeHandler = null;
134
+ }
135
+ // Stop all strategies
136
+ for (const [_, config] of this.strategies) {
137
+ config.strategy.stop();
138
+ }
139
+ // Stop tick
140
+ this.stopTick();
141
+ }
142
+ /**
143
+ * Reset all data
144
+ */
145
+ reset() {
146
+ this.confidenceScore = 0;
147
+ for (const [_, config] of this.strategies) {
148
+ config.strategy.reset();
149
+ }
150
+ }
151
+ /**
152
+ * Calculate human likelihood score
153
+ */
154
+ async score(options = {}) {
155
+ const breakdown = this.calculateStrategyScore();
156
+ const result = {
157
+ score: breakdown.overall,
158
+ };
159
+ if (options.breakdown) {
160
+ result.breakdown = breakdown;
161
+ }
162
+ // Note: auditTrail not available in strategy mode
163
+ // Each strategy manages its own data independently
164
+ return result;
165
+ }
166
+ /**
167
+ * Check if currently tracking
168
+ */
169
+ isActive() {
170
+ return this.isTracking;
171
+ }
172
+ /**
173
+ * Check if currently paused due to tab visibility
174
+ */
175
+ isPaused() {
176
+ return this.isPausedByVisibility;
177
+ }
178
+ /**
179
+ * Get event count from all strategies
180
+ */
181
+ getEventCount() {
182
+ var _a, _b;
183
+ const counts = {};
184
+ for (const [name, config] of this.strategies) {
185
+ const debug = (_b = (_a = config.strategy).getDebugInfo) === null || _b === void 0 ? void 0 : _b.call(_a);
186
+ if ((debug === null || debug === void 0 ? void 0 : debug.eventCount) !== undefined) {
187
+ counts[name] = debug.eventCount;
188
+ }
189
+ }
190
+ return counts;
191
+ }
192
+ /**
193
+ * Get debug info from all strategies
194
+ */
195
+ getStrategyDebugInfo() {
196
+ const debug = {};
197
+ for (const [name, config] of this.strategies) {
198
+ if (config.strategy.getDebugInfo) {
199
+ debug[name] = config.strategy.getDebugInfo();
200
+ }
201
+ }
202
+ return debug;
203
+ }
204
+ /**
205
+ * Get current confidence score (0-1)
206
+ * Represents how much interaction data we've collected
207
+ * Higher confidence = more reliable classification
208
+ */
209
+ getConfidence() {
210
+ return Math.min(1, this.confidenceScore);
211
+ }
212
+ /**
213
+ * Check if confidence is above threshold for reliable classification
214
+ * @param threshold - Minimum confidence (0-1), default 0.3
215
+ */
216
+ hasConfidentData(threshold = 0.3) {
217
+ return this.getConfidence() >= threshold;
218
+ }
219
+ /**
220
+ * Cleanup resources
221
+ */
222
+ destroy() {
223
+ this.stop();
224
+ this.strategies.clear();
225
+ }
226
+ /**
227
+ * Handle visibility change events
228
+ */
229
+ handleVisibilityChange() {
230
+ if (!this.isTracking)
231
+ return;
232
+ if (document.hidden) {
233
+ // Tab became hidden - pause detection
234
+ if (!this.isPausedByVisibility) {
235
+ this.isPausedByVisibility = true;
236
+ // Stop all strategies
237
+ for (const [_, config] of this.strategies) {
238
+ if (config.enabled) {
239
+ config.strategy.stop();
240
+ }
241
+ }
242
+ // Stop tick
243
+ this.stopTick();
244
+ }
245
+ }
246
+ else {
247
+ // Tab became visible - resume detection
248
+ if (this.isPausedByVisibility) {
249
+ this.isPausedByVisibility = false;
250
+ // Restart all enabled strategies
251
+ for (const [_, config] of this.strategies) {
252
+ if (config.enabled) {
253
+ config.strategy.start();
254
+ }
255
+ }
256
+ // Restart tick
257
+ this.startTick();
258
+ }
259
+ }
260
+ }
261
+ /**
262
+ * Start tick mechanism for strategies
263
+ */
264
+ startTick() {
265
+ if (this.tickInterval !== null)
266
+ return;
267
+ this.tickInterval = window.setInterval(() => {
268
+ const now = Date.now();
269
+ for (const [_, config] of this.strategies) {
270
+ if (config.enabled && config.strategy.onTick) {
271
+ config.strategy.onTick(now);
272
+ }
273
+ }
274
+ }, this.tickIntervalMs);
275
+ }
276
+ /**
277
+ * Stop tick mechanism
278
+ */
279
+ stopTick() {
280
+ if (this.tickInterval !== null) {
281
+ clearInterval(this.tickInterval);
282
+ this.tickInterval = null;
283
+ }
284
+ }
285
+ /**
286
+ * Calculate score using strategies
287
+ */
288
+ calculateStrategyScore() {
289
+ const factors = {};
290
+ const weights = {};
291
+ let totalWeight = 0;
292
+ let weightedSum = 0;
293
+ for (const [name, config] of this.strategies) {
294
+ if (!config.enabled)
295
+ continue;
296
+ const score = config.strategy.score();
297
+ factors[name] = score;
298
+ weights[name] = config.weight;
299
+ if (score !== undefined && score !== null) {
300
+ totalWeight += config.weight;
301
+ weightedSum += score * config.weight;
302
+ }
303
+ }
304
+ const overall = totalWeight > 0 ? weightedSum / totalWeight : 0.5;
305
+ return {
306
+ overall: Math.max(0, Math.min(1, overall)),
307
+ factors,
308
+ weights,
309
+ };
310
+ }
311
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Browser Build - Auto-initializing standalone bundle for CDN usage
3
+ *
4
+ * Usage:
5
+ * <script src="https://cdn.../behavior-detector.js"></script>
6
+ * <script>
7
+ * window.bdSettings = {
8
+ * strategies: {
9
+ * mouse: { weight: 0.3 },
10
+ * scroll: { weight: 0.15 },
11
+ * click: { weight: 0.3 },
12
+ * keyboard: { weight: 0.1 },
13
+ * environment: { weight: 0.08 },
14
+ * resize: { weight: 0.02 }
15
+ * },
16
+ * autoStart: true,
17
+ * checkMs: 2000,
18
+ * pauseOnHidden: true, // Auto-pause when tab hidden (default: true)
19
+ * onScore: (result) => {
20
+ * console.log('Human likelihood:', result.score);
21
+ * if (result.score < 0.3) {
22
+ * // Likely bot
23
+ * }
24
+ * },
25
+ * ifBot: (result) => {
26
+ * // Called when score < threshold
27
+ * console.warn('Bot detected:', result);
28
+ * },
29
+ * botThreshold: 0.3
30
+ * }
31
+ * </script>
32
+ */
33
+ export {};