@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.
- package/README.md +55 -0
- package/lib/application/auto-init.d.ts +44 -0
- package/lib/application/auto-init.js +245 -0
- package/lib/application/device-identifier.d.ts +116 -0
- package/lib/application/device-identifier.js +169 -0
- package/lib/domain/models/crosscutting/device-intelligence.d.ts +11 -0
- package/lib/domain/models/crosscutting/device-intelligence.js +18 -0
- package/lib/domain/models/crosscutting/index.d.ts +1 -0
- package/lib/domain/models/crosscutting/index.js +17 -0
- package/lib/domain/protocols/cache/get-storage.d.ts +8 -0
- package/lib/domain/protocols/cache/get-storage.js +2 -0
- package/lib/domain/protocols/cache/index.d.ts +3 -0
- package/lib/domain/protocols/cache/index.js +19 -0
- package/lib/domain/protocols/cache/remove-storage.d.ts +6 -0
- package/lib/domain/protocols/cache/remove-storage.js +2 -0
- package/lib/domain/protocols/cache/set-storage.d.ts +7 -0
- package/lib/domain/protocols/cache/set-storage.js +2 -0
- package/lib/domain/protocols/http/http-client.d.ts +23 -0
- package/lib/domain/protocols/http/http-client.js +13 -0
- package/lib/domain/protocols/services/behavioural-biometrics/cursor-behavior.d.ts +63 -0
- package/lib/domain/protocols/services/behavioural-biometrics/cursor-behavior.js +2 -0
- package/lib/domain/protocols/services/behavioural-biometrics/index.d.ts +3 -0
- package/lib/domain/protocols/services/behavioural-biometrics/index.js +19 -0
- package/lib/domain/protocols/services/behavioural-biometrics/keystorke-metrics.d.ts +35 -0
- package/lib/domain/protocols/services/behavioural-biometrics/keystorke-metrics.js +2 -0
- package/lib/domain/protocols/services/behavioural-biometrics/touch-behavior.d.ts +88 -0
- package/lib/domain/protocols/services/behavioural-biometrics/touch-behavior.js +2 -0
- package/lib/domain/protocols/services/device-fingerprint/display.d.ts +22 -0
- package/lib/domain/protocols/services/device-fingerprint/display.js +2 -0
- package/lib/domain/protocols/services/device-fingerprint/index.d.ts +3 -0
- package/lib/domain/protocols/services/device-fingerprint/index.js +19 -0
- package/lib/domain/protocols/services/device-fingerprint/navigator.d.ts +63 -0
- package/lib/domain/protocols/services/device-fingerprint/navigator.js +2 -0
- package/lib/domain/protocols/services/device-fingerprint/utils.d.ts +40 -0
- package/lib/domain/protocols/services/device-fingerprint/utils.js +2 -0
- package/lib/domain/protocols/services/device-identifier.d.ts +96 -0
- package/lib/domain/protocols/services/device-identifier.js +2 -0
- package/lib/domain/protocols/services/device-intelligence.d.ts +5 -0
- package/lib/domain/protocols/services/device-intelligence.js +2 -0
- package/lib/domain/protocols/services/listeners.d.ts +9 -0
- package/lib/domain/protocols/services/listeners.js +2 -0
- package/lib/domain/protocols/services/single-script-injection/auto-init.d.ts +120 -0
- package/lib/domain/protocols/services/single-script-injection/auto-init.js +2 -0
- package/lib/domain/protocols/services/single-script-injection/index.d.ts +1 -0
- package/lib/domain/protocols/services/single-script-injection/index.js +17 -0
- package/lib/domain/services/behavioural-biometrics/collector.d.ts +94 -0
- package/lib/domain/services/behavioural-biometrics/collector.js +229 -0
- package/lib/domain/services/behavioural-biometrics/cursor-behavior.d.ts +87 -0
- package/lib/domain/services/behavioural-biometrics/cursor-behavior.js +369 -0
- package/lib/domain/services/behavioural-biometrics/index.d.ts +4 -0
- package/lib/domain/services/behavioural-biometrics/index.js +20 -0
- package/lib/domain/services/behavioural-biometrics/keystroke-metrics.d.ts +60 -0
- package/lib/domain/services/behavioural-biometrics/keystroke-metrics.js +213 -0
- package/lib/domain/services/behavioural-biometrics/touch-behavior.d.ts +125 -0
- package/lib/domain/services/behavioural-biometrics/touch-behavior.js +413 -0
- package/lib/domain/services/device-fingerprint/audio.d.ts +14 -0
- package/lib/domain/services/device-fingerprint/audio.js +109 -0
- package/lib/domain/services/device-fingerprint/canvas.d.ts +10 -0
- package/lib/domain/services/device-fingerprint/canvas.js +33 -0
- package/lib/domain/services/device-fingerprint/display.d.ts +49 -0
- package/lib/domain/services/device-fingerprint/display.js +88 -0
- package/lib/domain/services/device-fingerprint/hash.d.ts +14 -0
- package/lib/domain/services/device-fingerprint/hash.js +60 -0
- package/lib/domain/services/device-fingerprint/index.d.ts +8 -0
- package/lib/domain/services/device-fingerprint/index.js +24 -0
- package/lib/domain/services/device-fingerprint/navigator.d.ts +110 -0
- package/lib/domain/services/device-fingerprint/navigator.js +213 -0
- package/lib/domain/services/device-fingerprint/pointer-capability.d.ts +14 -0
- package/lib/domain/services/device-fingerprint/pointer-capability.js +42 -0
- package/lib/domain/services/device-fingerprint/storage.d.ts +44 -0
- package/lib/domain/services/device-fingerprint/storage.js +64 -0
- package/lib/domain/services/device-fingerprint/utils.d.ts +42 -0
- package/lib/domain/services/device-fingerprint/utils.js +95 -0
- package/lib/domain/services/device-intelligence.d.ts +10 -0
- package/lib/domain/services/device-intelligence.js +59 -0
- package/lib/domain/services/single-script-injection/checkpoint-scheduler.d.ts +37 -0
- package/lib/domain/services/single-script-injection/checkpoint-scheduler.js +94 -0
- package/lib/domain/services/single-script-injection/index.d.ts +3 -0
- package/lib/domain/services/single-script-injection/index.js +19 -0
- package/lib/domain/services/single-script-injection/response-delivery.d.ts +47 -0
- package/lib/domain/services/single-script-injection/response-delivery.js +115 -0
- package/lib/domain/services/single-script-injection/schedule-store.d.ts +69 -0
- package/lib/domain/services/single-script-injection/schedule-store.js +146 -0
- package/lib/domain/services/snapshot-store.d.ts +19 -0
- package/lib/domain/services/snapshot-store.js +36 -0
- package/lib/domain/services/spa-navigation.d.ts +15 -0
- package/lib/domain/services/spa-navigation.js +40 -0
- package/lib/index.d.ts +19 -0
- package/lib/index.js +27 -0
- package/lib/infra/cache/index.d.ts +3 -0
- package/lib/infra/cache/index.js +19 -0
- package/lib/infra/cache/local-storage-adapter.d.ts +8 -0
- package/lib/infra/cache/local-storage-adapter.js +14 -0
- package/lib/infra/cache/session-storage-adapter.d.ts +8 -0
- package/lib/infra/cache/session-storage-adapter.js +14 -0
- package/lib/infra/cache/web-storage-adapter.d.ts +13 -0
- package/lib/infra/cache/web-storage-adapter.js +33 -0
- package/lib/infra/http/axios-http-client.d.ts +4 -0
- package/lib/infra/http/axios-http-client.js +39 -0
- package/lib/validation/builder.d.ts +43 -0
- package/lib/validation/builder.js +85 -0
- package/lib/validation/composite.d.ts +13 -0
- package/lib/validation/composite.js +23 -0
- package/lib/validation/errors/index.d.ts +2 -0
- package/lib/validation/errors/index.js +18 -0
- package/lib/validation/errors/invalid-field-error.d.ts +7 -0
- package/lib/validation/errors/invalid-field-error.js +14 -0
- package/lib/validation/errors/required-field-error.d.ts +3 -0
- package/lib/validation/errors/required-field-error.js +10 -0
- package/lib/validation/index.d.ts +5 -0
- package/lib/validation/index.js +21 -0
- package/lib/validation/protocols/index.d.ts +1 -0
- package/lib/validation/protocols/index.js +17 -0
- package/lib/validation/protocols/validator.d.ts +11 -0
- package/lib/validation/protocols/validator.js +2 -0
- package/lib/validation/validators/behavioural-signal.d.ts +27 -0
- package/lib/validation/validators/behavioural-signal.js +56 -0
- package/lib/validation/validators/country-code.d.ts +14 -0
- package/lib/validation/validators/country-code.js +24 -0
- package/lib/validation/validators/global-function.d.ts +14 -0
- package/lib/validation/validators/global-function.js +29 -0
- package/lib/validation/validators/index.d.ts +9 -0
- package/lib/validation/validators/index.js +25 -0
- package/lib/validation/validators/interpolated-template.d.ts +14 -0
- package/lib/validation/validators/interpolated-template.js +28 -0
- package/lib/validation/validators/json-object.d.ts +14 -0
- package/lib/validation/validators/json-object.js +33 -0
- package/lib/validation/validators/language-code.d.ts +13 -0
- package/lib/validation/validators/language-code.js +23 -0
- package/lib/validation/validators/positive-number.d.ts +13 -0
- package/lib/validation/validators/positive-number.js +24 -0
- package/lib/validation/validators/required-string.d.ts +10 -0
- package/lib/validation/validators/required-string.js +19 -0
- package/lib/validation/validators/schedule-record.d.ts +26 -0
- package/lib/validation/validators/schedule-record.js +33 -0
- package/package.json +51 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ScheduleCheckpoint, ScheduleRecord } from '../../protocols/services/single-script-injection/auto-init';
|
|
2
|
+
import { GetStorage, RemoveStorage, SetStorage } from '../../protocols/cache';
|
|
3
|
+
/**
|
|
4
|
+
* How long a record outlives its final checkpoint. A respondent who comes back
|
|
5
|
+
* the next day gets a fresh schedule, not one long overdue.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SCHEDULE_TTL_MS = 3600000;
|
|
8
|
+
/** The first call does not wait for the LOI. */
|
|
9
|
+
export declare const INITIAL_CHECKPOINT_DELAY_MS = 10000;
|
|
10
|
+
/** The later checkpoints, as multiples of the LOI. */
|
|
11
|
+
export declare const LOI_CHECKPOINT_FRACTIONS: readonly [number, number, 1, 2];
|
|
12
|
+
/** Most calls a transaction can make: the initial one plus each LOI fraction. */
|
|
13
|
+
export declare const CHECKPOINT_COUNT: number;
|
|
14
|
+
/**
|
|
15
|
+
* Owns the `ps_di_schedule_<surveyId>` record: which checkpoints are still
|
|
16
|
+
* outstanding on a later page load. Storage is injected, so this holds only the
|
|
17
|
+
* schedule rules, not where the record lives.
|
|
18
|
+
*/
|
|
19
|
+
export declare class ScheduleStore {
|
|
20
|
+
private readonly surveyId;
|
|
21
|
+
private readonly storage;
|
|
22
|
+
constructor(surveyId: string, storage: GetStorage & SetStorage & RemoveStorage);
|
|
23
|
+
get key(): string;
|
|
24
|
+
/**
|
|
25
|
+
* Checkpoint 1 fires `INITIAL_CHECKPOINT_DELAY_MS` in regardless; the rest are
|
|
26
|
+
* LOI multiples, so an unknown LOI yields a one-checkpoint schedule rather
|
|
27
|
+
* than none. At LOI 6 min: T+10s, T+120s, T+240s, T+360s, T+720s.
|
|
28
|
+
*/
|
|
29
|
+
static buildCheckpoints(startedAt: number, loi: number | null): ScheduleCheckpoint[];
|
|
30
|
+
/** The record is kept until an hour past the last call it still owes. */
|
|
31
|
+
private static expiryFor;
|
|
32
|
+
/**
|
|
33
|
+
* Reads the record for this transaction, or null when it is absent, unreadable
|
|
34
|
+
* or expired. Anything unusable is evicted rather than driven off, so the next
|
|
35
|
+
* `resumeOrCreate` starts a clean schedule.
|
|
36
|
+
*/
|
|
37
|
+
read(): ScheduleRecord | null;
|
|
38
|
+
/**
|
|
39
|
+
* Returns the existing record for this transaction, or creates one when the
|
|
40
|
+
* transaction has not been seen on this origin yet.
|
|
41
|
+
*
|
|
42
|
+
* Resuming deliberately keeps the stored `startedAt` and `checkpoints`: the
|
|
43
|
+
* checkpoints stay on the clock started by the very first page load, so a
|
|
44
|
+
* respondent cannot reset the schedule by reloading.
|
|
45
|
+
*/
|
|
46
|
+
resumeOrCreate(): ScheduleRecord;
|
|
47
|
+
/**
|
|
48
|
+
* Adds the LOI-derived checkpoints, measured from the original `startedAt` so
|
|
49
|
+
* applying the LOI later does not push the schedule back. Ignored when an LOI
|
|
50
|
+
* is already set — renumbering could disturb sent calls.
|
|
51
|
+
*/
|
|
52
|
+
applyLoi(loi: number): ScheduleRecord | null;
|
|
53
|
+
/**
|
|
54
|
+
* Marks checkpoint `n` as delivered. Written as a max so an out-of-order
|
|
55
|
+
* write — two tabs firing around the same due time — can never lower the
|
|
56
|
+
* counter and re-open a checkpoint that already went out.
|
|
57
|
+
*/
|
|
58
|
+
markSent(n: number): ScheduleRecord | null;
|
|
59
|
+
/**
|
|
60
|
+
* Stores the latest Device-Intelligence response alongside the schedule so it
|
|
61
|
+
* can be replayed on the next page load.
|
|
62
|
+
*/
|
|
63
|
+
storeResponse(response: Record<string, any>): void;
|
|
64
|
+
/**
|
|
65
|
+
* Stores the transaction id returned alongside the schedule.
|
|
66
|
+
*/
|
|
67
|
+
storeTransactionId(transactionId: string): void;
|
|
68
|
+
private now;
|
|
69
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ScheduleStore = exports.CHECKPOINT_COUNT = exports.LOI_CHECKPOINT_FRACTIONS = exports.INITIAL_CHECKPOINT_DELAY_MS = exports.SCHEDULE_TTL_MS = void 0;
|
|
4
|
+
const validation_1 = require("../../../validation");
|
|
5
|
+
/**
|
|
6
|
+
* How long a record outlives its final checkpoint. A respondent who comes back
|
|
7
|
+
* the next day gets a fresh schedule, not one long overdue.
|
|
8
|
+
*/
|
|
9
|
+
exports.SCHEDULE_TTL_MS = 3600000; // 1 hour
|
|
10
|
+
/** The first call does not wait for the LOI. */
|
|
11
|
+
exports.INITIAL_CHECKPOINT_DELAY_MS = 10000; // 10 seconds
|
|
12
|
+
/** The later checkpoints, as multiples of the LOI. */
|
|
13
|
+
exports.LOI_CHECKPOINT_FRACTIONS = [1 / 3, 2 / 3, 1, 2];
|
|
14
|
+
/** Most calls a transaction can make: the initial one plus each LOI fraction. */
|
|
15
|
+
exports.CHECKPOINT_COUNT = 1 + exports.LOI_CHECKPOINT_FRACTIONS.length;
|
|
16
|
+
/**
|
|
17
|
+
* Owns the `ps_di_schedule_<surveyId>` record: which checkpoints are still
|
|
18
|
+
* outstanding on a later page load. Storage is injected, so this holds only the
|
|
19
|
+
* schedule rules, not where the record lives.
|
|
20
|
+
*/
|
|
21
|
+
class ScheduleStore {
|
|
22
|
+
constructor(surveyId, storage) {
|
|
23
|
+
this.surveyId = surveyId;
|
|
24
|
+
this.storage = storage;
|
|
25
|
+
}
|
|
26
|
+
get key() {
|
|
27
|
+
return `ps_di_schedule_${this.surveyId}`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Checkpoint 1 fires `INITIAL_CHECKPOINT_DELAY_MS` in regardless; the rest are
|
|
31
|
+
* LOI multiples, so an unknown LOI yields a one-checkpoint schedule rather
|
|
32
|
+
* than none. At LOI 6 min: T+10s, T+120s, T+240s, T+360s, T+720s.
|
|
33
|
+
*/
|
|
34
|
+
static buildCheckpoints(startedAt, loi) {
|
|
35
|
+
const checkpoints = [
|
|
36
|
+
{ n: 1, dueAt: startedAt + exports.INITIAL_CHECKPOINT_DELAY_MS },
|
|
37
|
+
];
|
|
38
|
+
if (loi !== null) {
|
|
39
|
+
const loiMs = loi * 60000;
|
|
40
|
+
exports.LOI_CHECKPOINT_FRACTIONS.forEach((fraction, i) => {
|
|
41
|
+
checkpoints.push({ n: i + 2, dueAt: startedAt + loiMs * fraction });
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return checkpoints;
|
|
45
|
+
}
|
|
46
|
+
/** The record is kept until an hour past the last call it still owes. */
|
|
47
|
+
static expiryFor(checkpoints) {
|
|
48
|
+
return Math.max(...checkpoints.map(c => c.dueAt)) + exports.SCHEDULE_TTL_MS;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Reads the record for this transaction, or null when it is absent, unreadable
|
|
52
|
+
* or expired. Anything unusable is evicted rather than driven off, so the next
|
|
53
|
+
* `resumeOrCreate` starts a clean schedule.
|
|
54
|
+
*/
|
|
55
|
+
read() {
|
|
56
|
+
const record = this.storage.get(this.key);
|
|
57
|
+
const isUnusable = !record
|
|
58
|
+
|| new validation_1.ScheduleRecordShape(record, this.key).validate() !== undefined
|
|
59
|
+
|| record.expiry <= this.now();
|
|
60
|
+
if (isUnusable) {
|
|
61
|
+
this.storage.remove(this.key);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return record;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Returns the existing record for this transaction, or creates one when the
|
|
68
|
+
* transaction has not been seen on this origin yet.
|
|
69
|
+
*
|
|
70
|
+
* Resuming deliberately keeps the stored `startedAt` and `checkpoints`: the
|
|
71
|
+
* checkpoints stay on the clock started by the very first page load, so a
|
|
72
|
+
* respondent cannot reset the schedule by reloading.
|
|
73
|
+
*/
|
|
74
|
+
resumeOrCreate() {
|
|
75
|
+
const existing = this.read();
|
|
76
|
+
if (existing)
|
|
77
|
+
return existing;
|
|
78
|
+
const startedAt = this.now();
|
|
79
|
+
const checkpoints = ScheduleStore.buildCheckpoints(startedAt, null);
|
|
80
|
+
const record = {
|
|
81
|
+
surveyId: this.surveyId,
|
|
82
|
+
loi: null,
|
|
83
|
+
startedAt,
|
|
84
|
+
checkpoints,
|
|
85
|
+
sent: 0,
|
|
86
|
+
expiry: ScheduleStore.expiryFor(checkpoints),
|
|
87
|
+
lastResponse: null,
|
|
88
|
+
transactionId: null,
|
|
89
|
+
};
|
|
90
|
+
this.storage.set(this.key, record);
|
|
91
|
+
return record;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Adds the LOI-derived checkpoints, measured from the original `startedAt` so
|
|
95
|
+
* applying the LOI later does not push the schedule back. Ignored when an LOI
|
|
96
|
+
* is already set — renumbering could disturb sent calls.
|
|
97
|
+
*/
|
|
98
|
+
applyLoi(loi) {
|
|
99
|
+
const record = this.read();
|
|
100
|
+
if (!record || record.loi !== null)
|
|
101
|
+
return record;
|
|
102
|
+
record.loi = loi;
|
|
103
|
+
record.checkpoints = ScheduleStore.buildCheckpoints(record.startedAt, loi);
|
|
104
|
+
record.expiry = ScheduleStore.expiryFor(record.checkpoints);
|
|
105
|
+
this.storage.set(this.key, record);
|
|
106
|
+
return record;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Marks checkpoint `n` as delivered. Written as a max so an out-of-order
|
|
110
|
+
* write — two tabs firing around the same due time — can never lower the
|
|
111
|
+
* counter and re-open a checkpoint that already went out.
|
|
112
|
+
*/
|
|
113
|
+
markSent(n) {
|
|
114
|
+
const record = this.read();
|
|
115
|
+
if (!record)
|
|
116
|
+
return null;
|
|
117
|
+
record.sent = Math.max(record.sent, n);
|
|
118
|
+
this.storage.set(this.key, record);
|
|
119
|
+
return record;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Stores the latest Device-Intelligence response alongside the schedule so it
|
|
123
|
+
* can be replayed on the next page load.
|
|
124
|
+
*/
|
|
125
|
+
storeResponse(response) {
|
|
126
|
+
const record = this.read();
|
|
127
|
+
if (!record)
|
|
128
|
+
return;
|
|
129
|
+
record.lastResponse = response;
|
|
130
|
+
this.storage.set(this.key, record);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Stores the transaction id returned alongside the schedule.
|
|
134
|
+
*/
|
|
135
|
+
storeTransactionId(transactionId) {
|
|
136
|
+
const record = this.read();
|
|
137
|
+
if (!record)
|
|
138
|
+
return;
|
|
139
|
+
record.transactionId = transactionId;
|
|
140
|
+
this.storage.set(this.key, record);
|
|
141
|
+
}
|
|
142
|
+
now() {
|
|
143
|
+
return new Date().getTime();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
exports.ScheduleStore = ScheduleStore;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { GetStorage, RemoveStorage, SetStorage } from '../protocols/cache';
|
|
2
|
+
/**
|
|
3
|
+
* Keeps a snapshot of live in-memory state mirrored into storage on a timer, so
|
|
4
|
+
* a page navigation can resume from it. Polling rather than writing on change:
|
|
5
|
+
* the state it mirrors is updated by high-frequency input events, and a write
|
|
6
|
+
* per event would cost far more than a write per tick.
|
|
7
|
+
*/
|
|
8
|
+
export declare class SnapshotStore<T extends object> {
|
|
9
|
+
private readonly key;
|
|
10
|
+
private readonly getData;
|
|
11
|
+
private readonly storage;
|
|
12
|
+
private readonly intervalMs;
|
|
13
|
+
private interval;
|
|
14
|
+
constructor(key: string, getData: () => T, storage: GetStorage & SetStorage & RemoveStorage, intervalMs?: number);
|
|
15
|
+
start(): void;
|
|
16
|
+
stop(): void;
|
|
17
|
+
read(): T | null;
|
|
18
|
+
clear(): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SnapshotStore = void 0;
|
|
4
|
+
const DEFAULT_PERSIST_INTERVAL_MS = 100;
|
|
5
|
+
/**
|
|
6
|
+
* Keeps a snapshot of live in-memory state mirrored into storage on a timer, so
|
|
7
|
+
* a page navigation can resume from it. Polling rather than writing on change:
|
|
8
|
+
* the state it mirrors is updated by high-frequency input events, and a write
|
|
9
|
+
* per event would cost far more than a write per tick.
|
|
10
|
+
*/
|
|
11
|
+
class SnapshotStore {
|
|
12
|
+
constructor(key, getData, storage, intervalMs = DEFAULT_PERSIST_INTERVAL_MS) {
|
|
13
|
+
this.key = key;
|
|
14
|
+
this.getData = getData;
|
|
15
|
+
this.storage = storage;
|
|
16
|
+
this.intervalMs = intervalMs;
|
|
17
|
+
this.interval = null;
|
|
18
|
+
}
|
|
19
|
+
start() {
|
|
20
|
+
this.stop();
|
|
21
|
+
this.interval = setInterval(() => this.storage.set(this.key, this.getData()), this.intervalMs);
|
|
22
|
+
}
|
|
23
|
+
stop() {
|
|
24
|
+
if (this.interval !== null) {
|
|
25
|
+
clearInterval(this.interval);
|
|
26
|
+
this.interval = null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
read() {
|
|
30
|
+
return this.storage.get(this.key);
|
|
31
|
+
}
|
|
32
|
+
clear() {
|
|
33
|
+
this.storage.remove(this.key);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.SnapshotStore = SnapshotStore;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type NavigationHandler = () => void;
|
|
2
|
+
/**
|
|
3
|
+
* Detects SPA navigations (back/forward, hash changes, pushState/replaceState)
|
|
4
|
+
* so consumers can re-run logic that would otherwise only run on full page loads.
|
|
5
|
+
*
|
|
6
|
+
* Browser frameworks like Next.js, Nuxt, and SvelteKit perform client-side
|
|
7
|
+
* routing that doesn't reload the JS module, leaving any setTimeout-based
|
|
8
|
+
* collection windows running against stale state. Hooking into the History API
|
|
9
|
+
* lets us reset that window when the URL actually changes.
|
|
10
|
+
*/
|
|
11
|
+
export declare class SpaNavigationDetector {
|
|
12
|
+
private static installed;
|
|
13
|
+
install(handler: NavigationHandler): void;
|
|
14
|
+
}
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SpaNavigationDetector = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Detects SPA navigations (back/forward, hash changes, pushState/replaceState)
|
|
6
|
+
* so consumers can re-run logic that would otherwise only run on full page loads.
|
|
7
|
+
*
|
|
8
|
+
* Browser frameworks like Next.js, Nuxt, and SvelteKit perform client-side
|
|
9
|
+
* routing that doesn't reload the JS module, leaving any setTimeout-based
|
|
10
|
+
* collection windows running against stale state. Hooking into the History API
|
|
11
|
+
* lets us reset that window when the URL actually changes.
|
|
12
|
+
*/
|
|
13
|
+
class SpaNavigationDetector {
|
|
14
|
+
install(handler) {
|
|
15
|
+
if (SpaNavigationDetector.installed)
|
|
16
|
+
return;
|
|
17
|
+
if (typeof window === 'undefined' || typeof history === 'undefined')
|
|
18
|
+
return;
|
|
19
|
+
SpaNavigationDetector.installed = true;
|
|
20
|
+
window.addEventListener('popstate', handler);
|
|
21
|
+
window.addEventListener('hashchange', handler);
|
|
22
|
+
try {
|
|
23
|
+
const origPush = history.pushState.bind(history);
|
|
24
|
+
history.pushState = (...args) => {
|
|
25
|
+
origPush(...args);
|
|
26
|
+
handler();
|
|
27
|
+
};
|
|
28
|
+
const origReplace = history.replaceState.bind(history);
|
|
29
|
+
history.replaceState = (...args) => {
|
|
30
|
+
origReplace(...args);
|
|
31
|
+
handler();
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (_a) {
|
|
35
|
+
// history may be sealed in some envs; popstate/hashchange still work
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.SpaNavigationDetector = SpaNavigationDetector;
|
|
40
|
+
SpaNavigationDetector.installed = false;
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public entry point of the bundle.
|
|
3
|
+
*
|
|
4
|
+
* Two responsibilities and nothing else: re-export what consumers are allowed to
|
|
5
|
+
* reach, and run the self-initializing script tag. Everything with behaviour
|
|
6
|
+
* lives behind a layer — `application` for the Device-Identifier and the
|
|
7
|
+
* bootstrap that drives it, `domain` for the services they are built from,
|
|
8
|
+
* `validation` for the rules.
|
|
9
|
+
*/
|
|
10
|
+
import { DeviceIdentifier } from './application/device-identifier';
|
|
11
|
+
export { DeviceIdentifier };
|
|
12
|
+
/**
|
|
13
|
+
* Public type surface, so `@purespectrum1/device-identifier` is the only import
|
|
14
|
+
* path consumers need. The deep `lib/domain/...` paths still work, but they are
|
|
15
|
+
* no longer part of the contract.
|
|
16
|
+
*/
|
|
17
|
+
export type { DeviceIdentifierOptions, DeviceIdentifierConfig, DeviceIdentifierMetadata, TrackDetails, TrackDetailsContext, TransactionDetails, CreateTransaction, } from './domain/protocols/services/device-identifier';
|
|
18
|
+
export type { DisplayChecks, DisplayChecksInput, Language, NavigatorUABrandVersion, UADataValues, NavigatorUAData, NetworkInformation, UAParserDeviceType, NavigationTiming, NetworkTimes, } from './domain/protocols/services/device-fingerprint';
|
|
19
|
+
export type { AutoInitConfig, ScheduleRecord, ScheduleCheckpoint, } from './domain/protocols/services/single-script-injection';
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DeviceIdentifier = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Public entry point of the bundle.
|
|
6
|
+
*
|
|
7
|
+
* Two responsibilities and nothing else: re-export what consumers are allowed to
|
|
8
|
+
* reach, and run the self-initializing script tag. Everything with behaviour
|
|
9
|
+
* lives behind a layer — `application` for the Device-Identifier and the
|
|
10
|
+
* bootstrap that drives it, `domain` for the services they are built from,
|
|
11
|
+
* `validation` for the rules.
|
|
12
|
+
*/
|
|
13
|
+
const device_identifier_1 = require("./application/device-identifier");
|
|
14
|
+
Object.defineProperty(exports, "DeviceIdentifier", { enumerable: true, get: function () { return device_identifier_1.DeviceIdentifier; } });
|
|
15
|
+
const auto_init_1 = require("./application/auto-init");
|
|
16
|
+
/**
|
|
17
|
+
* Self-initializing script tag.
|
|
18
|
+
*
|
|
19
|
+
* When the tag that loaded this bundle carries `data-auto-init="true"`, the
|
|
20
|
+
* bundle bootstraps itself from its own `data-*` attributes and drives the three
|
|
21
|
+
* Device-Intelligence calls off an LOI-derived schedule — the buyer writes no
|
|
22
|
+
* bootstrap code and no `trackDetails` call at all.
|
|
23
|
+
*
|
|
24
|
+
* Without that attribute this is a no-op, which is what keeps the existing
|
|
25
|
+
* integration path unchanged.
|
|
26
|
+
*/
|
|
27
|
+
new auto_init_1.AutoInitService(config => new device_identifier_1.DeviceIdentifier({ singleScriptInjection: true }, { token: config.token, env: config.env })).run();
|
|
@@ -0,0 +1,19 @@
|
|
|
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("./web-storage-adapter"), exports);
|
|
18
|
+
__exportStar(require("./local-storage-adapter"), exports);
|
|
19
|
+
__exportStar(require("./session-storage-adapter"), exports);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { WebStorageAdapter } from './web-storage-adapter';
|
|
2
|
+
/**
|
|
3
|
+
* Persists across pages, tabs and browser restarts. Used for records that must
|
|
4
|
+
* outlive the tab, such as the checkpoint schedule.
|
|
5
|
+
*/
|
|
6
|
+
export declare class LocalStorageAdapter extends WebStorageAdapter {
|
|
7
|
+
protected store(): Storage;
|
|
8
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LocalStorageAdapter = void 0;
|
|
4
|
+
const web_storage_adapter_1 = require("./web-storage-adapter");
|
|
5
|
+
/**
|
|
6
|
+
* Persists across pages, tabs and browser restarts. Used for records that must
|
|
7
|
+
* outlive the tab, such as the checkpoint schedule.
|
|
8
|
+
*/
|
|
9
|
+
class LocalStorageAdapter extends web_storage_adapter_1.WebStorageAdapter {
|
|
10
|
+
store() {
|
|
11
|
+
return localStorage;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.LocalStorageAdapter = LocalStorageAdapter;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { WebStorageAdapter } from './web-storage-adapter';
|
|
2
|
+
/**
|
|
3
|
+
* Scoped to the tab and cleared when it closes. Used for state that should not
|
|
4
|
+
* survive the session, such as the in-progress behavioural-biometrics buffers.
|
|
5
|
+
*/
|
|
6
|
+
export declare class SessionStorageAdapter extends WebStorageAdapter {
|
|
7
|
+
protected store(): Storage;
|
|
8
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SessionStorageAdapter = void 0;
|
|
4
|
+
const web_storage_adapter_1 = require("./web-storage-adapter");
|
|
5
|
+
/**
|
|
6
|
+
* Scoped to the tab and cleared when it closes. Used for state that should not
|
|
7
|
+
* survive the session, such as the in-progress behavioural-biometrics buffers.
|
|
8
|
+
*/
|
|
9
|
+
class SessionStorageAdapter extends web_storage_adapter_1.WebStorageAdapter {
|
|
10
|
+
store() {
|
|
11
|
+
return sessionStorage;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.SessionStorageAdapter = SessionStorageAdapter;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GetStorage, RemoveStorage, SetStorage } from '../../domain/protocols/cache';
|
|
2
|
+
/**
|
|
3
|
+
* Shared behaviour of the Web Storage adapters. Owns serialization, so callers
|
|
4
|
+
* pass objects, and availability: access throws in private modes and disabled
|
|
5
|
+
* WebViews, so failures degrade to "nothing stored" instead of reaching the page.
|
|
6
|
+
*/
|
|
7
|
+
export declare abstract class WebStorageAdapter implements GetStorage, SetStorage, RemoveStorage {
|
|
8
|
+
/** Resolved per operation, inside the guard — merely naming it can throw. */
|
|
9
|
+
protected abstract store(): Storage;
|
|
10
|
+
get(key: string): any;
|
|
11
|
+
set(key: string, value: object): void;
|
|
12
|
+
remove(key: string): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WebStorageAdapter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Shared behaviour of the Web Storage adapters. Owns serialization, so callers
|
|
6
|
+
* pass objects, and availability: access throws in private modes and disabled
|
|
7
|
+
* WebViews, so failures degrade to "nothing stored" instead of reaching the page.
|
|
8
|
+
*/
|
|
9
|
+
class WebStorageAdapter {
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
get(key) {
|
|
12
|
+
try {
|
|
13
|
+
const raw = this.store().getItem(key);
|
|
14
|
+
return raw ? JSON.parse(raw) : null;
|
|
15
|
+
}
|
|
16
|
+
catch (_a) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
set(key, value) {
|
|
21
|
+
try {
|
|
22
|
+
this.store().setItem(key, JSON.stringify(value));
|
|
23
|
+
}
|
|
24
|
+
catch ( /* storage disabled or full — the value simply does not persist */_a) { /* storage disabled or full — the value simply does not persist */ }
|
|
25
|
+
}
|
|
26
|
+
remove(key) {
|
|
27
|
+
try {
|
|
28
|
+
this.store().removeItem(key);
|
|
29
|
+
}
|
|
30
|
+
catch ( /* storage disabled — there is nothing to remove */_a) { /* storage disabled — there is nothing to remove */ }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.WebStorageAdapter = WebStorageAdapter;
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.AxiosHttpClient = void 0;
|
|
16
|
+
const axios_1 = __importDefault(require("axios"));
|
|
17
|
+
class AxiosHttpClient {
|
|
18
|
+
request(data) {
|
|
19
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
20
|
+
let axiosResponse;
|
|
21
|
+
try {
|
|
22
|
+
axiosResponse = yield axios_1.default.request({
|
|
23
|
+
url: data.url,
|
|
24
|
+
method: data.method,
|
|
25
|
+
data: data.body,
|
|
26
|
+
headers: data.headers
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
axiosResponse = error.response;
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
statusCode: axiosResponse.status,
|
|
34
|
+
body: axiosResponse.data
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.AxiosHttpClient = AxiosHttpClient;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Validator } from './protocols';
|
|
2
|
+
/**
|
|
3
|
+
* Fluent assembly of the rules that apply to one field.
|
|
4
|
+
*
|
|
5
|
+
* `ValidationBuilder.of({ value, fieldName }).required().interpolated().build()`
|
|
6
|
+
* reads as the requirement itself, and keeps the field name stated once instead
|
|
7
|
+
* of repeated into every rule.
|
|
8
|
+
*/
|
|
9
|
+
export declare class ValidationBuilder {
|
|
10
|
+
private readonly value;
|
|
11
|
+
private readonly fieldName;
|
|
12
|
+
private readonly validators;
|
|
13
|
+
private skipWhenEmpty;
|
|
14
|
+
private constructor();
|
|
15
|
+
static of({ value, fieldName }: {
|
|
16
|
+
value: any;
|
|
17
|
+
fieldName: string;
|
|
18
|
+
}): ValidationBuilder;
|
|
19
|
+
/**
|
|
20
|
+
* An absent or blank value passes every rule; the rules only apply to a value
|
|
21
|
+
* that is actually there.
|
|
22
|
+
*/
|
|
23
|
+
optional(): ValidationBuilder;
|
|
24
|
+
/** The value must be a non-empty, non-whitespace string. */
|
|
25
|
+
required(): ValidationBuilder;
|
|
26
|
+
/** The value must be a finite number greater than zero. */
|
|
27
|
+
positiveNumber(): ValidationBuilder;
|
|
28
|
+
/** The survey platform must have interpolated the value. */
|
|
29
|
+
interpolated(): ValidationBuilder;
|
|
30
|
+
/** When present, the value must parse to a JSON object. */
|
|
31
|
+
jsonObject(): ValidationBuilder;
|
|
32
|
+
/** The value must be shaped like an ISO 3166-1 alpha-2 country code. */
|
|
33
|
+
countryCode(): ValidationBuilder;
|
|
34
|
+
/** The value must be shaped like an ISO 639-1 language code. */
|
|
35
|
+
languageCode(): ValidationBuilder;
|
|
36
|
+
/** The value must name a function on `window`. */
|
|
37
|
+
globalFunction(): ValidationBuilder;
|
|
38
|
+
/** The value must be a usable schedule record. */
|
|
39
|
+
scheduleRecord(): ValidationBuilder;
|
|
40
|
+
build(): Validator[];
|
|
41
|
+
/** Assembles the rules and runs them, reporting the first failure. */
|
|
42
|
+
validate(): Error | undefined;
|
|
43
|
+
}
|