@purespectrum1/purescore-client 0.7.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 (136) hide show
  1. package/README.md +55 -0
  2. package/lib/application/auto-init.d.ts +44 -0
  3. package/lib/application/auto-init.js +245 -0
  4. package/lib/application/device-identifier.d.ts +116 -0
  5. package/lib/application/device-identifier.js +169 -0
  6. package/lib/domain/models/crosscutting/device-intelligence.d.ts +11 -0
  7. package/lib/domain/models/crosscutting/device-intelligence.js +18 -0
  8. package/lib/domain/models/crosscutting/index.d.ts +1 -0
  9. package/lib/domain/models/crosscutting/index.js +17 -0
  10. package/lib/domain/protocols/cache/get-storage.d.ts +8 -0
  11. package/lib/domain/protocols/cache/get-storage.js +2 -0
  12. package/lib/domain/protocols/cache/index.d.ts +3 -0
  13. package/lib/domain/protocols/cache/index.js +19 -0
  14. package/lib/domain/protocols/cache/remove-storage.d.ts +6 -0
  15. package/lib/domain/protocols/cache/remove-storage.js +2 -0
  16. package/lib/domain/protocols/cache/set-storage.d.ts +7 -0
  17. package/lib/domain/protocols/cache/set-storage.js +2 -0
  18. package/lib/domain/protocols/http/http-client.d.ts +23 -0
  19. package/lib/domain/protocols/http/http-client.js +13 -0
  20. package/lib/domain/protocols/services/behavioural-biometrics/cursor-behavior.d.ts +63 -0
  21. package/lib/domain/protocols/services/behavioural-biometrics/cursor-behavior.js +2 -0
  22. package/lib/domain/protocols/services/behavioural-biometrics/index.d.ts +3 -0
  23. package/lib/domain/protocols/services/behavioural-biometrics/index.js +19 -0
  24. package/lib/domain/protocols/services/behavioural-biometrics/keystorke-metrics.d.ts +35 -0
  25. package/lib/domain/protocols/services/behavioural-biometrics/keystorke-metrics.js +2 -0
  26. package/lib/domain/protocols/services/behavioural-biometrics/touch-behavior.d.ts +88 -0
  27. package/lib/domain/protocols/services/behavioural-biometrics/touch-behavior.js +2 -0
  28. package/lib/domain/protocols/services/device-fingerprint/display.d.ts +22 -0
  29. package/lib/domain/protocols/services/device-fingerprint/display.js +2 -0
  30. package/lib/domain/protocols/services/device-fingerprint/index.d.ts +3 -0
  31. package/lib/domain/protocols/services/device-fingerprint/index.js +19 -0
  32. package/lib/domain/protocols/services/device-fingerprint/navigator.d.ts +63 -0
  33. package/lib/domain/protocols/services/device-fingerprint/navigator.js +2 -0
  34. package/lib/domain/protocols/services/device-fingerprint/utils.d.ts +40 -0
  35. package/lib/domain/protocols/services/device-fingerprint/utils.js +2 -0
  36. package/lib/domain/protocols/services/device-identifier.d.ts +96 -0
  37. package/lib/domain/protocols/services/device-identifier.js +2 -0
  38. package/lib/domain/protocols/services/device-intelligence.d.ts +5 -0
  39. package/lib/domain/protocols/services/device-intelligence.js +2 -0
  40. package/lib/domain/protocols/services/listeners.d.ts +9 -0
  41. package/lib/domain/protocols/services/listeners.js +2 -0
  42. package/lib/domain/protocols/services/single-script-injection/auto-init.d.ts +120 -0
  43. package/lib/domain/protocols/services/single-script-injection/auto-init.js +2 -0
  44. package/lib/domain/protocols/services/single-script-injection/index.d.ts +1 -0
  45. package/lib/domain/protocols/services/single-script-injection/index.js +17 -0
  46. package/lib/domain/services/behavioural-biometrics/collector.d.ts +94 -0
  47. package/lib/domain/services/behavioural-biometrics/collector.js +229 -0
  48. package/lib/domain/services/behavioural-biometrics/cursor-behavior.d.ts +87 -0
  49. package/lib/domain/services/behavioural-biometrics/cursor-behavior.js +369 -0
  50. package/lib/domain/services/behavioural-biometrics/index.d.ts +4 -0
  51. package/lib/domain/services/behavioural-biometrics/index.js +20 -0
  52. package/lib/domain/services/behavioural-biometrics/keystroke-metrics.d.ts +60 -0
  53. package/lib/domain/services/behavioural-biometrics/keystroke-metrics.js +213 -0
  54. package/lib/domain/services/behavioural-biometrics/touch-behavior.d.ts +125 -0
  55. package/lib/domain/services/behavioural-biometrics/touch-behavior.js +413 -0
  56. package/lib/domain/services/device-fingerprint/audio.d.ts +14 -0
  57. package/lib/domain/services/device-fingerprint/audio.js +109 -0
  58. package/lib/domain/services/device-fingerprint/canvas.d.ts +10 -0
  59. package/lib/domain/services/device-fingerprint/canvas.js +33 -0
  60. package/lib/domain/services/device-fingerprint/display.d.ts +49 -0
  61. package/lib/domain/services/device-fingerprint/display.js +88 -0
  62. package/lib/domain/services/device-fingerprint/hash.d.ts +14 -0
  63. package/lib/domain/services/device-fingerprint/hash.js +60 -0
  64. package/lib/domain/services/device-fingerprint/index.d.ts +8 -0
  65. package/lib/domain/services/device-fingerprint/index.js +24 -0
  66. package/lib/domain/services/device-fingerprint/navigator.d.ts +110 -0
  67. package/lib/domain/services/device-fingerprint/navigator.js +213 -0
  68. package/lib/domain/services/device-fingerprint/pointer-capability.d.ts +14 -0
  69. package/lib/domain/services/device-fingerprint/pointer-capability.js +42 -0
  70. package/lib/domain/services/device-fingerprint/storage.d.ts +44 -0
  71. package/lib/domain/services/device-fingerprint/storage.js +64 -0
  72. package/lib/domain/services/device-fingerprint/utils.d.ts +42 -0
  73. package/lib/domain/services/device-fingerprint/utils.js +95 -0
  74. package/lib/domain/services/device-intelligence.d.ts +10 -0
  75. package/lib/domain/services/device-intelligence.js +59 -0
  76. package/lib/domain/services/single-script-injection/checkpoint-scheduler.d.ts +37 -0
  77. package/lib/domain/services/single-script-injection/checkpoint-scheduler.js +94 -0
  78. package/lib/domain/services/single-script-injection/index.d.ts +3 -0
  79. package/lib/domain/services/single-script-injection/index.js +19 -0
  80. package/lib/domain/services/single-script-injection/response-delivery.d.ts +47 -0
  81. package/lib/domain/services/single-script-injection/response-delivery.js +115 -0
  82. package/lib/domain/services/single-script-injection/schedule-store.d.ts +69 -0
  83. package/lib/domain/services/single-script-injection/schedule-store.js +146 -0
  84. package/lib/domain/services/snapshot-store.d.ts +19 -0
  85. package/lib/domain/services/snapshot-store.js +36 -0
  86. package/lib/domain/services/spa-navigation.d.ts +15 -0
  87. package/lib/domain/services/spa-navigation.js +40 -0
  88. package/lib/index.d.ts +19 -0
  89. package/lib/index.js +27 -0
  90. package/lib/infra/cache/index.d.ts +3 -0
  91. package/lib/infra/cache/index.js +19 -0
  92. package/lib/infra/cache/local-storage-adapter.d.ts +8 -0
  93. package/lib/infra/cache/local-storage-adapter.js +14 -0
  94. package/lib/infra/cache/session-storage-adapter.d.ts +8 -0
  95. package/lib/infra/cache/session-storage-adapter.js +14 -0
  96. package/lib/infra/cache/web-storage-adapter.d.ts +13 -0
  97. package/lib/infra/cache/web-storage-adapter.js +33 -0
  98. package/lib/infra/http/axios-http-client.d.ts +4 -0
  99. package/lib/infra/http/axios-http-client.js +39 -0
  100. package/lib/validation/builder.d.ts +43 -0
  101. package/lib/validation/builder.js +85 -0
  102. package/lib/validation/composite.d.ts +13 -0
  103. package/lib/validation/composite.js +23 -0
  104. package/lib/validation/errors/index.d.ts +2 -0
  105. package/lib/validation/errors/index.js +18 -0
  106. package/lib/validation/errors/invalid-field-error.d.ts +7 -0
  107. package/lib/validation/errors/invalid-field-error.js +14 -0
  108. package/lib/validation/errors/required-field-error.d.ts +3 -0
  109. package/lib/validation/errors/required-field-error.js +10 -0
  110. package/lib/validation/index.d.ts +5 -0
  111. package/lib/validation/index.js +21 -0
  112. package/lib/validation/protocols/index.d.ts +1 -0
  113. package/lib/validation/protocols/index.js +17 -0
  114. package/lib/validation/protocols/validator.d.ts +11 -0
  115. package/lib/validation/protocols/validator.js +2 -0
  116. package/lib/validation/validators/behavioural-signal.d.ts +27 -0
  117. package/lib/validation/validators/behavioural-signal.js +56 -0
  118. package/lib/validation/validators/country-code.d.ts +14 -0
  119. package/lib/validation/validators/country-code.js +24 -0
  120. package/lib/validation/validators/global-function.d.ts +14 -0
  121. package/lib/validation/validators/global-function.js +29 -0
  122. package/lib/validation/validators/index.d.ts +9 -0
  123. package/lib/validation/validators/index.js +25 -0
  124. package/lib/validation/validators/interpolated-template.d.ts +14 -0
  125. package/lib/validation/validators/interpolated-template.js +28 -0
  126. package/lib/validation/validators/json-object.d.ts +14 -0
  127. package/lib/validation/validators/json-object.js +33 -0
  128. package/lib/validation/validators/language-code.d.ts +13 -0
  129. package/lib/validation/validators/language-code.js +23 -0
  130. package/lib/validation/validators/positive-number.d.ts +13 -0
  131. package/lib/validation/validators/positive-number.js +24 -0
  132. package/lib/validation/validators/required-string.d.ts +10 -0
  133. package/lib/validation/validators/required-string.js +19 -0
  134. package/lib/validation/validators/schedule-record.d.ts +26 -0
  135. package/lib/validation/validators/schedule-record.js +33 -0
  136. package/package.json +51 -0
