client-parser
Version:
A utility to detect the device type (Android, iOS, Windows Phone, PC, unknown) based on the User Agent string.
317 lines (314 loc) • 12.7 kB
JavaScript
// src/constants/regex.ts
var USER_AGENT_REGEX = Object.freeze({
// --- OS and Device Detection ---
/** Regular expression to detect Windows Phone and extract its OS version. */
WINDOWS_PHONE: /Windows Phone (?:OS )?([\d.]+)/i,
// Handles "Windows Phone" and "Windows Phone OS 7.0"
/** Regular expression to detect iPad and extract its OS version. */
// Modern iPads on iPadOS often report as "MacIntel" and "Mac OS X" in UA string,
// so it's crucial to detect "iPad" first.
IPAD: /iPad.*(?:OS|CPU) ([\d_.]+)/i,
// Covers "iPad; CPU OS 13_5_1" or "iPad; OS 14_0"
/** Regular expression to detect iPhone and extract its OS version. */
IPHONE: /iPhone.*(?:OS|CPU) ([\d_.]+)/i,
// Covers "iPhone; CPU OS 13_5_1" or "iPhone; OS 14_0"
/** Regular expression to detect iPod and extract its OS version (often similar to iPhone). */
IPOD: /iPod.*(?:OS|CPU) ([\d_.]+)/i,
// Added for completeness if you want to differentiate iPods
/** Regular expression to extract iOS version (common pattern for all iOS devices). */
IOS_VERSION: /(?:iPhone|iPad|iPod).*OS ([\d_]+)/i,
// More robust for any iOS device
/** Regular expression to detect Android and extract its version. */
ANDROID: /Android ([\d.]+)/i,
/** Regular expression to detect if the device is specifically a mobile device. */
// Used to differentiate between Android phone and tablet if 'Android' alone isn't enough.
// 'Mobi' is a strong indicator for phones.
MOBILE_INDICATOR: /Mobi/i,
/** Regular expression to detect common tablet indicators beyond just 'Android'.
* This helps distinguish tablets when 'Mobi' is absent. */
TABLET_INDICATOR: /Tablet|iPad|SM-T\d+|KF[A-Z0-9]{2,}|Nexus 7|Nexus 10/i,
// Added more common tablet UAs
/** Regular expression to detect Windows NT (desktop Windows) and extract its version. */
// Version mapping: 5.1 -> XP, 6.0 -> Vista, 6.1 -> 7, 6.2 -> 8, 6.3 -> 8.1, 10.0 -> 10/11
WINDOWS_NT: /Windows NT ([\d.]+)/i,
/** Regular expression to detect macOS and extract its version. */
// macOS versions are typically X_Y or X_Y_Z.
MAC_OS_X: /Macintosh; Intel Mac OS X ([\d_.]+)/i,
/** Regular expression to detect Linux (excluding Android due to order of operations). */
LINUX: /Linux/i,
// --- Browser Detection ---
/** Regular expression to detect Microsoft Edge (Chromium-based) and extract its version. */
// "Edg/" for Chromium Edge, "Edge/" for legacy EdgeHTML. Prioritize "Edg".
EDGE: /Edg[eA]?\/([\d.]+)/i,
// "Edg", "Edge", "EdgA" (Android)
/** Regular expression to detect Opera browser and extract its version. */
// OPR for Opera Touch/Mini, Opera for desktop. OPR is usually more modern.
OPERA: /(?:Opera|OPR)\/([\d.]+)/i,
/** Regular expression to detect Firefox browser and extract its version. */
FIREFOX: /Firefox\/([\d.]+)/i,
/** Regular expression to detect Chrome (desktop) or CriOS (Chrome on iOS) and extract its version. */
// Prioritize this AFTER Edge and Opera detection.
CHROME_CRIOS: /(?:Chrome|CriOS|Chromium)\/([\d.]+)/i,
// Added Chromium for broader detection
/** Regular expression to detect Safari browser. Needs to be checked AFTER Chrome/Opera/Edge/Firefox. */
SAFARI: /Safari\/([\d.]+)/i,
// This captures the WebKit version, typically.
/** Regular expression to extract a Safari browser 'Version/' number. This is the true browser version. */
SAFARI_VERSION: /Version\/([\d.]+).*Safari/i,
// This is key for actual Safari version
/** Regular expression to detect Internet Explorer (MSIE for IE10 and below, Trident for IE11). */
INTERNET_EXPLORER: /MSIE ([\d.]+)|Trident\/.+?rv:([\d.]+)/i,
// Capture either MSIE version or rv: version
// --- Rendering Engine Detection ---
/** Regular expression to detect WebKit engine and extract its version (used by Safari, older Chrome/Opera). */
WEBKIT_ENGINE: /AppleWebKit\/([\d.]+)/i,
/** Regular expression to detect Gecko engine and extract its version (used by Firefox). */
GECKO_ENGINE: /Gecko\/(\d{8}|\d+\.\d+)/i,
// Gecko version can be YYYYMMDD or X.Y
/** Regular expression to detect Trident engine and extract its version (used by Internet Explorer 11). */
TRIDENT_ENGINE: /Trident\/([\d.]+)/i,
/** Regular expression to detect Presto engine and extract its version (older Opera). */
PRESTO_ENGINE: /Presto\/([\d.]+)/i
// Added for older Opera support
});
var regex_default = USER_AGENT_REGEX;
// src/constants/names.ts
var DEVICE_TYPES = Object.freeze({
/** Represents an unknown or uncategorized device type. */
UNKNOWN: "unknown",
/** Represents a Windows Phone device. */
WINDOWS_PHONE: "windows_phone",
/** Represents an iOS device (iPhone, iPad, iPod Touch). */
IOS: "ios",
/** Represents an Android device. */
ANDROID: "android",
/** Represents a personal computer (desktop or laptop). */
PC: "pc"
});
var OS_NAMES = Object.freeze({
/** Represents an unknown or uncategorized operating system. */
UNKNOWN: "unknown",
/** Represents the Windows Phone operating system. */
WINDOWS_PHONE: "Windows Phone",
/** Represents the iOS operating system. */
IOS: "iOS",
/** Represents the Android operating system. */
ANDROID: "Android",
/** Represents the Windows desktop operating system. */
WINDOWS: "Windows",
/** Represents the macOS operating system. */
MACOS: "macOS",
/** Represents the Linux operating system. */
LINUX: "Linux"
});
var BROWSER_NAMES = Object.freeze({
/** Represents an unknown or uncategorized browser. */
UNKNOWN: "unknown",
/** Represents the Microsoft Edge browser. */
EDGE: "Edge",
/** Represents the Opera browser. */
OPERA: "Opera",
/** Represents the Mozilla Firefox browser. */
FIREFOX: "Firefox",
/** Represents the Google Chrome browser. */
CHROME: "Chrome",
/** Represents the Apple Safari browser. */
SAFARI: "Safari",
/** Represents the Internet Explorer browser. */
INTERNET_EXPLORER: "Internet Explorer"
});
// src/index.ts
var _detectOSAndDevice = (userAgent, deviceInfo) => {
if (regex_default.WINDOWS_PHONE.test(userAgent)) {
deviceInfo.device.type = DEVICE_TYPES.WINDOWS_PHONE;
deviceInfo.os.name = OS_NAMES.WINDOWS_PHONE;
const wpMatch = userAgent.match(regex_default.WINDOWS_PHONE);
if (wpMatch && wpMatch[1]) {
deviceInfo.os.version = wpMatch[1];
}
deviceInfo.device.name = "Windows Phone";
return;
}
if (regex_default.IPAD.test(userAgent)) {
deviceInfo.device.type = DEVICE_TYPES.IOS;
deviceInfo.os.name = OS_NAMES.IOS;
const versionMatch = userAgent.match(regex_default.IOS_VERSION);
if (versionMatch && versionMatch[1]) {
deviceInfo.os.version = versionMatch[1].replace(/_/g, ".");
}
deviceInfo.device.name = "iPad";
return;
}
const iPhoneMatch = userAgent.match(regex_default.IPHONE);
if (iPhoneMatch) {
deviceInfo.device.type = DEVICE_TYPES.IOS;
deviceInfo.os.name = OS_NAMES.IOS;
const versionMatch = userAgent.match(regex_default.IOS_VERSION);
if (versionMatch && versionMatch[1]) {
deviceInfo.os.version = versionMatch[1].replace(/_/g, ".");
}
deviceInfo.device.name = "iPhone";
return;
}
const iPodMatch = userAgent.match(regex_default.IPOD);
if (iPodMatch) {
deviceInfo.device.type = DEVICE_TYPES.IOS;
deviceInfo.os.name = OS_NAMES.IOS;
const versionMatch = userAgent.match(regex_default.IOS_VERSION);
if (versionMatch && versionMatch[1]) {
deviceInfo.os.version = versionMatch[1].replace(/_/g, ".");
}
deviceInfo.device.name = "iPod Touch";
return;
}
const androidMatch = userAgent.match(regex_default.ANDROID);
if (androidMatch) {
deviceInfo.os.name = OS_NAMES.ANDROID;
deviceInfo.os.version = androidMatch[1];
if (regex_default.MOBILE_INDICATOR.test(userAgent)) {
deviceInfo.device.type = DEVICE_TYPES.ANDROID;
deviceInfo.device.name = "Android Phone";
} else if (regex_default.TABLET_INDICATOR.test(userAgent)) {
deviceInfo.device.type = DEVICE_TYPES.ANDROID;
deviceInfo.device.name = "Android Tablet";
} else {
deviceInfo.device.type = DEVICE_TYPES.ANDROID;
deviceInfo.device.name = "Android Device";
}
return;
}
const windowsMatch = userAgent.match(regex_default.WINDOWS_NT);
if (windowsMatch) {
deviceInfo.device.type = DEVICE_TYPES.PC;
deviceInfo.os.name = OS_NAMES.WINDOWS;
deviceInfo.os.version = windowsMatch[1];
deviceInfo.device.name = "Windows PC";
return;
}
if (regex_default.MAC_OS_X.test(userAgent)) {
deviceInfo.device.type = DEVICE_TYPES.PC;
deviceInfo.os.name = OS_NAMES.MACOS;
const macMatch = userAgent.match(regex_default.MAC_OS_X);
if (macMatch && macMatch[1]) {
deviceInfo.os.version = macMatch[1].replace(/_/g, ".");
}
deviceInfo.device.name = "macOS PC";
return;
}
if (regex_default.LINUX.test(userAgent)) {
deviceInfo.device.type = DEVICE_TYPES.PC;
deviceInfo.os.name = OS_NAMES.LINUX;
deviceInfo.device.name = "Linux PC";
return;
}
};
var _detectBrowser = (userAgent, deviceInfo) => {
const edgeMatch = userAgent.match(regex_default.EDGE);
if (edgeMatch) {
deviceInfo.browser.name = BROWSER_NAMES.EDGE;
deviceInfo.browser.version = edgeMatch[1];
const webkitMatch = userAgent.match(regex_default.WEBKIT_ENGINE);
if (webkitMatch && webkitMatch[1]) {
deviceInfo.engine.name = "Blink";
deviceInfo.engine.version = webkitMatch[1];
}
return;
}
const operaMatch = userAgent.match(regex_default.OPERA);
if (operaMatch) {
deviceInfo.browser.name = BROWSER_NAMES.OPERA;
deviceInfo.browser.version = operaMatch[1];
const webkitMatch = userAgent.match(regex_default.WEBKIT_ENGINE);
if (webkitMatch && webkitMatch[1]) {
deviceInfo.engine.name = "Blink";
deviceInfo.engine.version = webkitMatch[1];
}
const prestoMatch = userAgent.match(regex_default.PRESTO_ENGINE);
if (prestoMatch && prestoMatch[1]) {
deviceInfo.engine.name = "Presto";
deviceInfo.engine.version = prestoMatch[1];
}
return;
}
const firefoxMatch = userAgent.match(regex_default.FIREFOX);
if (firefoxMatch) {
deviceInfo.browser.name = BROWSER_NAMES.FIREFOX;
deviceInfo.browser.version = firefoxMatch[1];
const geckoMatch = userAgent.match(regex_default.GECKO_ENGINE);
if (geckoMatch && geckoMatch[1]) {
deviceInfo.engine.name = "Gecko";
deviceInfo.engine.version = geckoMatch[1];
}
return;
}
const chromeMatch = userAgent.match(regex_default.CHROME_CRIOS);
if (chromeMatch) {
deviceInfo.browser.name = BROWSER_NAMES.CHROME;
deviceInfo.browser.version = chromeMatch[1];
const webkitMatch = userAgent.match(regex_default.WEBKIT_ENGINE);
if (webkitMatch && webkitMatch[1]) {
deviceInfo.engine.name = "Blink";
deviceInfo.engine.version = webkitMatch[1];
}
return;
}
const safariVersionMatch = userAgent.match(regex_default.SAFARI_VERSION);
if (safariVersionMatch) {
deviceInfo.browser.name = BROWSER_NAMES.SAFARI;
deviceInfo.browser.version = safariVersionMatch[1];
const webkitMatch = userAgent.match(regex_default.WEBKIT_ENGINE);
if (webkitMatch && webkitMatch[1]) {
deviceInfo.engine.name = "WebKit";
deviceInfo.engine.version = webkitMatch[1];
}
return;
}
const ieMatch = userAgent.match(regex_default.INTERNET_EXPLORER);
if (ieMatch) {
deviceInfo.browser.name = BROWSER_NAMES.INTERNET_EXPLORER;
deviceInfo.browser.version = ieMatch[1] || ieMatch[2];
const tridentMatch = userAgent.match(regex_default.TRIDENT_ENGINE);
if (tridentMatch && tridentMatch[1]) {
deviceInfo.engine.name = "Trident";
deviceInfo.engine.version = tridentMatch[1];
}
return;
}
};
var getDeviceType = (userAgent, platform = "") => {
const deviceInfo = {
userAgentString: userAgent,
device: {
type: DEVICE_TYPES.UNKNOWN,
name: "unknown",
model: "unknown",
manufacturer: "unknown"
},
engine: {
name: "unknown",
version: "unknown"
},
os: {
name: OS_NAMES.UNKNOWN,
version: void 0,
// undefined initially, will be populated if detected
architecture: void 0
// undefined initially, will be populated if detected
},
browser: {
name: BROWSER_NAMES.UNKNOWN,
version: "unknown"
},
platform,
// Can be passed as an argument if available (e.g., navigator.platform)
isBot: false
// Requires separate bot detection logic
};
_detectOSAndDevice(userAgent, deviceInfo);
_detectBrowser(userAgent, deviceInfo);
return deviceInfo;
};
var index_default = getDeviceType;
export {
index_default as default
};