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