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.mjs CHANGED
@@ -1,12 +1,13 @@
1
1
  // package.json
2
2
  var package_default = {
3
- version: "1.3.0"};
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";
303
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";
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,25 +685,11 @@ 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
 
520
- // src/utils/windows-version.ts
521
- async function getWindowsVersion(nav) {
522
- var _a;
523
- if (!nav.userAgentData || nav.userAgentData.platform !== "Windows") {
524
- return null;
525
- }
526
- try {
527
- const data = await nav.userAgentData.getHighEntropyValues(["platformVersion"]);
528
- const major = parseInt(((_a = data["platformVersion"]) == null ? void 0 : _a.split(".")[0]) || "0", 10);
529
- return major >= 13 ? "11" : "10";
530
- } catch (e) {
531
- return null;
532
- }
533
- }
534
-
535
693
  // src/utils/env-context.ts
536
694
  var OS_FONTS = [
537
695
  ".AppleSystemUIFont",
@@ -559,21 +717,56 @@ function probeFonts() {
559
717
  }
560
718
  }
561
719
  function getWebGLInfo() {
562
- var _a;
720
+ var _a, _b;
563
721
  try {
564
722
  const canvas = document.createElement("canvas");
565
723
  const gl = (_a = canvas.getContext("webgl")) != null ? _a : canvas.getContext("experimental-webgl");
566
724
  if (!gl) return {};
567
725
  const ext = gl.getExtension("WEBGL_debug_renderer_info");
568
- if (!ext) return {};
569
- return {
570
- renderer: gl.getParameter(ext.UNMASKED_RENDERER_WEBGL),
571
- 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
+ }
572
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;
573
741
  } catch (e) {
574
742
  return {};
575
743
  }
576
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
+ }
577
770
  function getPointerType() {
578
771
  try {
579
772
  if (window.matchMedia("(pointer: coarse)").matches) return "coarse";
@@ -597,20 +790,44 @@ function deriveWindowsVersion(platformVersion) {
597
790
  return isNaN(major) ? null : major >= 13 ? "11" : "10";
598
791
  }
599
792
  async function getEnvContext() {
793
+ var _a, _b, _c, _d, _e;
600
794
  const base = getNavContext();
601
- const ctx = { ...base };
795
+ const ctx = {
796
+ userAgent: base.userAgent,
797
+ platform: base.platform,
798
+ language: base.language,
799
+ maxTouchPoints: base.maxTouchPoints
800
+ };
801
+ if (base.browserLanguage !== void 0) ctx.browserLanguage = base.browserLanguage;
802
+ if (base.mimeTypes !== void 0) ctx.mimeTypes = base.mimeTypes;
803
+ if (base.connection !== void 0) ctx.connection = base.connection;
804
+ if (base.userAgentData !== void 0) ctx.userAgentData = base.userAgentData;
602
805
  if (typeof navigator !== "undefined") {
603
806
  ctx.hardwareConcurrency = navigator.hardwareConcurrency;
604
807
  ctx.deviceMemory = navigator.deviceMemory;
605
808
  }
606
809
  if (typeof window !== "undefined") {
607
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();
608
814
  ctx.pointerType = getPointerType();
609
815
  ctx.hoverCapability = getHoverCapability();
610
816
  ctx.fontProbes = probeFonts();
611
- 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();
612
826
  if (renderer !== void 0) ctx.webglRenderer = renderer;
613
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;
614
831
  }
615
832
  if (base.userAgentData) {
616
833
  try {
@@ -637,6 +854,26 @@ async function getEnvContext() {
637
854
  return ctx;
638
855
  }
639
856
 
857
+ // src/utils/windows-version.ts
858
+ async function getWindowsVersion(nav) {
859
+ var _a;
860
+ if (!nav.userAgentData || nav.userAgentData.platform !== "Windows") {
861
+ return null;
862
+ }
863
+ try {
864
+ const data = await nav.userAgentData.getHighEntropyValues(["platformVersion"]);
865
+ const major = parseInt(((_a = data["platformVersion"]) == null ? void 0 : _a.split(".")[0]) || "0", 10);
866
+ return major >= 13 ? "11" : "10";
867
+ } catch (e) {
868
+ return null;
869
+ }
870
+ }
871
+
872
+ // src/utils/satisfies.ts
873
+ function satisfies(info, criteria) {
874
+ return Object.keys(criteria).every((key) => info[key] === criteria[key]);
875
+ }
876
+
640
877
  // src/parse-headers.ts
641
878
  var ACCEPT_CH = [
642
879
  "Sec-CH-UA-Arch",
@@ -656,7 +893,7 @@ function deriveWindowsVersion2(platformVersion) {
656
893
  return isNaN(major) ? null : major >= 13 ? "11" : "10";
657
894
  }
658
895
  function parseHeaders(headers) {
659
- var _a, _b;
896
+ var _a, _b, _c, _d, _e;
660
897
  const normalised = {};
661
898
  for (const key of Object.keys(headers)) {
662
899
  normalised[key.toLowerCase()] = headers[key];
@@ -666,11 +903,13 @@ function parseHeaders(headers) {
666
903
  return Array.isArray(v) ? v[0] : v;
667
904
  };
668
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 : "" : "";
669
908
  const architecture = unquote(get("sec-ch-ua-arch"));
670
909
  const bitness = unquote(get("sec-ch-ua-bitness"));
671
910
  const model = unquote(get("sec-ch-ua-model"));
672
911
  const platformVersion = unquote(get("sec-ch-ua-platform-version"));
673
- const platform = (_b = unquote(get("sec-ch-ua-platform"))) != null ? _b : "";
912
+ const platform = (_e = unquote(get("sec-ch-ua-platform"))) != null ? _e : "";
674
913
  const highEntropyData = {};
675
914
  if (architecture !== void 0) highEntropyData.architecture = architecture;
676
915
  if (bitness !== void 0) highEntropyData.bitness = bitness;
@@ -681,7 +920,7 @@ function parseHeaders(headers) {
681
920
  const ctx = {
682
921
  userAgent: ua,
683
922
  platform,
684
- language: "",
923
+ language,
685
924
  maxTouchPoints: isMobile ? 1 : 0,
686
925
  highEntropyData: Object.keys(highEntropyData).length > 0 ? highEntropyData : void 0,
687
926
  windowsVersion
@@ -691,15 +930,19 @@ function parseHeaders(headers) {
691
930
 
692
931
  // src/index.ts
693
932
  var { version: VERSION } = package_default;
694
- function uaBrowser(ua) {
933
+ function uaBrowser() {
695
934
  const nav = getNavContext();
696
- return parseUA(ua != null ? ua : nav.userAgent, { nav });
935
+ return parseUA(nav.userAgent, { nav });
697
936
  }
937
+ uaBrowser.detect = async (ua) => {
938
+ const ctx = await getEnvContext();
939
+ return parseUA(ua != null ? ua : ctx.userAgent, { ctx });
940
+ };
698
941
  uaBrowser.isWebview = isWebview;
699
942
  uaBrowser.getLanguage = () => getLanguage(getNavContext());
700
943
  uaBrowser.VERSION = VERSION;
701
944
  var src_default = uaBrowser;
702
945
 
703
- 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 };
704
947
  //# sourceMappingURL=index.mjs.map
705
948
  //# sourceMappingURL=index.mjs.map