@ztimson/momentum 1.5.7 → 1.5.9
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/dist/analytics.d.ts +17 -3
- package/dist/analytics.d.ts.map +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/captcha.d.ts +2 -2
- package/dist/captcha.d.ts.map +1 -1
- package/dist/client.d.ts +0 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/index.js +331 -202
- package/dist/index.mjs +331 -202
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -235,30 +235,37 @@ function includes(target, values, allowMissing = false) {
|
|
|
235
235
|
if (type == "function") return target.toString() == values.toString();
|
|
236
236
|
return target == values;
|
|
237
237
|
}
|
|
238
|
-
function isEqual(a, b,
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
238
|
+
function isEqual(a, b, compare) {
|
|
239
|
+
const eq = (a2, b2, compare2, seen, path) => {
|
|
240
|
+
if (Array.isArray(compare2)) {
|
|
241
|
+
if (compare2.some((p) => new RegExp(`^${p.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "[^.]*")}(\\.|$)`).test(path))) return true;
|
|
242
|
+
} else if (typeof compare2 === "function") {
|
|
243
|
+
const r = compare2(path, a2, b2);
|
|
244
|
+
if (r !== void 0) return r;
|
|
245
|
+
}
|
|
246
|
+
if (a2 === b2) return true;
|
|
247
|
+
if (a2 instanceof Date && b2 instanceof Date) return a2.getTime() === b2.getTime();
|
|
248
|
+
if (a2 instanceof RegExp && b2 instanceof RegExp) return a2.source === b2.source && a2.flags === b2.flags;
|
|
249
|
+
if (typeof a2 !== "object" || a2 === null || typeof b2 !== "object" || b2 === null) {
|
|
250
|
+
if (Number.isNaN(a2) && Number.isNaN(b2)) return true;
|
|
251
|
+
if (typeof a2 === "function" && typeof b2 === "function") return a2.toString() === b2.toString();
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
if (seen.has(a2)) return seen.get(a2) === b2;
|
|
255
|
+
seen.set(a2, b2);
|
|
256
|
+
const isArrayA = Array.isArray(a2), isArrayB = Array.isArray(b2);
|
|
257
|
+
if (isArrayA && isArrayB) {
|
|
258
|
+
if (a2.length !== b2.length) return false;
|
|
259
|
+
for (let i = 0; i < a2.length; i++) if (!eq(a2[i], b2[i], compare2, seen, path ? `${path}.${i}` : `${i}`)) return false;
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
if (isArrayA !== isArrayB) return false;
|
|
263
|
+
const keysA = Object.keys(a2), keysB = Object.keys(b2);
|
|
264
|
+
if (keysA.length !== keysB.length) return false;
|
|
265
|
+
for (const key of keysA) if (!Object.prototype.hasOwnProperty.call(b2, key) || !eq(a2[key], b2[key], compare2, seen, path ? `${path}.${key}` : key)) return false;
|
|
254
266
|
return true;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const keysA = Object.keys(a);
|
|
258
|
-
const keysB = Object.keys(b);
|
|
259
|
-
if (keysA.length !== keysB.length) return false;
|
|
260
|
-
for (const key of keysA) if (!Object.prototype.hasOwnProperty.call(b, key) || !isEqual(a[key], b[key], seen)) return false;
|
|
261
|
-
return true;
|
|
267
|
+
};
|
|
268
|
+
return eq(a, b, compare, /* @__PURE__ */ new WeakMap(), "");
|
|
262
269
|
}
|
|
263
270
|
var ASet = class ASet extends Array {
|
|
264
271
|
/** Number of elements in set */
|
|
@@ -1133,34 +1140,88 @@ var Table = class {
|
|
|
1133
1140
|
};
|
|
1134
1141
|
var PromiseProgress = class PromiseProgress extends Promise {
|
|
1135
1142
|
listeners = [];
|
|
1136
|
-
|
|
1143
|
+
_meta = {
|
|
1144
|
+
start: Date.now(),
|
|
1145
|
+
end: null,
|
|
1146
|
+
elapsed: 0,
|
|
1147
|
+
speed: 0,
|
|
1148
|
+
progress: 0,
|
|
1149
|
+
size: 0,
|
|
1150
|
+
total: 0,
|
|
1151
|
+
eta: null
|
|
1152
|
+
};
|
|
1153
|
+
get meta() {
|
|
1154
|
+
return this._meta;
|
|
1155
|
+
}
|
|
1156
|
+
get start() {
|
|
1157
|
+
return this._meta.start;
|
|
1158
|
+
}
|
|
1159
|
+
get end() {
|
|
1160
|
+
return this._meta.end;
|
|
1161
|
+
}
|
|
1162
|
+
get elapsed() {
|
|
1163
|
+
return this._meta.elapsed;
|
|
1164
|
+
}
|
|
1165
|
+
get speed() {
|
|
1166
|
+
return this._meta.speed;
|
|
1167
|
+
}
|
|
1137
1168
|
get progress() {
|
|
1138
|
-
return this.
|
|
1169
|
+
return this._meta.progress;
|
|
1139
1170
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1171
|
+
get size() {
|
|
1172
|
+
return this._meta.size;
|
|
1173
|
+
}
|
|
1174
|
+
get total() {
|
|
1175
|
+
return this._meta.total;
|
|
1176
|
+
}
|
|
1177
|
+
get eta() {
|
|
1178
|
+
return this._meta.eta;
|
|
1179
|
+
}
|
|
1180
|
+
update(size, total) {
|
|
1181
|
+
const elapsed = (Date.now() - this._meta.start) / 1e3;
|
|
1182
|
+
const speed = elapsed ? size / elapsed : 0;
|
|
1183
|
+
const progress = total ? size / total : this._meta.progress;
|
|
1184
|
+
this._meta = {
|
|
1185
|
+
...this._meta,
|
|
1186
|
+
size,
|
|
1187
|
+
total,
|
|
1188
|
+
progress,
|
|
1189
|
+
elapsed,
|
|
1190
|
+
speed,
|
|
1191
|
+
eta: speed && total ? (total - size) / speed : null
|
|
1192
|
+
};
|
|
1193
|
+
(this.listeners ||= []).forEach((l) => l(this._meta));
|
|
1144
1194
|
}
|
|
1145
1195
|
constructor(executor) {
|
|
1146
|
-
super((resolve, reject) => executor((value) =>
|
|
1196
|
+
super((resolve, reject) => executor((value) => {
|
|
1197
|
+
this._meta = {
|
|
1198
|
+
...this._meta,
|
|
1199
|
+
end: Date.now()
|
|
1200
|
+
};
|
|
1201
|
+
resolve(value);
|
|
1202
|
+
}, (reason) => {
|
|
1203
|
+
this._meta = {
|
|
1204
|
+
...this._meta,
|
|
1205
|
+
end: Date.now()
|
|
1206
|
+
};
|
|
1207
|
+
reject(reason);
|
|
1208
|
+
}, (size, total) => this.update(size, total ?? this._meta.total)));
|
|
1147
1209
|
}
|
|
1148
1210
|
static from(promise) {
|
|
1149
1211
|
if (promise instanceof PromiseProgress) return promise;
|
|
1150
|
-
return new PromiseProgress((res, rej) => promise.then(
|
|
1212
|
+
return new PromiseProgress((res, rej) => promise.then(res).catch(rej));
|
|
1151
1213
|
}
|
|
1152
1214
|
from(promise) {
|
|
1153
1215
|
const newPromise = PromiseProgress.from(promise);
|
|
1154
|
-
this.onProgress((
|
|
1216
|
+
this.onProgress((m) => newPromise.update(m.size, m.total));
|
|
1155
1217
|
return newPromise;
|
|
1156
1218
|
}
|
|
1157
1219
|
onProgress(callback) {
|
|
1158
|
-
this.listeners.push(callback);
|
|
1220
|
+
(this.listeners ||= []).push(callback);
|
|
1159
1221
|
return this;
|
|
1160
1222
|
}
|
|
1161
1223
|
then(res, rej) {
|
|
1162
|
-
|
|
1163
|
-
return this.from(resp);
|
|
1224
|
+
return this.from(super.then(res, rej));
|
|
1164
1225
|
}
|
|
1165
1226
|
catch(rej) {
|
|
1166
1227
|
return this.from(super.catch(rej));
|
|
@@ -1209,7 +1270,7 @@ function uploadWithProgress(options) {
|
|
|
1209
1270
|
const formData2 = new FormData();
|
|
1210
1271
|
options.files.forEach((f) => formData2.append("files", f));
|
|
1211
1272
|
xhr.withCredentials = !!options.withCredentials;
|
|
1212
|
-
xhr.upload.addEventListener("progress", (event) => event.lengthComputable ? prog(event.loaded
|
|
1273
|
+
xhr.upload.addEventListener("progress", (event) => event.lengthComputable ? prog(event.loaded, event.total) : null);
|
|
1213
1274
|
xhr.addEventListener("loadend", () => res(JSONAttemptParse(xhr.responseText)));
|
|
1214
1275
|
xhr.addEventListener("error", () => rej(JSONAttemptParse(xhr.responseText)));
|
|
1215
1276
|
xhr.addEventListener("timeout", () => rej({ error: "Request timed out" }));
|
|
@@ -2247,6 +2308,182 @@ var Ai = class extends PathEventEmitter {
|
|
|
2247
2308
|
}
|
|
2248
2309
|
};
|
|
2249
2310
|
//#endregion
|
|
2311
|
+
//#region src/captcha.ts
|
|
2312
|
+
var Captcha = class {
|
|
2313
|
+
start = Date.now();
|
|
2314
|
+
events = {
|
|
2315
|
+
mouse: [],
|
|
2316
|
+
keys: [],
|
|
2317
|
+
clicks: [],
|
|
2318
|
+
scroll: [],
|
|
2319
|
+
focus: []
|
|
2320
|
+
};
|
|
2321
|
+
lastMouse = null;
|
|
2322
|
+
velocities = [];
|
|
2323
|
+
_score = .5;
|
|
2324
|
+
onScoreChange;
|
|
2325
|
+
set score(v) {
|
|
2326
|
+
const next = +Math.max(0, Math.min(1, Number.isFinite(v) ? v : .5)).toFixed(2);
|
|
2327
|
+
if (next == this._score) return;
|
|
2328
|
+
this._score = next;
|
|
2329
|
+
this.onScoreChange?.(next);
|
|
2330
|
+
}
|
|
2331
|
+
get score() {
|
|
2332
|
+
return this._score;
|
|
2333
|
+
}
|
|
2334
|
+
get verdict() {
|
|
2335
|
+
if (this.score >= .45 && this.score <= .55) return "unknown";
|
|
2336
|
+
if (this.score > .5) return "human";
|
|
2337
|
+
return "bot";
|
|
2338
|
+
}
|
|
2339
|
+
constructor(onScoreChange) {
|
|
2340
|
+
this.onScoreChange = onScoreChange;
|
|
2341
|
+
if (typeof window === "undefined" || !document) {
|
|
2342
|
+
this._score = 0;
|
|
2343
|
+
return;
|
|
2344
|
+
}
|
|
2345
|
+
document.addEventListener("pointermove", (e) => {
|
|
2346
|
+
const now = Date.now();
|
|
2347
|
+
const curr = {
|
|
2348
|
+
x: e.clientX,
|
|
2349
|
+
y: e.clientY,
|
|
2350
|
+
t: now
|
|
2351
|
+
};
|
|
2352
|
+
this.events.mouse.push(curr);
|
|
2353
|
+
if (this.lastMouse) {
|
|
2354
|
+
const dt = now - this.lastMouse.t;
|
|
2355
|
+
const dx = curr.x - this.lastMouse.x;
|
|
2356
|
+
const dy = curr.y - this.lastMouse.y;
|
|
2357
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
2358
|
+
if (dt > 0 && dt < 1e3) this.velocities.push(dist / dt);
|
|
2359
|
+
}
|
|
2360
|
+
this.lastMouse = curr;
|
|
2361
|
+
if (this.events.mouse.length > 80) this.events.mouse.shift();
|
|
2362
|
+
if (this.velocities.length > 40) this.velocities.shift();
|
|
2363
|
+
}, { passive: true });
|
|
2364
|
+
document.addEventListener("keydown", () => {
|
|
2365
|
+
this.events.keys.push(Date.now());
|
|
2366
|
+
if (this.events.keys.length > 30) this.events.keys.shift();
|
|
2367
|
+
this.update();
|
|
2368
|
+
});
|
|
2369
|
+
document.addEventListener("pointerdown", (e) => {
|
|
2370
|
+
this.events.clicks.push({
|
|
2371
|
+
x: e.clientX,
|
|
2372
|
+
y: e.clientY,
|
|
2373
|
+
t: Date.now()
|
|
2374
|
+
});
|
|
2375
|
+
if (this.events.clicks.length > 20) this.events.clicks.shift();
|
|
2376
|
+
this.update();
|
|
2377
|
+
}, { passive: true });
|
|
2378
|
+
window.addEventListener("scroll", () => {
|
|
2379
|
+
this.events.scroll.push({
|
|
2380
|
+
x: window.scrollX,
|
|
2381
|
+
y: window.scrollY,
|
|
2382
|
+
t: Date.now()
|
|
2383
|
+
});
|
|
2384
|
+
if (this.events.scroll.length > 20) this.events.scroll.shift();
|
|
2385
|
+
}, { passive: true });
|
|
2386
|
+
document.addEventListener("visibilitychange", () => {
|
|
2387
|
+
this.events.focus.push({
|
|
2388
|
+
visible: document.visibilityState == "visible",
|
|
2389
|
+
t: Date.now()
|
|
2390
|
+
});
|
|
2391
|
+
if (this.events.focus.length > 20) this.events.focus.shift();
|
|
2392
|
+
this.update();
|
|
2393
|
+
});
|
|
2394
|
+
window.addEventListener("focus", () => {
|
|
2395
|
+
this.events.focus.push({
|
|
2396
|
+
visible: true,
|
|
2397
|
+
t: Date.now()
|
|
2398
|
+
});
|
|
2399
|
+
this.update();
|
|
2400
|
+
});
|
|
2401
|
+
window.addEventListener("blur", () => {
|
|
2402
|
+
this.events.focus.push({
|
|
2403
|
+
visible: false,
|
|
2404
|
+
t: Date.now()
|
|
2405
|
+
});
|
|
2406
|
+
this.update();
|
|
2407
|
+
});
|
|
2408
|
+
setInterval(() => this.update(), 2e3);
|
|
2409
|
+
}
|
|
2410
|
+
update() {
|
|
2411
|
+
if (navigator?.webdriver) {
|
|
2412
|
+
this._score = 0;
|
|
2413
|
+
this.onScoreChange?.(0);
|
|
2414
|
+
return;
|
|
2415
|
+
}
|
|
2416
|
+
let delta = 0;
|
|
2417
|
+
const elapsed = (Date.now() - this.start) / 1e3;
|
|
2418
|
+
delta += elapsed > 30 ? .08 : elapsed > 10 ? .04 : elapsed > 3 ? .01 : -.05;
|
|
2419
|
+
if (this.events.mouse.length >= 6) {
|
|
2420
|
+
const recent = this.events.mouse.slice(-30);
|
|
2421
|
+
const buckets = new Array(8).fill(0);
|
|
2422
|
+
for (let i = 1; i < recent.length; i++) {
|
|
2423
|
+
const dx = recent[i].x - recent[i - 1].x;
|
|
2424
|
+
const dy = recent[i].y - recent[i - 1].y;
|
|
2425
|
+
if (dx == 0 && dy == 0) continue;
|
|
2426
|
+
let angle = Math.atan2(dy, dx);
|
|
2427
|
+
if (angle < 0) angle += Math.PI * 2;
|
|
2428
|
+
buckets[Math.min(7, Math.floor(angle / (Math.PI * 2) * 8))]++;
|
|
2429
|
+
}
|
|
2430
|
+
const total = buckets.reduce((a, b) => a + b, 0);
|
|
2431
|
+
if (total >= 4) {
|
|
2432
|
+
let entropy = 0;
|
|
2433
|
+
for (const count of buckets) {
|
|
2434
|
+
if (!count) continue;
|
|
2435
|
+
const p = count / total;
|
|
2436
|
+
entropy -= p * Math.log2(p);
|
|
2437
|
+
}
|
|
2438
|
+
entropy /= Math.log2(8);
|
|
2439
|
+
if (entropy > .65) delta += .12;
|
|
2440
|
+
else if (entropy > .4) delta += .05;
|
|
2441
|
+
else delta -= .07;
|
|
2442
|
+
}
|
|
2443
|
+
} else if (this.events.mouse.length == 0) delta -= .01;
|
|
2444
|
+
if (this.velocities.length >= 5) {
|
|
2445
|
+
const avg = this.velocities.reduce((a, b) => a + b, 0) / this.velocities.length;
|
|
2446
|
+
const variance = this.velocities.reduce((a, b) => a + Math.pow(b - avg, 2), 0) / this.velocities.length;
|
|
2447
|
+
const cv = avg > 0 ? Math.sqrt(variance) / avg : 0;
|
|
2448
|
+
if (cv > .45) delta += .1;
|
|
2449
|
+
else if (cv > .18) delta += .04;
|
|
2450
|
+
else if (avg > 0) delta -= .08;
|
|
2451
|
+
}
|
|
2452
|
+
if (this.events.clicks.length > 0) {
|
|
2453
|
+
const lastClick = this.events.clicks.at(-1);
|
|
2454
|
+
const mouseBeforeClick = this.events.mouse.some((m) => m.t < lastClick.t && lastClick.t - m.t < 2e3);
|
|
2455
|
+
delta += mouseBeforeClick ? .06 : -.08;
|
|
2456
|
+
const suspiciousClicks = this.events.clicks.filter((c) => Number.isInteger(c.x) && Number.isInteger(c.y) && c.x % 10 == 0 && c.y % 10 == 0).length;
|
|
2457
|
+
if (this.events.clicks.length >= 4 && suspiciousClicks / this.events.clicks.length > .5) delta -= .06;
|
|
2458
|
+
}
|
|
2459
|
+
if (this.events.keys.length >= 4) {
|
|
2460
|
+
const intervals = [];
|
|
2461
|
+
for (let i = 1; i < this.events.keys.length; i++) intervals.push(this.events.keys[i] - this.events.keys[i - 1]);
|
|
2462
|
+
const avg = intervals.reduce((a, b) => a + b, 0) / intervals.length;
|
|
2463
|
+
const stdDev = Math.sqrt(intervals.reduce((a, b) => a + Math.pow(b - avg, 2), 0) / intervals.length);
|
|
2464
|
+
if (avg < 20) delta -= .15;
|
|
2465
|
+
else if (stdDev < 5) delta -= .1;
|
|
2466
|
+
else if (avg < 50) delta -= .04;
|
|
2467
|
+
else if (avg < 1e3 && stdDev > 20) delta += .08;
|
|
2468
|
+
}
|
|
2469
|
+
if (this.events.scroll.length >= 4) {
|
|
2470
|
+
let directionChanges = 0;
|
|
2471
|
+
let previousDirection = 0;
|
|
2472
|
+
for (let i = 1; i < this.events.scroll.length; i++) {
|
|
2473
|
+
const direction = Math.sign(this.events.scroll[i].y - this.events.scroll[i - 1].y);
|
|
2474
|
+
if (direction && previousDirection && direction != previousDirection) directionChanges++;
|
|
2475
|
+
if (direction) previousDirection = direction;
|
|
2476
|
+
}
|
|
2477
|
+
if (directionChanges >= 2) delta += .04;
|
|
2478
|
+
}
|
|
2479
|
+
if (this.events.focus.length >= 2) {
|
|
2480
|
+
const visible = this.events.focus.filter((x) => x.visible).length;
|
|
2481
|
+
delta += visible / this.events.focus.length > .4 ? .02 : -.02;
|
|
2482
|
+
}
|
|
2483
|
+
this.score = this.score + delta * .25;
|
|
2484
|
+
}
|
|
2485
|
+
};
|
|
2486
|
+
//#endregion
|
|
2250
2487
|
//#region src/dialog.ts
|
|
2251
2488
|
var dialogCount = 0;
|
|
2252
2489
|
var closed = 0;
|
|
@@ -2459,6 +2696,16 @@ function createDialog(html, opts = {}) {
|
|
|
2459
2696
|
var Analytics = class extends AssetController {
|
|
2460
2697
|
momentum;
|
|
2461
2698
|
lastPage;
|
|
2699
|
+
captcha = new Captcha((score) => this.updateCaptcha(score));
|
|
2700
|
+
captchaSecret;
|
|
2701
|
+
captchaSequence = 0;
|
|
2702
|
+
captchaUpdate;
|
|
2703
|
+
get captchaScore() {
|
|
2704
|
+
return this.captcha.score;
|
|
2705
|
+
}
|
|
2706
|
+
get actorType() {
|
|
2707
|
+
return this.captcha.verdict;
|
|
2708
|
+
}
|
|
2462
2709
|
_ready;
|
|
2463
2710
|
ready = new Promise((res) => this._ready = res);
|
|
2464
2711
|
_performance = {
|
|
@@ -2537,6 +2784,9 @@ var Analytics = class extends AssetController {
|
|
|
2537
2784
|
this._id = "";
|
|
2538
2785
|
this._consent = void 0;
|
|
2539
2786
|
this._new = true;
|
|
2787
|
+
this.captchaSecret = void 0;
|
|
2788
|
+
this.captchaSequence = 0;
|
|
2789
|
+
delete this.momentum.api.headers["X-Captcha"];
|
|
2540
2790
|
});
|
|
2541
2791
|
this.momentum.auth.on("login:c", async () => {
|
|
2542
2792
|
if (this.consent) await this.init();
|
|
@@ -2613,7 +2863,7 @@ var Analytics = class extends AssetController {
|
|
|
2613
2863
|
async init() {
|
|
2614
2864
|
if (!this.momentum.opts.analytics) return;
|
|
2615
2865
|
const performance = await this.performance;
|
|
2616
|
-
|
|
2866
|
+
const response = await this.momentum.api.request({
|
|
2617
2867
|
url: `api/analytics`,
|
|
2618
2868
|
body: [
|
|
2619
2869
|
navigator?.language,
|
|
@@ -2631,6 +2881,10 @@ var Analytics = class extends AssetController {
|
|
|
2631
2881
|
performance.total
|
|
2632
2882
|
]
|
|
2633
2883
|
});
|
|
2884
|
+
this.id = response.id;
|
|
2885
|
+
this.captchaSecret = response.captchaSecret;
|
|
2886
|
+
this.captchaSequence = response.captchaSequence;
|
|
2887
|
+
await this.updateCaptcha(this.captcha.score, true);
|
|
2634
2888
|
if (this._ready) {
|
|
2635
2889
|
this._ready();
|
|
2636
2890
|
this._ready = null;
|
|
@@ -2640,6 +2894,25 @@ var Analytics = class extends AssetController {
|
|
|
2640
2894
|
window.addEventListener("beforeunload", () => this.page("CLOSED"));
|
|
2641
2895
|
return this.id;
|
|
2642
2896
|
}
|
|
2897
|
+
updateCaptcha(score, initial = false) {
|
|
2898
|
+
if (!this.captchaSecret || !this.id || !globalThis.crypto?.subtle) return Promise.resolve();
|
|
2899
|
+
const update = this.captchaUpdate?.catch(() => {}) || Promise.resolve();
|
|
2900
|
+
this.captchaUpdate = update.then(() => this.attestCaptcha(score, initial)).catch(() => {});
|
|
2901
|
+
return this.captchaUpdate;
|
|
2902
|
+
}
|
|
2903
|
+
async attestCaptcha(score, initial) {
|
|
2904
|
+
if (!this.captchaSecret || !this.id) return;
|
|
2905
|
+
if (!initial) this.captchaSequence++;
|
|
2906
|
+
const canonical = Math.max(0, Math.min(1, Number.isFinite(score) ? score : .5)).toFixed(2);
|
|
2907
|
+
const payload = `${canonical}:${this.captchaSequence}`;
|
|
2908
|
+
const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(this.captchaSecret), {
|
|
2909
|
+
name: "HMAC",
|
|
2910
|
+
hash: "SHA-256"
|
|
2911
|
+
}, false, ["sign"]);
|
|
2912
|
+
const signature = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(payload));
|
|
2913
|
+
const proof = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
2914
|
+
this.momentum.api.headers["X-Captcha"] = `${canonical},${this.captchaSequence},${proof}`;
|
|
2915
|
+
}
|
|
2643
2916
|
async consentPrompt(content) {
|
|
2644
2917
|
const settings = await this.momentum.settings.map();
|
|
2645
2918
|
const { close, dialog, result } = createDialog(`
|
|
@@ -3013,19 +3286,29 @@ var Auth = class extends PathEventEmitter {
|
|
|
3013
3286
|
loginRedirect(host = location.origin) {
|
|
3014
3287
|
return new Promise((res, rej) => {
|
|
3015
3288
|
let listener, win;
|
|
3289
|
+
const loginOrigin = new URL(this.momentum.opts.loginUrl, location.href).origin;
|
|
3290
|
+
const hostOrigin = new URL(host, location.href).origin;
|
|
3016
3291
|
window.addEventListener("message", listener = async (event) => {
|
|
3292
|
+
if (event.origin !== loginOrigin || event?.data?.sender !== loginOrigin) return;
|
|
3017
3293
|
const data = event?.data || {};
|
|
3018
|
-
if (
|
|
3019
|
-
|
|
3294
|
+
if (!data.token) {
|
|
3295
|
+
window.removeEventListener("message", listener);
|
|
3296
|
+
return rej(/* @__PURE__ */ new Error("Unknown response from login"));
|
|
3297
|
+
}
|
|
3020
3298
|
window.removeEventListener("message", listener);
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3299
|
+
try {
|
|
3300
|
+
await this.readSession(data.token, true);
|
|
3301
|
+
win?.close();
|
|
3302
|
+
res(data.token);
|
|
3303
|
+
} catch (err) {
|
|
3304
|
+
win?.close();
|
|
3305
|
+
rej(err);
|
|
3306
|
+
}
|
|
3024
3307
|
});
|
|
3025
|
-
win = window.open(`${this.momentum.opts.loginUrl}?redirect=postmessage&host=${
|
|
3308
|
+
win = window.open(`${this.momentum.opts.loginUrl}?redirect=postmessage&host=${encodeURIComponent(hostOrigin)}`, "_blank");
|
|
3026
3309
|
if (!win) {
|
|
3027
3310
|
window.removeEventListener("message", listener);
|
|
3028
|
-
return rej("Unable to open login");
|
|
3311
|
+
return rej(/* @__PURE__ */ new Error("Unable to open login"));
|
|
3029
3312
|
}
|
|
3030
3313
|
});
|
|
3031
3314
|
}
|
|
@@ -3151,174 +3434,20 @@ var Call = class extends PathEventEmitter {
|
|
|
3151
3434
|
}
|
|
3152
3435
|
};
|
|
3153
3436
|
//#endregion
|
|
3154
|
-
//#region src/captcha.ts
|
|
3155
|
-
var Captcha = class {
|
|
3156
|
-
start = Date.now();
|
|
3157
|
-
events = {
|
|
3158
|
-
mouse: [],
|
|
3159
|
-
keys: [],
|
|
3160
|
-
clicks: []
|
|
3161
|
-
};
|
|
3162
|
-
lastMouse = null;
|
|
3163
|
-
velocities = [];
|
|
3164
|
-
_score = .5;
|
|
3165
|
-
set score(v) {
|
|
3166
|
-
this._score = v;
|
|
3167
|
-
}
|
|
3168
|
-
get score() {
|
|
3169
|
-
return this._score;
|
|
3170
|
-
}
|
|
3171
|
-
get verdict() {
|
|
3172
|
-
if (this.score == .5) return "unknown";
|
|
3173
|
-
if (this.score > .5) return "human";
|
|
3174
|
-
return "bot";
|
|
3175
|
-
}
|
|
3176
|
-
constructor() {
|
|
3177
|
-
if (typeof window === "undefined" || !window.innerWidth || !this.validUserAgent()) {
|
|
3178
|
-
this.score = 0;
|
|
3179
|
-
return;
|
|
3180
|
-
}
|
|
3181
|
-
document.addEventListener("mousemove", (e) => {
|
|
3182
|
-
const now = Date.now();
|
|
3183
|
-
const curr = {
|
|
3184
|
-
x: e.clientX,
|
|
3185
|
-
y: e.clientY,
|
|
3186
|
-
t: now
|
|
3187
|
-
};
|
|
3188
|
-
this.events.mouse.push(curr);
|
|
3189
|
-
if (this.lastMouse) {
|
|
3190
|
-
const dt = now - this.lastMouse.t;
|
|
3191
|
-
const dx = curr.x - this.lastMouse.x;
|
|
3192
|
-
const dy = curr.y - this.lastMouse.y;
|
|
3193
|
-
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
3194
|
-
if (dt > 0) this.velocities.push(dist / dt);
|
|
3195
|
-
}
|
|
3196
|
-
this.lastMouse = curr;
|
|
3197
|
-
if (this.events.mouse.length > 50) this.events.mouse.shift();
|
|
3198
|
-
if (this.velocities.length > 30) this.velocities.shift();
|
|
3199
|
-
});
|
|
3200
|
-
document.addEventListener("keydown", (e) => {
|
|
3201
|
-
this.events.keys.push(Date.now());
|
|
3202
|
-
if (this.events.keys.length > 20) this.events.keys.shift();
|
|
3203
|
-
});
|
|
3204
|
-
document.addEventListener("click", (e) => {
|
|
3205
|
-
this.events.clicks.push({
|
|
3206
|
-
x: e.clientX,
|
|
3207
|
-
y: e.clientY,
|
|
3208
|
-
t: Date.now()
|
|
3209
|
-
});
|
|
3210
|
-
if (this.events.clicks.length > 15) this.events.clicks.shift();
|
|
3211
|
-
});
|
|
3212
|
-
setInterval(() => this.update(), 1e3);
|
|
3213
|
-
}
|
|
3214
|
-
validUserAgent() {
|
|
3215
|
-
if (!navigator?.userAgent) return false;
|
|
3216
|
-
const ua = navigator.userAgent.toLowerCase();
|
|
3217
|
-
if ([
|
|
3218
|
-
"bot",
|
|
3219
|
-
"crawler",
|
|
3220
|
-
"spider",
|
|
3221
|
-
"scraper",
|
|
3222
|
-
"headless",
|
|
3223
|
-
"phantom",
|
|
3224
|
-
"selenium",
|
|
3225
|
-
"webdriver",
|
|
3226
|
-
"puppeteer",
|
|
3227
|
-
"playwright",
|
|
3228
|
-
"cypress",
|
|
3229
|
-
"automation"
|
|
3230
|
-
].some((pattern) => ua.includes(pattern))) return false;
|
|
3231
|
-
if (![
|
|
3232
|
-
"chrome",
|
|
3233
|
-
"firefox",
|
|
3234
|
-
"safari",
|
|
3235
|
-
"edge",
|
|
3236
|
-
"opera"
|
|
3237
|
-
].some((browser) => ua.includes(browser))) return false;
|
|
3238
|
-
if (![
|
|
3239
|
-
"windows",
|
|
3240
|
-
"mac",
|
|
3241
|
-
"linux",
|
|
3242
|
-
"android",
|
|
3243
|
-
"ios"
|
|
3244
|
-
].some((os) => ua.includes(os))) return false;
|
|
3245
|
-
return ua.length > 20 && ua.length < 1e3;
|
|
3246
|
-
}
|
|
3247
|
-
update() {
|
|
3248
|
-
if (navigator.webdriver) {
|
|
3249
|
-
this.score = 0;
|
|
3250
|
-
return;
|
|
3251
|
-
}
|
|
3252
|
-
let delta = 0;
|
|
3253
|
-
const elapsed = (Date.now() - this.start) / 1e3;
|
|
3254
|
-
delta += elapsed > 30 ? .08 : elapsed > 10 ? .04 : elapsed > 3 ? .01 : -.05;
|
|
3255
|
-
if (this.events.mouse.length >= 5) {
|
|
3256
|
-
const recent = this.events.mouse.slice(-20);
|
|
3257
|
-
let changes = 0;
|
|
3258
|
-
let maxStraightRun = 0;
|
|
3259
|
-
let currentRun = 0;
|
|
3260
|
-
for (let i = 2; i < recent.length; i++) {
|
|
3261
|
-
const a1 = Math.atan2(recent[i - 1].y - recent[i - 2].y, recent[i - 1].x - recent[i - 2].x);
|
|
3262
|
-
const a2 = Math.atan2(recent[i].y - recent[i - 1].y, recent[i].x - recent[i - 1].x);
|
|
3263
|
-
if (Math.abs(a1 - a2) > .3) {
|
|
3264
|
-
changes++;
|
|
3265
|
-
currentRun = 0;
|
|
3266
|
-
} else {
|
|
3267
|
-
currentRun++;
|
|
3268
|
-
maxStraightRun = Math.max(maxStraightRun, currentRun);
|
|
3269
|
-
}
|
|
3270
|
-
}
|
|
3271
|
-
const entropy = changes / (recent.length - 2);
|
|
3272
|
-
if (entropy > .4) delta += .12;
|
|
3273
|
-
else if (entropy > .2) delta += .05;
|
|
3274
|
-
else delta -= .08;
|
|
3275
|
-
if (maxStraightRun > 8) delta -= .1;
|
|
3276
|
-
} else if (this.events.mouse.length === 0) delta -= .03;
|
|
3277
|
-
if (this.velocities.length >= 5) {
|
|
3278
|
-
const avg = this.velocities.reduce((a, b) => a + b, 0) / this.velocities.length;
|
|
3279
|
-
const variance = this.velocities.reduce((a, b) => a + Math.pow(b - avg, 2), 0) / this.velocities.length;
|
|
3280
|
-
const cv = avg > 0 ? Math.sqrt(variance) / avg : 0;
|
|
3281
|
-
if (cv > .4) delta += .1;
|
|
3282
|
-
else if (cv > .15) delta += .04;
|
|
3283
|
-
else if (avg > 0) delta -= .1;
|
|
3284
|
-
}
|
|
3285
|
-
if (this.events.clicks.length > 0) {
|
|
3286
|
-
const lastClick = this.events.clicks[this.events.clicks.length - 1];
|
|
3287
|
-
const mouseBeforeClick = this.events.mouse.some((m) => m.t < lastClick.t && lastClick.t - m.t < 2e3);
|
|
3288
|
-
delta += mouseBeforeClick ? .08 : -.12;
|
|
3289
|
-
if (this.events.clicks.filter((c) => Number.isInteger(c.x) && Number.isInteger(c.y) && c.x % 10 === 0 && c.y % 10 === 0).length / this.events.clicks.length > .5) delta -= .08;
|
|
3290
|
-
}
|
|
3291
|
-
if (this.events.keys.length >= 3) {
|
|
3292
|
-
const intervals = [];
|
|
3293
|
-
for (let i = 1; i < this.events.keys.length; i++) intervals.push(this.events.keys[i] - this.events.keys[i - 1]);
|
|
3294
|
-
const avg = intervals.reduce((a, b) => a + b, 0) / intervals.length;
|
|
3295
|
-
const stdDev = Math.sqrt(intervals.reduce((a, b) => a + Math.pow(b - avg, 2), 0) / intervals.length);
|
|
3296
|
-
if (avg < 20) delta -= .15;
|
|
3297
|
-
else if (stdDev < 5) delta -= .12;
|
|
3298
|
-
else if (avg < 50) delta -= .05;
|
|
3299
|
-
else if (avg < 1e3 && stdDev > 20) delta += .1;
|
|
3300
|
-
}
|
|
3301
|
-
const raw = this.score + delta;
|
|
3302
|
-
this.score = this.score * .75 + raw * .25;
|
|
3303
|
-
this.score = Math.max(.05, Math.min(.95, this.score));
|
|
3304
|
-
}
|
|
3305
|
-
};
|
|
3306
|
-
//#endregion
|
|
3307
3437
|
//#region src/client.ts
|
|
3308
3438
|
/** Handle device settings */
|
|
3309
3439
|
var Client = class Client extends PathEventEmitter {
|
|
3310
3440
|
momentum;
|
|
3311
|
-
captcha = new Captcha();
|
|
3312
3441
|
installTimeout;
|
|
3313
3442
|
nativePwaPrompt;
|
|
3314
3443
|
pendingInstall = null;
|
|
3315
3444
|
speed;
|
|
3316
3445
|
updating = false;
|
|
3317
3446
|
get actorType() {
|
|
3318
|
-
return this.
|
|
3447
|
+
return this.momentum.analytics.actorType;
|
|
3319
3448
|
}
|
|
3320
3449
|
get captchaScore() {
|
|
3321
|
-
return this.
|
|
3450
|
+
return this.momentum.analytics.captchaScore;
|
|
3322
3451
|
}
|
|
3323
3452
|
/** Check for updates */
|
|
3324
3453
|
updateReady = new Promise((res) => {
|
|
@@ -4837,7 +4966,7 @@ var Storage = class extends PathEventEmitter {
|
|
|
4837
4966
|
headers: this.momentum.api.headers,
|
|
4838
4967
|
files: f
|
|
4839
4968
|
}).onProgress((p) => {
|
|
4840
|
-
prog(p);
|
|
4969
|
+
prog(p.progress);
|
|
4841
4970
|
}).then((resp) => {
|
|
4842
4971
|
this.emit(PES`${this.module}/${path}:c`, resp);
|
|
4843
4972
|
res(resp);
|
|
@@ -5015,7 +5144,7 @@ var Static = class extends Storage {
|
|
|
5015
5144
|
};
|
|
5016
5145
|
//#endregion
|
|
5017
5146
|
//#region package.json
|
|
5018
|
-
var version = "1.5.
|
|
5147
|
+
var version = "1.5.9";
|
|
5019
5148
|
//#endregion
|
|
5020
5149
|
//#region src/webRtc.ts
|
|
5021
5150
|
var WebRtc = class extends PathEventEmitter {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ztimson/momentum",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.9",
|
|
4
4
|
"description": "Client library for momentum",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Momentum"
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"postbuild": "npm run build:docs && node -e \"const fs=require('fs');fs.cpSync('dist/index.mjs','../server/public/momentum.mjs');fs.cpSync('dist/index.js','../server/public/momentum.js');fs.cpSync('dist/momentum.worker.mjs','../server/public/momentum.worker.mjs')\""
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@ztimson/utils": "^0.
|
|
36
|
+
"@ztimson/utils": "^0.31.1"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/node": "^26.1.1",
|