ua-browser 1.0.2 → 1.1.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 +220 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +73 -9
- package/dist/index.d.ts +73 -9
- package/dist/index.min.js +2 -2
- package/dist/index.min.js.map +1 -1
- package/dist/index.mjs +218 -19
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -1
package/dist/index.cjs
CHANGED
|
@@ -4,7 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
// package.json
|
|
6
6
|
var package_default = {
|
|
7
|
-
version: "1.0
|
|
7
|
+
version: "1.1.0"};
|
|
8
8
|
|
|
9
9
|
// src/constants/browsers.ts
|
|
10
10
|
var BROWSER_DEFS = [
|
|
@@ -166,7 +166,10 @@ var ENGINE_DEFS = [
|
|
|
166
166
|
{ name: "Trident", detect: /(Trident|NET CLR)/ },
|
|
167
167
|
{ name: "Presto", detect: /Presto/ },
|
|
168
168
|
{ name: "Gecko", detect: /Gecko\// },
|
|
169
|
-
{ name: "WebKit", detect: /AppleWebKit/ }
|
|
169
|
+
{ name: "WebKit", detect: /AppleWebKit/ },
|
|
170
|
+
// ArkWeb is HarmonyOS Next's rendering engine; only present in UAs that explicitly contain the
|
|
171
|
+
// token. It must come last so it overrides the WebKit entry that also matches those UAs.
|
|
172
|
+
{ name: "ArkWeb", detect: /ArkWeb/ }
|
|
170
173
|
];
|
|
171
174
|
|
|
172
175
|
// src/detectors/engine.ts
|
|
@@ -209,13 +212,16 @@ var OS_DEFS = [
|
|
|
209
212
|
{ name: "MacOS", detect: /Macintosh/, versionPattern: /Mac OS X -?([\d_.]+)/ },
|
|
210
213
|
{ name: "Android", detect: /(Android|Adr)/, versionPattern: /(?:Android|Adr) ([\d.]+)/ },
|
|
211
214
|
// HarmonyOS must come after Android: HarmonyOS UAs include "Android", so Android matches
|
|
212
|
-
// first, then HarmonyOS overrides it.
|
|
215
|
+
// first, then HarmonyOS overrides it. versionPattern tries direct extraction first (5.0+
|
|
216
|
+
// pure HarmonyOS UAs don't have Android token), then falls back to Android version + lookup.
|
|
213
217
|
{
|
|
214
218
|
name: "HarmonyOS",
|
|
215
219
|
detect: /HarmonyOS/,
|
|
216
|
-
versionPattern: /Android ([\d.]+)[;)]
|
|
217
|
-
versionLookup: { "10": "2" }
|
|
220
|
+
versionPattern: [/HarmonyOS[\s/]([\d.]+)/, /Android ([\d.]+)[;)]/],
|
|
221
|
+
versionLookup: { "10": "2", "11": "3", "12": "3", "13": "4" }
|
|
218
222
|
},
|
|
223
|
+
// OpenHarmony (open-source base) must come after HarmonyOS to override any earlier match.
|
|
224
|
+
{ name: "OpenHarmony", detect: /OpenHarmony/, versionPattern: /OpenHarmony[\s/]([\d.]+)/ },
|
|
219
225
|
{ name: "KaiOS", detect: /KAIOS/, versionPattern: /KAIOS\/([\d.]+)/ },
|
|
220
226
|
// Windows must come before Windows Phone: Windows Phone UAs contain "Windows", so Windows
|
|
221
227
|
// matches first, then Windows Phone overrides it.
|
|
@@ -249,7 +255,7 @@ function detectOs(ua, windowsVersion) {
|
|
|
249
255
|
if (!matchedDef) return { os: "unknown", osVersion: "unknown" };
|
|
250
256
|
let osVersion = "unknown";
|
|
251
257
|
if (matchedDef.versionPattern) {
|
|
252
|
-
const raw = extractVersion(ua, matchedDef.versionPattern);
|
|
258
|
+
const raw = Array.isArray(matchedDef.versionPattern) ? extractVersionFromPatterns(ua, matchedDef.versionPattern) : extractVersion(ua, matchedDef.versionPattern);
|
|
253
259
|
if (raw !== null) {
|
|
254
260
|
const normalised = raw.replace(/_/g, ".");
|
|
255
261
|
if (matchedDef.versionLookup) {
|
|
@@ -259,6 +265,8 @@ function detectOs(ua, windowsVersion) {
|
|
|
259
265
|
} else if (matchedDef.name === "Windows") {
|
|
260
266
|
const n = parseInt(normalised, 10);
|
|
261
267
|
osVersion = isNaN(n) ? "unknown" : n.toString();
|
|
268
|
+
} else {
|
|
269
|
+
osVersion = normalised;
|
|
262
270
|
}
|
|
263
271
|
} else {
|
|
264
272
|
osVersion = normalised;
|
|
@@ -350,7 +358,33 @@ var ARCH_DEFS = [
|
|
|
350
358
|
];
|
|
351
359
|
|
|
352
360
|
// src/detectors/arch.ts
|
|
353
|
-
function detectArch(ua) {
|
|
361
|
+
function detectArch(ua, ctx) {
|
|
362
|
+
if (ctx == null ? void 0 : ctx.highEntropyData) {
|
|
363
|
+
const { architecture, bitness } = ctx.highEntropyData;
|
|
364
|
+
if (architecture === "arm") {
|
|
365
|
+
return bitness === "64" ? "arm64" : "arm";
|
|
366
|
+
}
|
|
367
|
+
if (architecture === "x86") {
|
|
368
|
+
return bitness === "64" ? "x86_64" : "x86";
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
const renderer = ctx == null ? void 0 : ctx.webglRenderer;
|
|
372
|
+
if (renderer) {
|
|
373
|
+
if (/Apple\s+(M\d|A\d{1,2}[A-Z]?)\b/i.test(renderer) || /APPLE M\d/i.test(renderer)) {
|
|
374
|
+
return "arm64";
|
|
375
|
+
}
|
|
376
|
+
if (/\b(Intel|AMD|NVIDIA|Radeon)\b/i.test(renderer)) {
|
|
377
|
+
return "x86_64";
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const platform = ctx == null ? void 0 : ctx.platform;
|
|
381
|
+
if (platform) {
|
|
382
|
+
if (/iPhone|iPad|iPod/.test(platform)) return "arm64";
|
|
383
|
+
if (/arm64|aarch64/i.test(platform)) return "arm64";
|
|
384
|
+
if (/arm/i.test(platform)) return "arm";
|
|
385
|
+
if (/Win64|WOW64|x86_64/i.test(platform)) return "x86_64";
|
|
386
|
+
if (/Win32|i686/i.test(platform)) return "x86";
|
|
387
|
+
}
|
|
354
388
|
for (const def of ARCH_DEFS) {
|
|
355
389
|
if (def.detect.test(ua)) {
|
|
356
390
|
return def.name;
|
|
@@ -365,6 +399,13 @@ function detectHeadless(ua) {
|
|
|
365
399
|
return HEADLESS_PATTERN.test(ua);
|
|
366
400
|
}
|
|
367
401
|
|
|
402
|
+
// src/detectors/webview.ts
|
|
403
|
+
function isWebview(ua) {
|
|
404
|
+
if (/; wv/.test(ua)) return true;
|
|
405
|
+
if (/like Mac OS X/.test(ua) && !/Version\//.test(ua) && !/Safari\//.test(ua)) return true;
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
|
|
368
409
|
// src/utils/navigator.ts
|
|
369
410
|
function getNavContext() {
|
|
370
411
|
if (typeof navigator === "undefined") {
|
|
@@ -389,17 +430,19 @@ function getLanguage(nav) {
|
|
|
389
430
|
|
|
390
431
|
// src/parse.ts
|
|
391
432
|
function parseUA(ua, options = {}) {
|
|
392
|
-
var _a, _b, _c, _d, _e;
|
|
393
|
-
const
|
|
433
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
434
|
+
const effectiveNav = (_a = options.ctx) != null ? _a : options.nav;
|
|
435
|
+
const effectiveWindowsVersion = (_c = (_b = options.ctx) == null ? void 0 : _b.windowsVersion) != null ? _c : options.windowsVersion;
|
|
394
436
|
const { browser: rawBrowser, version: rawVersion } = detectBrowser(ua);
|
|
395
|
-
const { os, osVersion: rawOsVersion } = detectOs(ua,
|
|
437
|
+
const { os, osVersion: rawOsVersion } = detectOs(ua, effectiveWindowsVersion);
|
|
396
438
|
let osVersion = rawOsVersion;
|
|
397
|
-
const device = detectDevice(ua,
|
|
398
|
-
const arch = detectArch(ua);
|
|
439
|
+
const device = detectDevice(ua, effectiveNav);
|
|
440
|
+
const arch = detectArch(ua, options.ctx);
|
|
441
|
+
const nav = effectiveNav;
|
|
399
442
|
const { isBot, botName } = detectBot(ua);
|
|
400
443
|
const isHeadless = detectHeadless(ua);
|
|
401
444
|
const language = nav ? getLanguage(nav) : "unknown";
|
|
402
|
-
const platform = (
|
|
445
|
+
const platform = (_d = nav == null ? void 0 : nav.platform) != null ? _d : "unknown";
|
|
403
446
|
let browser = rawBrowser;
|
|
404
447
|
let version = rawVersion;
|
|
405
448
|
if (nav) {
|
|
@@ -421,7 +464,7 @@ function parseUA(ua, options = {}) {
|
|
|
421
464
|
}
|
|
422
465
|
}
|
|
423
466
|
if (is360) {
|
|
424
|
-
const saveDataEnabled = ((
|
|
467
|
+
const saveDataEnabled = ((_e = nav.connection) == null ? void 0 : _e.saveData) === true;
|
|
425
468
|
if (getMimeType(nav, "application/gameplugin") || !saveDataEnabled) {
|
|
426
469
|
browser = "360SE";
|
|
427
470
|
} else {
|
|
@@ -434,8 +477,8 @@ function parseUA(ua, options = {}) {
|
|
|
434
477
|
}
|
|
435
478
|
if (browser === "Baidu" && /(Opera|OPR|OPT)/.test(ua)) {
|
|
436
479
|
browser = "Opera";
|
|
437
|
-
const opVer = (
|
|
438
|
-
version = (
|
|
480
|
+
const opVer = (_g = (_f = /OPR\/([\d.]+)/.exec(ua)) != null ? _f : /OPT\/([\d.]+)/.exec(ua)) != null ? _g : /Opera\/([\d.]+)/.exec(ua);
|
|
481
|
+
version = (_h = opVer == null ? void 0 : opVer[1]) != null ? _h : "unknown";
|
|
439
482
|
}
|
|
440
483
|
if (browser === "Chrome" && /\S+Browser\//.test(ua)) {
|
|
441
484
|
const m = /(\S+Browser)\/([\d.]+)/.exec(ua);
|
|
@@ -475,7 +518,7 @@ function parseUA(ua, options = {}) {
|
|
|
475
518
|
osVersion,
|
|
476
519
|
device,
|
|
477
520
|
arch,
|
|
478
|
-
isWebview:
|
|
521
|
+
isWebview: isWebview(ua),
|
|
479
522
|
isHeadless,
|
|
480
523
|
isBot,
|
|
481
524
|
botName,
|
|
@@ -499,9 +542,165 @@ async function getWindowsVersion(nav) {
|
|
|
499
542
|
}
|
|
500
543
|
}
|
|
501
544
|
|
|
545
|
+
// src/utils/env-context.ts
|
|
546
|
+
var OS_FONTS = [
|
|
547
|
+
".AppleSystemUIFont",
|
|
548
|
+
"Segoe UI",
|
|
549
|
+
"Noto Color Emoji",
|
|
550
|
+
"Ubuntu",
|
|
551
|
+
"HarmonyOS Sans"
|
|
552
|
+
];
|
|
553
|
+
function probeFonts() {
|
|
554
|
+
try {
|
|
555
|
+
const canvas = document.createElement("canvas");
|
|
556
|
+
const ctx = canvas.getContext("2d");
|
|
557
|
+
if (!ctx) return {};
|
|
558
|
+
const TEST = "mmmmmmmmmmlli";
|
|
559
|
+
ctx.font = "72px monospace";
|
|
560
|
+
const baseline = ctx.measureText(TEST).width;
|
|
561
|
+
const result = {};
|
|
562
|
+
for (const font of OS_FONTS) {
|
|
563
|
+
ctx.font = `72px '${font}', monospace`;
|
|
564
|
+
result[font] = ctx.measureText(TEST).width !== baseline;
|
|
565
|
+
}
|
|
566
|
+
return result;
|
|
567
|
+
} catch (e) {
|
|
568
|
+
return {};
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
function getWebGLInfo() {
|
|
572
|
+
var _a;
|
|
573
|
+
try {
|
|
574
|
+
const canvas = document.createElement("canvas");
|
|
575
|
+
const gl = (_a = canvas.getContext("webgl")) != null ? _a : canvas.getContext("experimental-webgl");
|
|
576
|
+
if (!gl) return {};
|
|
577
|
+
const ext = gl.getExtension("WEBGL_debug_renderer_info");
|
|
578
|
+
if (!ext) return {};
|
|
579
|
+
return {
|
|
580
|
+
renderer: gl.getParameter(ext.UNMASKED_RENDERER_WEBGL),
|
|
581
|
+
vendor: gl.getParameter(ext.UNMASKED_VENDOR_WEBGL)
|
|
582
|
+
};
|
|
583
|
+
} catch (e) {
|
|
584
|
+
return {};
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
function getPointerType() {
|
|
588
|
+
try {
|
|
589
|
+
if (window.matchMedia("(pointer: coarse)").matches) return "coarse";
|
|
590
|
+
if (window.matchMedia("(pointer: fine)").matches) return "fine";
|
|
591
|
+
if (window.matchMedia("(pointer: none)").matches) return "none";
|
|
592
|
+
} catch (e) {
|
|
593
|
+
}
|
|
594
|
+
return void 0;
|
|
595
|
+
}
|
|
596
|
+
function getHoverCapability() {
|
|
597
|
+
try {
|
|
598
|
+
return window.matchMedia("(hover: hover)").matches;
|
|
599
|
+
} catch (e) {
|
|
600
|
+
return void 0;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function deriveWindowsVersion(platformVersion) {
|
|
604
|
+
var _a;
|
|
605
|
+
if (!platformVersion) return null;
|
|
606
|
+
const major = parseInt((_a = platformVersion.split(".")[0]) != null ? _a : "0", 10);
|
|
607
|
+
return isNaN(major) ? null : major >= 13 ? "11" : "10";
|
|
608
|
+
}
|
|
609
|
+
async function getEnvContext() {
|
|
610
|
+
const base = getNavContext();
|
|
611
|
+
const ctx = { ...base };
|
|
612
|
+
if (typeof navigator !== "undefined") {
|
|
613
|
+
ctx.hardwareConcurrency = navigator.hardwareConcurrency;
|
|
614
|
+
ctx.deviceMemory = navigator.deviceMemory;
|
|
615
|
+
}
|
|
616
|
+
if (typeof window !== "undefined") {
|
|
617
|
+
ctx.devicePixelRatio = window.devicePixelRatio;
|
|
618
|
+
ctx.pointerType = getPointerType();
|
|
619
|
+
ctx.hoverCapability = getHoverCapability();
|
|
620
|
+
ctx.fontProbes = probeFonts();
|
|
621
|
+
const { renderer, vendor } = getWebGLInfo();
|
|
622
|
+
if (renderer !== void 0) ctx.webglRenderer = renderer;
|
|
623
|
+
if (vendor !== void 0) ctx.webglVendor = vendor;
|
|
624
|
+
}
|
|
625
|
+
if (base.userAgentData) {
|
|
626
|
+
try {
|
|
627
|
+
const data = await base.userAgentData.getHighEntropyValues([
|
|
628
|
+
"architecture",
|
|
629
|
+
"bitness",
|
|
630
|
+
"model",
|
|
631
|
+
"platformVersion",
|
|
632
|
+
"fullVersionList"
|
|
633
|
+
]);
|
|
634
|
+
ctx.highEntropyData = {
|
|
635
|
+
architecture: data["architecture"],
|
|
636
|
+
bitness: data["bitness"],
|
|
637
|
+
model: data["model"],
|
|
638
|
+
platformVersion: data["platformVersion"],
|
|
639
|
+
fullVersionList: data["fullVersionList"]
|
|
640
|
+
};
|
|
641
|
+
if (base.userAgentData.platform === "Windows") {
|
|
642
|
+
ctx.windowsVersion = deriveWindowsVersion(data["platformVersion"]);
|
|
643
|
+
}
|
|
644
|
+
} catch (e) {
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
return ctx;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// src/parse-headers.ts
|
|
651
|
+
var ACCEPT_CH = [
|
|
652
|
+
"Sec-CH-UA-Arch",
|
|
653
|
+
"Sec-CH-UA-Bitness",
|
|
654
|
+
"Sec-CH-UA-Platform-Version",
|
|
655
|
+
"Sec-CH-UA-Model",
|
|
656
|
+
"Sec-CH-UA-Full-Version-List"
|
|
657
|
+
].join(", ");
|
|
658
|
+
function unquote(value) {
|
|
659
|
+
if (value == null) return void 0;
|
|
660
|
+
return value.replace(/^"|"$/g, "").trim() || void 0;
|
|
661
|
+
}
|
|
662
|
+
function deriveWindowsVersion2(platformVersion) {
|
|
663
|
+
var _a;
|
|
664
|
+
if (!platformVersion) return null;
|
|
665
|
+
const major = parseInt((_a = platformVersion.split(".")[0]) != null ? _a : "0", 10);
|
|
666
|
+
return isNaN(major) ? null : major >= 13 ? "11" : "10";
|
|
667
|
+
}
|
|
668
|
+
function parseHeaders(headers) {
|
|
669
|
+
var _a, _b;
|
|
670
|
+
const normalised = {};
|
|
671
|
+
for (const key of Object.keys(headers)) {
|
|
672
|
+
normalised[key.toLowerCase()] = headers[key];
|
|
673
|
+
}
|
|
674
|
+
const get = (name) => {
|
|
675
|
+
const v = normalised[name.toLowerCase()];
|
|
676
|
+
return Array.isArray(v) ? v[0] : v;
|
|
677
|
+
};
|
|
678
|
+
const ua = (_a = get("user-agent")) != null ? _a : "";
|
|
679
|
+
const architecture = unquote(get("sec-ch-ua-arch"));
|
|
680
|
+
const bitness = unquote(get("sec-ch-ua-bitness"));
|
|
681
|
+
const model = unquote(get("sec-ch-ua-model"));
|
|
682
|
+
const platformVersion = unquote(get("sec-ch-ua-platform-version"));
|
|
683
|
+
const platform = (_b = unquote(get("sec-ch-ua-platform"))) != null ? _b : "";
|
|
684
|
+
const highEntropyData = {};
|
|
685
|
+
if (architecture !== void 0) highEntropyData.architecture = architecture;
|
|
686
|
+
if (bitness !== void 0) highEntropyData.bitness = bitness;
|
|
687
|
+
if (model !== void 0) highEntropyData.model = model;
|
|
688
|
+
if (platformVersion !== void 0) highEntropyData.platformVersion = platformVersion;
|
|
689
|
+
const isMobile = get("sec-ch-ua-mobile") === "?1";
|
|
690
|
+
const windowsVersion = platform === "Windows" ? deriveWindowsVersion2(platformVersion) : null;
|
|
691
|
+
const ctx = {
|
|
692
|
+
userAgent: ua,
|
|
693
|
+
platform,
|
|
694
|
+
language: "",
|
|
695
|
+
maxTouchPoints: isMobile ? 1 : 0,
|
|
696
|
+
highEntropyData: Object.keys(highEntropyData).length > 0 ? highEntropyData : void 0,
|
|
697
|
+
windowsVersion
|
|
698
|
+
};
|
|
699
|
+
return parseUA(ua, { ctx });
|
|
700
|
+
}
|
|
701
|
+
|
|
502
702
|
// src/index.ts
|
|
503
703
|
var { version: VERSION } = package_default;
|
|
504
|
-
var isWebview = (ua) => /; wv/.test(ua);
|
|
505
704
|
var isWechatMiniapp = () => {
|
|
506
705
|
try {
|
|
507
706
|
return typeof __wxjs_environment !== "undefined" && __wxjs_environment === "miniprogram";
|
|
@@ -519,16 +718,19 @@ uaBrowser.getLanguage = () => getLanguage(getNavContext());
|
|
|
519
718
|
uaBrowser.VERSION = VERSION;
|
|
520
719
|
var src_default = uaBrowser;
|
|
521
720
|
|
|
721
|
+
exports.ACCEPT_CH = ACCEPT_CH;
|
|
522
722
|
exports.VERSION = VERSION;
|
|
523
723
|
exports.default = src_default;
|
|
524
724
|
exports.detectArch = detectArch;
|
|
525
725
|
exports.detectBot = detectBot;
|
|
526
726
|
exports.detectHeadless = detectHeadless;
|
|
727
|
+
exports.getEnvContext = getEnvContext;
|
|
527
728
|
exports.getLanguage = getLanguage;
|
|
528
729
|
exports.getNavContext = getNavContext;
|
|
529
730
|
exports.getWindowsVersion = getWindowsVersion;
|
|
530
731
|
exports.isWebview = isWebview;
|
|
531
732
|
exports.isWechatMiniapp = isWechatMiniapp;
|
|
733
|
+
exports.parseHeaders = parseHeaders;
|
|
532
734
|
exports.parseUA = parseUA;
|
|
533
735
|
//# sourceMappingURL=index.cjs.map
|
|
534
736
|
//# sourceMappingURL=index.cjs.map
|