UNPKG

express-useragent

Version:

JS Library & ExpressJS user-agent middleware exposing

1,269 lines (1,266 loc) 41.9 kB
"use strict"; var expressUseragent = (() => { var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/browser.ts var browser_exports = {}; __export(browser_exports, { UserAgent: () => UserAgent, default: () => browser_default, hydrate: () => hydrate, parse: () => parse }); // src/express-useragent.ts var BOTS = [ "\\+https:\\/\\/developers.google.com\\/\\+\\/web\\/snippet\\/", "ad\\smonitoring", "adsbot", "apex", "applebot", "archive.org_bot", "baiduspider", "bingbot", "chromeheadless", "cloudflare", "cloudinary", "crawler", "curl", "discordbot", "duckduckbot", "embedly", "exabot", "facebookexternalhit", "facebot", "flipboard", "google", "googlebot", "gsa-crawler", "gurujibot", "guzzlehttp", "heritrix", "ia_archiver", "insights", "linkedinbot", "ltx71", "mediapartners", "msnbot", "odklbot", "phantom\\.js", "phantomjs", "pingdom", "pinterest", "python", "rtlnieuws", "skypeuripreview", "slackbot", "slurp", "spbot", "telegrambot", "test\\scertificate", "testing", "tiabot", "tumblr ", "twitterbot", "vkshare", "web\\sscraper", "wget", "yandexbot", "whatsapp", "orangebot", "smtbot", "qwantify", "mj12bot", "ahrefsbot", "seznambot", "panscient\\.com", "duckduckgo-favicons-bot", "uptimerobot", "semrushbot", "postman", "dotbot", "zoominfobot", "ifttt", "sogou", "ru_bot", "researchscan", "nimbostratus-bot", "slack-imgproxy", "node-superagent", "go-http-client", "jersey", "dataprovider.com", "github-camo", "dispatch", "checkmarknetwork", "screaming frog", "whatweb", "daum", "netcraftsurveyagent", "mojeekbot", "surdotlybot", "springbot" ]; var IS_BOT_REGEXP = new RegExp(`(${BOTS.join("|")})`, "i"); var SILK_REGEXP = /silk/i; var SILK_ACCELERATED_REGEXP = /Silk-Accelerated=true/i; var SMART_TV_REGEXP = /smart-tv|smarttv|googletv|appletv|hbbtv|pov_tv|netcast.tv/i; var ANDROID_TABLET_REGEXP = /mobile/i; var MOBILE_REGEXP = /mobile|^ios-/i; var DALVIK_REGEXP = /dalvik/i; var IOS_SCALE_REGEXP = /scale/i; var OKHTTP_REGEXP = /okhttp/i; var WEBKIT_REGEXP = /applewebkit/i; var WECHAT_REGEXP = /micromessenger/i; var DEFAULT_AGENT = { isYaBrowser: false, isAuthoritative: true, isMobile: false, isMobileNative: false, isTablet: false, isiPad: false, isiPod: false, isiPhone: false, isiPhoneNative: false, isAndroid: false, isAndroidNative: false, isBlackberry: false, isOpera: false, isIE: false, isEdge: false, isIECompatibilityMode: false, isSafari: false, isFirefox: false, isWebkit: false, isChrome: false, isKonqueror: false, isOmniWeb: false, isSeaMonkey: false, isFlock: false, isAmaya: false, isPhantomJS: false, isEpiphany: false, isDesktop: false, isWindows: false, isLinux: false, isLinux64: false, isMac: false, isChromeOS: false, isBada: false, isSamsung: false, isRaspberry: false, isBot: false, botName: "", isCurl: false, isAndroidTablet: false, isWinJs: false, isKindleFire: false, isSilk: false, isCaptive: false, isSmartTV: false, isUC: false, isFacebook: false, isAlamoFire: false, isElectron: false, silkAccelerated: false, browser: "unknown", version: "unknown", os: "unknown", platform: "unknown", isWechat: false, isWindowsPhone: false, SilkAccelerated: false, geoIp: {}, source: "", electronVersion: "", isDuckDuckGo: false, clientHints: null }; function createDefaultAgent() { return { ...DEFAULT_AGENT, geoIp: {}, source: "", electronVersion: "", botName: "", clientHints: null }; } var isProductTokenChar = (charCode) => charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122 || charCode === 45 || charCode === 46 || charCode === 95; var isDigit = (charCode) => charCode >= 48 && charCode <= 57; var readProductToken = (source, startIndex = 0) => { const slashIndex = source.indexOf("/", startIndex); if (slashIndex <= startIndex) { return null; } for (let index = startIndex; index < slashIndex; index += 1) { if (!isProductTokenChar(source.charCodeAt(index))) { return null; } } let endIndex = slashIndex + 1; while (endIndex < source.length && isProductTokenChar(source.charCodeAt(endIndex))) { endIndex += 1; } if (endIndex === slashIndex + 1) { return null; } return { name: source.slice(startIndex, slashIndex), version: source.slice(slashIndex + 1, endIndex) }; }; var readVersionAfterProduct = (source, productName) => { const token = readProductToken(source); return (token == null ? void 0 : token.name.toLowerCase()) === productName.toLowerCase() ? token.version : null; }; var readVersionAfterKnownPrefix = (source, prefixes) => { const lowerSource = source.toLowerCase(); let bestStart = -1; let bestPrefix = ""; for (const prefix of prefixes) { const prefixStart = lowerSource.indexOf(prefix.toLowerCase()); if (prefixStart !== -1 && (bestStart === -1 || prefixStart < bestStart)) { bestStart = prefixStart; bestPrefix = prefix; } } if (bestStart === -1) { return null; } const versionStart = bestStart + bestPrefix.length; let versionEnd = versionStart; while (versionEnd < source.length && isProductTokenChar(source.charCodeAt(versionEnd))) { versionEnd += 1; } return versionEnd > versionStart ? source.slice(versionStart, versionEnd) : null; }; var readVersionAfterKnownProduct = (source, productNames) => readVersionAfterKnownPrefix( source, productNames.map((productName) => `${productName}/`) ); var readInternetExplorerVersion = (source) => { const lowerSource = source.toLowerCase(); const msieIndex = lowerSource.indexOf("msie"); if (msieIndex !== -1) { let versionStart2 = msieIndex + 4; while (source[versionStart2] === " ") { versionStart2 += 1; } let versionEnd2 = versionStart2; while (versionEnd2 < source.length) { const charCode = source.charCodeAt(versionEnd2); if (!isDigit(charCode) && charCode !== 46) { break; } versionEnd2 += 1; } if (versionEnd2 > versionStart2) { return source.slice(versionStart2, versionEnd2); } } const tridentIndex = lowerSource.indexOf("trident/"); if (tridentIndex === -1) { return null; } const rvIndex = lowerSource.indexOf("rv:", tridentIndex + 8); if (rvIndex === -1) { return null; } const versionStart = rvIndex + 3; let versionEnd = versionStart; while (versionEnd < source.length) { const charCode = source.charCodeAt(versionEnd); if (!isDigit(charCode) && charCode !== 46) { break; } versionEnd += 1; } return versionEnd > versionStart ? source.slice(versionStart, versionEnd) : null; }; var readTrailingProductToken = (source, requireClosingParen) => { let endIndex = source.length; while (endIndex > 0 && source.charCodeAt(endIndex - 1) === 32) { endIndex -= 1; } if (source[endIndex - 1] === ")") { endIndex -= 1; } else if (requireClosingParen) { return null; } let startIndex = endIndex; while (startIndex > 0 && isProductTokenChar(source.charCodeAt(startIndex - 1))) { startIndex -= 1; } return startIndex < endIndex ? source.slice(startIndex, endIndex) : null; }; var readIOSDeviceOSMatch = (source, device) => { const deviceLower = device.toLowerCase(); let searchFrom = 0; while (searchFrom < source.length) { const openIndex = source.indexOf("(", searchFrom); if (openIndex === -1) { return null; } const closeIndex = source.indexOf(")", openIndex + 1); const segmentEnd = closeIndex === -1 ? source.length : closeIndex; const segment = source.slice(openIndex + 1, segmentEnd); const segmentLower = segment.toLowerCase(); if (segmentLower.startsWith(deviceLower)) { let osSearchFrom = 0; while (osSearchFrom < segmentLower.length) { const osIndex = segmentLower.indexOf("os ", osSearchFrom); if (osIndex === -1) { break; } let versionStart = osIndex + 3; while (segment[versionStart] === " ") { versionStart += 1; } let majorEnd = versionStart; while (majorEnd < segment.length && isDigit(segment.charCodeAt(majorEnd))) { majorEnd += 1; } const separator = segment[majorEnd]; if (majorEnd > versionStart && (separator === "." || separator === "_")) { let minorEnd = majorEnd + 1; while (minorEnd < segment.length && isDigit(segment.charCodeAt(minorEnd))) { minorEnd += 1; } if (minorEnd > majorEnd + 1) { return `(${segment.slice(0, minorEnd)}`.replace("_", "."); } } osSearchFrom = osIndex + 3; } } searchFrom = segmentEnd + 1; } return null; }; var DUCKDUCKGO_WEBKIT_REGEXP = /\sDdg\/\d+(?:\.\d+)*$/; var _UserAgent = class _UserAgent { constructor() { this.versions = { Edge: /(?:edge|edga|edgios|edg)\/([A-Za-z0-9_.-]+)/i, Firefox: /(?:firefox|fxios)\/([A-Za-z0-9_.-]+)/i, IE: /msie\s(\d+(?:\.\d+)?)|trident\/\d+(?:\.\d+)?;[^)]*\brv:(\d+(?:\.\d+)?)/i, YaBrowser: /(?:yabrowser|yowser)\/([A-Za-z0-9_.-]+)/i, Chrome: /(?:chrome|crios)\/([A-Za-z0-9_.-]+)/i, Chromium: /chromium\/([A-Za-z0-9_.-]+)/i, Safari: /(version|safari)\/([A-Za-z0-9_.-]+)/i, Opera: /version\/([A-Za-z0-9_.-]+)|OPR\/([A-Za-z0-9_.-]+)/i, Amaya: /amaya\/([A-Za-z0-9_.-]+)/i, SeaMonkey: /seamonkey\/([A-Za-z0-9_.-]+)/i, OmniWeb: /omniweb\/v([A-Za-z0-9_.-]+)/i, Flock: /flock\/([A-Za-z0-9_.-]+)/i, Epiphany: /epiphany\/([A-Za-z0-9_.-]+)/i, WinJs: /msapphost\/([A-Za-z0-9_.-]+)/i, PhantomJS: /phantomjs\/([A-Za-z0-9_.-]+)/i, AlamoFire: /alamofire\/([A-Za-z0-9_.-]+)/i, UC: /ucbrowser\/([A-Za-z0-9_.]+)/i, Facebook: /FBAV\/([A-Za-z0-9_.]+)/i, WebKit: /applewebkit\/([A-Za-z0-9_.]+)/i, Wechat: /micromessenger\/([A-Za-z0-9_.]+)/i, Electron: /Electron\/([A-Za-z0-9_.]+)/i, DuckDuckGo: /\sDdg\/(\d+(?:\.\d+)*)$/i }; this.browsers = { YaBrowser: /yabrowser|yowser/i, Edge: /edge|edga|edgios|edg/i, Amaya: /amaya/i, Konqueror: /konqueror/i, Epiphany: /epiphany/i, SeaMonkey: /seamonkey/i, Flock: /flock/i, OmniWeb: /omniweb/i, Chromium: /chromium/i, Chrome: /chrome|crios/i, Safari: /safari/i, IE: /msie|trident/i, Opera: /opera|OPR\//i, PS3: /playstation 3/i, PSP: /playstation portable/i, Firefox: /firefox|fxios/i, WinJs: /msapphost/i, PhantomJS: /phantomjs/i, AlamoFire: /alamofire/i, UC: /UCBrowser/i, Facebook: /FBA[NV]/, DuckDuckGo: /\sDdg\/[\d.]+$/i }; this.os = { Windows11: /\bwindows(?:\s|_|)11(?:\.\d+)?/i, Windows10: /windows nt 10\.0/i, Windows81: /windows nt 6\.3/i, Windows8: /windows nt 6\.2/i, Windows7: /windows nt 6\.1/i, UnknownWindows: /windows nt 6\.\d+/i, WindowsVista: /windows nt 6\.0/i, Windows2003: /windows nt 5\.2/i, WindowsXP: /windows nt 5\.1/i, Windows2000: /windows nt 5\.0/i, WindowsPhone81: /windows phone 8\.1/i, WindowsPhone80: /windows phone 8\.0/i, OSXCheetah: /os x 10[._]0/i, OSXPuma: /os x 10[._]1(\D|$)/i, OSXJaguar: /os x 10[._]2/i, OSXPanther: /os x 10[._]3/i, OSXTiger: /os x 10[._]4/i, OSXLeopard: /os x 10[._]5/i, OSXSnowLeopard: /os x 10[._]6/i, OSXLion: /os x 10[._]7/i, OSXMountainLion: /os x 10[._]8/i, OSXMavericks: /os x 10[._]9/i, OSXYosemite: /os x 10[._]10/i, OSXElCapitan: /os x 10[._]11/i, MacOSSierra: /os x 10[._]12/i, MacOSHighSierra: /os x 10[._]13/i, MacOSMojave: /os x 10[._]14/i, MacOSCatalina: /os x 10[._]15/i, MacOSBigSur: /(mac os x 10[._]16(?:[._]\d+)?|mac os (?:x )?11[._]\d+)/i, MacOSMonterey: /mac os (?:x )?12[._]\d+/i, MacOSVentura: /mac os (?:x )?13[._]\d+/i, MacOSSonoma: /mac os (?:x )?14[._]\d+/i, MacOSSequoia: /mac os (?:x )?15[._]\d+/i, MacOSTahoe: /mac os (?:x )?26[._]\d+/i, Mac: /os x/i, Linux: /linux/i, Linux64: /linux x86_64/i, ChromeOS: /cros/i, Wii: /wii/i, PS3: /playstation 3/i, PSP: /playstation portable/i, iOS: /ios/i, Bada: /Bada\/(\d+)\.(\d+)/i, Curl: /curl\/(\d+)\.(\d+)\.(\d+)/i, Electron: /Electron\/(\d+)\.(\d+)\.(\d+)/i }; this.platform = { Windows: /windows nt/i, WindowsPhone: /windows phone/i, Mac: /macintosh/i, Linux: /linux/i, Wii: /wii/i, Playstation: /playstation/i, iPad: /ipad/i, iPod: /ipod/i, iPhone: /iphone/i, Android: /android/i, Blackberry: /blackberry/i, Samsung: /samsung/i, Curl: /curl/i, Electron: /Electron/i, iOS: /^ios-/i }; this.Agent = createDefaultAgent(); } reset() { this.Agent = createDefaultAgent(); return this; } testNginxGeoIP(headers) { Object.entries(headers != null ? headers : {}).forEach(([key, value]) => { if (/^GEOIP/i.test(key) && value !== void 0) { this.Agent.geoIp[key] = Array.isArray(value) ? value.join(",") : value; } }); return this; } /** * Parse User-Agent Client Hints from HTTP headers * @see https://wicg.github.io/ua-client-hints/ */ parseClientHints(headers) { var _a, _b, _c, _d, _e, _f, _g; const resolveHeader = (value) => { try { if (value === null || value === void 0) { return ""; } if (Array.isArray(value)) { const first = value[0]; if (typeof first !== "string") { return ""; } return first.slice(0, _UserAgent.MAX_HEADER_LENGTH); } if (typeof value !== "string") { return ""; } return value.slice(0, _UserAgent.MAX_HEADER_LENGTH); } catch (e) { return ""; } }; if (headers === null || headers === void 0 || typeof headers !== "object") { return null; } const normalizedHeaders = {}; try { let headerCount = 0; const maxHeaders = 50; for (const [key, value] of Object.entries(headers)) { if (++headerCount > maxHeaders) break; if (typeof key !== "string") continue; normalizedHeaders[key.toLowerCase()] = resolveHeader(value); } } catch (e) { return null; } const secChUa = normalizedHeaders["sec-ch-ua"]; if (!secChUa) { return null; } const parseBrandList = (header) => { try { if (!header || typeof header !== "string") return []; const brands = []; const brandRegex = /"([^"]{1,128})";\s*v="([^"]{1,64})"/g; let match; let iterations = 0; while ((match = brandRegex.exec(header)) !== null) { if (++iterations > _UserAgent.MAX_BRAND_COUNT) break; brands.push({ brand: match[1], version: match[2] }); } return brands; } catch (e) { return []; } }; const parseMobile = (header) => { try { if (typeof header !== "string") return false; return header === "?1"; } catch (e) { return false; } }; const parseQuotedString = (header) => { try { if (typeof header !== "string") return ""; const truncated = header.slice(0, 256); const match = /^"([^"]*)"$/.exec(truncated); return match ? match[1] : truncated; } catch (e) { return ""; } }; try { const clientHints = { brands: parseBrandList(secChUa), mobile: parseMobile((_a = normalizedHeaders["sec-ch-ua-mobile"]) != null ? _a : ""), platform: parseQuotedString((_b = normalizedHeaders["sec-ch-ua-platform"]) != null ? _b : ""), platformVersion: parseQuotedString((_c = normalizedHeaders["sec-ch-ua-platform-version"]) != null ? _c : ""), architecture: parseQuotedString((_d = normalizedHeaders["sec-ch-ua-arch"]) != null ? _d : ""), bitness: parseQuotedString((_e = normalizedHeaders["sec-ch-ua-bitness"]) != null ? _e : ""), model: parseQuotedString((_f = normalizedHeaders["sec-ch-ua-model"]) != null ? _f : ""), fullVersionList: parseBrandList((_g = normalizedHeaders["sec-ch-ua-full-version-list"]) != null ? _g : "") }; this.Agent.clientHints = clientHints; return clientHints; } catch (e) { return null; } } /** * Test for DuckDuckGo browser using both Client Hints and UA string patterns * - Chromium platforms (Android, Windows): Sec-CH-UA brand "DuckDuckGo" * - WebKit platforms (iOS, macOS): UA string ends with " Ddg/X.Y.Z" */ testDuckDuckGo() { var _a, _b, _c, _d, _e; if ((_a = this.Agent.clientHints) == null ? void 0 : _a.brands) { const hasDdgBrand = this.Agent.clientHints.brands.some( (brand) => brand.brand === "DuckDuckGo" ); if (hasDdgBrand) { this.Agent.isDuckDuckGo = true; this.Agent.browser = "DuckDuckGo"; this.Agent.version = (_b = this.getDuckDuckGoVersion()) != null ? _b : "unknown"; return; } } if ((_c = this.Agent.clientHints) == null ? void 0 : _c.fullVersionList) { const hasDdgBrand = this.Agent.clientHints.fullVersionList.some( (brand) => brand.brand === "DuckDuckGo" ); if (hasDdgBrand) { this.Agent.isDuckDuckGo = true; this.Agent.browser = "DuckDuckGo"; this.Agent.version = (_d = this.getDuckDuckGoVersion()) != null ? _d : "unknown"; return; } } if (DUCKDUCKGO_WEBKIT_REGEXP.test(this.Agent.source)) { this.Agent.isDuckDuckGo = true; this.Agent.browser = "DuckDuckGo"; this.Agent.version = (_e = this.getDuckDuckGoVersion()) != null ? _e : "unknown"; } } testBot() { const source = this.Agent.source.toLowerCase(); const match = IS_BOT_REGEXP.exec(source); if (match) { const botIdentifier = match[1]; if (botIdentifier === "google" && (source.includes("tiktok") || source.includes("trill") || source.includes("bytedance"))) { this.Agent.isBot = false; this.Agent.botName = ""; return; } this.Agent.isBot = true; this.Agent.botName = botIdentifier; } else if (!this.Agent.isAuthoritative) { this.Agent.isBot = /bot/i.test(this.Agent.source); this.Agent.botName = this.Agent.isBot ? "bot" : ""; } else { this.Agent.isBot = false; this.Agent.botName = ""; } } testSmartTV() { this.Agent.isSmartTV = SMART_TV_REGEXP.test(this.Agent.source.toLowerCase()); } testMobile() { if (this.Agent.isWindows || this.Agent.isLinux || this.Agent.isMac || this.Agent.isChromeOS) { this.Agent.isDesktop = true; } else if (this.Agent.isAndroid || this.Agent.isSamsung) { this.Agent.isMobile = true; } if (this.Agent.isiPad || this.Agent.isiPod || this.Agent.isiPhone || this.Agent.isBada || this.Agent.isBlackberry || this.Agent.isAndroid || this.Agent.isWindowsPhone) { this.Agent.isMobile = true; this.Agent.isDesktop = false; } if (MOBILE_REGEXP.test(this.Agent.source)) { this.Agent.isMobile = true; this.Agent.isDesktop = false; } if (DALVIK_REGEXP.test(this.Agent.source)) { this.Agent.isAndroidNative = true; this.Agent.isMobileNative = true; } if (OKHTTP_REGEXP.test(this.Agent.source)) { this.Agent.isAndroidNative = true; this.Agent.isMobileNative = true; this.Agent.isMobile = true; this.Agent.isAndroid = true; this.Agent.isDesktop = false; } if (IOS_SCALE_REGEXP.test(this.Agent.source)) { this.Agent.isiPhoneNative = true; this.Agent.isMobileNative = true; } } testAndroidTablet() { if (this.Agent.isAndroid && !ANDROID_TABLET_REGEXP.test(this.Agent.source)) { this.Agent.isAndroidTablet = true; } } testTablet() { if (this.Agent.isiPad || this.Agent.isAndroidTablet || this.Agent.isKindleFire) { this.Agent.isTablet = true; } if (/tablet/i.test(this.Agent.source)) { this.Agent.isTablet = true; } } testCompatibilityMode() { if (!this.Agent.isIE) { return; } const tridentMatch = /Trident\/(\d)\.0/i.exec(this.Agent.source); if (!tridentMatch) { return; } const tridentVersion = parseInt(tridentMatch[1], 10); const version = parseInt(String(this.Agent.version), 10); if (version === 7 && tridentVersion === 7) { this.Agent.isIECompatibilityMode = true; this.Agent.version = "11.0"; } if (version === 7 && tridentVersion === 6) { this.Agent.isIECompatibilityMode = true; this.Agent.version = "10.0"; } if (version === 7 && tridentVersion === 5) { this.Agent.isIECompatibilityMode = true; this.Agent.version = "9.0"; } if (version === 7 && tridentVersion === 4) { this.Agent.isIECompatibilityMode = true; this.Agent.version = "8.0"; } } testSilk() { if (SILK_REGEXP.test(this.Agent.source)) { this.Agent.isSilk = true; } if (SILK_ACCELERATED_REGEXP.test(this.Agent.source)) { this.Agent.silkAccelerated = true; this.Agent.SilkAccelerated = true; } return this.Agent.isSilk ? "Silk" : false; } testKindleFire() { const { source } = this.Agent; if (/KFOT/gi.test(source)) { this.Agent.isKindleFire = true; return "Kindle Fire"; } if (/KFTT/gi.test(source)) { this.Agent.isKindleFire = true; return "Kindle Fire HD"; } if (/KFJWI/gi.test(source)) { this.Agent.isKindleFire = true; return "Kindle Fire HD 8.9"; } if (/KFJWA/gi.test(source)) { this.Agent.isKindleFire = true; return "Kindle Fire HD 8.9 4G"; } if (/KFSOWI/gi.test(source)) { this.Agent.isKindleFire = true; return "Kindle Fire HD 7"; } if (/KFTHWI/gi.test(source)) { this.Agent.isKindleFire = true; return "Kindle Fire HDX 7"; } if (/KFTHWA/gi.test(source)) { this.Agent.isKindleFire = true; return "Kindle Fire HDX 7 4G"; } if (/KFAPWI/gi.test(source)) { this.Agent.isKindleFire = true; return "Kindle Fire HDX 8.9"; } if (/KFAPWA/gi.test(source)) { this.Agent.isKindleFire = true; return "Kindle Fire HDX 8.9 4G"; } return false; } testCaptiveNetwork() { if (/CaptiveNetwork/gi.test(this.Agent.source)) { this.Agent.isCaptive = true; this.Agent.isMac = true; this.Agent.platform = "Apple Mac"; return "CaptiveNetwork"; } return false; } testWebkit() { if (this.Agent.browser === "unknown" && WEBKIT_REGEXP.test(this.Agent.source)) { this.Agent.browser = "Apple WebKit"; this.Agent.isWebkit = true; } } testWechat() { if (WECHAT_REGEXP.test(this.Agent.source)) { this.Agent.isWechat = true; this.Agent.version = this.getWechatVersion(this.Agent.source); } } parse(source) { return new _UserAgent().hydrate(source).Agent; } /** * Hydrate agent from UA string and HTTP headers (including Client Hints) * This method should be preferred when headers are available as it enables * detection of browsers that use Client Hints (e.g., DuckDuckGo on Chromium) */ hydrateFromHeaders(source, headers) { this.hydrate(source); this.parseClientHints(headers); this.testDuckDuckGo(); return this; } hydrate(source) { this.Agent = createDefaultAgent(); this.Agent.source = source.trim(); this.Agent.os = this.getOS(this.Agent.source); this.Agent.platform = this.getPlatform(this.Agent.source); this.Agent.browser = this.getBrowser(this.Agent.source); this.Agent.version = this.getBrowserVersion(this.Agent.source); this.Agent.electronVersion = this.getElectronVersion(this.Agent.source); this.testBot(); this.testSmartTV(); this.testMobile(); this.testAndroidTablet(); this.testTablet(); this.testCompatibilityMode(); this.testSilk(); this.testKindleFire(); this.testCaptiveNetwork(); this.testWebkit(); this.testWechat(); return this; } getBrowser(string) { const agent = this.Agent; if (this.browsers.YaBrowser.test(string)) { agent.isYaBrowser = true; return "YaBrowser"; } if (this.browsers.AlamoFire.test(string)) { agent.isAlamoFire = true; return "AlamoFire"; } if (this.browsers.Edge.test(string)) { agent.isEdge = true; return "Edge"; } if (this.browsers.PhantomJS.test(string)) { agent.isPhantomJS = true; return "PhantomJS"; } if (this.browsers.Konqueror.test(string)) { agent.isKonqueror = true; return "Konqueror"; } if (this.browsers.Amaya.test(string)) { agent.isAmaya = true; return "Amaya"; } if (this.browsers.Epiphany.test(string)) { agent.isEpiphany = true; return "Epiphany"; } if (this.browsers.SeaMonkey.test(string)) { agent.isSeaMonkey = true; return "SeaMonkey"; } if (this.browsers.Flock.test(string)) { agent.isFlock = true; return "Flock"; } if (this.browsers.OmniWeb.test(string)) { agent.isOmniWeb = true; return "OmniWeb"; } if (this.browsers.Opera.test(string)) { agent.isOpera = true; return "Opera"; } if (this.browsers.Chromium.test(string)) { agent.isChrome = true; return "Chromium"; } if (this.browsers.Facebook.test(string)) { agent.isFacebook = true; return "Facebook"; } if (this.browsers.Chrome.test(string)) { agent.isChrome = true; return "Chrome"; } if (this.browsers.WinJs.test(string)) { agent.isWinJs = true; return "WinJs"; } if (this.browsers.IE.test(string)) { agent.isIE = true; return "IE"; } if (this.browsers.Firefox.test(string)) { agent.isFirefox = true; return "Firefox"; } if (/android/i.test(string) && /version\//i.test(string) && /mobile safari\//i.test(string) && !/chrome/i.test(string) && !/silk/i.test(string)) { agent.isAndroid = true; return "Android Browser"; } if (this.browsers.Safari.test(string)) { agent.isSafari = true; return "Safari"; } if (this.browsers.PS3.test(string)) { return "ps3"; } if (this.browsers.PSP.test(string)) { return "psp"; } if (this.browsers.UC.test(string)) { agent.isUC = true; return "UCBrowser"; } if (string.includes("Dalvik")) { return "unknown"; } if (!string.startsWith("Mozilla")) { const guess = readProductToken(string); if (guess) { agent.isAuthoritative = false; return guess.name; } } return "unknown"; } getBrowserVersion(string) { var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w; const agent = this.Agent; const browser = agent.browser; switch (browser) { case "Edge": return (_a = readVersionAfterKnownProduct(string, ["edge", "edga", "edgios", "edg"])) != null ? _a : "unknown"; case "PhantomJS": return (_b = readVersionAfterKnownProduct(string, ["phantomjs"])) != null ? _b : "unknown"; case "YaBrowser": return (_c = readVersionAfterKnownProduct(string, ["yabrowser", "yowser"])) != null ? _c : "unknown"; case "Chrome": return (_d = readVersionAfterKnownProduct(string, ["chrome", "crios"])) != null ? _d : "unknown"; case "Chromium": return (_e = readVersionAfterKnownProduct(string, ["chromium"])) != null ? _e : "unknown"; case "Safari": return (_f = readVersionAfterKnownProduct(string, ["version", "safari"])) != null ? _f : "unknown"; case "Opera": return (_g = readVersionAfterKnownProduct(string, ["version", "OPR"])) != null ? _g : "unknown"; case "Firefox": return (_h = readVersionAfterKnownProduct(string, ["firefox", "fxios"])) != null ? _h : "unknown"; case "WinJs": return (_i = readVersionAfterKnownProduct(string, ["msapphost"])) != null ? _i : "unknown"; case "IE": return (_j = readInternetExplorerVersion(string)) != null ? _j : "unknown"; case "ps3": return (_k = readTrailingProductToken(string, true)) != null ? _k : "unknown"; case "psp": return (_l = readTrailingProductToken(string, false)) != null ? _l : "unknown"; case "Amaya": return (_m = readVersionAfterKnownProduct(string, ["amaya"])) != null ? _m : "unknown"; case "Epiphany": return (_n = readVersionAfterKnownProduct(string, ["epiphany"])) != null ? _n : "unknown"; case "SeaMonkey": return (_o = readVersionAfterKnownProduct(string, ["seamonkey"])) != null ? _o : "unknown"; case "Flock": return (_p = readVersionAfterKnownProduct(string, ["flock"])) != null ? _p : "unknown"; case "OmniWeb": return (_q = readVersionAfterKnownPrefix(string, ["omniweb/v"])) != null ? _q : "unknown"; case "UCBrowser": return (_r = readVersionAfterKnownProduct(string, ["ucbrowser"])) != null ? _r : "unknown"; case "Facebook": return (_s = readVersionAfterKnownProduct(string, ["FBAV"])) != null ? _s : "unknown"; case "Android Browser": return (_t = readVersionAfterKnownProduct(string, ["version", "safari"])) != null ? _t : "unknown"; case "DuckDuckGo": return (_u = this.getDuckDuckGoVersion()) != null ? _u : "unknown"; default: if (browser !== "unknown") { return (_v = readVersionAfterProduct(string, browser)) != null ? _v : "unknown"; } else { this.testWebkit(); if (this.Agent.isWebkit) { return (_w = readVersionAfterKnownProduct(string, ["applewebkit"])) != null ? _w : "unknown"; } } } return "unknown"; } getWechatVersion(string) { const match = string.match(this.versions.Wechat); return match ? match[1] : "unknown"; } getDuckDuckGoVersion() { const hints = this.Agent.clientHints; if (hints) { const fullBrand = hints.fullVersionList.find((b) => b.brand === "DuckDuckGo"); if (fullBrand) { return fullBrand.version; } const brand = hints.brands.find((b) => b.brand === "DuckDuckGo"); if (brand) { return brand.version; } } const match = this.Agent.source.match(this.versions.DuckDuckGo); return match ? match[1] : null; } getElectronVersion(string) { const match = string.match(this.versions.Electron); if (match) { this.Agent.isElectron = true; return match[1]; } return ""; } getOS(string) { if (this.os.WindowsVista.test(string)) { this.Agent.isWindows = true; return "Windows Vista"; } if (this.os.Windows7.test(string)) { this.Agent.isWindows = true; return "Windows 7"; } if (this.os.Windows8.test(string)) { this.Agent.isWindows = true; return "Windows 8"; } if (this.os.Windows81.test(string)) { this.Agent.isWindows = true; return "Windows 8.1"; } if (this.os.Windows11.test(string)) { this.Agent.isWindows = true; return "Windows 11"; } if (this.os.Windows10.test(string)) { this.Agent.isWindows = true; return "Windows 10.0"; } if (this.os.Windows2003.test(string)) { this.Agent.isWindows = true; return "Windows 2003"; } if (this.os.WindowsXP.test(string)) { this.Agent.isWindows = true; return "Windows XP"; } if (this.os.Windows2000.test(string)) { this.Agent.isWindows = true; return "Windows 2000"; } if (this.os.WindowsPhone81.test(string)) { this.Agent.isWindowsPhone = true; return "Windows Phone 8.1"; } if (this.os.WindowsPhone80.test(string)) { this.Agent.isWindowsPhone = true; return "Windows Phone 8.0"; } if (this.os.Linux64.test(string)) { this.Agent.isLinux = true; this.Agent.isLinux64 = true; return "Linux 64"; } if (this.os.Linux.test(string)) { this.Agent.isLinux = true; return "Linux"; } if (this.os.ChromeOS.test(string)) { this.Agent.isChromeOS = true; return "Chrome OS"; } if (this.os.Wii.test(string)) { return "Wii"; } if (this.os.PS3.test(string)) { return "Playstation"; } if (this.os.PSP.test(string)) { return "Playstation"; } if (this.os.OSXCheetah.test(string)) { this.Agent.isMac = true; return "OS X Cheetah"; } if (this.os.OSXPuma.test(string)) { this.Agent.isMac = true; return "OS X Puma"; } if (this.os.OSXJaguar.test(string)) { this.Agent.isMac = true; return "OS X Jaguar"; } if (this.os.OSXPanther.test(string)) { this.Agent.isMac = true; return "OS X Panther"; } if (this.os.OSXTiger.test(string)) { this.Agent.isMac = true; return "OS X Tiger"; } if (this.os.OSXLeopard.test(string)) { this.Agent.isMac = true; return "OS X Leopard"; } if (this.os.OSXSnowLeopard.test(string)) { this.Agent.isMac = true; return "OS X Snow Leopard"; } if (this.os.OSXLion.test(string)) { this.Agent.isMac = true; return "OS X Lion"; } if (this.os.OSXMountainLion.test(string)) { this.Agent.isMac = true; return "OS X Mountain Lion"; } if (this.os.OSXMavericks.test(string)) { this.Agent.isMac = true; return "OS X Mavericks"; } if (this.os.OSXYosemite.test(string)) { this.Agent.isMac = true; return "OS X Yosemite"; } if (this.os.OSXElCapitan.test(string)) { this.Agent.isMac = true; return "OS X El Capitan"; } if (this.os.MacOSSierra.test(string)) { this.Agent.isMac = true; return "macOS Sierra"; } if (this.os.MacOSHighSierra.test(string)) { this.Agent.isMac = true; return "macOS High Sierra"; } if (this.os.MacOSMojave.test(string)) { this.Agent.isMac = true; return "macOS Mojave"; } if (this.os.MacOSCatalina.test(string)) { this.Agent.isMac = true; return "macOS Catalina"; } if (this.os.MacOSBigSur.test(string)) { this.Agent.isMac = true; return "macOS Big Sur"; } if (this.os.MacOSMonterey.test(string)) { this.Agent.isMac = true; return "macOS Monterey"; } if (this.os.MacOSVentura.test(string)) { this.Agent.isMac = true; return "macOS Ventura"; } if (this.os.MacOSSonoma.test(string)) { this.Agent.isMac = true; return "macOS Sonoma"; } if (this.os.MacOSSequoia.test(string)) { this.Agent.isMac = true; return "macOS Sequoia"; } if (this.os.MacOSTahoe.test(string)) { this.Agent.isMac = true; return "macOS Tahoe"; } if (this.os.Mac.test(string)) { this.Agent.isMac = true; return "OS X"; } const iPadMatch = readIOSDeviceOSMatch(string, "iPad"); if (iPadMatch) { this.Agent.isiPad = true; return iPadMatch; } const iPhoneMatch = readIOSDeviceOSMatch(string, "iPhone"); if (iPhoneMatch) { this.Agent.isiPhone = true; return iPhoneMatch; } if (this.os.Bada.test(string)) { this.Agent.isBada = true; return "Bada"; } if (this.os.Curl.test(string)) { this.Agent.isCurl = true; return "Curl"; } if (this.os.iOS.test(string)) { this.Agent.isiPhone = true; return "iOS"; } if (this.os.Electron.test(string)) { this.Agent.isElectron = true; return "Electron"; } return "unknown"; } getPlatform(string) { if (this.platform.Windows.test(string)) { return "Microsoft Windows"; } if (this.platform.WindowsPhone.test(string)) { this.Agent.isWindowsPhone = true; return "Microsoft Windows Phone"; } if (this.platform.Mac.test(string)) { return "Apple Mac"; } if (this.platform.Curl.test(string)) { return "Curl"; } if (this.platform.Electron.test(string)) { this.Agent.isElectron = true; return "Electron"; } if (this.platform.Android.test(string)) { this.Agent.isAndroid = true; if (this.platform.Samsung.test(string)) { this.Agent.isSamsung = true; } return "Android"; } if (this.platform.Blackberry.test(string)) { this.Agent.isBlackberry = true; return "Blackberry"; } if (this.platform.Linux.test(string)) { return "Linux"; } if (this.platform.Wii.test(string)) { return "Wii"; } if (this.platform.Playstation.test(string)) { return "Playstation"; } if (this.platform.iPad.test(string)) { this.Agent.isiPad = true; return "iPad"; } if (this.platform.iPod.test(string)) { this.Agent.isiPod = true; return "iPod"; } if (this.platform.iPhone.test(string)) { this.Agent.isiPhone = true; return "iPhone"; } if (this.platform.Samsung.test(string)) { this.Agent.isSamsung = true; return "Samsung"; } if (this.platform.iOS.test(string)) { return "Apple iOS"; } return "unknown"; } }; /** Maximum header length to process (prevents DoS from oversized headers) */ _UserAgent.MAX_HEADER_LENGTH = 2048; /** Maximum number of brands to parse from a brand list */ _UserAgent.MAX_BRAND_COUNT = 20; var UserAgent = _UserAgent; // src/browser.ts var singleton = new UserAgent(); var parse = (source) => new UserAgent().parse(source); var hydrate = (source) => new UserAgent().hydrate(source); var api = Object.assign(singleton, { UserAgent, parse, hydrate }); var isBrowserWindow = (host2) => typeof host2.window === "object" && host2.window === host2 && typeof host2.document === "object"; var host = globalThis; if (isBrowserWindow(host)) { host.UserAgent = UserAgent; host.useragent = api; } var browser_default = api; return __toCommonJS(browser_exports); })(); //# sourceMappingURL=express-useragent.global.js.map