@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.
- package/README.md +828 -0
- package/dist/behavior-detection.esm.min.js +2 -0
- package/dist/behavior-detection.esm.min.js.map +7 -0
- package/dist/behavior-detection.min.js +2 -0
- package/dist/behavior-detection.min.js.map +7 -0
- package/dist/behavior-detector.d.ts +102 -0
- package/dist/browser.d.ts +33 -0
- package/dist/cjs/behavior-detector.d.ts +102 -0
- package/dist/cjs/behavior-detector.js +315 -0
- package/dist/cjs/browser.d.ts +33 -0
- package/dist/cjs/browser.js +226 -0
- package/dist/cjs/index.d.ts +38 -0
- package/dist/cjs/index.js +55 -0
- package/dist/cjs/math-utils.d.ts +84 -0
- package/dist/cjs/math-utils.js +141 -0
- package/dist/cjs/strategies/click.d.ts +39 -0
- package/dist/cjs/strategies/click.js +173 -0
- package/dist/cjs/strategies/environment.d.ts +52 -0
- package/dist/cjs/strategies/environment.js +148 -0
- package/dist/cjs/strategies/index.d.ts +18 -0
- package/dist/cjs/strategies/index.js +36 -0
- package/dist/cjs/strategies/keyboard.d.ts +43 -0
- package/dist/cjs/strategies/keyboard.js +233 -0
- package/dist/cjs/strategies/mouse.d.ts +39 -0
- package/dist/cjs/strategies/mouse.js +159 -0
- package/dist/cjs/strategies/resize.d.ts +21 -0
- package/dist/cjs/strategies/resize.js +97 -0
- package/dist/cjs/strategies/scroll.d.ts +37 -0
- package/dist/cjs/strategies/scroll.js +149 -0
- package/dist/cjs/strategies/tap.d.ts +38 -0
- package/dist/cjs/strategies/tap.js +214 -0
- package/dist/cjs/strategy.d.ts +107 -0
- package/dist/cjs/strategy.js +33 -0
- package/dist/cjs/types.d.ts +168 -0
- package/dist/cjs/types.js +26 -0
- package/dist/esm/behavior-detector.d.ts +102 -0
- package/dist/esm/behavior-detector.js +311 -0
- package/dist/esm/browser.d.ts +33 -0
- package/dist/esm/browser.js +224 -0
- package/dist/esm/index.d.ts +38 -0
- package/dist/esm/index.js +36 -0
- package/dist/esm/math-utils.d.ts +84 -0
- package/dist/esm/math-utils.js +127 -0
- package/dist/esm/strategies/click.d.ts +39 -0
- package/dist/esm/strategies/click.js +169 -0
- package/dist/esm/strategies/environment.d.ts +52 -0
- package/dist/esm/strategies/environment.js +144 -0
- package/dist/esm/strategies/index.d.ts +18 -0
- package/dist/esm/strategies/index.js +19 -0
- package/dist/esm/strategies/keyboard.d.ts +43 -0
- package/dist/esm/strategies/keyboard.js +229 -0
- package/dist/esm/strategies/mouse.d.ts +39 -0
- package/dist/esm/strategies/mouse.js +155 -0
- package/dist/esm/strategies/resize.d.ts +21 -0
- package/dist/esm/strategies/resize.js +93 -0
- package/dist/esm/strategies/scroll.d.ts +37 -0
- package/dist/esm/strategies/scroll.js +145 -0
- package/dist/esm/strategies/tap.d.ts +38 -0
- package/dist/esm/strategies/tap.js +210 -0
- package/dist/esm/strategy.d.ts +107 -0
- package/dist/esm/strategy.js +29 -0
- package/dist/esm/types.d.ts +168 -0
- package/dist/esm/types.js +23 -0
- package/dist/index.d.ts +38 -0
- package/dist/math-utils.d.ts +84 -0
- package/dist/strategies/click.d.ts +39 -0
- package/dist/strategies/environment.d.ts +52 -0
- package/dist/strategies/index.d.ts +18 -0
- package/dist/strategies/keyboard.d.ts +43 -0
- package/dist/strategies/mouse.d.ts +39 -0
- package/dist/strategies/resize.d.ts +21 -0
- package/dist/strategies/scroll.d.ts +37 -0
- package/dist/strategies/tap.d.ts +38 -0
- package/dist/strategy.d.ts +107 -0
- package/dist/types.d.ts +168 -0
- package/package.json +60 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BehaviorDetector = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Main class for behavior detection
|
|
6
|
+
* Uses modular strategy pattern for tree-shakeable, autonomous detection modules
|
|
7
|
+
*/
|
|
8
|
+
class BehaviorDetector {
|
|
9
|
+
constructor(tickOptions) {
|
|
10
|
+
// Strategy mode
|
|
11
|
+
this.strategies = new Map();
|
|
12
|
+
this.isTracking = false;
|
|
13
|
+
this.isPausedByVisibility = false;
|
|
14
|
+
this.tickInterval = null;
|
|
15
|
+
this.tickIntervalMs = 1000; // Default: 1 second
|
|
16
|
+
this.pauseOnHidden = true;
|
|
17
|
+
this.visibilityChangeHandler = null;
|
|
18
|
+
// Confidence tracking
|
|
19
|
+
this.confidenceScore = 0;
|
|
20
|
+
this.CONFIDENCE_TARGET = 1.0; // Target confidence for reliable classification
|
|
21
|
+
this.CONFIDENCE_DECAY = 0.95; // Per event decay to prevent infinite growth
|
|
22
|
+
if (tickOptions === null || tickOptions === void 0 ? void 0 : tickOptions.interval) {
|
|
23
|
+
this.tickIntervalMs = tickOptions.interval;
|
|
24
|
+
}
|
|
25
|
+
if ((tickOptions === null || tickOptions === void 0 ? void 0 : tickOptions.pauseOnHidden) !== undefined) {
|
|
26
|
+
this.pauseOnHidden = tickOptions.pauseOnHidden;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Add a detection strategy
|
|
31
|
+
*/
|
|
32
|
+
addStrategy(strategy, weight) {
|
|
33
|
+
const config = {
|
|
34
|
+
strategy,
|
|
35
|
+
weight: weight !== null && weight !== void 0 ? weight : strategy.defaultWeight,
|
|
36
|
+
enabled: true,
|
|
37
|
+
};
|
|
38
|
+
this.strategies.set(strategy.name, config);
|
|
39
|
+
// Set up event callback for confidence tracking
|
|
40
|
+
if (strategy.setEventCallback) {
|
|
41
|
+
strategy.setEventCallback((event) => {
|
|
42
|
+
this.onStrategyEvent(event, config.weight);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
// If already tracking, start this strategy immediately
|
|
46
|
+
if (this.isTracking) {
|
|
47
|
+
strategy.start();
|
|
48
|
+
}
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Handle event from strategy - update confidence
|
|
53
|
+
*/
|
|
54
|
+
onStrategyEvent(event, strategyWeight) {
|
|
55
|
+
// Apply decay to existing confidence to prevent infinite growth
|
|
56
|
+
this.confidenceScore *= this.CONFIDENCE_DECAY;
|
|
57
|
+
// Add weighted contribution from this event
|
|
58
|
+
// eventWeight (0-1) * strategyWeight (e.g. 0.3 for mouse) = contribution
|
|
59
|
+
const contribution = event.weight * strategyWeight;
|
|
60
|
+
this.confidenceScore = Math.min(this.CONFIDENCE_TARGET, this.confidenceScore + contribution);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Remove a detection strategy
|
|
64
|
+
*/
|
|
65
|
+
removeStrategy(name) {
|
|
66
|
+
const config = this.strategies.get(name);
|
|
67
|
+
if (config) {
|
|
68
|
+
if (this.isTracking) {
|
|
69
|
+
config.strategy.stop();
|
|
70
|
+
}
|
|
71
|
+
this.strategies.delete(name);
|
|
72
|
+
}
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Enable/disable a strategy
|
|
77
|
+
*/
|
|
78
|
+
setStrategyEnabled(name, enabled) {
|
|
79
|
+
const config = this.strategies.get(name);
|
|
80
|
+
if (config) {
|
|
81
|
+
config.enabled = enabled;
|
|
82
|
+
// If tracking and being disabled, stop it
|
|
83
|
+
if (!enabled && this.isTracking) {
|
|
84
|
+
config.strategy.stop();
|
|
85
|
+
}
|
|
86
|
+
// If tracking and being enabled, start it
|
|
87
|
+
if (enabled && this.isTracking) {
|
|
88
|
+
config.strategy.start();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get all registered strategies
|
|
95
|
+
*/
|
|
96
|
+
getStrategies() {
|
|
97
|
+
return this.strategies;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Start tracking
|
|
101
|
+
*/
|
|
102
|
+
start() {
|
|
103
|
+
if (this.isTracking)
|
|
104
|
+
return;
|
|
105
|
+
this.isTracking = true;
|
|
106
|
+
// Set up visibility change listener
|
|
107
|
+
if (this.pauseOnHidden && typeof document !== 'undefined') {
|
|
108
|
+
this.visibilityChangeHandler = this.handleVisibilityChange.bind(this);
|
|
109
|
+
document.addEventListener('visibilitychange', this.visibilityChangeHandler);
|
|
110
|
+
// Check initial visibility state
|
|
111
|
+
if (document.hidden) {
|
|
112
|
+
this.isPausedByVisibility = true;
|
|
113
|
+
return; // Don't start strategies if already hidden
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Start all enabled strategies
|
|
117
|
+
for (const [_, config] of this.strategies) {
|
|
118
|
+
if (config.enabled) {
|
|
119
|
+
config.strategy.start();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Start tick mechanism
|
|
123
|
+
this.startTick();
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Stop tracking
|
|
127
|
+
*/
|
|
128
|
+
stop() {
|
|
129
|
+
if (!this.isTracking)
|
|
130
|
+
return;
|
|
131
|
+
this.isTracking = false;
|
|
132
|
+
this.isPausedByVisibility = false;
|
|
133
|
+
// Remove visibility change listener
|
|
134
|
+
if (this.visibilityChangeHandler && typeof document !== 'undefined') {
|
|
135
|
+
document.removeEventListener('visibilitychange', this.visibilityChangeHandler);
|
|
136
|
+
this.visibilityChangeHandler = null;
|
|
137
|
+
}
|
|
138
|
+
// Stop all strategies
|
|
139
|
+
for (const [_, config] of this.strategies) {
|
|
140
|
+
config.strategy.stop();
|
|
141
|
+
}
|
|
142
|
+
// Stop tick
|
|
143
|
+
this.stopTick();
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Reset all data
|
|
147
|
+
*/
|
|
148
|
+
reset() {
|
|
149
|
+
this.confidenceScore = 0;
|
|
150
|
+
for (const [_, config] of this.strategies) {
|
|
151
|
+
config.strategy.reset();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Calculate human likelihood score
|
|
156
|
+
*/
|
|
157
|
+
async score(options = {}) {
|
|
158
|
+
const breakdown = this.calculateStrategyScore();
|
|
159
|
+
const result = {
|
|
160
|
+
score: breakdown.overall,
|
|
161
|
+
};
|
|
162
|
+
if (options.breakdown) {
|
|
163
|
+
result.breakdown = breakdown;
|
|
164
|
+
}
|
|
165
|
+
// Note: auditTrail not available in strategy mode
|
|
166
|
+
// Each strategy manages its own data independently
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Check if currently tracking
|
|
171
|
+
*/
|
|
172
|
+
isActive() {
|
|
173
|
+
return this.isTracking;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Check if currently paused due to tab visibility
|
|
177
|
+
*/
|
|
178
|
+
isPaused() {
|
|
179
|
+
return this.isPausedByVisibility;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get event count from all strategies
|
|
183
|
+
*/
|
|
184
|
+
getEventCount() {
|
|
185
|
+
var _a, _b;
|
|
186
|
+
const counts = {};
|
|
187
|
+
for (const [name, config] of this.strategies) {
|
|
188
|
+
const debug = (_b = (_a = config.strategy).getDebugInfo) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
189
|
+
if ((debug === null || debug === void 0 ? void 0 : debug.eventCount) !== undefined) {
|
|
190
|
+
counts[name] = debug.eventCount;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return counts;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Get debug info from all strategies
|
|
197
|
+
*/
|
|
198
|
+
getStrategyDebugInfo() {
|
|
199
|
+
const debug = {};
|
|
200
|
+
for (const [name, config] of this.strategies) {
|
|
201
|
+
if (config.strategy.getDebugInfo) {
|
|
202
|
+
debug[name] = config.strategy.getDebugInfo();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return debug;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Get current confidence score (0-1)
|
|
209
|
+
* Represents how much interaction data we've collected
|
|
210
|
+
* Higher confidence = more reliable classification
|
|
211
|
+
*/
|
|
212
|
+
getConfidence() {
|
|
213
|
+
return Math.min(1, this.confidenceScore);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Check if confidence is above threshold for reliable classification
|
|
217
|
+
* @param threshold - Minimum confidence (0-1), default 0.3
|
|
218
|
+
*/
|
|
219
|
+
hasConfidentData(threshold = 0.3) {
|
|
220
|
+
return this.getConfidence() >= threshold;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Cleanup resources
|
|
224
|
+
*/
|
|
225
|
+
destroy() {
|
|
226
|
+
this.stop();
|
|
227
|
+
this.strategies.clear();
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Handle visibility change events
|
|
231
|
+
*/
|
|
232
|
+
handleVisibilityChange() {
|
|
233
|
+
if (!this.isTracking)
|
|
234
|
+
return;
|
|
235
|
+
if (document.hidden) {
|
|
236
|
+
// Tab became hidden - pause detection
|
|
237
|
+
if (!this.isPausedByVisibility) {
|
|
238
|
+
this.isPausedByVisibility = true;
|
|
239
|
+
// Stop all strategies
|
|
240
|
+
for (const [_, config] of this.strategies) {
|
|
241
|
+
if (config.enabled) {
|
|
242
|
+
config.strategy.stop();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// Stop tick
|
|
246
|
+
this.stopTick();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
// Tab became visible - resume detection
|
|
251
|
+
if (this.isPausedByVisibility) {
|
|
252
|
+
this.isPausedByVisibility = false;
|
|
253
|
+
// Restart all enabled strategies
|
|
254
|
+
for (const [_, config] of this.strategies) {
|
|
255
|
+
if (config.enabled) {
|
|
256
|
+
config.strategy.start();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Restart tick
|
|
260
|
+
this.startTick();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Start tick mechanism for strategies
|
|
266
|
+
*/
|
|
267
|
+
startTick() {
|
|
268
|
+
if (this.tickInterval !== null)
|
|
269
|
+
return;
|
|
270
|
+
this.tickInterval = window.setInterval(() => {
|
|
271
|
+
const now = Date.now();
|
|
272
|
+
for (const [_, config] of this.strategies) {
|
|
273
|
+
if (config.enabled && config.strategy.onTick) {
|
|
274
|
+
config.strategy.onTick(now);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}, this.tickIntervalMs);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Stop tick mechanism
|
|
281
|
+
*/
|
|
282
|
+
stopTick() {
|
|
283
|
+
if (this.tickInterval !== null) {
|
|
284
|
+
clearInterval(this.tickInterval);
|
|
285
|
+
this.tickInterval = null;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Calculate score using strategies
|
|
290
|
+
*/
|
|
291
|
+
calculateStrategyScore() {
|
|
292
|
+
const factors = {};
|
|
293
|
+
const weights = {};
|
|
294
|
+
let totalWeight = 0;
|
|
295
|
+
let weightedSum = 0;
|
|
296
|
+
for (const [name, config] of this.strategies) {
|
|
297
|
+
if (!config.enabled)
|
|
298
|
+
continue;
|
|
299
|
+
const score = config.strategy.score();
|
|
300
|
+
factors[name] = score;
|
|
301
|
+
weights[name] = config.weight;
|
|
302
|
+
if (score !== undefined && score !== null) {
|
|
303
|
+
totalWeight += config.weight;
|
|
304
|
+
weightedSum += score * config.weight;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const overall = totalWeight > 0 ? weightedSum / totalWeight : 0.5;
|
|
308
|
+
return {
|
|
309
|
+
overall: Math.max(0, Math.min(1, overall)),
|
|
310
|
+
factors,
|
|
311
|
+
weights,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
exports.BehaviorDetector = BehaviorDetector;
|
|
@@ -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 {};
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Browser Build - Auto-initializing standalone bundle for CDN usage
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* <script src="https://cdn.../behavior-detector.js"></script>
|
|
7
|
+
* <script>
|
|
8
|
+
* window.bdSettings = {
|
|
9
|
+
* strategies: {
|
|
10
|
+
* mouse: { weight: 0.3 },
|
|
11
|
+
* scroll: { weight: 0.15 },
|
|
12
|
+
* click: { weight: 0.3 },
|
|
13
|
+
* keyboard: { weight: 0.1 },
|
|
14
|
+
* environment: { weight: 0.08 },
|
|
15
|
+
* resize: { weight: 0.02 }
|
|
16
|
+
* },
|
|
17
|
+
* autoStart: true,
|
|
18
|
+
* checkMs: 2000,
|
|
19
|
+
* pauseOnHidden: true, // Auto-pause when tab hidden (default: true)
|
|
20
|
+
* onScore: (result) => {
|
|
21
|
+
* console.log('Human likelihood:', result.score);
|
|
22
|
+
* if (result.score < 0.3) {
|
|
23
|
+
* // Likely bot
|
|
24
|
+
* }
|
|
25
|
+
* },
|
|
26
|
+
* ifBot: (result) => {
|
|
27
|
+
* // Called when score < threshold
|
|
28
|
+
* console.warn('Bot detected:', result);
|
|
29
|
+
* },
|
|
30
|
+
* botThreshold: 0.3
|
|
31
|
+
* }
|
|
32
|
+
* </script>
|
|
33
|
+
*/
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
const behavior_detector_1 = require("./behavior-detector");
|
|
36
|
+
const index_js_1 = require("./strategies/index.js");
|
|
37
|
+
/**
|
|
38
|
+
* Detect if the current device is mobile
|
|
39
|
+
*/
|
|
40
|
+
function isMobileDevice() {
|
|
41
|
+
const hasTouchScreen = navigator.maxTouchPoints > 0 || 'ontouchstart' in window;
|
|
42
|
+
const mobileUA = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
43
|
+
const smallScreen = window.innerWidth < 768 && window.innerHeight < 1024;
|
|
44
|
+
return (hasTouchScreen && smallScreen) || mobileUA;
|
|
45
|
+
}
|
|
46
|
+
class BehaviorDetectionAPI {
|
|
47
|
+
constructor() {
|
|
48
|
+
this.detector = null;
|
|
49
|
+
this.checkInterval = null;
|
|
50
|
+
this.settings = null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Initialize the detector with settings
|
|
54
|
+
*/
|
|
55
|
+
init(settings) {
|
|
56
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
|
|
57
|
+
if (this.detector) {
|
|
58
|
+
this.stop();
|
|
59
|
+
}
|
|
60
|
+
this.settings = settings;
|
|
61
|
+
this.detector = new behavior_detector_1.BehaviorDetector({
|
|
62
|
+
pauseOnHidden: settings.pauseOnHidden !== undefined ? settings.pauseOnHidden : true
|
|
63
|
+
});
|
|
64
|
+
// Detect if mobile to adjust strategy selection and weights
|
|
65
|
+
const isMobile = isMobileDevice();
|
|
66
|
+
const strategies = settings.strategies || {};
|
|
67
|
+
// Mobile-adjusted default weights
|
|
68
|
+
const defaultWeights = isMobile ? {
|
|
69
|
+
mouse: 0, // Disabled on mobile
|
|
70
|
+
scroll: 0.35, // Increased (more reliable on mobile)
|
|
71
|
+
click: 0, // Disabled on mobile (use tap instead)
|
|
72
|
+
tap: 0.35, // Mobile tap detection
|
|
73
|
+
keyboard: 0.15,
|
|
74
|
+
environment: 0.10,
|
|
75
|
+
resize: 0.05, // Orientation changes
|
|
76
|
+
} : {
|
|
77
|
+
mouse: 0.30,
|
|
78
|
+
scroll: 0.15,
|
|
79
|
+
click: 0.30,
|
|
80
|
+
tap: 0, // Disabled on desktop
|
|
81
|
+
keyboard: 0.10,
|
|
82
|
+
environment: 0.08,
|
|
83
|
+
resize: 0.02,
|
|
84
|
+
};
|
|
85
|
+
// Mouse - Desktop only
|
|
86
|
+
if (!isMobile && ((_a = strategies.mouse) === null || _a === void 0 ? void 0 : _a.enabled) !== false) {
|
|
87
|
+
const strategy = new index_js_1.MouseStrategy();
|
|
88
|
+
const weight = (_c = (_b = strategies.mouse) === null || _b === void 0 ? void 0 : _b.weight) !== null && _c !== void 0 ? _c : defaultWeights.mouse;
|
|
89
|
+
this.detector.addStrategy(strategy, weight);
|
|
90
|
+
}
|
|
91
|
+
// Scroll - Universal
|
|
92
|
+
if (((_d = strategies.scroll) === null || _d === void 0 ? void 0 : _d.enabled) !== false) {
|
|
93
|
+
const strategy = new index_js_1.ScrollStrategy();
|
|
94
|
+
const weight = (_f = (_e = strategies.scroll) === null || _e === void 0 ? void 0 : _e.weight) !== null && _f !== void 0 ? _f : defaultWeights.scroll;
|
|
95
|
+
this.detector.addStrategy(strategy, weight);
|
|
96
|
+
}
|
|
97
|
+
// Click - Desktop only
|
|
98
|
+
if (!isMobile && ((_g = strategies.click) === null || _g === void 0 ? void 0 : _g.enabled) !== false) {
|
|
99
|
+
const strategy = new index_js_1.ClickStrategy();
|
|
100
|
+
const weight = (_j = (_h = strategies.click) === null || _h === void 0 ? void 0 : _h.weight) !== null && _j !== void 0 ? _j : defaultWeights.click;
|
|
101
|
+
this.detector.addStrategy(strategy, weight);
|
|
102
|
+
}
|
|
103
|
+
// Tap - Mobile only
|
|
104
|
+
if (isMobile && ((_k = strategies.tap) === null || _k === void 0 ? void 0 : _k.enabled) !== false) {
|
|
105
|
+
const strategy = new index_js_1.TapStrategy();
|
|
106
|
+
const weight = (_m = (_l = strategies.tap) === null || _l === void 0 ? void 0 : _l.weight) !== null && _m !== void 0 ? _m : defaultWeights.tap;
|
|
107
|
+
this.detector.addStrategy(strategy, weight);
|
|
108
|
+
}
|
|
109
|
+
// Keyboard - Universal
|
|
110
|
+
if (((_o = strategies.keyboard) === null || _o === void 0 ? void 0 : _o.enabled) !== false) {
|
|
111
|
+
const strategy = new index_js_1.KeyboardStrategy();
|
|
112
|
+
const weight = (_q = (_p = strategies.keyboard) === null || _p === void 0 ? void 0 : _p.weight) !== null && _q !== void 0 ? _q : defaultWeights.keyboard;
|
|
113
|
+
this.detector.addStrategy(strategy, weight);
|
|
114
|
+
}
|
|
115
|
+
// Environment - Universal
|
|
116
|
+
if (((_r = strategies.environment) === null || _r === void 0 ? void 0 : _r.enabled) !== false) {
|
|
117
|
+
const strategy = new index_js_1.EnvironmentStrategy();
|
|
118
|
+
const weight = (_t = (_s = strategies.environment) === null || _s === void 0 ? void 0 : _s.weight) !== null && _t !== void 0 ? _t : defaultWeights.environment;
|
|
119
|
+
this.detector.addStrategy(strategy, weight);
|
|
120
|
+
}
|
|
121
|
+
// Resize - Universal
|
|
122
|
+
if (((_u = strategies.resize) === null || _u === void 0 ? void 0 : _u.enabled) !== false) {
|
|
123
|
+
const strategy = new index_js_1.ResizeStrategy();
|
|
124
|
+
const weight = (_w = (_v = strategies.resize) === null || _v === void 0 ? void 0 : _v.weight) !== null && _w !== void 0 ? _w : defaultWeights.resize;
|
|
125
|
+
this.detector.addStrategy(strategy, weight);
|
|
126
|
+
}
|
|
127
|
+
// Start if autoStart is enabled
|
|
128
|
+
if (settings.autoStart !== false) {
|
|
129
|
+
this.start();
|
|
130
|
+
}
|
|
131
|
+
// Set up periodic checking if checkMs is specified
|
|
132
|
+
if (settings.checkMs && settings.checkMs > 0) {
|
|
133
|
+
this.startPeriodicCheck(settings.checkMs);
|
|
134
|
+
}
|
|
135
|
+
// Call onReady callback
|
|
136
|
+
if (settings.onReady) {
|
|
137
|
+
settings.onReady(this);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Start detection
|
|
142
|
+
*/
|
|
143
|
+
start() {
|
|
144
|
+
if (!this.detector) {
|
|
145
|
+
console.warn('BehaviorDetector: Call init() before start()');
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
this.detector.start();
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Stop detection
|
|
152
|
+
*/
|
|
153
|
+
stop() {
|
|
154
|
+
if (this.detector) {
|
|
155
|
+
this.detector.stop();
|
|
156
|
+
}
|
|
157
|
+
if (this.checkInterval !== null) {
|
|
158
|
+
clearInterval(this.checkInterval);
|
|
159
|
+
this.checkInterval = null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get current score
|
|
164
|
+
*/
|
|
165
|
+
async score() {
|
|
166
|
+
if (!this.detector) {
|
|
167
|
+
console.warn('BehaviorDetector: Call init() before score()');
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
return this.detector.score({ breakdown: true });
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Start periodic checking
|
|
174
|
+
*/
|
|
175
|
+
startPeriodicCheck(intervalMs) {
|
|
176
|
+
if (this.checkInterval !== null) {
|
|
177
|
+
clearInterval(this.checkInterval);
|
|
178
|
+
}
|
|
179
|
+
this.checkInterval = window.setInterval(async () => {
|
|
180
|
+
var _a, _b;
|
|
181
|
+
const result = await this.score();
|
|
182
|
+
if (!result || !this.settings)
|
|
183
|
+
return;
|
|
184
|
+
// Call onScore callback
|
|
185
|
+
if (this.settings.onScore) {
|
|
186
|
+
this.settings.onScore(result);
|
|
187
|
+
}
|
|
188
|
+
// Check thresholds
|
|
189
|
+
const botThreshold = (_a = this.settings.botThreshold) !== null && _a !== void 0 ? _a : 0.3;
|
|
190
|
+
const humanThreshold = (_b = this.settings.humanThreshold) !== null && _b !== void 0 ? _b : 0.7;
|
|
191
|
+
if (result.score < botThreshold && this.settings.ifBot) {
|
|
192
|
+
this.settings.ifBot(result);
|
|
193
|
+
}
|
|
194
|
+
else if (result.score >= humanThreshold && this.settings.ifHuman) {
|
|
195
|
+
this.settings.ifHuman(result);
|
|
196
|
+
}
|
|
197
|
+
}, intervalMs);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Get the underlying detector instance for advanced usage
|
|
201
|
+
*/
|
|
202
|
+
getDetector() {
|
|
203
|
+
return this.detector;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Create global API instance
|
|
207
|
+
const api = new BehaviorDetectionAPI();
|
|
208
|
+
// Auto-initialize if settings are already present
|
|
209
|
+
if (typeof window !== 'undefined') {
|
|
210
|
+
// Expose API globally
|
|
211
|
+
window.BehaviorDetector = api;
|
|
212
|
+
// Check for settings on load
|
|
213
|
+
const checkAndInit = () => {
|
|
214
|
+
const settings = window.bdSettings;
|
|
215
|
+
if (settings) {
|
|
216
|
+
api.init(settings);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
// Try immediate init
|
|
220
|
+
checkAndInit();
|
|
221
|
+
// Also try after DOM ready
|
|
222
|
+
if (document.readyState === 'loading') {
|
|
223
|
+
document.addEventListener('DOMContentLoaded', checkAndInit);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// No exports needed for browser IIFE build - window.BehaviorDetector is sufficient
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @axeptio/behavior-detection
|
|
3
|
+
*
|
|
4
|
+
* Lightweight behavior detection library to assess human likelihood of user sessions
|
|
5
|
+
*
|
|
6
|
+
* @example Settings-based (all strategies included)
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { BehaviorDetector } from '@axeptio/behavior-detection';
|
|
9
|
+
*
|
|
10
|
+
* const detector = new BehaviorDetector({
|
|
11
|
+
* weights: {
|
|
12
|
+
* mouseMovement: 0.3,
|
|
13
|
+
* clickAccuracy: 0.3,
|
|
14
|
+
* },
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* detector.start();
|
|
18
|
+
* const result = await detector.score({ breakdown: true });
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @example Strategy-based (tree-shakeable, import only what you need)
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { BehaviorDetector, Mouse, Click, Keyboard } from '@axeptio/behavior-detection';
|
|
24
|
+
*
|
|
25
|
+
* const detector = new BehaviorDetector()
|
|
26
|
+
* .addStrategy(new Mouse())
|
|
27
|
+
* .addStrategy(new Click())
|
|
28
|
+
* .addStrategy(new Keyboard());
|
|
29
|
+
*
|
|
30
|
+
* detector.start();
|
|
31
|
+
* const result = await detector.score();
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export { BehaviorDetector } from './behavior-detector';
|
|
35
|
+
export type { DetectionStrategy, StrategyConfig } from './strategy';
|
|
36
|
+
export { Mouse, Scroll, Click, Tap, Keyboard, Environment, Resize, MouseStrategy, ScrollStrategy, ClickStrategy, TapStrategy, KeyboardStrategy, EnvironmentStrategy, ResizeStrategy, } from './strategies/index.js';
|
|
37
|
+
export type { BehaviorSettings, ScoreOptions, ScoreResult, ScoreBreakdown, TrackedEvent, EventType, ScoringFunction, } from './types';
|
|
38
|
+
export { DEFAULT_SETTINGS } from './types';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @axeptio/behavior-detection
|
|
4
|
+
*
|
|
5
|
+
* Lightweight behavior detection library to assess human likelihood of user sessions
|
|
6
|
+
*
|
|
7
|
+
* @example Settings-based (all strategies included)
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { BehaviorDetector } from '@axeptio/behavior-detection';
|
|
10
|
+
*
|
|
11
|
+
* const detector = new BehaviorDetector({
|
|
12
|
+
* weights: {
|
|
13
|
+
* mouseMovement: 0.3,
|
|
14
|
+
* clickAccuracy: 0.3,
|
|
15
|
+
* },
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* detector.start();
|
|
19
|
+
* const result = await detector.score({ breakdown: true });
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @example Strategy-based (tree-shakeable, import only what you need)
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { BehaviorDetector, Mouse, Click, Keyboard } from '@axeptio/behavior-detection';
|
|
25
|
+
*
|
|
26
|
+
* const detector = new BehaviorDetector()
|
|
27
|
+
* .addStrategy(new Mouse())
|
|
28
|
+
* .addStrategy(new Click())
|
|
29
|
+
* .addStrategy(new Keyboard());
|
|
30
|
+
*
|
|
31
|
+
* detector.start();
|
|
32
|
+
* const result = await detector.score();
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.DEFAULT_SETTINGS = exports.ResizeStrategy = exports.EnvironmentStrategy = exports.KeyboardStrategy = exports.TapStrategy = exports.ClickStrategy = exports.ScrollStrategy = exports.MouseStrategy = exports.Resize = exports.Environment = exports.Keyboard = exports.Tap = exports.Click = exports.Scroll = exports.Mouse = exports.BehaviorDetector = void 0;
|
|
37
|
+
var behavior_detector_1 = require("./behavior-detector");
|
|
38
|
+
Object.defineProperty(exports, "BehaviorDetector", { enumerable: true, get: function () { return behavior_detector_1.BehaviorDetector; } });
|
|
39
|
+
var index_js_1 = require("./strategies/index.js");
|
|
40
|
+
Object.defineProperty(exports, "Mouse", { enumerable: true, get: function () { return index_js_1.Mouse; } });
|
|
41
|
+
Object.defineProperty(exports, "Scroll", { enumerable: true, get: function () { return index_js_1.Scroll; } });
|
|
42
|
+
Object.defineProperty(exports, "Click", { enumerable: true, get: function () { return index_js_1.Click; } });
|
|
43
|
+
Object.defineProperty(exports, "Tap", { enumerable: true, get: function () { return index_js_1.Tap; } });
|
|
44
|
+
Object.defineProperty(exports, "Keyboard", { enumerable: true, get: function () { return index_js_1.Keyboard; } });
|
|
45
|
+
Object.defineProperty(exports, "Environment", { enumerable: true, get: function () { return index_js_1.Environment; } });
|
|
46
|
+
Object.defineProperty(exports, "Resize", { enumerable: true, get: function () { return index_js_1.Resize; } });
|
|
47
|
+
Object.defineProperty(exports, "MouseStrategy", { enumerable: true, get: function () { return index_js_1.MouseStrategy; } });
|
|
48
|
+
Object.defineProperty(exports, "ScrollStrategy", { enumerable: true, get: function () { return index_js_1.ScrollStrategy; } });
|
|
49
|
+
Object.defineProperty(exports, "ClickStrategy", { enumerable: true, get: function () { return index_js_1.ClickStrategy; } });
|
|
50
|
+
Object.defineProperty(exports, "TapStrategy", { enumerable: true, get: function () { return index_js_1.TapStrategy; } });
|
|
51
|
+
Object.defineProperty(exports, "KeyboardStrategy", { enumerable: true, get: function () { return index_js_1.KeyboardStrategy; } });
|
|
52
|
+
Object.defineProperty(exports, "EnvironmentStrategy", { enumerable: true, get: function () { return index_js_1.EnvironmentStrategy; } });
|
|
53
|
+
Object.defineProperty(exports, "ResizeStrategy", { enumerable: true, get: function () { return index_js_1.ResizeStrategy; } });
|
|
54
|
+
var types_1 = require("./types");
|
|
55
|
+
Object.defineProperty(exports, "DEFAULT_SETTINGS", { enumerable: true, get: function () { return types_1.DEFAULT_SETTINGS; } });
|