@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/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, seen = /* @__PURE__ */ new WeakMap()) {
239
- if (a === b) return true;
240
- if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
241
- if (a instanceof RegExp && b instanceof RegExp) return a.source === b.source && a.flags === b.flags;
242
- if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) {
243
- if (Number.isNaN(a) && Number.isNaN(b)) return true;
244
- if (typeof a === "function" && typeof b === "function") return a.toString() === b.toString();
245
- return false;
246
- }
247
- if (seen.has(a)) return seen.get(a) === b;
248
- seen.set(a, b);
249
- const isArrayA = Array.isArray(a);
250
- const isArrayB = Array.isArray(b);
251
- if (isArrayA && isArrayB) {
252
- if (a.length !== b.length) return false;
253
- for (let i = 0; i < a.length; i++) if (!isEqual(a[i], b[i], seen)) return false;
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
- if (isArrayA !== isArrayB) return false;
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
- _progress = 0;
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._progress;
1169
+ return this._meta.progress;
1139
1170
  }
1140
- set progress(p) {
1141
- if (p == this._progress) return;
1142
- this._progress = p;
1143
- this.listeners.forEach((l) => l(p));
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) => resolve(value), (reason) => reject(reason), (progress) => this.progress = progress));
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((...args) => res(...args)).catch((...args) => rej(...args)));
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((p) => newPromise.progress = p);
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
- const resp = super.then(res, rej);
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 / event.total) : null);
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
- this.id = await this.momentum.api.request({
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 (event.origin != origin || data.sender != origin) return;
3019
- if (!data.token) return rej("Unknown response from login");
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
- await this.readSession(data.token, true);
3022
- win.close();
3023
- res(data.token);
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=${encodeURI(host)}`, "_blank");
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.captcha.verdict;
3447
+ return this.momentum.analytics.actorType;
3319
3448
  }
3320
3449
  get captchaScore() {
3321
- return this.captcha.score;
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.7";
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.7",
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.30.8"
36
+ "@ztimson/utils": "^0.31.1"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^26.1.1",