@axeptio/behavior-detection 1.1.1 → 1.2.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.
@@ -15,6 +15,9 @@ export declare class BehaviorDetector {
15
15
  private confidenceScore;
16
16
  private readonly CONFIDENCE_TARGET;
17
17
  private readonly CONFIDENCE_DECAY;
18
+ private scoreMemory;
19
+ private scoreFloor;
20
+ private lastEventTime;
18
21
  constructor(tickOptions?: TickOptions);
19
22
  /**
20
23
  * Add a detection strategy
@@ -46,6 +46,7 @@ module.exports = __toCommonJS(index_exports);
46
46
  // dist/esm/behavior-detector.js
47
47
  var BehaviorDetector = class {
48
48
  constructor(tickOptions) {
49
+ var _a, _b, _c, _d;
49
50
  this.strategies = /* @__PURE__ */ new Map();
50
51
  this.isTracking = false;
51
52
  this.isPausedByVisibility = false;
@@ -56,12 +57,21 @@ var BehaviorDetector = class {
56
57
  this.confidenceScore = 0;
57
58
  this.CONFIDENCE_TARGET = 1;
58
59
  this.CONFIDENCE_DECAY = 0.95;
60
+ this.scoreFloor = 1;
61
+ this.lastEventTime = 0;
59
62
  if (tickOptions === null || tickOptions === void 0 ? void 0 : tickOptions.interval) {
60
63
  this.tickIntervalMs = tickOptions.interval;
61
64
  }
62
65
  if ((tickOptions === null || tickOptions === void 0 ? void 0 : tickOptions.pauseOnHidden) !== void 0) {
63
66
  this.pauseOnHidden = tickOptions.pauseOnHidden;
64
67
  }
68
+ const mem = tickOptions === null || tickOptions === void 0 ? void 0 : tickOptions.scoreMemory;
69
+ this.scoreMemory = {
70
+ enabled: (_a = mem === null || mem === void 0 ? void 0 : mem.enabled) !== null && _a !== void 0 ? _a : true,
71
+ inactivityThreshold: (_b = mem === null || mem === void 0 ? void 0 : mem.inactivityThreshold) !== null && _b !== void 0 ? _b : 2e3,
72
+ maxRecovery: (_c = mem === null || mem === void 0 ? void 0 : mem.maxRecovery) !== null && _c !== void 0 ? _c : 0.1,
73
+ recoveryPerEvent: (_d = mem === null || mem === void 0 ? void 0 : mem.recoveryPerEvent) !== null && _d !== void 0 ? _d : 0.02
74
+ };
65
75
  }
66
76
  /**
67
77
  * Add a detection strategy
@@ -90,6 +100,10 @@ var BehaviorDetector = class {
90
100
  this.confidenceScore *= this.CONFIDENCE_DECAY;
91
101
  const contribution = event.weight * strategyWeight;
92
102
  this.confidenceScore = Math.min(this.CONFIDENCE_TARGET, this.confidenceScore + contribution);
103
+ this.lastEventTime = Date.now();
104
+ if (this.scoreMemory.enabled) {
105
+ this.scoreFloor = Math.min(1, this.scoreFloor + this.scoreMemory.recoveryPerEvent);
106
+ }
93
107
  }
94
108
  /**
95
109
  * Remove a detection strategy
@@ -170,6 +184,8 @@ var BehaviorDetector = class {
170
184
  */
171
185
  reset() {
172
186
  this.confidenceScore = 0;
187
+ this.scoreFloor = 1;
188
+ this.lastEventTime = 0;
173
189
  for (const [_, config] of this.strategies) {
174
190
  config.strategy.reset();
175
191
  }
@@ -179,8 +195,19 @@ var BehaviorDetector = class {
179
195
  */
180
196
  async score(options = {}) {
181
197
  const breakdown = this.calculateStrategyScore();
198
+ let effectiveScore = breakdown.overall;
199
+ if (this.scoreMemory.enabled && this.lastEventTime > 0) {
200
+ if (effectiveScore < this.scoreFloor) {
201
+ this.scoreFloor = effectiveScore;
202
+ }
203
+ const timeSinceEvent = Date.now() - this.lastEventTime;
204
+ if (timeSinceEvent > this.scoreMemory.inactivityThreshold) {
205
+ const cap = this.scoreFloor + this.scoreMemory.maxRecovery;
206
+ effectiveScore = Math.min(effectiveScore, cap);
207
+ }
208
+ }
182
209
  const result = {
183
- score: breakdown.overall
210
+ score: effectiveScore
184
211
  };
185
212
  if (options.breakdown) {
186
213
  result.breakdown = breakdown;
@@ -398,7 +425,7 @@ var MouseStrategy = class extends BaseStrategy {
398
425
  this.lastPosition = null;
399
426
  this.lastAngle = 0;
400
427
  this.cumulativeAngle = 0;
401
- this.rollingWindowMs = 5e3;
428
+ this.rollingWindowMs = 3e4;
402
429
  this.listener = null;
403
430
  this.leaveListener = null;
404
431
  this.enterListener = null;
@@ -681,7 +708,7 @@ var ScrollStrategy = class extends BaseStrategy {
681
708
  this.velocitySeries = [];
682
709
  this.lastScrollY = null;
683
710
  this.lastTimestamp = 0;
684
- this.rollingWindowMs = 5e3;
711
+ this.rollingWindowMs = 3e4;
685
712
  this.documentHeight = 1;
686
713
  this.listener = null;
687
714
  this.isActive = false;
@@ -15,6 +15,9 @@ export declare class BehaviorDetector {
15
15
  private confidenceScore;
16
16
  private readonly CONFIDENCE_TARGET;
17
17
  private readonly CONFIDENCE_DECAY;
18
+ private scoreMemory;
19
+ private scoreFloor;
20
+ private lastEventTime;
18
21
  constructor(tickOptions?: TickOptions);
19
22
  /**
20
23
  * Add a detection strategy
@@ -4,6 +4,7 @@
4
4
  */
5
5
  export class BehaviorDetector {
6
6
  constructor(tickOptions) {
7
+ var _a, _b, _c, _d;
7
8
  // Strategy mode
8
9
  this.strategies = new Map();
9
10
  this.isTracking = false;
@@ -16,12 +17,22 @@ export class BehaviorDetector {
16
17
  this.confidenceScore = 0;
17
18
  this.CONFIDENCE_TARGET = 1.0; // Target confidence for reliable classification
18
19
  this.CONFIDENCE_DECAY = 0.95; // Per event decay to prevent infinite growth
20
+ this.scoreFloor = 1.0;
21
+ this.lastEventTime = 0;
19
22
  if (tickOptions === null || tickOptions === void 0 ? void 0 : tickOptions.interval) {
20
23
  this.tickIntervalMs = tickOptions.interval;
21
24
  }
22
25
  if ((tickOptions === null || tickOptions === void 0 ? void 0 : tickOptions.pauseOnHidden) !== undefined) {
23
26
  this.pauseOnHidden = tickOptions.pauseOnHidden;
24
27
  }
28
+ // Score memory defaults
29
+ const mem = tickOptions === null || tickOptions === void 0 ? void 0 : tickOptions.scoreMemory;
30
+ this.scoreMemory = {
31
+ enabled: (_a = mem === null || mem === void 0 ? void 0 : mem.enabled) !== null && _a !== void 0 ? _a : true,
32
+ inactivityThreshold: (_b = mem === null || mem === void 0 ? void 0 : mem.inactivityThreshold) !== null && _b !== void 0 ? _b : 2000,
33
+ maxRecovery: (_c = mem === null || mem === void 0 ? void 0 : mem.maxRecovery) !== null && _c !== void 0 ? _c : 0.1,
34
+ recoveryPerEvent: (_d = mem === null || mem === void 0 ? void 0 : mem.recoveryPerEvent) !== null && _d !== void 0 ? _d : 0.02,
35
+ };
25
36
  }
26
37
  /**
27
38
  * Add a detection strategy
@@ -55,6 +66,11 @@ export class BehaviorDetector {
55
66
  // eventWeight (0-1) * strategyWeight (e.g. 0.3 for mouse) = contribution
56
67
  const contribution = event.weight * strategyWeight;
57
68
  this.confidenceScore = Math.min(this.CONFIDENCE_TARGET, this.confidenceScore + contribution);
69
+ // Score memory: track activity and slowly raise the floor
70
+ this.lastEventTime = Date.now();
71
+ if (this.scoreMemory.enabled) {
72
+ this.scoreFloor = Math.min(1.0, this.scoreFloor + this.scoreMemory.recoveryPerEvent);
73
+ }
58
74
  }
59
75
  /**
60
76
  * Remove a detection strategy
@@ -144,6 +160,8 @@ export class BehaviorDetector {
144
160
  */
145
161
  reset() {
146
162
  this.confidenceScore = 0;
163
+ this.scoreFloor = 1.0;
164
+ this.lastEventTime = 0;
147
165
  for (const [_, config] of this.strategies) {
148
166
  config.strategy.reset();
149
167
  }
@@ -153,8 +171,22 @@ export class BehaviorDetector {
153
171
  */
154
172
  async score(options = {}) {
155
173
  const breakdown = this.calculateStrategyScore();
174
+ let effectiveScore = breakdown.overall;
175
+ // Score memory: cap recovery during inactivity
176
+ if (this.scoreMemory.enabled && this.lastEventTime > 0) {
177
+ // Track the lowest score observed
178
+ if (effectiveScore < this.scoreFloor) {
179
+ this.scoreFloor = effectiveScore;
180
+ }
181
+ // If inactive, prevent the score from recovering freely
182
+ const timeSinceEvent = Date.now() - this.lastEventTime;
183
+ if (timeSinceEvent > this.scoreMemory.inactivityThreshold) {
184
+ const cap = this.scoreFloor + this.scoreMemory.maxRecovery;
185
+ effectiveScore = Math.min(effectiveScore, cap);
186
+ }
187
+ }
156
188
  const result = {
157
- score: breakdown.overall,
189
+ score: effectiveScore,
158
190
  };
159
191
  if (options.breakdown) {
160
192
  result.breakdown = breakdown;
@@ -32,7 +32,7 @@
32
32
  * ```
33
33
  */
34
34
  export { BehaviorDetector } from './behavior-detector.js';
35
- export type { DetectionStrategy, StrategyConfig } from './strategy.js';
35
+ export type { DetectionStrategy, StrategyConfig, ScoreMemoryOptions } from './strategy.js';
36
36
  export { Mouse, Scroll, Click, Tap, Keyboard, Environment, Resize, Timing, Visibility, MouseStrategy, ScrollStrategy, ClickStrategy, TapStrategy, KeyboardStrategy, EnvironmentStrategy, ResizeStrategy, TimingStrategy, VisibilityStrategy, } from './strategies/index.js';
37
37
  export type { BehaviorSettings, ScoreOptions, ScoreResult, ScoreBreakdown, TrackedEvent, EventType, ScoringFunction, } from './types.js';
38
38
  export { DEFAULT_SETTINGS } from './types.js';
@@ -20,7 +20,7 @@ export class MouseStrategy extends BaseStrategy {
20
20
  this.lastPosition = null;
21
21
  this.lastAngle = 0;
22
22
  this.cumulativeAngle = 0;
23
- this.rollingWindowMs = 5000;
23
+ this.rollingWindowMs = 30000;
24
24
  this.listener = null;
25
25
  this.leaveListener = null;
26
26
  this.enterListener = null;
@@ -14,7 +14,7 @@ export class ScrollStrategy extends BaseStrategy {
14
14
  this.velocitySeries = [];
15
15
  this.lastScrollY = null;
16
16
  this.lastTimestamp = 0;
17
- this.rollingWindowMs = 5000;
17
+ this.rollingWindowMs = 30000;
18
18
  this.documentHeight = 1;
19
19
  this.listener = null;
20
20
  this.isActive = false;
@@ -104,4 +104,37 @@ export interface TickOptions {
104
104
  * Default: true (recommended for better performance and battery life)
105
105
  */
106
106
  pauseOnHidden?: boolean;
107
+ /**
108
+ * Score memory options to prevent score recovery during inactivity.
109
+ * When enabled, the detector remembers the lowest score observed and prevents
110
+ * the score from jumping back up when behavioral data expires from rolling windows.
111
+ * Default: enabled with sensible defaults
112
+ */
113
+ scoreMemory?: ScoreMemoryOptions;
114
+ }
115
+ export interface ScoreMemoryOptions {
116
+ /**
117
+ * Enable score memory (default: true)
118
+ * When enabled, the detector tracks the lowest score observed and caps
119
+ * score recovery during periods of inactivity.
120
+ */
121
+ enabled: boolean;
122
+ /**
123
+ * Milliseconds of inactivity before the score cap kicks in (default: 2000)
124
+ * If no strategy events are received for this duration, the score cannot
125
+ * recover freely — it's capped near the lowest observed score.
126
+ */
127
+ inactivityThreshold?: number;
128
+ /**
129
+ * Maximum score increase allowed above the floor during inactivity (default: 0.1)
130
+ * During inactivity, effective score = min(rawScore, floor + maxRecovery)
131
+ */
132
+ maxRecovery?: number;
133
+ /**
134
+ * How much the floor rises per strategy event (default: 0.02)
135
+ * Each incoming event nudges the floor upward, allowing genuine human
136
+ * activity to eventually recover the score. At 0.02, it takes ~15 events
137
+ * to recover 0.3 points of score.
138
+ */
139
+ recoveryPerEvent?: number;
107
140
  }
package/dist/index.d.ts CHANGED
@@ -32,7 +32,7 @@
32
32
  * ```
33
33
  */
34
34
  export { BehaviorDetector } from './behavior-detector.js';
35
- export type { DetectionStrategy, StrategyConfig } from './strategy.js';
35
+ export type { DetectionStrategy, StrategyConfig, ScoreMemoryOptions } from './strategy.js';
36
36
  export { Mouse, Scroll, Click, Tap, Keyboard, Environment, Resize, Timing, Visibility, MouseStrategy, ScrollStrategy, ClickStrategy, TapStrategy, KeyboardStrategy, EnvironmentStrategy, ResizeStrategy, TimingStrategy, VisibilityStrategy, } from './strategies/index.js';
37
37
  export type { BehaviorSettings, ScoreOptions, ScoreResult, ScoreBreakdown, TrackedEvent, EventType, ScoringFunction, } from './types.js';
38
38
  export { DEFAULT_SETTINGS } from './types.js';
@@ -104,4 +104,37 @@ export interface TickOptions {
104
104
  * Default: true (recommended for better performance and battery life)
105
105
  */
106
106
  pauseOnHidden?: boolean;
107
+ /**
108
+ * Score memory options to prevent score recovery during inactivity.
109
+ * When enabled, the detector remembers the lowest score observed and prevents
110
+ * the score from jumping back up when behavioral data expires from rolling windows.
111
+ * Default: enabled with sensible defaults
112
+ */
113
+ scoreMemory?: ScoreMemoryOptions;
114
+ }
115
+ export interface ScoreMemoryOptions {
116
+ /**
117
+ * Enable score memory (default: true)
118
+ * When enabled, the detector tracks the lowest score observed and caps
119
+ * score recovery during periods of inactivity.
120
+ */
121
+ enabled: boolean;
122
+ /**
123
+ * Milliseconds of inactivity before the score cap kicks in (default: 2000)
124
+ * If no strategy events are received for this duration, the score cannot
125
+ * recover freely — it's capped near the lowest observed score.
126
+ */
127
+ inactivityThreshold?: number;
128
+ /**
129
+ * Maximum score increase allowed above the floor during inactivity (default: 0.1)
130
+ * During inactivity, effective score = min(rawScore, floor + maxRecovery)
131
+ */
132
+ maxRecovery?: number;
133
+ /**
134
+ * How much the floor rises per strategy event (default: 0.02)
135
+ * Each incoming event nudges the floor upward, allowing genuine human
136
+ * activity to eventually recover the score. At 0.02, it takes ~15 events
137
+ * to recover 0.3 points of score.
138
+ */
139
+ recoveryPerEvent?: number;
107
140
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axeptio/behavior-detection",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Lightweight behavior detection library to assess human likelihood of user sessions",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.cjs",