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