ua-browser 1.3.0 → 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.0"};
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";
307
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";
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,25 +689,11 @@ 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
 
524
- // src/utils/windows-version.ts
525
- async function getWindowsVersion(nav) {
526
- var _a;
527
- if (!nav.userAgentData || nav.userAgentData.platform !== "Windows") {
528
- return null;
529
- }
530
- try {
531
- const data = await nav.userAgentData.getHighEntropyValues(["platformVersion"]);
532
- const major = parseInt(((_a = data["platformVersion"]) == null ? void 0 : _a.split(".")[0]) || "0", 10);
533
- return major >= 13 ? "11" : "10";
534
- } catch (e) {
535
- return null;
536
- }
537
- }
538
-
539
697
  // src/utils/env-context.ts
540
698
  var OS_FONTS = [
541
699
  ".AppleSystemUIFont",
@@ -563,21 +721,56 @@ function probeFonts() {
563
721
  }
564
722
  }
565
723
  function getWebGLInfo() {
566
- var _a;
724
+ var _a, _b;
567
725
  try {
568
726
  const canvas = document.createElement("canvas");
569
727
  const gl = (_a = canvas.getContext("webgl")) != null ? _a : canvas.getContext("experimental-webgl");
570
728
  if (!gl) return {};
571
729
  const ext = gl.getExtension("WEBGL_debug_renderer_info");
572
- if (!ext) return {};
573
- return {
574
- renderer: gl.getParameter(ext.UNMASKED_RENDERER_WEBGL),
575
- 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
+ }
576
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;
577
745
  } catch (e) {
578
746
  return {};
579
747
  }
580
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
+ }
581
774
  function getPointerType() {
582
775
  try {
583
776
  if (window.matchMedia("(pointer: coarse)").matches) return "coarse";
@@ -601,20 +794,44 @@ function deriveWindowsVersion(platformVersion) {
601
794
  return isNaN(major) ? null : major >= 13 ? "11" : "10";
602
795
  }
603
796
  async function getEnvContext() {
797
+ var _a, _b, _c, _d, _e;
604
798
  const base = getNavContext();
605
- const ctx = { ...base };
799
+ const ctx = {
800
+ userAgent: base.userAgent,
801
+ platform: base.platform,
802
+ language: base.language,
803
+ maxTouchPoints: base.maxTouchPoints
804
+ };
805
+ if (base.browserLanguage !== void 0) ctx.browserLanguage = base.browserLanguage;
806
+ if (base.mimeTypes !== void 0) ctx.mimeTypes = base.mimeTypes;
807
+ if (base.connection !== void 0) ctx.connection = base.connection;
808
+ if (base.userAgentData !== void 0) ctx.userAgentData = base.userAgentData;
606
809
  if (typeof navigator !== "undefined") {
607
810
  ctx.hardwareConcurrency = navigator.hardwareConcurrency;
608
811
  ctx.deviceMemory = navigator.deviceMemory;
609
812
  }
610
813
  if (typeof window !== "undefined") {
611
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();
612
818
  ctx.pointerType = getPointerType();
613
819
  ctx.hoverCapability = getHoverCapability();
614
820
  ctx.fontProbes = probeFonts();
615
- 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();
616
830
  if (renderer !== void 0) ctx.webglRenderer = renderer;
617
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;
618
835
  }
619
836
  if (base.userAgentData) {
620
837
  try {
@@ -641,6 +858,26 @@ async function getEnvContext() {
641
858
  return ctx;
642
859
  }
643
860
 
861
+ // src/utils/windows-version.ts
862
+ async function getWindowsVersion(nav) {
863
+ var _a;
864
+ if (!nav.userAgentData || nav.userAgentData.platform !== "Windows") {
865
+ return null;
866
+ }
867
+ try {
868
+ const data = await nav.userAgentData.getHighEntropyValues(["platformVersion"]);
869
+ const major = parseInt(((_a = data["platformVersion"]) == null ? void 0 : _a.split(".")[0]) || "0", 10);
870
+ return major >= 13 ? "11" : "10";
871
+ } catch (e) {
872
+ return null;
873
+ }
874
+ }
875
+
876
+ // src/utils/satisfies.ts
877
+ function satisfies(info, criteria) {
878
+ return Object.keys(criteria).every((key) => info[key] === criteria[key]);
879
+ }
880
+
644
881
  // src/parse-headers.ts
645
882
  var ACCEPT_CH = [
646
883
  "Sec-CH-UA-Arch",
@@ -660,7 +897,7 @@ function deriveWindowsVersion2(platformVersion) {
660
897
  return isNaN(major) ? null : major >= 13 ? "11" : "10";
661
898
  }
662
899
  function parseHeaders(headers) {
663
- var _a, _b;
900
+ var _a, _b, _c, _d, _e;
664
901
  const normalised = {};
665
902
  for (const key of Object.keys(headers)) {
666
903
  normalised[key.toLowerCase()] = headers[key];
@@ -670,11 +907,13 @@ function parseHeaders(headers) {
670
907
  return Array.isArray(v) ? v[0] : v;
671
908
  };
672
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 : "" : "";
673
912
  const architecture = unquote(get("sec-ch-ua-arch"));
674
913
  const bitness = unquote(get("sec-ch-ua-bitness"));
675
914
  const model = unquote(get("sec-ch-ua-model"));
676
915
  const platformVersion = unquote(get("sec-ch-ua-platform-version"));
677
- const platform = (_b = unquote(get("sec-ch-ua-platform"))) != null ? _b : "";
916
+ const platform = (_e = unquote(get("sec-ch-ua-platform"))) != null ? _e : "";
678
917
  const highEntropyData = {};
679
918
  if (architecture !== void 0) highEntropyData.architecture = architecture;
680
919
  if (bitness !== void 0) highEntropyData.bitness = bitness;
@@ -685,7 +924,7 @@ function parseHeaders(headers) {
685
924
  const ctx = {
686
925
  userAgent: ua,
687
926
  platform,
688
- language: "",
927
+ language,
689
928
  maxTouchPoints: isMobile ? 1 : 0,
690
929
  highEntropyData: Object.keys(highEntropyData).length > 0 ? highEntropyData : void 0,
691
930
  windowsVersion
@@ -695,10 +934,14 @@ function parseHeaders(headers) {
695
934
 
696
935
  // src/index.ts
697
936
  var { version: VERSION } = package_default;
698
- function uaBrowser(ua) {
937
+ function uaBrowser() {
699
938
  const nav = getNavContext();
700
- return parseUA(ua != null ? ua : nav.userAgent, { nav });
939
+ return parseUA(nav.userAgent, { nav });
701
940
  }
941
+ uaBrowser.detect = async (ua) => {
942
+ const ctx = await getEnvContext();
943
+ return parseUA(ua != null ? ua : ctx.userAgent, { ctx });
944
+ };
702
945
  uaBrowser.isWebview = isWebview;
703
946
  uaBrowser.getLanguage = () => getLanguage(getNavContext());
704
947
  uaBrowser.VERSION = VERSION;
@@ -709,7 +952,9 @@ exports.VERSION = VERSION;
709
952
  exports.default = src_default;
710
953
  exports.detectArch = detectArch;
711
954
  exports.detectBot = detectBot;
955
+ exports.detectBrowser = detectBrowser;
712
956
  exports.detectHeadless = detectHeadless;
957
+ exports.detectOS = detectOs;
713
958
  exports.getEnvContext = getEnvContext;
714
959
  exports.getLanguage = getLanguage;
715
960
  exports.getNavContext = getNavContext;
@@ -717,5 +962,6 @@ exports.getWindowsVersion = getWindowsVersion;
717
962
  exports.isWebview = isWebview;
718
963
  exports.parseHeaders = parseHeaders;
719
964
  exports.parseUA = parseUA;
965
+ exports.satisfies = satisfies;
720
966
  //# sourceMappingURL=index.cjs.map
721
967
  //# sourceMappingURL=index.cjs.map