@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,120 @@
1
+ import { DeviceIdentifierMetadata } from '../device-identifier';
2
+ /**
3
+ * A single scheduled Device-Intelligence call.
4
+ */
5
+ export interface ScheduleCheckpoint {
6
+ /**
7
+ * 1-based checkpoint number. Checkpoint `n` is only fired once every
8
+ * checkpoint before it has been sent, so this doubles as the ordering key.
9
+ */
10
+ n: number;
11
+ /**
12
+ * Absolute epoch milliseconds at which this checkpoint becomes due.
13
+ */
14
+ dueAt: number;
15
+ }
16
+ /**
17
+ * The `ps_di_schedule_<surveyId>` record.
18
+ *
19
+ * It is keyed by survey id and handed to a storage adapter rather than
20
+ * held in page memory, so that a page navigation resumes the schedule instead of
21
+ * restarting it: `startedAt` is written once, by the very first page load of the
22
+ * transaction.
23
+ */
24
+ export interface ScheduleRecord {
25
+ /**
26
+ * The survey the schedule belongs to.
27
+ */
28
+ surveyId: string;
29
+ /**
30
+ * Length of the interview, in minutes, from `data-expected-survey-loi`. Null
31
+ * only between creating the record and applying the LOI to it.
32
+ */
33
+ loi: number | null;
34
+ /**
35
+ * Epoch milliseconds of the first page load of this transaction.
36
+ */
37
+ startedAt: number;
38
+ /**
39
+ * When each call should be performed.
40
+ */
41
+ checkpoints: ScheduleCheckpoint[];
42
+ /**
43
+ * How many checkpoints have already been delivered. A checkpoint is only
44
+ * fired when `n > sent`, so this is what keeps a page reload from repeating
45
+ * a call that already went out.
46
+ */
47
+ sent: number;
48
+ /**
49
+ * Epoch milliseconds after which the record is considered stale and replaced.
50
+ */
51
+ expiry: number;
52
+ /**
53
+ * Replay buffer: the most recent Device-Intelligence response, so a page that
54
+ * registers a listener after the checkpoint fired still receives the verdict.
55
+ */
56
+ lastResponse?: Record<string, any> | null;
57
+ /**
58
+ * The respondent's transaction id.
59
+ */
60
+ transactionId?: string | null;
61
+ }
62
+ /**
63
+ * The bootstrap configuration read off the `data-*` attributes of the script tag.
64
+ */
65
+ export interface AutoInitConfig {
66
+ /**
67
+ * `data-token` — Device-Intelligence bearer token.
68
+ */
69
+ token: string;
70
+ /**
71
+ * `data-env` — defaults to `staging`.
72
+ */
73
+ env: 'staging' | 'production';
74
+ /**
75
+ * `buyer_id` from `data-buyer-id`, when it is set.
76
+ */
77
+ metadata: DeviceIdentifierMetadata;
78
+ /**
79
+ * `data-callback` — name of a global function to invoke with the response.
80
+ */
81
+ callbackName?: string;
82
+ /**
83
+ * `data-survey-id`.
84
+ */
85
+ surveyId: string;
86
+ /**
87
+ * `data-expected-survey-loi` — length of the interview, in minutes. Drives the
88
+ * checkpoint schedule.
89
+ */
90
+ expectedSurveyLOI: number;
91
+ /**
92
+ * `data-survey-country` — ISO 3166-1 alpha-2, upper case (`US`).
93
+ */
94
+ surveyCountry: string;
95
+ /**
96
+ * `data-survey-language` — ISO 639-1, lower case (`en`).
97
+ */
98
+ surveyLanguage: string;
99
+ /**
100
+ * `data-supplier-id`.
101
+ */
102
+ supplierId?: string;
103
+ /**
104
+ * `data-supplier-member-id`.
105
+ */
106
+ supplierMemberId?: string;
107
+ /**
108
+ * `data-email-hash` — accepted as-is, not validated yet.
109
+ */
110
+ emailHash?: string;
111
+ /**
112
+ * `data-demographics-age`, `data-demographics-gender` and
113
+ * `data-demographics-zip`; only the ones that are set.
114
+ */
115
+ demographics?: {
116
+ age?: number;
117
+ gender?: string;
118
+ zip?: string;
119
+ };
120
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
1
+ export * from './auto-init';
@@ -0,0 +1,17 @@
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("./auto-init"), exports);
@@ -0,0 +1,94 @@
1
+ import { BehaviouralBiometricsOptions } from '../../protocols/services/device-identifier';
2
+ import { GetStorage, RemoveStorage, SetStorage } from '../../protocols/cache';
3
+ import { CursorBehavior } from './cursor-behavior';
4
+ import { KeyboardMetrics } from './keystroke-metrics';
5
+ import { SpaNavigationDetector } from '../spa-navigation';
6
+ import { TouchBehavior } from './touch-behavior';
7
+ /**
8
+ * How long the tracker listeners stay attached collecting interactions before
9
+ * they auto-detach (one collection cycle).
10
+ */
11
+ export declare const BB_WINDOW_MS = 300000;
12
+ export declare const BB_BUCKET_MS = 30000;
13
+ export declare const BB_MAX_BUCKETS: number;
14
+ export type MetricsBucket = {
15
+ touchBehavior: ReturnType<TouchBehavior['getTouchBehaviorBiometrics']>;
16
+ cursorBehavior: ReturnType<CursorBehavior['getCursorBehaviorBiometrics']>;
17
+ keystrokeDynamics: ReturnType<KeyboardMetrics['getKeyboardMetrics']>;
18
+ };
19
+ /** What a flush needs: the metrics to send and how long they took to gather. */
20
+ export type BehaviouralBiometricsSnapshot = {
21
+ metrics: MetricsBucket;
22
+ collectionDuration: number;
23
+ };
24
+ /**
25
+ * Collects behavioural biometrics across a respondent's session.
26
+ *
27
+ * A cycle attaches the trackers and seals their output into a bucket every
28
+ * `BB_BUCKET_MS`, keeping at most `BB_MAX_BUCKETS` before the window expires and
29
+ * the listeners detach. Buckets and in-progress buffers are mirrored into
30
+ * storage so a page navigation resumes the cycle rather than restarting it.
31
+ *
32
+ * One instance is shared for the lifetime of the page: the trackers it owns are
33
+ * what makes behavioural data survive a `DeviceIdentifier` being re-instantiated.
34
+ */
35
+ export declare class BehaviouralBiometricsCollector {
36
+ private readonly touch;
37
+ private readonly cursor;
38
+ private readonly keyboard;
39
+ private readonly spaNavigation;
40
+ private buckets;
41
+ private bucketTimer;
42
+ private durationTimer;
43
+ private bucketsSealedThisWindow;
44
+ private collectionDuration;
45
+ private cycleActive;
46
+ private activeOptions;
47
+ private readonly snapshots;
48
+ constructor(touch: TouchBehavior, cursor: CursorBehavior, keyboard: KeyboardMetrics, storage: GetStorage & SetStorage & RemoveStorage, spaNavigation: SpaNavigationDetector);
49
+ /**
50
+ * Attaches the trackers without the bucket cycle, for callers that flush on
51
+ * their own schedule.
52
+ */
53
+ startTrackers(options?: BehaviouralBiometricsOptions): void;
54
+ /**
55
+ * Starts a fresh collection cycle: re-attaches the listeners and restarts the
56
+ * bucket-sealing timer, stopping any active window first so the trackers can be
57
+ * (re)initialized cleanly.
58
+ */
59
+ startCycle(options?: BehaviouralBiometricsOptions): void;
60
+ /** Stops sealing buckets, leaving the collected data in place for a flush. */
61
+ pauseWindow(): void;
62
+ /** Detaches everything and discards what was collected. */
63
+ deactivate(): void;
64
+ /**
65
+ * The metrics worth sending: the most recent bucket that captured something,
66
+ * falling back to whatever the trackers hold right now.
67
+ */
68
+ snapshot(): BehaviouralBiometricsSnapshot;
69
+ /** Clears everything a successful flush has already delivered. */
70
+ reset(): void;
71
+ /**
72
+ * On SPA navigations (Next.js, Nuxt, SvelteKit, etc.), re-attach tracker
73
+ * listeners and reset the collection window so interactions on the new page are
74
+ * not missed when the previous window had already expired.
75
+ */
76
+ private installSpaReArm;
77
+ private startWindow;
78
+ /** Restores the buffers of whichever trackers were just wiped. */
79
+ private hydrate;
80
+ private sealCurrentBucket;
81
+ /** Snapshots whatever the trackers have captured in the in-progress bucket. */
82
+ private captureLive;
83
+ /**
84
+ * A bucket is "zeroed" when it captured no behavioural signal at all, and is
85
+ * never worth sending. The rule itself lives in the validation layer.
86
+ */
87
+ private isZeroed;
88
+ private mostRecentNonZeroedBucket;
89
+ private persistable;
90
+ private resetTrackers;
91
+ private startDurationTimer;
92
+ private stopDurationTimer;
93
+ private stopBucketTimer;
94
+ }
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BehaviouralBiometricsCollector = exports.BB_MAX_BUCKETS = exports.BB_BUCKET_MS = exports.BB_WINDOW_MS = void 0;
4
+ const validation_1 = require("../../../validation");
5
+ const snapshot_store_1 = require("../snapshot-store");
6
+ /**
7
+ * How long the tracker listeners stay attached collecting interactions before
8
+ * they auto-detach (one collection cycle).
9
+ */
10
+ exports.BB_WINDOW_MS = 300000; // 5 minutes
11
+ exports.BB_BUCKET_MS = 30000; // 30 seconds
12
+ exports.BB_MAX_BUCKETS = Math.floor(exports.BB_WINDOW_MS / exports.BB_BUCKET_MS);
13
+ const BB_PERSIST_KEY = 'ps_deviceIdentifier_behavioralBiometrics';
14
+ /**
15
+ * Collects behavioural biometrics across a respondent's session.
16
+ *
17
+ * A cycle attaches the trackers and seals their output into a bucket every
18
+ * `BB_BUCKET_MS`, keeping at most `BB_MAX_BUCKETS` before the window expires and
19
+ * the listeners detach. Buckets and in-progress buffers are mirrored into
20
+ * storage so a page navigation resumes the cycle rather than restarting it.
21
+ *
22
+ * One instance is shared for the lifetime of the page: the trackers it owns are
23
+ * what makes behavioural data survive a `DeviceIdentifier` being re-instantiated.
24
+ */
25
+ class BehaviouralBiometricsCollector {
26
+ constructor(touch, cursor, keyboard, storage, spaNavigation) {
27
+ this.touch = touch;
28
+ this.cursor = cursor;
29
+ this.keyboard = keyboard;
30
+ this.spaNavigation = spaNavigation;
31
+ this.buckets = [];
32
+ this.bucketTimer = null;
33
+ this.durationTimer = null;
34
+ this.bucketsSealedThisWindow = 0;
35
+ this.collectionDuration = 0;
36
+ this.cycleActive = false;
37
+ this.activeOptions = undefined;
38
+ this.snapshots = new snapshot_store_1.SnapshotStore(BB_PERSIST_KEY, () => this.persistable(), storage);
39
+ }
40
+ /**
41
+ * Attaches the trackers without the bucket cycle, for callers that flush on
42
+ * their own schedule.
43
+ */
44
+ startTrackers(options) {
45
+ this.touch.startTracking(options);
46
+ this.cursor.startTracking(options === null || options === void 0 ? void 0 : options.cursorMaxDataPoints);
47
+ this.keyboard.startTracking(options === null || options === void 0 ? void 0 : options.keystrokeMaxDataPoints);
48
+ }
49
+ /**
50
+ * Starts a fresh collection cycle: re-attaches the listeners and restarts the
51
+ * bucket-sealing timer, stopping any active window first so the trackers can be
52
+ * (re)initialized cleanly.
53
+ */
54
+ startCycle(options) {
55
+ this.cycleActive = true;
56
+ this.activeOptions = options;
57
+ this.installSpaReArm();
58
+ this.startWindow(options);
59
+ this.snapshots.start();
60
+ }
61
+ /** Stops sealing buckets, leaving the collected data in place for a flush. */
62
+ pauseWindow() {
63
+ this.stopBucketTimer();
64
+ }
65
+ /** Detaches everything and discards what was collected. */
66
+ deactivate() {
67
+ this.cycleActive = false;
68
+ this.activeOptions = undefined;
69
+ this.stopBucketTimer();
70
+ this.stopDurationTimer();
71
+ this.buckets = [];
72
+ this.collectionDuration = 0;
73
+ this.snapshots.stop();
74
+ this.touch.stopTracking();
75
+ this.cursor.stopTracking();
76
+ this.keyboard.stopTracking();
77
+ this.resetTrackers();
78
+ }
79
+ /**
80
+ * The metrics worth sending: the most recent bucket that captured something,
81
+ * falling back to whatever the trackers hold right now.
82
+ */
83
+ snapshot() {
84
+ var _a;
85
+ return {
86
+ metrics: (_a = this.mostRecentNonZeroedBucket()) !== null && _a !== void 0 ? _a : this.captureLive(),
87
+ collectionDuration: this.collectionDuration,
88
+ };
89
+ }
90
+ /** Clears everything a successful flush has already delivered. */
91
+ reset() {
92
+ this.resetTrackers();
93
+ this.buckets = [];
94
+ this.collectionDuration = 0;
95
+ this.snapshots.stop();
96
+ this.snapshots.clear();
97
+ }
98
+ /**
99
+ * On SPA navigations (Next.js, Nuxt, SvelteKit, etc.), re-attach tracker
100
+ * listeners and reset the collection window so interactions on the new page are
101
+ * not missed when the previous window had already expired.
102
+ */
103
+ installSpaReArm() {
104
+ this.spaNavigation.install(() => {
105
+ if (!this.cycleActive)
106
+ return;
107
+ this.startWindow(this.activeOptions);
108
+ });
109
+ }
110
+ startWindow(options) {
111
+ this.stopBucketTimer();
112
+ this.stopDurationTimer();
113
+ this.bucketsSealedThisWindow = 0;
114
+ // startTracking() calls resetData() internally, but only when that tracker
115
+ // is not already listening. Capture each tracker's "was reset" signal
116
+ // independently so we hydrate only the ones whose buffers were just wiped —
117
+ // never overwriting a tracker that still has fresher in-memory data.
118
+ const touchWasReset = !this.touch.hasActiveListeners();
119
+ const cursorWasReset = !this.cursor.hasActiveListeners();
120
+ const keyboardWasReset = !this.keyboard.hasActiveListeners();
121
+ this.startTrackers(options);
122
+ if (touchWasReset || cursorWasReset || keyboardWasReset) {
123
+ this.hydrate({ touchWasReset, cursorWasReset, keyboardWasReset });
124
+ }
125
+ this.startDurationTimer();
126
+ this.bucketTimer = setInterval(() => {
127
+ this.sealCurrentBucket();
128
+ this.bucketsSealedThisWindow++;
129
+ if (this.bucketsSealedThisWindow >= exports.BB_MAX_BUCKETS) {
130
+ this.stopBucketTimer();
131
+ this.stopDurationTimer();
132
+ this.touch.stopTracking();
133
+ this.cursor.stopTracking();
134
+ this.keyboard.stopTracking();
135
+ }
136
+ }, exports.BB_BUCKET_MS);
137
+ }
138
+ /** Restores the buffers of whichever trackers were just wiped. */
139
+ hydrate({ touchWasReset, cursorWasReset, keyboardWasReset }) {
140
+ var _a, _b;
141
+ const stored = this.snapshots.read();
142
+ if (stored === null || stored === void 0 ? void 0 : stored.state) {
143
+ if (touchWasReset && stored.state.touchBehavior)
144
+ this.touch.restoreState(stored.state.touchBehavior);
145
+ if (cursorWasReset && stored.state.cursorBehavior)
146
+ this.cursor.restoreState(stored.state.cursorBehavior);
147
+ if (keyboardWasReset && stored.state.keystrokeDynamics)
148
+ this.keyboard.restoreState(stored.state.keystrokeDynamics);
149
+ this.collectionDuration = (_a = stored.state.collectionDuration) !== null && _a !== void 0 ? _a : 0;
150
+ }
151
+ else {
152
+ this.collectionDuration = 0;
153
+ }
154
+ if (this.buckets.length === 0 && ((_b = stored === null || stored === void 0 ? void 0 : stored.buckets) === null || _b === void 0 ? void 0 : _b.length)) {
155
+ this.buckets = stored.buckets.slice(-exports.BB_MAX_BUCKETS);
156
+ }
157
+ }
158
+ sealCurrentBucket() {
159
+ const bucket = this.captureLive();
160
+ if (!this.isZeroed(bucket)) {
161
+ this.buckets.push(bucket);
162
+ if (this.buckets.length > exports.BB_MAX_BUCKETS) {
163
+ this.buckets = this.buckets.slice(-exports.BB_MAX_BUCKETS);
164
+ }
165
+ }
166
+ this.collectionDuration = 0;
167
+ this.resetTrackers();
168
+ }
169
+ /** Snapshots whatever the trackers have captured in the in-progress bucket. */
170
+ captureLive() {
171
+ return {
172
+ touchBehavior: this.touch.getTouchBehaviorBiometrics(),
173
+ cursorBehavior: this.cursor.getCursorBehaviorBiometrics(),
174
+ keystrokeDynamics: this.keyboard.getKeyboardMetrics(),
175
+ };
176
+ }
177
+ /**
178
+ * A bucket is "zeroed" when it captured no behavioural signal at all, and is
179
+ * never worth sending. The rule itself lives in the validation layer.
180
+ */
181
+ isZeroed(bucket) {
182
+ return new validation_1.BehaviouralSignal(bucket).validate() !== undefined;
183
+ }
184
+ mostRecentNonZeroedBucket() {
185
+ for (let i = this.buckets.length - 1; i >= 0; i--) {
186
+ if (!this.isZeroed(this.buckets[i]))
187
+ return this.buckets[i];
188
+ }
189
+ return null;
190
+ }
191
+ persistable() {
192
+ return {
193
+ state: {
194
+ touchBehavior: this.touch.getState(),
195
+ cursorBehavior: this.cursor.getState(),
196
+ keystrokeDynamics: this.keyboard.getState(),
197
+ collectionDuration: this.collectionDuration,
198
+ },
199
+ buckets: this.buckets,
200
+ };
201
+ }
202
+ resetTrackers() {
203
+ this.touch.resetData();
204
+ this.cursor.resetData();
205
+ this.keyboard.resetData();
206
+ }
207
+ startDurationTimer() {
208
+ this.stopDurationTimer();
209
+ this.durationTimer = setInterval(() => {
210
+ this.collectionDuration++;
211
+ if (this.collectionDuration >= exports.BB_BUCKET_MS / 1000) {
212
+ this.collectionDuration = 0;
213
+ }
214
+ }, 1000);
215
+ }
216
+ stopDurationTimer() {
217
+ if (this.durationTimer !== null) {
218
+ clearInterval(this.durationTimer);
219
+ this.durationTimer = null;
220
+ }
221
+ }
222
+ stopBucketTimer() {
223
+ if (this.bucketTimer !== null) {
224
+ clearInterval(this.bucketTimer);
225
+ this.bucketTimer = null;
226
+ }
227
+ }
228
+ }
229
+ exports.BehaviouralBiometricsCollector = BehaviouralBiometricsCollector;
@@ -0,0 +1,87 @@
1
+ import { MousePoint, MouseVelocity, MouseAcceleration, ScrollEvent, CursorBehaviorMetrics } from '../../protocols/services/behavioural-biometrics/cursor-behavior';
2
+ export declare class CursorBehavior {
3
+ private movements;
4
+ private velocities;
5
+ private accelerations;
6
+ private scrolls;
7
+ private lastPoint;
8
+ private lastVelocity;
9
+ private lastSampleTime;
10
+ private angleChangeCount;
11
+ private lastAngle;
12
+ private hoverStart;
13
+ private isTracking;
14
+ private mouseMoveHandler;
15
+ private mouseDownHandler;
16
+ private wheelHandler;
17
+ private scrollHandler;
18
+ private readonly sampleRate;
19
+ private readonly significantAngleChange;
20
+ private maxDataPoints;
21
+ private readonly pruneInterval;
22
+ private pruneCounter;
23
+ constructor();
24
+ /**
25
+ * Start tracking mouse behavior
26
+ */
27
+ startTracking(maxDataPoints?: number): void;
28
+ /**
29
+ * Stops tracking cursor behavior without clearing collected data.
30
+ */
31
+ stopTracking(): void;
32
+ /**
33
+ * Returns true if this tracker currently has its DOM listeners attached.
34
+ */
35
+ hasActiveListeners(): boolean;
36
+ /**
37
+ * Snapshot of raw event buffers for cross-page persistence.
38
+ */
39
+ getState(): {
40
+ movements: MousePoint[];
41
+ velocities: MouseVelocity[];
42
+ accelerations: MouseAcceleration[];
43
+ scrolls: ScrollEvent[];
44
+ angleChangeCount: number;
45
+ };
46
+ /**
47
+ * Restores raw event buffers from a snapshot. Transient fields (lastPoint,
48
+ * lastVelocity, hoverStart, etc.) are not restored — they only make sense
49
+ * within a continuous interaction sequence on a single page.
50
+ */
51
+ restoreState(state: {
52
+ movements?: MousePoint[];
53
+ velocities?: MouseVelocity[];
54
+ accelerations?: MouseAcceleration[];
55
+ scrolls?: ScrollEvent[];
56
+ angleChangeCount?: number;
57
+ }): void;
58
+ /**
59
+ * Reset all collected data
60
+ */
61
+ resetData(): void;
62
+ /**
63
+ * Get current behavioral metrics
64
+ */
65
+ getCursorBehaviorBiometrics(): CursorBehaviorMetrics;
66
+ private handleMouseMove;
67
+ private handleMouseDown;
68
+ private handleWheel;
69
+ private handleScroll;
70
+ private calculateVelocity;
71
+ private calculateAcceleration;
72
+ private calculateDistance;
73
+ private pruneOldData;
74
+ private calculateAverageSpeed;
75
+ private calculateMaxSpeed;
76
+ private calculateMinSpeed;
77
+ private calculateSpeedStdDev;
78
+ private calculateAverageAcceleration;
79
+ private calculateMaxAcceleration;
80
+ private countDecelerations;
81
+ private countAccelerations;
82
+ private calculateAverageCurvature;
83
+ private calculateStraightLinePercentage;
84
+ private calculateAverageJerkiness;
85
+ private calculateTotalScrollDistance;
86
+ private calculateAverageScrollVelocity;
87
+ }