ua-browser 1.3.1 → 1.4.0-beta.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/dist/index.mjs CHANGED
@@ -1,12 +1,13 @@
1
1
  // package.json
2
2
  var package_default = {
3
- version: "1.3.1"};
3
+ version: "1.4.0-beta.0"};
4
4
 
5
5
  // src/constants/browsers.ts
6
6
  var BROWSER_DEFS = [
7
7
  // ── Generic / low-priority base browsers ──────────────────────────────────
8
8
  { name: "Safari", priority: 10, detect: /Safari/, versionPattern: /Version\/([\d.]+)/ },
9
9
  { name: "Chrome", priority: 20, detect: /(Chrome|CriOS)/, versionPattern: [/Chrome\/([\d.]+)/, /CriOS\/([\d.]+)/] },
10
+ { name: "Arc", priority: 35, detect: /Arc\//, versionPattern: /Arc\/([\d.]+)/ },
10
11
  { name: "IE", priority: 30, detect: /(MSIE|Trident)/, versionPattern: [/MSIE ([\d.]+)/, /rv:([\d.]+)/] },
11
12
  { name: "Edge", priority: 40, detect: /(Edge|Edg\/|EdgA|EdgiOS)/, versionPattern: [/Edge\/([\d.]+)/, /Edg\/([\d.]+)/, /EdgA\/([\d.]+)/, /EdgiOS\/([\d.]+)/] },
12
13
  { name: "Firefox", priority: 50, detect: /(Firefox|FxiOS)/, versionPattern: [/Firefox\/([\d.]+)/, /FxiOS\/([\d.]+)/] },
@@ -16,7 +17,7 @@ var BROWSER_DEFS = [
16
17
  { name: "Vivaldi", priority: 80, detect: /Vivaldi/, versionPattern: /Vivaldi\/([\d.]+)/ },
17
18
  { name: "Yandex", priority: 90, detect: /YaBrowser/, versionPattern: /YaBrowser\/([\d.]+)/ },
18
19
  { name: "Samsung Internet", priority: 92, detect: /SamsungBrowser/, versionPattern: /SamsungBrowser\/([\d.]+)/ },
19
- { name: "DuckDuckGo", priority: 94, detect: /DuckDuckGo\//, versionPattern: /DuckDuckGo\/([\d.]+)/ },
20
+ { name: "DuckDuckGo", priority: 94, detect: /(DuckDuckGo|Ddg)\//, versionPattern: [/DuckDuckGo\/([\d.]+)/, /Ddg\/([\d.]+)/] },
20
21
  { name: "Puffin", priority: 96, detect: /Puffin\//, versionPattern: /Puffin\/([\d.]+)/ },
21
22
  { name: "Coc Coc", priority: 130, detect: /coc_coc_browser/, versionPattern: /coc_coc_browser\/([\d.]+)/ },
22
23
  { name: "Kindle", priority: 140, detect: /(Kindle|Silk\/)/, versionPattern: /Version\/([\d.]+)/ },
@@ -60,7 +61,7 @@ var BROWSER_DEFS = [
60
61
  }
61
62
  },
62
63
  { name: "UC", priority: 330, detect: /(UCBrowser|UBrowser|UCWEB)/, versionPattern: /UC?Browser\/([\d.]+)/ },
63
- { name: "QQBrowser", priority: 340, detect: /QQBrowser/, versionPattern: /QQBrowser\/([\d.]+)/ },
64
+ { name: "QQBrowser", priority: 340, detect: /(MQQBrowser|QQBrowser)/, versionPattern: [/MQQBrowser\/([\d.]+)/, /QQBrowser\/([\d.]+)/] },
64
65
  { name: "QQ", priority: 345, detect: /QQ\//, versionPattern: /QQ\/([\d.]+)/ },
65
66
  { name: "Baidu", priority: 350, detect: /(Baidu|BIDUBrowser|baidubrowser|baiduboxapp|BaiduHD)/, versionPattern: [/BIDUBrowser[\s/]([\d.]+)/, /baiduboxapp\/([\d.]+)/] },
66
67
  { name: "Sogou", priority: 360, detect: /(MetaSr|Sogou)/, versionPattern: [/SE ([\d.X]+)/, /SogouMobileBrowser\/([\d.]+)/] },
@@ -208,6 +209,10 @@ var OS_DEFS = [
208
209
  { name: "Tizen", detect: /Tizen/, versionPattern: /Tizen ([\d.]+)/ },
209
210
  { name: "iOS", detect: /like Mac OS X/, versionPattern: /OS ([\d_]+) like/ },
210
211
  { name: "MacOS", detect: /Macintosh/, versionPattern: /Mac OS X -?([\d_.]+)/ },
212
+ // visionOS / tvOS must come AFTER iOS: their UAs also contain "like Mac OS X",
213
+ // so they need to override iOS via the last-match-wins iteration.
214
+ { name: "visionOS", detect: /visionOS/, versionPattern: /visionOS ([\d_]+)/ },
215
+ { name: "tvOS", detect: /Apple TV/, versionPattern: /OS ([\d_]+) like/ },
211
216
  { name: "Android", detect: /(Android|Adr)/, versionPattern: /(?:Android|Adr) ([\d.]+)/ },
212
217
  // HarmonyOS must come after Android: HarmonyOS UAs include "Android", so Android matches
213
218
  // first, then HarmonyOS overrides it. versionPattern tries direct extraction first (5.0+
@@ -285,24 +290,59 @@ var DEVICE_DEFS = [
285
290
 
286
291
  // src/detectors/device.ts
287
292
  function detectDevice(ua, nav) {
288
- if (/(SMART-TV|HbbTV|SmartTV|TV Safari|Android TV|GoogleTV)/.test(ua)) {
289
- return "TV";
290
- }
291
- if ((nav == null ? void 0 : nav.platform) === "MacIntel" && nav.maxTouchPoints > 1) {
292
- return "Tablet";
293
- }
294
- if (/iPad/.test(ua)) {
295
- return "Tablet";
296
- }
297
- if (/Android/.test(ua) && !/Mobile/.test(ua)) {
298
- return "Tablet";
293
+ var _a, _b;
294
+ function uaDetect() {
295
+ if (/PlayStation|Xbox|Nintendo/.test(ua)) return "Console";
296
+ if (/visionOS|Quest/.test(ua)) return "XR";
297
+ if (/(SMART-TV|HbbTV|SmartTV|TV Safari|Android TV|GoogleTV)/.test(ua)) return "TV";
298
+ if (/iPad/.test(ua)) return "Tablet";
299
+ if (/Android/.test(ua) && !/Mobile/.test(ua)) return "Tablet";
300
+ for (const def of DEVICE_DEFS) {
301
+ if (def.detect.test(ua)) return def.name;
302
+ }
303
+ return null;
299
304
  }
300
- for (const def of DEVICE_DEFS) {
301
- if (def.detect.test(ua)) {
302
- return def.name;
305
+ function hwDetect() {
306
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
307
+ if (!nav) return null;
308
+ if (nav.webglRenderer) {
309
+ if (/^ANGLE\b/.test(nav.webglRenderer)) return "PC";
310
+ if (/Adreno/i.test(nav.webglRenderer) || /Mali[-\s]/i.test(nav.webglRenderer)) {
311
+ return ((_a2 = nav.screenWidth) != null ? _a2 : 0) >= 768 ? "Tablet" : "Mobile";
312
+ }
313
+ if (/Apple GPU/i.test(nav.webglRenderer)) {
314
+ const w = (_b2 = nav.screenWidth) != null ? _b2 : 0;
315
+ if (w <= 1366) return w >= 768 ? "Tablet" : "Mobile";
316
+ return "PC";
317
+ }
318
+ }
319
+ if (nav.platform === "MacIntel" && ((_c = nav.maxTouchPoints) != null ? _c : 0) > 1) {
320
+ return nav.screenWidth !== void 0 && nav.screenWidth < 768 ? "Mobile" : "Tablet";
321
+ }
322
+ if (nav.webglMaxTextureSize !== void 0 && nav.webglMaxTextureSize <= 8192 && ((_d = nav.maxTouchPoints) != null ? _d : 0) > 1 && ((_e = nav.screenWidth) != null ? _e : 9999) < 1367) {
323
+ return ((_f = nav.screenWidth) != null ? _f : 0) >= 768 ? "Tablet" : "Mobile";
324
+ }
325
+ if (((_g = nav.webglCompressedFormats) == null ? void 0 : _g.pvrtc) && !nav.webglCompressedFormats.s3tc) {
326
+ return ((_h = nav.screenWidth) != null ? _h : 0) >= 768 ? "Tablet" : "Mobile";
327
+ }
328
+ if (nav.safeAreaInsetTop !== void 0 && nav.safeAreaInsetTop > 0) {
329
+ return ((_i = nav.screenWidth) != null ? _i : 0) >= 768 ? "Tablet" : "Mobile";
330
+ }
331
+ if (nav.hasVibration || nav.hasDeviceMotion) {
332
+ return ((_j = nav.screenWidth) != null ? _j : 0) >= 768 ? "Tablet" : "Mobile";
333
+ }
334
+ if (((_k = nav.connection) == null ? void 0 : _k.effectiveType) && ["2g", "3g", "slow-2g"].includes(nav.connection.effectiveType)) {
335
+ return ((_l = nav.screenWidth) != null ? _l : 0) >= 768 ? "Tablet" : "Mobile";
336
+ }
337
+ if (((_m = nav.devicePixelRatio) != null ? _m : 0) > 2 && ((_n = nav.maxTouchPoints) != null ? _n : 0) > 1) {
338
+ return ((_o = nav.screenWidth) != null ? _o : 0) >= 768 ? "Tablet" : "Mobile";
303
339
  }
340
+ if (nav.pointerType === "coarse" && nav.hoverCapability === false) {
341
+ return ((_p = nav.screenWidth) != null ? _p : 0) >= 768 ? "Tablet" : "Mobile";
342
+ }
343
+ return null;
304
344
  }
305
- return "PC";
345
+ return (_b = (_a = hwDetect()) != null ? _a : uaDetect()) != null ? _b : "PC";
306
346
  }
307
347
 
308
348
  // src/constants/bots.ts
@@ -318,28 +358,49 @@ var BOT_DEFS = [
318
358
  { name: "Sogou", detect: /(Sogou|sogou).*[Ss]pider/ },
319
359
  { name: "360Spider", detect: /360Spider/ },
320
360
  { name: "PetalBot", detect: /PetalBot/ },
361
+ { name: "Applebot-Extended", detect: /Applebot-Extended/ },
321
362
  { name: "Applebot", detect: /Applebot/ },
322
- // Social
363
+ // Social media crawlers
323
364
  { name: "Facebookbot", detect: /(facebookexternalhit|FacebookBot)/ },
324
365
  { name: "Twitterbot", detect: /Twitterbot/ },
325
366
  { name: "LinkedInBot", detect: /LinkedInBot/ },
367
+ { name: "PinterestBot", detect: /Pinterest/ },
368
+ // Messaging link preview bots
369
+ { name: "Slackbot", detect: /Slackbot/ },
370
+ { name: "Discordbot", detect: /Discordbot/ },
371
+ { name: "TelegramBot", detect: /TelegramBot/ },
372
+ { name: "WhatsApp", detect: /WhatsApp/ },
326
373
  // SEO tools
327
374
  { name: "SemrushBot", detect: /SemrushBot/ },
328
375
  { name: "AhrefsBot", detect: /AhrefsBot/ },
329
376
  { name: "MJ12bot", detect: /MJ12bot/ },
377
+ { name: "ScreamingFrog", detect: /Screaming Frog/ },
378
+ { name: "DataForSeoBot", detect: /DataForSeoBot/ },
330
379
  // AI / LLM crawlers
331
380
  { name: "GPTBot", detect: /GPTBot/ },
381
+ { name: "OAI-SearchBot", detect: /OAI-SearchBot/ },
382
+ { name: "ChatGPT-User", detect: /ChatGPT-User/ },
332
383
  { name: "ClaudeBot", detect: /ClaudeBot/ },
333
384
  { name: "PerplexityBot", detect: /PerplexityBot/ },
334
385
  { name: "CCBot", detect: /CCBot/ },
335
386
  { name: "AdsBot", detect: /AdsBot-Google/ },
387
+ { name: "Google-Extended", detect: /Google-Extended/ },
388
+ { name: "Meta-ExternalAgent", detect: /meta-externalagent/i },
389
+ { name: "Amazonbot", detect: /Amazonbot/ },
390
+ { name: "Diffbot", detect: /Diffbot/ },
391
+ { name: "cohere-ai", detect: /cohere-ai/ },
392
+ { name: "YouBot", detect: /YouBot/ },
393
+ // Monitoring / archiving
394
+ { name: "UptimeRobot", detect: /UptimeRobot/ },
395
+ { name: "ia_archiver", detect: /ia_archiver/ },
336
396
  // Generic catch-all (must be last)
337
397
  { name: "GenericBot", detect: /(bot|crawler|spider|crawling|scraper)/i }
338
398
  ];
339
399
 
340
400
  // src/detectors/bot.ts
341
- function detectBot(ua) {
342
- for (const def of BOT_DEFS) {
401
+ function detectBot(ua, customDefs) {
402
+ const defs = customDefs ? [...BOT_DEFS.slice(0, -1), ...customDefs, BOT_DEFS[BOT_DEFS.length - 1]] : BOT_DEFS;
403
+ for (const def of defs) {
343
404
  if (def.detect.test(ua)) {
344
405
  return { isBot: true, botName: def.name };
345
406
  }
@@ -349,7 +410,8 @@ function detectBot(ua) {
349
410
 
350
411
  // src/constants/arch.ts
351
412
  var ARCH_DEFS = [
352
- { name: "arm64", detect: /(aarch64|arm64|ARM64)/ },
413
+ // aarch64/arm64 explicit tokens + iOS devices (iPhone 5S+/iPad Air+ are exclusively arm64)
414
+ { name: "arm64", detect: /(aarch64|arm64|ARM64|iPhone|iPad|iPod)/ },
353
415
  { name: "arm", detect: /\bARM\b/ },
354
416
  { name: "x86_64", detect: /(x86_64|Win64|WOW64|x64;|amd64)/i },
355
417
  { name: "x86", detect: /(i[36]86|i686|x86;)/ }
@@ -368,7 +430,7 @@ function detectArch(ua, ctx) {
368
430
  }
369
431
  const renderer = ctx == null ? void 0 : ctx.webglRenderer;
370
432
  if (renderer) {
371
- if (/Apple\s+(M\d|A\d{1,2}[A-Z]?)\b/i.test(renderer) || /APPLE M\d/i.test(renderer)) {
433
+ if (/Apple\s+(M\d|A\d{1,2}[A-Z]?)\b/i.test(renderer) || /APPLE M\d/i.test(renderer) || /^Apple GPU$/i.test(renderer)) {
372
434
  return "arm64";
373
435
  }
374
436
  if (/\b(Intel|AMD|NVIDIA|Radeon)\b/i.test(renderer)) {
@@ -427,22 +489,95 @@ function getLanguage(nav) {
427
489
  }
428
490
 
429
491
  // src/parse.ts
492
+ var BRAND_TO_BROWSER = [
493
+ ["Microsoft Edge", "Edge"],
494
+ ["Opera", "Opera"],
495
+ ["Vivaldi", "Vivaldi"],
496
+ ["Google Chrome", "Chrome"],
497
+ ["Chromium", "Chromium"]
498
+ ];
499
+ function osFromHardware(ctx) {
500
+ var _a, _b, _c, _d, _e, _f, _g;
501
+ const p = ((_a = ctx.platform) != null ? _a : "").toLowerCase();
502
+ const chPlatform = (_b = ctx.userAgentData) == null ? void 0 : _b.platform;
503
+ const platformVersion = (_c = ctx.highEntropyData) == null ? void 0 : _c.platformVersion;
504
+ const hasPlatformVersion = !!(platformVersion && platformVersion !== "0.0.0");
505
+ if (((_d = ctx.webglCompressedFormats) == null ? void 0 : _d.pvrtc) && !ctx.webglCompressedFormats.s3tc) return "iOS";
506
+ if (ctx.webglRenderer && /^ANGLE\b/.test(ctx.webglRenderer)) {
507
+ if (p.startsWith("mac") || chPlatform === "macOS") return "MacOS";
508
+ if (p.startsWith("win") || chPlatform === "Windows") return "Windows";
509
+ if (chPlatform === "Chrome OS") return "Chrome OS";
510
+ return null;
511
+ }
512
+ if (ctx.webglRenderer && (/Adreno/i.test(ctx.webglRenderer) || /Mali[-\s]/i.test(ctx.webglRenderer))) {
513
+ return "Android";
514
+ }
515
+ if (chPlatform && hasPlatformVersion) {
516
+ const chMap = {
517
+ Windows: "Windows",
518
+ macOS: "MacOS",
519
+ Android: "Android",
520
+ iOS: "iOS",
521
+ "Chrome OS": "Chrome OS"
522
+ };
523
+ const mapped = chMap[chPlatform];
524
+ if (mapped) return mapped;
525
+ }
526
+ if (p.startsWith("mac")) return "MacOS";
527
+ if (p.startsWith("win")) return "Windows";
528
+ if (p === "iphone" || p === "ipad" || p === "ipod") return "iOS";
529
+ if (((_e = ctx.safeAreaInsetTop) != null ? _e : 0) > 0) return "iOS";
530
+ if (chPlatform === "Chrome OS") return "Chrome OS";
531
+ if (chPlatform === "Android" && (p.includes("android") || /^linux arm/i.test((_f = ctx.platform) != null ? _f : ""))) {
532
+ return "Android";
533
+ }
534
+ if (p.includes("android") || /^linux arm/i.test((_g = ctx.platform) != null ? _g : "")) return "Android";
535
+ return null;
536
+ }
537
+ function platformFromUA(ua) {
538
+ if (/iPhone/.test(ua)) return "iPhone";
539
+ if (/iPad/.test(ua)) return "iPad";
540
+ if (/iPod/.test(ua)) return "iPod";
541
+ if (/Macintosh/.test(ua)) return "MacIntel";
542
+ if (/Windows NT/.test(ua)) return /Win64|WOW64/i.test(ua) ? "Win64" : "Win32";
543
+ if (/CrOS/.test(ua)) return "Linux x86_64";
544
+ if (/Android/.test(ua)) return /aarch64|arm64/i.test(ua) ? "Linux aarch64" : "Linux armv8l";
545
+ if (/Linux/.test(ua)) {
546
+ if (/x86_64/i.test(ua)) return "Linux x86_64";
547
+ if (/aarch64|arm64/i.test(ua)) return "Linux aarch64";
548
+ return "Linux";
549
+ }
550
+ return "unknown";
551
+ }
552
+ function languageFromUA(ua) {
553
+ const re = /[;(]\s*([a-z]{2,3}(?:[-_][A-Za-z]{2,4})+)\s*[;)]/g;
554
+ let m;
555
+ while ((m = re.exec(ua)) !== null) {
556
+ const parts = m[1].replace(/_/g, "-").split("-");
557
+ if (parts.length >= 2) {
558
+ return `${parts[0].toLowerCase()}-${parts.slice(1).map((p) => p.toUpperCase()).join("-")}`;
559
+ }
560
+ }
561
+ return "unknown";
562
+ }
430
563
  function parseUA(ua, options = {}) {
431
- var _a, _b, _c, _d, _e, _f, _g, _h;
564
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
432
565
  const effectiveNav = (_a = options.ctx) != null ? _a : options.nav;
433
566
  const effectiveWindowsVersion = (_c = (_b = options.ctx) == null ? void 0 : _b.windowsVersion) != null ? _c : options.windowsVersion;
434
567
  const { browser: rawBrowser, version: rawVersion } = detectBrowser(ua);
435
- const { os, osVersion: rawOsVersion } = detectOs(ua, effectiveWindowsVersion);
568
+ const { os: rawOs, osVersion: rawOsVersion } = detectOs(ua, effectiveWindowsVersion);
569
+ let os = rawOs;
436
570
  let osVersion = rawOsVersion;
437
571
  const device = detectDevice(ua, effectiveNav);
438
- const arch = detectArch(ua, options.ctx);
572
+ const arch = detectArch(ua, (_d = options.ctx) != null ? _d : effectiveNav);
439
573
  const nav = effectiveNav;
440
- const { isBot, botName } = detectBot(ua);
574
+ const { isBot, botName } = detectBot(ua, options.customBotDefs);
441
575
  const isHeadless = detectHeadless(ua);
442
- const language = nav ? getLanguage(nav) : "unknown";
443
- const platform = (_d = nav == null ? void 0 : nav.platform) != null ? _d : "unknown";
576
+ const language = options.language || ((nav == null ? void 0 : nav.language) || (nav == null ? void 0 : nav.browserLanguage) ? getLanguage(nav) : "") || languageFromUA(ua);
577
+ const platform = (nav == null ? void 0 : nav.platform) || platformFromUA(ua);
444
578
  let browser = rawBrowser;
445
579
  let version = rawVersion;
580
+ if ((_e = options.ctx) == null ? void 0 : _e.hasBrave) browser = "Brave";
446
581
  if (nav) {
447
582
  const chromeGlobal = typeof chrome !== "undefined" ? chrome : void 0;
448
583
  const chromeMatch = /Chrome\/([\d]+)/.exec(ua);
@@ -462,7 +597,7 @@ function parseUA(ua, options = {}) {
462
597
  }
463
598
  }
464
599
  if (is360) {
465
- const saveDataEnabled = ((_e = nav.connection) == null ? void 0 : _e.saveData) === true;
600
+ const saveDataEnabled = ((_f = nav.connection) == null ? void 0 : _f.saveData) === true;
466
601
  if (getMimeType(nav, "application/gameplugin") || !saveDataEnabled) {
467
602
  browser = "360SE";
468
603
  } else {
@@ -475,8 +610,8 @@ function parseUA(ua, options = {}) {
475
610
  }
476
611
  if (browser === "Baidu" && /(Opera|OPR|OPT)/.test(ua)) {
477
612
  browser = "Opera";
478
- const opVer = (_g = (_f = /OPR\/([\d.]+)/.exec(ua)) != null ? _f : /OPT\/([\d.]+)/.exec(ua)) != null ? _g : /Opera\/([\d.]+)/.exec(ua);
479
- version = (_h = opVer == null ? void 0 : opVer[1]) != null ? _h : "unknown";
613
+ const opVer = (_h = (_g = /OPR\/([\d.]+)/.exec(ua)) != null ? _g : /OPT\/([\d.]+)/.exec(ua)) != null ? _h : /Opera\/([\d.]+)/.exec(ua);
614
+ version = (_i = opVer == null ? void 0 : opVer[1]) != null ? _i : "unknown";
480
615
  }
481
616
  if (browser === "Chrome" && /\S+Browser\//.test(ua)) {
482
617
  const m = /(\S+Browser)\/([\d.]+)/.exec(ua);
@@ -499,10 +634,47 @@ function parseUA(ua, options = {}) {
499
634
  osVersion = m[1];
500
635
  }
501
636
  }
637
+ if (os === "MacOS" && browser === "Safari") {
638
+ const m = /Version\/([\d.]+)/.exec(ua);
639
+ if (m && parseInt(m[1], 10) >= 26) {
640
+ osVersion = m[1];
641
+ }
642
+ }
643
+ if (options.ctx) {
644
+ const hwOs = osFromHardware(options.ctx);
645
+ if (hwOs !== null) {
646
+ if (hwOs !== rawOs) {
647
+ os = hwOs;
648
+ osVersion = "unknown";
649
+ }
650
+ if (os !== "Windows") {
651
+ const pv = (_j = options.ctx.highEntropyData) == null ? void 0 : _j.platformVersion;
652
+ if (pv && pv !== "0.0.0") {
653
+ const parts = pv.split(".").map(Number);
654
+ while (parts.length > 1 && parts[parts.length - 1] === 0) parts.pop();
655
+ if (parts[0]) osVersion = parts.join(".");
656
+ }
657
+ }
658
+ }
659
+ const fullVersionList = (_k = options.ctx.highEntropyData) == null ? void 0 : _k.fullVersionList;
660
+ if (fullVersionList) {
661
+ for (const [brand, browserName] of BRAND_TO_BROWSER) {
662
+ const entry = fullVersionList.find((e) => e.brand === brand);
663
+ if (entry == null ? void 0 : entry.version) {
664
+ browser = browserName;
665
+ version = entry.version;
666
+ break;
667
+ }
668
+ }
669
+ }
670
+ }
502
671
  const engine = detectEngine(ua, browser, version);
672
+ const versionMajor = parseInt((_l = version.split(".")[0]) != null ? _l : "0", 10) || 0;
673
+ const connectionType = (_p = (_o = (_n = (_m = options.ctx) != null ? _m : options.nav) == null ? void 0 : _n.connection) == null ? void 0 : _o.effectiveType) != null ? _p : "unknown";
503
674
  return {
504
675
  browser,
505
676
  version,
677
+ versionMajor,
506
678
  engine,
507
679
  os,
508
680
  osVersion,
@@ -513,7 +685,8 @@ function parseUA(ua, options = {}) {
513
685
  isBot,
514
686
  botName,
515
687
  language,
516
- platform
688
+ platform,
689
+ connectionType
517
690
  };
518
691
  }
519
692
 
@@ -544,21 +717,56 @@ function probeFonts() {
544
717
  }
545
718
  }
546
719
  function getWebGLInfo() {
547
- var _a;
720
+ var _a, _b;
548
721
  try {
549
722
  const canvas = document.createElement("canvas");
550
723
  const gl = (_a = canvas.getContext("webgl")) != null ? _a : canvas.getContext("experimental-webgl");
551
724
  if (!gl) return {};
552
725
  const ext = gl.getExtension("WEBGL_debug_renderer_info");
553
- if (!ext) return {};
554
- return {
555
- renderer: gl.getParameter(ext.UNMASKED_RENDERER_WEBGL),
556
- vendor: gl.getParameter(ext.UNMASKED_VENDOR_WEBGL)
726
+ const result = {
727
+ maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE),
728
+ fragPrecision: (_b = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT)) == null ? void 0 : _b.rangeMax,
729
+ compressedFormats: {
730
+ s3tc: !!gl.getExtension("WEBGL_compressed_texture_s3tc"),
731
+ pvrtc: !!gl.getExtension("WEBGL_compressed_texture_pvrtc"),
732
+ etc2: !!gl.getExtension("WEBGL_compressed_texture_etc"),
733
+ astc: !!gl.getExtension("WEBGL_compressed_texture_astc_ldr")
734
+ }
557
735
  };
736
+ if (ext) {
737
+ result.renderer = gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
738
+ result.vendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL);
739
+ }
740
+ return result;
558
741
  } catch (e) {
559
742
  return {};
560
743
  }
561
744
  }
745
+ function getSafeAreaInsetTop() {
746
+ try {
747
+ const el = document.createElement("div");
748
+ el.style.cssText = "position:fixed;top:env(safe-area-inset-top,0px);visibility:hidden;pointer-events:none";
749
+ document.body.appendChild(el);
750
+ const top = parseFloat(getComputedStyle(el).top);
751
+ document.body.removeChild(el);
752
+ return isNaN(top) ? 0 : top;
753
+ } catch (e) {
754
+ return 0;
755
+ }
756
+ }
757
+ function getAudioSampleRate() {
758
+ var _a;
759
+ try {
760
+ const AC = (_a = window.AudioContext) != null ? _a : window.webkitAudioContext;
761
+ if (!AC) return void 0;
762
+ const ac = new AC();
763
+ const rate = ac.sampleRate;
764
+ void ac.close();
765
+ return rate;
766
+ } catch (e) {
767
+ return void 0;
768
+ }
769
+ }
562
770
  function getPointerType() {
563
771
  try {
564
772
  if (window.matchMedia("(pointer: coarse)").matches) return "coarse";
@@ -582,6 +790,7 @@ function deriveWindowsVersion(platformVersion) {
582
790
  return isNaN(major) ? null : major >= 13 ? "11" : "10";
583
791
  }
584
792
  async function getEnvContext() {
793
+ var _a, _b, _c, _d, _e;
585
794
  const base = getNavContext();
586
795
  const ctx = {
587
796
  userAgent: base.userAgent,
@@ -599,12 +808,26 @@ async function getEnvContext() {
599
808
  }
600
809
  if (typeof window !== "undefined") {
601
810
  ctx.devicePixelRatio = window.devicePixelRatio;
811
+ ctx.screenWidth = (_a = window.screen) == null ? void 0 : _a.width;
812
+ ctx.screenHeight = (_b = window.screen) == null ? void 0 : _b.height;
813
+ ctx.safeAreaInsetTop = getSafeAreaInsetTop();
602
814
  ctx.pointerType = getPointerType();
603
815
  ctx.hoverCapability = getHoverCapability();
604
816
  ctx.fontProbes = probeFonts();
605
- const { renderer, vendor } = getWebGLInfo();
817
+ ctx.audioSampleRate = getAudioSampleRate();
818
+ ctx.hasVibration = "vibrate" in navigator;
819
+ ctx.hasDeviceMotion = "DeviceMotionEvent" in window;
820
+ try {
821
+ ctx.hasBrave = (_e = await ((_d = (_c = navigator.brave) == null ? void 0 : _c.isBrave) == null ? void 0 : _d.call(_c))) != null ? _e : false;
822
+ } catch (e) {
823
+ ctx.hasBrave = false;
824
+ }
825
+ const { renderer, vendor, maxTextureSize, fragPrecision, compressedFormats } = getWebGLInfo();
606
826
  if (renderer !== void 0) ctx.webglRenderer = renderer;
607
827
  if (vendor !== void 0) ctx.webglVendor = vendor;
828
+ if (maxTextureSize !== void 0) ctx.webglMaxTextureSize = maxTextureSize;
829
+ if (fragPrecision !== void 0) ctx.webglFragPrecision = fragPrecision;
830
+ if (compressedFormats !== void 0) ctx.webglCompressedFormats = compressedFormats;
608
831
  }
609
832
  if (base.userAgentData) {
610
833
  try {
@@ -646,6 +869,11 @@ async function getWindowsVersion(nav) {
646
869
  }
647
870
  }
648
871
 
872
+ // src/utils/satisfies.ts
873
+ function satisfies(info, criteria) {
874
+ return Object.keys(criteria).every((key) => info[key] === criteria[key]);
875
+ }
876
+
649
877
  // src/parse-headers.ts
650
878
  var ACCEPT_CH = [
651
879
  "Sec-CH-UA-Arch",
@@ -665,7 +893,7 @@ function deriveWindowsVersion2(platformVersion) {
665
893
  return isNaN(major) ? null : major >= 13 ? "11" : "10";
666
894
  }
667
895
  function parseHeaders(headers) {
668
- var _a, _b;
896
+ var _a, _b, _c, _d, _e;
669
897
  const normalised = {};
670
898
  for (const key of Object.keys(headers)) {
671
899
  normalised[key.toLowerCase()] = headers[key];
@@ -675,11 +903,13 @@ function parseHeaders(headers) {
675
903
  return Array.isArray(v) ? v[0] : v;
676
904
  };
677
905
  const ua = (_a = get("user-agent")) != null ? _a : "";
906
+ const rawLang = get("accept-language");
907
+ const language = rawLang ? (_d = (_c = (_b = rawLang.split(",")[0]) == null ? void 0 : _b.split(";")[0]) == null ? void 0 : _c.trim()) != null ? _d : "" : "";
678
908
  const architecture = unquote(get("sec-ch-ua-arch"));
679
909
  const bitness = unquote(get("sec-ch-ua-bitness"));
680
910
  const model = unquote(get("sec-ch-ua-model"));
681
911
  const platformVersion = unquote(get("sec-ch-ua-platform-version"));
682
- const platform = (_b = unquote(get("sec-ch-ua-platform"))) != null ? _b : "";
912
+ const platform = (_e = unquote(get("sec-ch-ua-platform"))) != null ? _e : "";
683
913
  const highEntropyData = {};
684
914
  if (architecture !== void 0) highEntropyData.architecture = architecture;
685
915
  if (bitness !== void 0) highEntropyData.bitness = bitness;
@@ -690,7 +920,7 @@ function parseHeaders(headers) {
690
920
  const ctx = {
691
921
  userAgent: ua,
692
922
  platform,
693
- language: "",
923
+ language,
694
924
  maxTouchPoints: isMobile ? 1 : 0,
695
925
  highEntropyData: Object.keys(highEntropyData).length > 0 ? highEntropyData : void 0,
696
926
  windowsVersion
@@ -700,9 +930,9 @@ function parseHeaders(headers) {
700
930
 
701
931
  // src/index.ts
702
932
  var { version: VERSION } = package_default;
703
- function uaBrowser(ua) {
933
+ function uaBrowser() {
704
934
  const nav = getNavContext();
705
- return parseUA(ua != null ? ua : nav.userAgent, { nav });
935
+ return parseUA(nav.userAgent, { nav });
706
936
  }
707
937
  uaBrowser.detect = async (ua) => {
708
938
  const ctx = await getEnvContext();
@@ -713,6 +943,6 @@ uaBrowser.getLanguage = () => getLanguage(getNavContext());
713
943
  uaBrowser.VERSION = VERSION;
714
944
  var src_default = uaBrowser;
715
945
 
716
- export { ACCEPT_CH, VERSION, src_default as default, detectArch, detectBot, detectHeadless, getEnvContext, getLanguage, getNavContext, getWindowsVersion, isWebview, parseHeaders, parseUA };
946
+ export { ACCEPT_CH, VERSION, src_default as default, detectArch, detectBot, detectBrowser, detectHeadless, detectOs as detectOS, getEnvContext, getLanguage, getNavContext, getWindowsVersion, isWebview, parseHeaders, parseUA, satisfies };
717
947
  //# sourceMappingURL=index.mjs.map
718
948
  //# sourceMappingURL=index.mjs.map