@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,125 @@
1
+ import { SwipeGesture, SwipeMovement, TouchBehaviorBiometrics } from '../../protocols/services/behavioural-biometrics/touch-behavior';
2
+ import { BehaviouralBiometricsOptions } from '../../protocols/services/device-identifier';
3
+ /**
4
+ * TouchBehavior class for capturing and analyzing touch/gesture behavioral signals.
5
+ *
6
+ * This class tracks various touchscreen interactions including:
7
+ * - Touch pressure (force sensitivity)
8
+ * - Swipe gestures (speed, angle, length)
9
+ * - Finger size/contact area
10
+ */
11
+ export declare class TouchBehavior {
12
+ private pressureData;
13
+ private touchSizes;
14
+ private swipeGestures;
15
+ private swipeMovements;
16
+ private touchSwipeStart;
17
+ private touchLastPosition;
18
+ private pointerSwipeStart;
19
+ private pointerLastPosition;
20
+ private isTracking;
21
+ private useTouchForSwipes;
22
+ private minSwipeLength;
23
+ private maxSwipeDuration;
24
+ private pointerDownHandler;
25
+ private pointerMoveHandler;
26
+ private pointerUpHandler;
27
+ private pointerCancelHandler;
28
+ private touchStartHandler;
29
+ private touchMoveHandler;
30
+ private touchEndHandler;
31
+ constructor();
32
+ /**
33
+ * Starts tracking touch behavior.
34
+ */
35
+ startTracking(options?: BehaviouralBiometricsOptions): void;
36
+ /**
37
+ * Stops tracking touch behavior without clearing collected data.
38
+ */
39
+ stopTracking(): void;
40
+ /**
41
+ * Returns true if this tracker currently has its DOM listeners attached
42
+ * (i.e., it is actively collecting touch/pointer events).
43
+ */
44
+ hasActiveListeners(): boolean;
45
+ /**
46
+ * Snapshot of the raw event buffers this tracker has accumulated.
47
+ * Used to persist state across page reloads / SSR navigations.
48
+ */
49
+ getState(): {
50
+ pressureData: number[];
51
+ touchSizes: Array<{
52
+ radiusX: number;
53
+ radiusY: number;
54
+ }>;
55
+ swipeGestures: SwipeGesture[];
56
+ swipeMovements: SwipeMovement[];
57
+ };
58
+ /**
59
+ * Restores raw event buffers from a previously captured snapshot. Transient
60
+ * mid-gesture state (swipe-start maps, last positions) is intentionally not
61
+ * restored — it is only meaningful within a single continuous interaction.
62
+ */
63
+ restoreState(state: {
64
+ pressureData?: number[];
65
+ touchSizes?: Array<{
66
+ radiusX: number;
67
+ radiusY: number;
68
+ }>;
69
+ swipeGestures?: SwipeGesture[];
70
+ swipeMovements?: SwipeMovement[];
71
+ }): void;
72
+ /**
73
+ * Resets all collected touch behavioral data.
74
+ */
75
+ resetData(): void;
76
+ private handlePointerDown;
77
+ private handlePointerMove;
78
+ /**
79
+ * Handles pointer up events and calculates swipe metrics.
80
+ */
81
+ private handlePointerUp;
82
+ /**
83
+ * Handles pointer cancel events (non-touch devices only).
84
+ */
85
+ private handlePointerCancel;
86
+ /**
87
+ * Handles touch start events.
88
+ */
89
+ private handleTouchStart;
90
+ /**
91
+ * touchmove - track the latest position so we know where the finger is.
92
+ */
93
+ private handleTouchMove;
94
+ /**
95
+ * touchend - finalize the swipe using the real touchend coordinates.
96
+ * This always fires, even if the browser took over scrolling.
97
+ */
98
+ private handleTouchEnd;
99
+ /**
100
+ * Records a swipe from start data to the given end coordinates.
101
+ */
102
+ private recordSwipe;
103
+ /**
104
+ * Finalizes a pointer-based swipe (non-touch devices only).
105
+ */
106
+ private finalizePointerSwipe;
107
+ /**
108
+ * Gets the complete touch behavioral biometrics.
109
+ */
110
+ getTouchBehaviorBiometrics(): TouchBehaviorBiometrics;
111
+ /**
112
+ * Returns all recorded swipe movements with direction and coordinates.
113
+ */
114
+ getSwipeMovements(): SwipeMovement[];
115
+ private calculateAngle;
116
+ private calculateDirection;
117
+ private getPressureData;
118
+ private getSwipeMetrics;
119
+ private getFingerSizeData;
120
+ private isPointerSupported;
121
+ private isTouchSupported;
122
+ private isTrackablePointer;
123
+ private isPointerPressureSupported;
124
+ private isPointerSizeSupported;
125
+ }
@@ -0,0 +1,413 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TouchBehavior = void 0;
4
+ /**
5
+ * TouchBehavior class for capturing and analyzing touch/gesture behavioral signals.
6
+ *
7
+ * This class tracks various touchscreen interactions including:
8
+ * - Touch pressure (force sensitivity)
9
+ * - Swipe gestures (speed, angle, length)
10
+ * - Finger size/contact area
11
+ */
12
+ class TouchBehavior {
13
+ constructor() {
14
+ this.pressureData = [];
15
+ this.touchSizes = [];
16
+ this.swipeGestures = [];
17
+ this.swipeMovements = [];
18
+ // Touch-based swipe tracking (primary on touch devices)
19
+ this.touchSwipeStart = new Map();
20
+ this.touchLastPosition = new Map();
21
+ // Pointer-based swipe tracking (used only on non-touch-pointer devices)
22
+ this.pointerSwipeStart = new Map();
23
+ this.pointerLastPosition = new Map();
24
+ this.isTracking = false;
25
+ this.useTouchForSwipes = false;
26
+ this.minSwipeLength = 15;
27
+ this.maxSwipeDuration = 2000;
28
+ this.pointerDownHandler = this.handlePointerDown.bind(this);
29
+ this.pointerMoveHandler = this.handlePointerMove.bind(this);
30
+ this.pointerUpHandler = this.handlePointerUp.bind(this);
31
+ this.pointerCancelHandler = this.handlePointerCancel.bind(this);
32
+ this.touchStartHandler = this.handleTouchStart.bind(this);
33
+ this.touchMoveHandler = this.handleTouchMove.bind(this);
34
+ this.touchEndHandler = this.handleTouchEnd.bind(this);
35
+ }
36
+ /**
37
+ * Starts tracking touch behavior.
38
+ */
39
+ startTracking(options) {
40
+ var _a, _b;
41
+ if (this.isTracking || typeof window === 'undefined' || typeof document === 'undefined') {
42
+ return;
43
+ }
44
+ this.minSwipeLength = (_a = options === null || options === void 0 ? void 0 : options.minSwipeLength) !== null && _a !== void 0 ? _a : 15;
45
+ this.maxSwipeDuration = (_b = options === null || options === void 0 ? void 0 : options.maxSwipeDuration) !== null && _b !== void 0 ? _b : 2000;
46
+ this.isTracking = true;
47
+ this.useTouchForSwipes = this.isTouchSupported();
48
+ // Reset data
49
+ this.resetData();
50
+ if (this.isPointerSupported()) {
51
+ document.addEventListener('pointerdown', this.pointerDownHandler, { passive: true });
52
+ document.addEventListener('pointermove', this.pointerMoveHandler, { passive: true });
53
+ // Only listen for pointerup/cancel for swipe tracking on non-touch devices
54
+ if (!this.useTouchForSwipes) {
55
+ document.addEventListener('pointerup', this.pointerUpHandler, { passive: true });
56
+ document.addEventListener('pointercancel', this.pointerCancelHandler, { passive: true });
57
+ }
58
+ }
59
+ if (this.useTouchForSwipes) {
60
+ document.addEventListener('touchstart', this.touchStartHandler, { passive: true });
61
+ document.addEventListener('touchmove', this.touchMoveHandler, { passive: true });
62
+ document.addEventListener('touchend', this.touchEndHandler, { passive: true });
63
+ }
64
+ }
65
+ /**
66
+ * Stops tracking touch behavior without clearing collected data.
67
+ */
68
+ stopTracking() {
69
+ if (!this.isTracking)
70
+ return;
71
+ this.isTracking = false;
72
+ document.removeEventListener('pointerdown', this.pointerDownHandler);
73
+ document.removeEventListener('pointermove', this.pointerMoveHandler);
74
+ document.removeEventListener('pointerup', this.pointerUpHandler);
75
+ document.removeEventListener('pointercancel', this.pointerCancelHandler);
76
+ document.removeEventListener('touchstart', this.touchStartHandler);
77
+ document.removeEventListener('touchmove', this.touchMoveHandler);
78
+ document.removeEventListener('touchend', this.touchEndHandler);
79
+ }
80
+ /**
81
+ * Returns true if this tracker currently has its DOM listeners attached
82
+ * (i.e., it is actively collecting touch/pointer events).
83
+ */
84
+ hasActiveListeners() {
85
+ return this.isTracking;
86
+ }
87
+ /**
88
+ * Snapshot of the raw event buffers this tracker has accumulated.
89
+ * Used to persist state across page reloads / SSR navigations.
90
+ */
91
+ getState() {
92
+ return {
93
+ pressureData: this.pressureData,
94
+ touchSizes: this.touchSizes,
95
+ swipeGestures: this.swipeGestures,
96
+ swipeMovements: this.swipeMovements,
97
+ };
98
+ }
99
+ /**
100
+ * Restores raw event buffers from a previously captured snapshot. Transient
101
+ * mid-gesture state (swipe-start maps, last positions) is intentionally not
102
+ * restored — it is only meaningful within a single continuous interaction.
103
+ */
104
+ restoreState(state) {
105
+ if (state.pressureData)
106
+ this.pressureData = state.pressureData;
107
+ if (state.touchSizes)
108
+ this.touchSizes = state.touchSizes;
109
+ if (state.swipeGestures)
110
+ this.swipeGestures = state.swipeGestures;
111
+ if (state.swipeMovements)
112
+ this.swipeMovements = state.swipeMovements;
113
+ }
114
+ /**
115
+ * Resets all collected touch behavioral data.
116
+ */
117
+ resetData() {
118
+ this.pressureData = [];
119
+ this.touchSizes = [];
120
+ this.swipeGestures = [];
121
+ this.swipeMovements = [];
122
+ this.touchSwipeStart.clear();
123
+ this.touchLastPosition.clear();
124
+ this.pointerSwipeStart.clear();
125
+ this.pointerLastPosition.clear();
126
+ }
127
+ handlePointerDown(event) {
128
+ if (!this.isTracking)
129
+ return;
130
+ if (!this.isTrackablePointer(event))
131
+ return;
132
+ // Collect pressure data
133
+ if (this.isPointerPressureSupported(event)) {
134
+ this.pressureData.push(event.pressure);
135
+ }
136
+ // Collect finger size data
137
+ if (this.isPointerSizeSupported(event)) {
138
+ this.touchSizes.push({
139
+ radiusX: (event.width || 0) / 2,
140
+ radiusY: (event.height || 0) / 2
141
+ });
142
+ }
143
+ // Track swipe start only on non-touch-pointer devices
144
+ if (!this.useTouchForSwipes) {
145
+ this.pointerSwipeStart.set(event.pointerId, {
146
+ x: event.clientX,
147
+ y: event.clientY,
148
+ time: Date.now()
149
+ });
150
+ }
151
+ }
152
+ handlePointerMove(event) {
153
+ if (!this.isTracking)
154
+ return;
155
+ if (!this.isTrackablePointer(event))
156
+ return;
157
+ // Collect pressure data
158
+ if (this.isPointerPressureSupported(event)) {
159
+ this.pressureData.push(event.pressure);
160
+ }
161
+ // Collect finger size data
162
+ if (this.isPointerSizeSupported(event)) {
163
+ this.touchSizes.push({
164
+ radiusX: (event.width || 0) / 2,
165
+ radiusY: (event.height || 0) / 2
166
+ });
167
+ }
168
+ // Track last position only on non-touch-pointer devices
169
+ if (!this.useTouchForSwipes) {
170
+ this.pointerLastPosition.set(event.pointerId, { x: event.clientX, y: event.clientY });
171
+ }
172
+ }
173
+ /**
174
+ * Handles pointer up events and calculates swipe metrics.
175
+ */
176
+ handlePointerUp(event) {
177
+ if (!this.isTracking)
178
+ return;
179
+ if (!this.isTrackablePointer(event))
180
+ return;
181
+ this.finalizePointerSwipe(event.pointerId, event.clientX, event.clientY, false);
182
+ }
183
+ /**
184
+ * Handles pointer cancel events (non-touch devices only).
185
+ */
186
+ handlePointerCancel(event) {
187
+ if (!this.isTracking)
188
+ return;
189
+ this.finalizePointerSwipe(event.pointerId, event.clientX, event.clientY, true);
190
+ }
191
+ /**
192
+ * Handles touch start events.
193
+ */
194
+ handleTouchStart(event) {
195
+ if (!this.isTracking)
196
+ return;
197
+ for (let i = 0; i < event.changedTouches.length; i++) {
198
+ const touch = event.changedTouches[i];
199
+ this.touchSwipeStart.set(touch.identifier, {
200
+ x: touch.clientX,
201
+ y: touch.clientY,
202
+ time: Date.now()
203
+ });
204
+ }
205
+ }
206
+ /**
207
+ * touchmove - track the latest position so we know where the finger is.
208
+ */
209
+ handleTouchMove(event) {
210
+ if (!this.isTracking)
211
+ return;
212
+ for (let i = 0; i < event.changedTouches.length; i++) {
213
+ const touch = event.changedTouches[i];
214
+ this.touchLastPosition.set(touch.identifier, {
215
+ x: touch.clientX,
216
+ y: touch.clientY
217
+ });
218
+ }
219
+ }
220
+ /**
221
+ * touchend - finalize the swipe using the real touchend coordinates.
222
+ * This always fires, even if the browser took over scrolling.
223
+ */
224
+ handleTouchEnd(event) {
225
+ if (!this.isTracking)
226
+ return;
227
+ for (let i = 0; i < event.changedTouches.length; i++) {
228
+ const touch = event.changedTouches[i];
229
+ const touchId = touch.identifier;
230
+ const endX = touch.clientX;
231
+ const endY = touch.clientY;
232
+ const startData = this.touchSwipeStart.get(touchId);
233
+ if (startData) {
234
+ this.recordSwipe(startData, endX, endY);
235
+ this.touchSwipeStart.delete(touchId);
236
+ this.touchLastPosition.delete(touchId);
237
+ }
238
+ }
239
+ }
240
+ /**
241
+ * Records a swipe from start data to the given end coordinates.
242
+ */
243
+ recordSwipe(startData, endX, endY) {
244
+ const endTime = Date.now();
245
+ const deltaX = endX - startData.x;
246
+ const deltaY = endY - startData.y;
247
+ const duration = endTime - startData.time;
248
+ const length = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
249
+ if (length >= this.minSwipeLength && duration <= this.maxSwipeDuration) {
250
+ const speed = length / duration;
251
+ const angle = this.calculateAngle(deltaX, deltaY);
252
+ const swipe = {
253
+ speed,
254
+ angle,
255
+ length,
256
+ duration,
257
+ timestamp: startData.time
258
+ };
259
+ this.swipeGestures.push(swipe);
260
+ const movement = {
261
+ direction: this.calculateDirection(deltaX, deltaY),
262
+ start: [startData.x, startData.y],
263
+ end: [endX, endY]
264
+ };
265
+ this.swipeMovements.push(movement);
266
+ }
267
+ }
268
+ /**
269
+ * Finalizes a pointer-based swipe (non-touch devices only).
270
+ */
271
+ finalizePointerSwipe(pointerId, endX, endY, useLastPosAsFallback) {
272
+ const startData = this.pointerSwipeStart.get(pointerId);
273
+ if (!startData)
274
+ return;
275
+ const lastPos = this.pointerLastPosition.get(pointerId);
276
+ const resolvedEndX = (useLastPosAsFallback && endX === 0 && endY === 0 && lastPos) ? lastPos.x : endX;
277
+ const resolvedEndY = (useLastPosAsFallback && endX === 0 && endY === 0 && lastPos) ? lastPos.y : endY;
278
+ this.recordSwipe(startData, resolvedEndX, resolvedEndY);
279
+ this.pointerSwipeStart.delete(pointerId);
280
+ this.pointerLastPosition.delete(pointerId);
281
+ }
282
+ // --- Biometrics output ---
283
+ /**
284
+ * Gets the complete touch behavioral biometrics.
285
+ */
286
+ getTouchBehaviorBiometrics() {
287
+ return {
288
+ pressure: this.getPressureData(),
289
+ swipe: this.getSwipeMetrics(),
290
+ fingerSize: this.getFingerSizeData()
291
+ };
292
+ }
293
+ /**
294
+ * Returns all recorded swipe movements with direction and coordinates.
295
+ */
296
+ getSwipeMovements() {
297
+ return this.swipeMovements;
298
+ }
299
+ // --- Helper methods ---
300
+ calculateAngle(deltaX, deltaY) {
301
+ let angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI);
302
+ if (angle < 0) {
303
+ angle += 360;
304
+ }
305
+ return angle;
306
+ }
307
+ calculateDirection(deltaX, deltaY) {
308
+ if (Math.abs(deltaX) >= Math.abs(deltaY)) {
309
+ return deltaX > 0 ? 'right' : 'left';
310
+ }
311
+ else {
312
+ return deltaY > 0 ? 'down' : 'up';
313
+ }
314
+ }
315
+ getPressureData() {
316
+ if (this.pressureData.length === 0) {
317
+ return {
318
+ averagePressure: null,
319
+ maxPressure: null,
320
+ minPressure: null,
321
+ pressureVariability: null,
322
+ };
323
+ }
324
+ const sum = this.pressureData.reduce((a, b) => a + b, 0);
325
+ const avg = sum / this.pressureData.length;
326
+ const max = Math.max(...this.pressureData);
327
+ const min = Math.min(...this.pressureData);
328
+ const variance = this.pressureData.reduce((acc, val) => acc + Math.pow(val - avg, 2), 0) / this.pressureData.length;
329
+ const stdDev = Math.sqrt(variance);
330
+ return {
331
+ averagePressure: avg,
332
+ maxPressure: max,
333
+ minPressure: min,
334
+ pressureVariability: stdDev
335
+ };
336
+ }
337
+ getSwipeMetrics() {
338
+ if (this.swipeGestures.length === 0) {
339
+ return {
340
+ averageSpeed: null,
341
+ averageAngle: null,
342
+ averageLength: null,
343
+ averageDuration: null,
344
+ speedVariability: null,
345
+ angleVariability: null,
346
+ swipeCount: 0,
347
+ swipes: []
348
+ };
349
+ }
350
+ const speeds = this.swipeGestures.map(s => s.speed);
351
+ const angles = this.swipeGestures.map(s => s.angle);
352
+ const lengths = this.swipeGestures.map(s => s.length);
353
+ const durations = this.swipeGestures.map(s => s.duration);
354
+ const avgSpeed = speeds.reduce((a, b) => a + b, 0) / speeds.length;
355
+ const avgAngle = angles.reduce((a, b) => a + b, 0) / angles.length;
356
+ const avgLength = lengths.reduce((a, b) => a + b, 0) / lengths.length;
357
+ const avgDuration = durations.reduce((a, b) => a + b, 0) / durations.length;
358
+ const speedVariance = speeds.reduce((acc, val) => acc + Math.pow(val - avgSpeed, 2), 0) / speeds.length;
359
+ const angleVariance = angles.reduce((acc, val) => acc + Math.pow(val - avgAngle, 2), 0) / angles.length;
360
+ return {
361
+ averageSpeed: avgSpeed,
362
+ averageAngle: avgAngle,
363
+ averageLength: avgLength,
364
+ averageDuration: avgDuration,
365
+ speedVariability: Math.sqrt(speedVariance),
366
+ angleVariability: Math.sqrt(angleVariance),
367
+ swipeCount: this.swipeGestures.length,
368
+ swipes: this.swipeMovements
369
+ };
370
+ }
371
+ getFingerSizeData() {
372
+ if (this.touchSizes.length === 0) {
373
+ return {
374
+ averageTouchRadius: null,
375
+ maxTouchRadius: null,
376
+ minTouchRadius: null,
377
+ sizeVariability: null,
378
+ averageTouchArea: null,
379
+ };
380
+ }
381
+ const radii = this.touchSizes.map(t => (t.radiusX + t.radiusY) / 2);
382
+ const areas = this.touchSizes.map(t => t.radiusX * t.radiusY);
383
+ const avgRadius = radii.reduce((a, b) => a + b, 0) / radii.length;
384
+ const maxRadius = Math.max(...radii);
385
+ const minRadius = Math.min(...radii);
386
+ const avgArea = areas.reduce((a, b) => a + b, 0) / areas.length;
387
+ const variance = radii.reduce((acc, val) => acc + Math.pow(val - avgRadius, 2), 0) / radii.length;
388
+ return {
389
+ averageTouchRadius: avgRadius,
390
+ maxTouchRadius: maxRadius,
391
+ minTouchRadius: minRadius,
392
+ sizeVariability: Math.sqrt(variance),
393
+ averageTouchArea: avgArea,
394
+ };
395
+ }
396
+ isPointerSupported() {
397
+ return 'PointerEvent' in window && 'onpointerdown' in window;
398
+ }
399
+ isTouchSupported() {
400
+ return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
401
+ }
402
+ isTrackablePointer(pointer) {
403
+ return pointer.pointerType === 'touch' || pointer.pointerType === 'pen';
404
+ }
405
+ isPointerPressureSupported(pointer) {
406
+ return typeof pointer.pressure === 'number' && pointer.pressure > 0;
407
+ }
408
+ isPointerSizeSupported(pointer) {
409
+ return typeof pointer.width === 'number' && typeof pointer.height === 'number' &&
410
+ (pointer.width > 0 || pointer.height > 0);
411
+ }
412
+ }
413
+ exports.TouchBehavior = TouchBehavior;
@@ -0,0 +1,14 @@
1
+ export declare const enum DefaultFingerprint {
2
+ /** The browser doesn't support audio context */
3
+ NotSupported = -1,
4
+ /** An unexpected timeout has happened */
5
+ Timeout = -2
6
+ }
7
+ export declare class AudioFingerprint {
8
+ private AudioContext;
9
+ getAudioFingerprint(): Promise<number>;
10
+ private setAudioParam;
11
+ private renderAudio;
12
+ private generateHash;
13
+ private makeErrorFormat;
14
+ }
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.AudioFingerprint = void 0;
13
+ // Define the AudioFingerprint class
14
+ // Based on FingerprintJs(Ref: https://fingerprint.com/blog/audio-fingerprinting/)
15
+ class AudioFingerprint {
16
+ constructor() {
17
+ this.AudioContext = window.OfflineAudioContext;
18
+ }
19
+ getAudioFingerprint() {
20
+ return __awaiter(this, void 0, void 0, function* () {
21
+ if (!this.AudioContext) {
22
+ return -1 /* DefaultFingerprint.NotSupported */;
23
+ }
24
+ const hashFromIndex = 4500;
25
+ const hashToIndex = 5000;
26
+ const context = new this.AudioContext(1, hashToIndex, 44100);
27
+ const oscillator = context.createOscillator();
28
+ oscillator.type = 'triangle';
29
+ this.setAudioParam(context, oscillator.frequency, 10000);
30
+ const compressor = context.createDynamicsCompressor();
31
+ this.setAudioParam(context, compressor.threshold, -50);
32
+ this.setAudioParam(context, compressor.knee, 40);
33
+ this.setAudioParam(context, compressor.ratio, 12);
34
+ this.setAudioParam(context, compressor.reduction, -20);
35
+ this.setAudioParam(context, compressor.attack, 0);
36
+ this.setAudioParam(context, compressor.release, 0.25);
37
+ oscillator.connect(compressor);
38
+ compressor.connect(context.destination);
39
+ oscillator.start(0);
40
+ let buffer;
41
+ try {
42
+ buffer = yield this.renderAudio(context);
43
+ }
44
+ catch (error) {
45
+ if (error.name === "timeout" /* ErrorsName.Timeout */ || error.name === "suspended" /* ErrorsName.Suspended */) {
46
+ return -2 /* DefaultFingerprint.Timeout */;
47
+ }
48
+ throw error;
49
+ }
50
+ finally {
51
+ oscillator.disconnect();
52
+ compressor.disconnect();
53
+ }
54
+ return this.generateHash(buffer.getChannelData(0).subarray(hashFromIndex, hashToIndex));
55
+ });
56
+ }
57
+ setAudioParam(context, param, value) {
58
+ const isAudioParam = (value) => {
59
+ return (value !== null &&
60
+ typeof value === 'object' &&
61
+ 'setValueAtTime' in value &&
62
+ typeof value.setValueAtTime === 'function');
63
+ };
64
+ if (isAudioParam(param)) {
65
+ param.setValueAtTime(value, context.currentTime);
66
+ }
67
+ }
68
+ renderAudio(context) {
69
+ const resumeTriesMaxCount = 3;
70
+ const resumeRetryDelay = 250;
71
+ const runningTimeout = 500;
72
+ return new Promise((resolve, reject) => {
73
+ context.oncomplete = (event) => resolve(event.renderedBuffer);
74
+ let resumeTriesLeft = resumeTriesMaxCount;
75
+ const tryResume = () => {
76
+ context.startRendering();
77
+ switch (context.state) {
78
+ case 'running':
79
+ setTimeout(() => reject(this.makeErrorFormat("timeout" /* ErrorsName.Timeout */)), runningTimeout);
80
+ break;
81
+ case 'suspended':
82
+ // Retry if suspended
83
+ resumeTriesLeft--;
84
+ if (resumeTriesLeft > 0) {
85
+ setTimeout(tryResume, resumeRetryDelay);
86
+ }
87
+ else {
88
+ reject(this.makeErrorFormat("suspended" /* ErrorsName.Suspended */));
89
+ }
90
+ break;
91
+ }
92
+ };
93
+ tryResume();
94
+ });
95
+ }
96
+ generateHash(signal) {
97
+ let hash = 0;
98
+ for (let i = 0; i < signal.length; ++i) {
99
+ hash += Math.abs(signal[i]);
100
+ }
101
+ return hash;
102
+ }
103
+ makeErrorFormat(name) {
104
+ const error = new Error(name);
105
+ error.name = name;
106
+ return error;
107
+ }
108
+ }
109
+ exports.AudioFingerprint = AudioFingerprint;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Generates a canvas fingerprint, which is a technique used to identify browsers based on their unique rendering of a canvas element.
3
+ */
4
+ export declare class Canvas {
5
+ getCanvasFingerprint(): string;
6
+ /**
7
+ * Checks if the browser supports the canvas element and successfully returns a 2D context.
8
+ */
9
+ private _isCanvasSupported;
10
+ }