@@ -0,0 +1,369 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CursorBehavior = void 0;
4
+ class CursorBehavior {
5
+ constructor() {
6
+ this.movements = [];
7
+ this.velocities = [];
8
+ this.accelerations = [];
9
+ this.scrolls = [];
10
+ this.lastPoint = null;
11
+ this.lastVelocity = null;
12
+ this.lastSampleTime = 0;
13
+ this.angleChangeCount = 0;
14
+ this.lastAngle = null;
15
+ this.hoverStart = null;
16
+ this.isTracking = false;
17
+ this.sampleRate = 50;
18
+ this.significantAngleChange = Math.PI / 6;
19
+ this.maxDataPoints = 500;
20
+ this.pruneInterval = 50;
21
+ this.pruneCounter = 0;
22
+ this.mouseMoveHandler = this.handleMouseMove.bind(this);
23
+ this.mouseDownHandler = this.handleMouseDown.bind(this);
24
+ this.wheelHandler = this.handleWheel.bind(this);
25
+ this.scrollHandler = this.handleScroll.bind(this);
26
+ }
27
+ /**
28
+ * Start tracking mouse behavior
29
+ */
30
+ startTracking(maxDataPoints) {
31
+ if (this.isTracking || typeof window === 'undefined') {
32
+ return;
33
+ }
34
+ this.maxDataPoints = maxDataPoints !== null && maxDataPoints !== void 0 ? maxDataPoints : 1000;
35
+ this.resetData();
36
+ this.isTracking = true;
37
+ window.addEventListener('mousemove', this.mouseMoveHandler);
38
+ window.addEventListener('mousedown', this.mouseDownHandler);
39
+ window.addEventListener('wheel', this.wheelHandler, { passive: true });
40
+ window.addEventListener('scroll', this.scrollHandler, { passive: true });
41
+ }
42
+ /**
43
+ * Stops tracking cursor behavior without clearing collected data.
44
+ */
45
+ stopTracking() {
46
+ if (!this.isTracking)
47
+ return;
48
+ this.isTracking = false;
49
+ window.removeEventListener('mousemove', this.mouseMoveHandler);
50
+ window.removeEventListener('mousedown', this.mouseDownHandler);
51
+ window.removeEventListener('wheel', this.wheelHandler);
52
+ window.removeEventListener('scroll', this.scrollHandler);
53
+ }
54
+ /**
55
+ * Returns true if this tracker currently has its DOM listeners attached.
56
+ */
57
+ hasActiveListeners() {
58
+ return this.isTracking;
59
+ }
60
+ /**
61
+ * Snapshot of raw event buffers for cross-page persistence.
62
+ */
63
+ getState() {
64
+ return {
65
+ movements: this.movements,
66
+ velocities: this.velocities,
67
+ accelerations: this.accelerations,
68
+ scrolls: this.scrolls,
69
+ angleChangeCount: this.angleChangeCount,
70
+ };
71
+ }
72
+ /**
73
+ * Restores raw event buffers from a snapshot. Transient fields (lastPoint,
74
+ * lastVelocity, hoverStart, etc.) are not restored — they only make sense
75
+ * within a continuous interaction sequence on a single page.
76
+ */
77
+ restoreState(state) {
78
+ if (state.movements)
79
+ this.movements = state.movements;
80
+ if (state.velocities)
81
+ this.velocities = state.velocities;
82
+ if (state.accelerations)
83
+ this.accelerations = state.accelerations;
84
+ if (state.scrolls)
85
+ this.scrolls = state.scrolls;
86
+ if (typeof state.angleChangeCount === 'number')
87
+ this.angleChangeCount = state.angleChangeCount;
88
+ }
89
+ /**
90
+ * Reset all collected data
91
+ */
92
+ resetData() {
93
+ this.movements = [];
94
+ this.velocities = [];
95
+ this.accelerations = [];
96
+ this.scrolls = [];
97
+ this.lastPoint = null;
98
+ this.lastVelocity = null;
99
+ this.lastSampleTime = 0;
100
+ this.angleChangeCount = 0;
101
+ this.lastAngle = null;
102
+ this.hoverStart = null;
103
+ this.pruneCounter = 0;
104
+ }
105
+ /**
106
+ * Get current behavioral metrics
107
+ */
108
+ getCursorBehaviorBiometrics() {
109
+ const averageSpeed = this.calculateAverageSpeed();
110
+ return {
111
+ movementMetrics: {
112
+ averageSpeed: averageSpeed,
113
+ maxSpeed: this.calculateMaxSpeed(),
114
+ minSpeed: this.calculateMinSpeed(),
115
+ speedStdDev: this.calculateSpeedStdDev(averageSpeed),
116
+ },
117
+ accelerationMetrics: {
118
+ averageAcceleration: this.calculateAverageAcceleration(),
119
+ maxAcceleration: this.calculateMaxAcceleration(),
120
+ decelerationCount: this.countDecelerations(),
121
+ accelerationCount: this.countAccelerations(),
122
+ },
123
+ trajectoryMetrics: {
124
+ averageCurvature: this.calculateAverageCurvature(),
125
+ straightLinePercentage: this.calculateStraightLinePercentage(),
126
+ averageJerkiness: this.calculateAverageJerkiness(),
127
+ angleChanges: this.angleChangeCount,
128
+ },
129
+ scrollMetrics: {
130
+ totalScrollDistance: this.calculateTotalScrollDistance(),
131
+ averageScrollVelocity: this.calculateAverageScrollVelocity(),
132
+ wheelScrollCount: this.scrolls.filter(s => s.type === 'wheel').length,
133
+ dragScrollCount: this.scrolls.filter(s => s.type === 'drag').length,
134
+ },
135
+ sessionInfo: {
136
+ totalMovements: this.movements.length,
137
+ },
138
+ };
139
+ }
140
+ handleMouseMove(event) {
141
+ const now = Date.now();
142
+ // Apply sample rate throttling
143
+ if (now - this.lastSampleTime < this.sampleRate) {
144
+ return;
145
+ }
146
+ this.lastSampleTime = now;
147
+ const point = {
148
+ x: event.clientX,
149
+ y: event.clientY,
150
+ timestamp: now
151
+ };
152
+ this.movements.push(point);
153
+ // Calculate velocity and acceleration
154
+ if (this.lastPoint) {
155
+ const velocity = this.calculateVelocity(this.lastPoint, point);
156
+ this.velocities.push(velocity);
157
+ // Track angle changes
158
+ const angle = Math.atan2(velocity.vy, velocity.vx);
159
+ if (this.lastAngle !== null) {
160
+ const angleDiff = Math.abs(angle - this.lastAngle);
161
+ if (angleDiff > this.significantAngleChange) {
162
+ this.angleChangeCount++;
163
+ }
164
+ }
165
+ this.lastAngle = angle;
166
+ if (this.lastVelocity) {
167
+ const acceleration = this.calculateAcceleration(this.lastVelocity, velocity, point.timestamp - this.lastPoint.timestamp);
168
+ this.accelerations.push(acceleration);
169
+ }
170
+ this.lastVelocity = velocity;
171
+ }
172
+ this.lastPoint = point;
173
+ // Update hover tracking
174
+ if (!this.hoverStart) {
175
+ this.hoverStart = point;
176
+ }
177
+ // Batch prune every N events
178
+ if (++this.pruneCounter >= this.pruneInterval) {
179
+ this.pruneCounter = 0;
180
+ this.pruneOldData();
181
+ }
182
+ }
183
+ ;
184
+ handleMouseDown(event) {
185
+ // Track when user starts hovering before clicking
186
+ const now = Date.now();
187
+ this.hoverStart = {
188
+ x: event.clientX,
189
+ y: event.clientY,
190
+ timestamp: now
191
+ };
192
+ }
193
+ ;
194
+ handleWheel(event) {
195
+ const now = Date.now();
196
+ const scrollEvent = {
197
+ deltaX: event.deltaX,
198
+ deltaY: event.deltaY,
199
+ timestamp: now,
200
+ velocity: Math.sqrt(event.deltaX ** 2 + event.deltaY ** 2),
201
+ type: 'wheel'
202
+ };
203
+ this.scrolls.push(scrollEvent);
204
+ }
205
+ ;
206
+ handleScroll(event) {
207
+ // This detects programmatic or drag scrolling
208
+ const now = Date.now();
209
+ // Avoid duplicate events from wheel
210
+ if (this.scrolls.length > 0) {
211
+ const lastScroll = this.scrolls[this.scrolls.length - 1];
212
+ if (now - lastScroll.timestamp < 50) {
213
+ return; // Too close to last scroll event, likely duplicate
214
+ }
215
+ }
216
+ const scrollEvent = {
217
+ deltaX: 0,
218
+ deltaY: 0,
219
+ timestamp: now,
220
+ velocity: 0,
221
+ type: 'drag'
222
+ };
223
+ this.scrolls.push(scrollEvent);
224
+ }
225
+ ;
226
+ calculateVelocity(p1, p2) {
227
+ const dt = p2.timestamp - p1.timestamp;
228
+ if (dt === 0) {
229
+ return { vx: 0, vy: 0, speed: 0 };
230
+ }
231
+ const vx = (p2.x - p1.x) / dt;
232
+ const vy = (p2.y - p1.y) / dt;
233
+ const speed = Math.sqrt(vx ** 2 + vy ** 2);
234
+ return { vx, vy, speed };
235
+ }
236
+ calculateAcceleration(v1, v2, dt) {
237
+ if (dt === 0) {
238
+ return { ax: 0, ay: 0, magnitude: 0 };
239
+ }
240
+ const ax = (v2.vx - v1.vx) / dt;
241
+ const ay = (v2.vy - v1.vy) / dt;
242
+ const magnitude = Math.sqrt(ax ** 2 + ay ** 2);
243
+ return { ax, ay, magnitude };
244
+ }
245
+ calculateDistance(p1, p2) {
246
+ return Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2);
247
+ }
248
+ pruneOldData() {
249
+ if (this.movements.length > this.maxDataPoints) {
250
+ this.movements = this.movements.slice(-this.maxDataPoints);
251
+ }
252
+ if (this.velocities.length > this.maxDataPoints) {
253
+ this.velocities = this.velocities.slice(-this.maxDataPoints);
254
+ }
255
+ if (this.accelerations.length > this.maxDataPoints) {
256
+ this.accelerations = this.accelerations.slice(-this.maxDataPoints);
257
+ }
258
+ if (this.scrolls.length > this.maxDataPoints) {
259
+ this.scrolls = this.scrolls.slice(-this.maxDataPoints);
260
+ }
261
+ }
262
+ // Metric calculation methods
263
+ calculateAverageSpeed() {
264
+ if (this.velocities.length === 0)
265
+ return null;
266
+ const sum = this.velocities.reduce((acc, v) => acc + v.speed, 0);
267
+ return sum / this.velocities.length;
268
+ }
269
+ calculateMaxSpeed() {
270
+ if (this.velocities.length === 0)
271
+ return null;
272
+ return Math.max(...this.velocities.map(v => v.speed));
273
+ }
274
+ calculateMinSpeed() {
275
+ if (this.velocities.length === 0)
276
+ return null;
277
+ return Math.min(...this.velocities.map(v => v.speed));
278
+ }
279
+ calculateSpeedStdDev(averageSpeed) {
280
+ if (this.velocities.length < 2 || averageSpeed === null)
281
+ return null;
282
+ const squaredDiffs = this.velocities.map(v => (v.speed - averageSpeed) ** 2);
283
+ const variance = squaredDiffs.reduce((acc, val) => acc + val, 0) / this.velocities.length;
284
+ return Math.sqrt(variance);
285
+ }
286
+ calculateAverageAcceleration() {
287
+ if (this.accelerations.length === 0)
288
+ return null;
289
+ const sum = this.accelerations.reduce((acc, a) => acc + a.magnitude, 0);
290
+ return sum / this.accelerations.length;
291
+ }
292
+ calculateMaxAcceleration() {
293
+ if (this.accelerations.length === 0)
294
+ return null;
295
+ return Math.max(...this.accelerations.map(a => a.magnitude));
296
+ }
297
+ countDecelerations() {
298
+ let count = 0;
299
+ for (let i = 1; i < this.velocities.length; i++) {
300
+ if (this.velocities[i].speed < this.velocities[i - 1].speed) {
301
+ count++;
302
+ }
303
+ }
304
+ return count;
305
+ }
306
+ countAccelerations() {
307
+ let count = 0;
308
+ for (let i = 1; i < this.velocities.length; i++) {
309
+ if (this.velocities[i].speed > this.velocities[i - 1].speed) {
310
+ count++;
311
+ }
312
+ }
313
+ return count;
314
+ }
315
+ calculateAverageCurvature() {
316
+ if (this.movements.length < 3)
317
+ return null;
318
+ let totalCurvature = 0;
319
+ let count = 0;
320
+ for (let i = 1; i < this.movements.length - 1; i++) {
321
+ const p1 = this.movements[i - 1];
322
+ const p2 = this.movements[i];
323
+ const p3 = this.movements[i + 1];
324
+ const angle1 = Math.atan2(p2.y - p1.y, p2.x - p1.x);
325
+ const angle2 = Math.atan2(p3.y - p2.y, p3.x - p2.x);
326
+ let curvature = Math.abs(angle2 - angle1);
327
+ if (curvature > Math.PI) {
328
+ curvature = 2 * Math.PI - curvature;
329
+ }
330
+ totalCurvature += curvature;
331
+ count++;
332
+ }
333
+ return count > 0 ? totalCurvature / count : null;
334
+ }
335
+ calculateStraightLinePercentage() {
336
+ if (this.movements.length < 3)
337
+ return null;
338
+ const start = this.movements[0];
339
+ const end = this.movements[this.movements.length - 1];
340
+ const directDistance = this.calculateDistance(start, end);
341
+ let pathDistance = 0;
342
+ for (let i = 1; i < this.movements.length; i++) {
343
+ pathDistance += this.calculateDistance(this.movements[i - 1], this.movements[i]);
344
+ }
345
+ if (pathDistance === 0)
346
+ return null;
347
+ return directDistance / pathDistance;
348
+ }
349
+ calculateAverageJerkiness() {
350
+ if (this.accelerations.length < 2)
351
+ return null;
352
+ let totalJerk = 0;
353
+ for (let i = 1; i < this.accelerations.length; i++) {
354
+ const da = this.accelerations[i].magnitude - this.accelerations[i - 1].magnitude;
355
+ totalJerk += Math.abs(da);
356
+ }
357
+ return totalJerk / (this.accelerations.length - 1);
358
+ }
359
+ calculateTotalScrollDistance() {
360
+ return this.scrolls.reduce((acc, s) => acc + Math.sqrt(s.deltaX ** 2 + s.deltaY ** 2), 0);
361
+ }
362
+ calculateAverageScrollVelocity() {
363
+ if (this.scrolls.length === 0)
364
+ return null;
365
+ const sum = this.scrolls.reduce((acc, s) => acc + s.velocity, 0);
366
+ return sum / this.scrolls.length;
367
+ }
368
+ }
369
+ exports.CursorBehavior = CursorBehavior;
@@ -0,0 +1,4 @@
1
+ export * from './collector';
2
+ export * from './cursor-behavior';
3
+ export * from './keystroke-metrics';
4
+ export * from './touch-behavior';
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./collector"), exports);
18
+ __exportStar(require("./cursor-behavior"), exports);
19
+ __exportStar(require("./keystroke-metrics"), exports);
20
+ __exportStar(require("./touch-behavior"), exports);
@@ -0,0 +1,60 @@
1
+ import { KeyLog, KeyboardMetric } from '../../protocols/services/behavioural-biometrics/keystorke-metrics';
2
+ export declare class KeyboardMetrics {
3
+ private keyHistory;
4
+ private activeKeys;
5
+ private isTracking;
6
+ private startTime;
7
+ private backspaceCount;
8
+ private deleteCount;
9
+ private shiftCount;
10
+ private totalKeyCount;
11
+ private keyDownHandler;
12
+ private keyUpHandler;
13
+ private maxDataPoints;
14
+ private readonly pruneInterval;
15
+ private pruneCounter;
16
+ constructor();
17
+ startTracking(maxDataPoints?: number): void;
18
+ stopTracking(): void;
19
+ /**
20
+ * Returns true if this tracker currently has its DOM listeners attached.
21
+ */
22
+ hasActiveListeners(): boolean;
23
+ /**
24
+ * Snapshot of raw event buffers + counters for cross-page persistence.
25
+ * `startTime` is included so typing speed is computed over the full session,
26
+ * not just the duration on the current page.
27
+ */
28
+ getState(): {
29
+ keyHistory: KeyLog[];
30
+ backspaceCount: number;
31
+ deleteCount: number;
32
+ shiftCount: number;
33
+ totalKeyCount: number;
34
+ startTime: number;
35
+ };
36
+ /**
37
+ * Restores raw event buffers + counters from a snapshot. Transient state
38
+ * (activeKeys map for in-progress key presses) is not restored.
39
+ */
40
+ restoreState(state: {
41
+ keyHistory?: KeyLog[];
42
+ backspaceCount?: number;
43
+ deleteCount?: number;
44
+ shiftCount?: number;
45
+ totalKeyCount?: number;
46
+ startTime?: number;
47
+ }): void;
48
+ resetData(): void;
49
+ private handleKeyDown;
50
+ private handleKeyUp;
51
+ private pruneOldData;
52
+ /**
53
+ * Metric Calculations
54
+ */
55
+ getKeyboardMetrics(): KeyboardMetric;
56
+ private calculateDwellTimes;
57
+ private calculateFlightTimes;
58
+ private calculateTypingSpeed;
59
+ private countQuickCorrections;
60
+ }
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KeyboardMetrics = void 0;
4
+ class KeyboardMetrics {
5
+ constructor() {
6
+ this.keyHistory = [];
7
+ this.activeKeys = new Map();
8
+ this.isTracking = false;
9
+ this.startTime = 0;
10
+ this.backspaceCount = 0;
11
+ this.deleteCount = 0;
12
+ this.shiftCount = 0;
13
+ this.totalKeyCount = 0;
14
+ this.maxDataPoints = 1000;
15
+ this.pruneInterval = 50;
16
+ this.pruneCounter = 0;
17
+ this.keyDownHandler = this.handleKeyDown.bind(this);
18
+ this.keyUpHandler = this.handleKeyUp.bind(this);
19
+ }
20
+ startTracking(maxDataPoints) {
21
+ if (this.isTracking || typeof document === 'undefined')
22
+ return;
23
+ this.maxDataPoints = maxDataPoints !== null && maxDataPoints !== void 0 ? maxDataPoints : 1000;
24
+ this.resetData();
25
+ this.isTracking = true;
26
+ this.startTime = Date.now();
27
+ document.addEventListener('keydown', this.keyDownHandler);
28
+ document.addEventListener('keyup', this.keyUpHandler);
29
+ }
30
+ stopTracking() {
31
+ if (!this.isTracking)
32
+ return;
33
+ document.removeEventListener('keydown', this.keyDownHandler);
34
+ document.removeEventListener('keyup', this.keyUpHandler);
35
+ this.isTracking = false;
36
+ }
37
+ /**
38
+ * Returns true if this tracker currently has its DOM listeners attached.
39
+ */
40
+ hasActiveListeners() {
41
+ return this.isTracking;
42
+ }
43
+ /**
44
+ * Snapshot of raw event buffers + counters for cross-page persistence.
45
+ * `startTime` is included so typing speed is computed over the full session,
46
+ * not just the duration on the current page.
47
+ */
48
+ getState() {
49
+ return {
50
+ keyHistory: this.keyHistory,
51
+ backspaceCount: this.backspaceCount,
52
+ deleteCount: this.deleteCount,
53
+ shiftCount: this.shiftCount,
54
+ totalKeyCount: this.totalKeyCount,
55
+ startTime: this.startTime,
56
+ };
57
+ }
58
+ /**
59
+ * Restores raw event buffers + counters from a snapshot. Transient state
60
+ * (activeKeys map for in-progress key presses) is not restored.
61
+ */
62
+ restoreState(state) {
63
+ if (state.keyHistory)
64
+ this.keyHistory = state.keyHistory;
65
+ if (typeof state.backspaceCount === 'number')
66
+ this.backspaceCount = state.backspaceCount;
67
+ if (typeof state.deleteCount === 'number')
68
+ this.deleteCount = state.deleteCount;
69
+ if (typeof state.shiftCount === 'number')
70
+ this.shiftCount = state.shiftCount;
71
+ if (typeof state.totalKeyCount === 'number')
72
+ this.totalKeyCount = state.totalKeyCount;
73
+ if (typeof state.startTime === 'number')
74
+ this.startTime = state.startTime;
75
+ }
76
+ resetData() {
77
+ this.keyHistory = [];
78
+ this.activeKeys.clear();
79
+ this.backspaceCount = 0;
80
+ this.deleteCount = 0;
81
+ this.shiftCount = 0;
82
+ this.totalKeyCount = 0;
83
+ this.pruneCounter = 0;
84
+ this.startTime = Date.now();
85
+ }
86
+ handleKeyDown(event) {
87
+ if (event.repeat)
88
+ return;
89
+ const now = Date.now();
90
+ this.activeKeys.set(event.key, now);
91
+ this.totalKeyCount++;
92
+ if (event.key === 'Backspace')
93
+ this.backspaceCount++;
94
+ if (event.key === 'Delete')
95
+ this.deleteCount++;
96
+ if (event.key === 'Shift')
97
+ this.shiftCount++;
98
+ }
99
+ handleKeyUp(event) {
100
+ const now = Date.now();
101
+ const pressTime = this.activeKeys.get(event.key);
102
+ if (pressTime) {
103
+ const duration = now - pressTime;
104
+ this.keyHistory.push({
105
+ key: event.key,
106
+ pressTime,
107
+ releaseTime: now,
108
+ duration
109
+ });
110
+ this.activeKeys.delete(event.key);
111
+ }
112
+ if (++this.pruneCounter >= this.pruneInterval) {
113
+ this.pruneCounter = 0;
114
+ this.pruneOldData();
115
+ }
116
+ }
117
+ pruneOldData() {
118
+ if (this.keyHistory.length > this.maxDataPoints) {
119
+ this.keyHistory = this.keyHistory.slice(-this.maxDataPoints);
120
+ }
121
+ }
122
+ /**
123
+ * Metric Calculations
124
+ */
125
+ getKeyboardMetrics() {
126
+ const dwellTimes = this.calculateDwellTimes();
127
+ const flightTimes = this.calculateFlightTimes();
128
+ const typingSpeed = this.calculateTypingSpeed();
129
+ const totalCorrections = this.backspaceCount + this.deleteCount;
130
+ const correctionRate = this.totalKeyCount > 0 ? (totalCorrections / this.totalKeyCount) : null;
131
+ // Error Rate Calculation:
132
+ // Ratio of corrections to the total intended characters produced
133
+ const allChars = Math.max(0, this.totalKeyCount - (2 * totalCorrections));
134
+ const errorRate = (allChars + totalCorrections) > 0
135
+ ? (totalCorrections / (allChars + totalCorrections))
136
+ : null;
137
+ return {
138
+ dwellTimes,
139
+ flightTimes,
140
+ statistics: {
141
+ backspaceCount: this.backspaceCount,
142
+ shiftKeyUsage: this.shiftCount,
143
+ deleteKeyCount: this.deleteCount,
144
+ },
145
+ errors: {
146
+ totalErrors: totalCorrections,
147
+ quickCorrections: this.countQuickCorrections(),
148
+ errorRate: errorRate,
149
+ correctionRate: correctionRate
150
+ },
151
+ typingSpeed
152
+ };
153
+ }
154
+ calculateDwellTimes() {
155
+ const map = new Map();
156
+ this.keyHistory.forEach(k => {
157
+ const stats = map.get(k.key) || { total: 0, count: 0, last: 0 };
158
+ stats.total += k.duration;
159
+ stats.count += 1;
160
+ stats.last = k.duration;
161
+ map.set(k.key, stats);
162
+ });
163
+ return Array.from(map.entries()).map(([key, stats]) => ({
164
+ key,
165
+ averageDuration: stats.total / stats.count,
166
+ duration: stats.last,
167
+ count: stats.count
168
+ }));
169
+ }
170
+ calculateFlightTimes() {
171
+ const flightMap = new Map();
172
+ for (let i = 1; i < this.keyHistory.length; i++) {
173
+ const from = this.keyHistory[i - 1];
174
+ const to = this.keyHistory[i];
175
+ const flightDuration = to.pressTime - from.releaseTime;
176
+ const pairKey = `${from.key}->${to.key}`;
177
+ const stats = flightMap.get(pairKey) || { total: 0, count: 0, last: 0 };
178
+ stats.total += flightDuration;
179
+ stats.count += 1;
180
+ stats.last = flightDuration;
181
+ flightMap.set(pairKey, stats);
182
+ }
183
+ return Array.from(flightMap.entries()).map(([pair, stats]) => {
184
+ const [fromKey, toKey] = pair.split('->');
185
+ return {
186
+ fromKey,
187
+ toKey,
188
+ averageDuration: stats.total / stats.count,
189
+ duration: stats.last,
190
+ count: stats.count
191
+ };
192
+ });
193
+ }
194
+ calculateTypingSpeed() {
195
+ if (this.totalKeyCount === 0)
196
+ return null;
197
+ const elapsedMinutes = (Date.now() - this.startTime) / 60000;
198
+ if (elapsedMinutes <= 0)
199
+ return null;
200
+ return (this.totalKeyCount / 5) / elapsedMinutes;
201
+ }
202
+ countQuickCorrections() {
203
+ let quickCorrections = 0;
204
+ for (let i = 1; i < this.keyHistory.length; i++) {
205
+ const isCorrective = this.keyHistory[i].key === 'Backspace' || this.keyHistory[i].key === 'Delete';
206
+ if (isCorrective && (this.keyHistory[i].pressTime - this.keyHistory[i - 1].releaseTime < 500)) {
207
+ quickCorrections++;
208
+ }
209
+ }
210
+ return quickCorrections;
211
+ }
212
+ }
213
+ exports.KeyboardMetrics = KeyboardMetrics;