aki-info-detect 2.0.2 → 2.0.3

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/README.md CHANGED
@@ -211,6 +211,37 @@ A: Yes! Visit the [live demo](https://akiinfodetect-js.pages.dev/) to see the li
211
211
  - 📦 **Tree-shakeable** — Import only what you need
212
212
  - 🎯 **Browser-only** — Optimized for browser environments
213
213
 
214
+ ## ⚠️ CRITICAL: OS Detection Accuracy
215
+
216
+ **Always prefer Client Hints (`getHighEntropyValues()` / the `hev` argument) over the parsed
217
+ User-Agent string when both are available.** Browser vendors have frozen UA strings for privacy:
218
+
219
+ - **Chromium UA Reduction** pins the Windows version token at `Windows NT 10.0` forever — a
220
+ real Windows 11 machine still reports "10" in its UA string. Only Client Hints
221
+ (`platform: "Windows"`, `platformVersion` major ≥ 13) reveals the truth.
222
+ - The same freeze applies to macOS: Chromium browsers on macOS report `Mac OS X 10_15_7`
223
+ (Catalina) forever regardless of the real OS version.
224
+ - **Client Hints (`navigator.userAgentData`) is Chromium-only.** Safari/WebKit never implements
225
+ it, so on Safari you always fall back to UA parsing — which is exactly where the next issue
226
+ matters.
227
+
228
+ **Known UA quirk — real iPhones parse as "macOS" unless you're on ≥ 2.0.3.** Apple's iOS UA
229
+ string contains the literal substring `"like Mac OS X"` for legacy WebKit compatibility, e.g.:
230
+
231
+ ```
232
+ Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 ...
233
+ ```
234
+
235
+ Versions **before 2.0.3** tested the `Mac OS X` pattern before the `iPhone|iPad|iPod` pattern,
236
+ so this literal substring made every real iPhone parse as `macOS`. Fixed in 2.0.3 by testing
237
+ device tokens first. If you consume `parseUserAgent()` directly instead of `akiInfoDetect()`/
238
+ `detectOS()`, make sure you're on `^2.0.3` or later.
239
+
240
+ `detectOS(hev, parsedOS)` and `detectBrowser(hev, parsedUA)` are exported individually
241
+ specifically so consumers who can't call the network-triggering default `akiInfoDetect()`
242
+ (e.g. a privacy-conscious analytics beacon) can still apply this "Client Hints first" rule
243
+ without duplicating the logic.
244
+
214
245
  ## Installation
215
246
 
216
247
  ### npm / yarn / pnpm
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * aki-info-detect v2.0.2
3
- * (c) 2025 lacvietanh
2
+ * aki-info-detect v2.0.3
3
+ * (c) 2026 lacvietanh
4
4
  * Released under the MIT License
5
5
  * https://akiinfodetect-js.pages.dev/
6
6
  */
@@ -41,6 +41,15 @@ function parseUserAgent(ua = navigator.userAgent) {
41
41
  };
42
42
  }
43
43
  },
44
+ {
45
+ test: /iPhone|iPad|iPod/,
46
+ parse: () => {
47
+ var _a;
48
+ const match = ua.match(/OS ([\d_]+)/);
49
+ const ver = ((_a = match == null ? void 0 : match[1]) == null ? void 0 : _a.replace(/_/g, ".")) || "";
50
+ return { family: `iOS ${ver}`, version: ver, architecture: 64 };
51
+ }
52
+ },
44
53
  {
45
54
  test: /Mac OS X/,
46
55
  parse: () => {
@@ -58,15 +67,6 @@ function parseUserAgent(ua = navigator.userAgent) {
58
67
  return { family: `Android ${ver}`, version: ver, architecture: 64 };
59
68
  }
60
69
  },
61
- {
62
- test: /iPhone|iPad|iPod/,
63
- parse: () => {
64
- var _a;
65
- const match = ua.match(/OS ([\d_]+)/);
66
- const ver = ((_a = match == null ? void 0 : match[1]) == null ? void 0 : _a.replace(/_/g, ".")) || "";
67
- return { family: `iOS ${ver}`, version: ver, architecture: 64 };
68
- }
69
- },
70
70
  {
71
71
  test: /Linux/,
72
72
  parse: () => ({
@@ -442,7 +442,9 @@ async function akiInfoDetect(forceNetworkRefresh = false) {
442
442
  export {
443
443
  akiInfoDetect,
444
444
  akiInfoDetect as default,
445
+ detectBrowser,
445
446
  detectGPU,
447
+ detectOS,
446
448
  getBattery,
447
449
  getConnection,
448
450
  getCountry,
@@ -1 +1 @@
1
- {"version":3,"file":"aki-info-detect.js","sources":["../src/modules/ua-parser.js","../src/modules/hardware.js","../src/modules/network.js","../src/modules/screen.js","../src/modules/battery.js","../src/modules/os.js","../src/modules/browser.js","../src/index.js"],"sourcesContent":["/**\n * @fileoverview User Agent and Browser/OS Parser\n * @description Parses user agent string and Client Hints for browser and OS detection\n */\n\n/**\n * Parse user agent string to extract browser and OS info\n * @param {string} [ua] - User agent string (defaults to navigator.userAgent)\n * @returns {ParsedUA} Parsed platform information\n * \n * @typedef {Object} ParsedUA\n * @property {string} name - Browser name\n * @property {string} version - Browser version\n * @property {string} product - Device product name\n * @property {string} manufacturer - Device manufacturer\n * @property {Object|null} os - Operating system info\n * @property {string} layout - Rendering engine\n */\nexport function parseUserAgent(ua = navigator.userAgent) {\n let os = null;\n let name = '';\n let version = '';\n let product = '';\n let manufacturer = '';\n\n // Browser detection (order matters - more specific first)\n const browserPatterns = [\n { test: /Edg(?:e)?\\//, name: 'Edge', regex: /Edg(?:e)?\\/([\\d.]+)/ },\n { test: /OPR\\/|Opera\\//, name: 'Opera', regex: /(?:OPR|Opera)\\/([\\d.]+)/ },\n { test: /Firefox\\//, name: 'Firefox', regex: /Firefox\\/([\\d.]+)/, exclude: /Seamonkey/ },\n { test: /Chrome\\//, name: 'Chrome', regex: /Chrome\\/([\\d.]+)/, exclude: /Edge|Edg|OPR|Opera/ },\n { test: /Safari\\//, name: 'Safari', regex: /Version\\/([\\d.]+)/, exclude: /Chrome|Edge|Edg|OPR|Opera/ },\n { test: /MSIE|Trident/, name: 'IE', regex: /(?:MSIE |rv:)([\\d.]+)/ }\n ];\n\n for (const { test, name: browserName, regex, exclude } of browserPatterns) {\n if (test.test(ua) && (!exclude || !exclude.test(ua))) {\n name = browserName;\n const match = ua.match(regex);\n version = match?.[1] || '';\n break;\n }\n }\n\n // OS detection\n const osPatterns = [\n {\n test: /Windows/,\n parse: () => {\n const verMap = { '10.0': '10', '6.3': '8.1', '6.2': '8', '6.1': '7', '6.0': 'Vista', '5.1': 'XP' };\n const match = ua.match(/Windows NT ([\\d.]+)/);\n const ntVer = match?.[1] || '';\n const winVer = verMap[ntVer] || ntVer;\n return {\n family: `Windows ${winVer}`,\n version: winVer,\n architecture: /WOW64|Win64|x64/.test(ua) ? 64 : 32\n };\n }\n },\n {\n test: /Mac OS X/,\n parse: () => {\n const match = ua.match(/Mac OS X ([\\d_.]+)/);\n const ver = match?.[1]?.replace(/_/g, '.') || '';\n return { family: `macOS ${ver}`, version: ver, architecture: 64 };\n }\n },\n {\n test: /Android/,\n parse: () => {\n const match = ua.match(/Android ([\\d.]+)/);\n const ver = match?.[1] || '';\n return { family: `Android ${ver}`, version: ver, architecture: 64 };\n }\n },\n {\n test: /iPhone|iPad|iPod/,\n parse: () => {\n const match = ua.match(/OS ([\\d_]+)/);\n const ver = match?.[1]?.replace(/_/g, '.') || '';\n return { family: `iOS ${ver}`, version: ver, architecture: 64 };\n }\n },\n {\n test: /Linux/,\n parse: () => ({\n family: 'Linux',\n version: '',\n architecture: /x86_64|amd64/.test(ua) ? 64 : 32\n })\n }\n ];\n\n for (const { test, parse } of osPatterns) {\n if (test.test(ua)) {\n os = parse();\n break;\n }\n }\n\n // Device detection\n if (/iPhone/.test(ua)) {\n product = 'iPhone';\n manufacturer = 'Apple';\n } else if (/iPad/.test(ua)) {\n product = 'iPad';\n manufacturer = 'Apple';\n } else if (/Android/.test(ua)) {\n const match = ua.match(/Android[^;]+;\\s*([^;)]+)/);\n if (match) {\n product = match[1].trim();\n // Detect manufacturer from product string\n const mfrMatch = product.match(/^(Samsung|LG|Motorola|HTC|Huawei|Xiaomi|OnePlus|Google|Sony|OPPO|Vivo|Realme)/i);\n manufacturer = mfrMatch?.[1] || '';\n }\n }\n\n // Rendering engine\n const layout = /AppleWebKit/.test(ua) ? 'WebKit' :\n /Gecko\\//.test(ua) ? 'Gecko' :\n /Trident/.test(ua) ? 'Trident' : '';\n\n return { name, version, product, manufacturer, os, layout };\n}\n\n/**\n * Get High Entropy Values from Client Hints API\n * @returns {Promise<Object>} High entropy values object\n */\nexport async function getHighEntropyValues() {\n if (!navigator.userAgentData?.getHighEntropyValues) {\n return {};\n }\n\n try {\n return await navigator.userAgentData.getHighEntropyValues([\n 'platform',\n 'platformVersion',\n 'architecture',\n 'model',\n 'mobile',\n 'bitness',\n 'brands',\n 'fullVersionList'\n ]);\n } catch {\n return {};\n }\n}\n","/**\n * @fileoverview Hardware Detection Module\n * @description Detects CPU, GPU, RAM, and other hardware information\n */\n\n/**\n * Detect GPU information using WebGL\n * @returns {string} GPU renderer string\n */\nexport function detectGPU() {\n try {\n const canvas = document.createElement('canvas');\n const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');\n \n if (!gl) return 'No WebGL support';\n\n const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');\n if (!debugInfo) return 'GPU info restricted';\n\n return gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) || 'Unknown GPU';\n } catch {\n return 'GPU detection failed';\n }\n}\n\n/**\n * Parse GPU string to detect Apple Silicon or other notable chips\n * Uses future-proof regex patterns (M1, M2, M3... MX)\n * @param {string} gpu - GPU renderer string\n * @returns {Object} Parsed chip info\n */\nexport function parseChipInfo(gpu) {\n const gpuLower = gpu.toLowerCase();\n \n // Apple Silicon detection - future-proof pattern for M1, M2, M3, M4... MX\n const appleSiliconMatch = gpu.match(/Apple\\s+M(\\d+)(?:\\s+(Pro|Max|Ultra))?/i);\n if (appleSiliconMatch) {\n const chipNum = appleSiliconMatch[1];\n const variant = appleSiliconMatch[2] || '';\n return {\n type: 'Apple Silicon',\n chip: `M${chipNum}${variant ? ' ' + variant : ''}`,\n architecture: 'arm64'\n };\n }\n\n // Generic Apple GPU (older or unidentified)\n if (gpuLower.includes('apple')) {\n return { type: 'Apple Silicon', chip: 'Apple GPU', architecture: 'arm64' };\n }\n\n // NVIDIA detection\n const nvidiaMatch = gpu.match(/NVIDIA\\s+(.+?)(?:\\s*\\/|$)/i) || gpu.match(/(GeForce|Quadro|RTX|GTX)\\s+[\\w\\s]+/i);\n if (nvidiaMatch) {\n return { type: 'NVIDIA', chip: nvidiaMatch[0].trim(), architecture: 'x86_64' };\n }\n\n // AMD detection\n const amdMatch = gpu.match(/(Radeon|AMD)\\s+[\\w\\s]+/i);\n if (amdMatch) {\n return { type: 'AMD', chip: amdMatch[0].trim(), architecture: 'x86_64' };\n }\n\n // Intel detection\n const intelMatch = gpu.match(/Intel.*?(UHD|Iris|HD)\\s*(?:Graphics)?\\s*(\\d*)/i);\n if (intelMatch || gpuLower.includes('intel')) {\n return { type: 'Intel', chip: intelMatch?.[0] || 'Intel Graphics', architecture: 'x86_64' };\n }\n\n return { type: 'Unknown', chip: gpu, architecture: '' };\n}\n\n/**\n * Get hardware information\n * @param {string} gpu - GPU string for chip detection\n * @returns {Object} Hardware specs\n */\nexport function getHardwareInfo(gpu) {\n const chipInfo = parseChipInfo(gpu);\n \n return {\n RAM: navigator.deviceMemory || 0,\n CPUCore: navigator.hardwareConcurrency || 0,\n GPU: gpu,\n CPU: chipInfo.type,\n arch: chipInfo.architecture || (navigator.userAgentData?.platform === 'macOS' ? 'arm64' : 'x86_64')\n };\n}\n","/**\n * @fileoverview Network Information Module\n * @description Handles IP, ISP, country detection with caching\n */\n\n/** Cache for network data */\nconst cache = {\n IP: '',\n ISP: '',\n country: '',\n lastUpdated: 0,\n TTL: 3600000 // 1 hour\n};\n\n/**\n * Fetch with timeout\n * @param {string} url - URL to fetch\n * @param {number} [timeout=2500] - Timeout in ms\n * @returns {Promise<Object|null>} JSON response or null\n */\nasync function fetchJSON(url, timeout = 2500) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const res = await fetch(url, { signal: controller.signal });\n clearTimeout(timeoutId);\n return res.ok ? await res.json() : null;\n } catch {\n clearTimeout(timeoutId);\n return null;\n }\n}\n\n/**\n * Get network information (IP, ISP, country)\n * @param {boolean} [forceRefresh=false] - Bypass cache\n * @returns {Promise<NetworkInfo>}\n * \n * @typedef {Object} NetworkInfo\n * @property {string} IP - Public IP address\n * @property {string} ISP - Internet Service Provider\n * @property {string} country - Country code (ISO 3166-1 alpha-2)\n */\nexport async function getNetworkInfo(forceRefresh = false) {\n // Return cached data if valid\n if (!forceRefresh && cache.IP && Date.now() - cache.lastUpdated < cache.TTL) {\n return { IP: cache.IP, ISP: cache.ISP, country: cache.country };\n }\n\n // Service providers in priority order\n const services = [\n {\n url: 'https://ipinfo.io/json',\n parse: (d) => ({ IP: d.ip, ISP: d.org || '', country: d.country || '' })\n },\n {\n url: 'https://ipwhois.app/json/',\n parse: (d) => ({ IP: d.ip, ISP: d.isp || '', country: d.country_code || '' })\n },\n {\n url: 'https://api.ipify.org?format=json',\n parse: (d) => ({ IP: d.ip, ISP: '', country: '' })\n }\n ];\n\n for (const { url, parse } of services) {\n const data = await fetchJSON(url);\n if (data) {\n const result = parse(data);\n if (result.IP) {\n cache.IP = result.IP;\n cache.ISP = result.ISP || cache.ISP;\n cache.country = result.country || cache.country;\n cache.lastUpdated = Date.now();\n return result;\n }\n }\n }\n\n // Return whatever we have in cache\n return { IP: cache.IP, ISP: cache.ISP, country: cache.country };\n}\n\n/**\n * Get public IP address\n * @param {boolean} [forceRefresh=false] - Bypass cache\n * @returns {Promise<string>}\n */\nexport async function getIP(forceRefresh = false) {\n const info = await getNetworkInfo(forceRefresh);\n return info.IP;\n}\n\n/**\n * Get ISP information\n * @param {boolean} [forceRefresh=false] - Bypass cache\n * @returns {Promise<string>}\n */\nexport async function getISP(forceRefresh = false) {\n const info = await getNetworkInfo(forceRefresh);\n return info.ISP;\n}\n\n/**\n * Get country code\n * @param {boolean} [forceRefresh=false] - Bypass cache\n * @returns {Promise<string>}\n */\nexport async function getCountry(forceRefresh = false) {\n const info = await getNetworkInfo(forceRefresh);\n return info.country;\n}\n\n/**\n * Get connection quality info (Network Information API)\n * @returns {ConnectionInfo|null}\n * \n * @typedef {Object} ConnectionInfo\n * @property {string} type - Connection type (wifi, cellular, etc.)\n * @property {string} effectiveType - Effective connection type (4g, 3g, etc.)\n * @property {number} downlink - Downlink speed in Mbps\n * @property {number} rtt - Round-trip time in ms\n * @property {boolean} saveData - Data saver mode enabled\n */\nexport function getConnection() {\n const conn = navigator.connection || navigator.mozConnection || navigator.webkitConnection;\n \n if (!conn) return null;\n\n return {\n type: conn.type || 'unknown',\n effectiveType: conn.effectiveType || 'unknown',\n downlink: conn.downlink || 0,\n rtt: conn.rtt || 0,\n saveData: conn.saveData || false\n };\n}\n\n/**\n * Get geolocation (requires user permission)\n * @param {Object} [options] - Geolocation options\n * @returns {Promise<GeolocationData|null>}\n * \n * @typedef {Object} GeolocationData\n * @property {number} latitude\n * @property {number} longitude\n * @property {number} accuracy - Accuracy in meters\n * @property {number} timestamp\n */\nexport function getLocation(options = { timeout: 10000, enableHighAccuracy: false }) {\n return new Promise((resolve) => {\n if (!navigator.geolocation) {\n resolve(null);\n return;\n }\n\n navigator.geolocation.getCurrentPosition(\n (pos) => resolve({\n latitude: pos.coords.latitude,\n longitude: pos.coords.longitude,\n accuracy: pos.coords.accuracy,\n timestamp: pos.timestamp\n }),\n () => resolve(null),\n options\n );\n });\n}\n\n/** Expose cache for reactive access */\nexport const networkCache = cache;\n","/**\n * @fileoverview Screen and Display Module\n * @description Detects screen resolution, pixel ratio, orientation\n */\n\n/**\n * Get screen and display information\n * @returns {ScreenInfo}\n * \n * @typedef {Object} ScreenInfo\n * @property {number} width - Screen width in pixels\n * @property {number} height - Screen height in pixels\n * @property {number} availWidth - Available width (excluding taskbars)\n * @property {number} availHeight - Available height\n * @property {number} colorDepth - Color depth in bits\n * @property {number} pixelRatio - Device pixel ratio\n * @property {string} orientation - Screen orientation\n */\nexport function getScreen() {\n const { screen } = window;\n \n return {\n width: screen.width,\n height: screen.height,\n availWidth: screen.availWidth,\n availHeight: screen.availHeight,\n colorDepth: screen.colorDepth,\n pixelRatio: window.devicePixelRatio || 1,\n orientation: screen.orientation?.type || \n (window.innerWidth > window.innerHeight ? 'landscape-primary' : 'portrait-primary')\n };\n}\n","/**\n * @fileoverview Battery Status Module\n * @description Detects battery level and charging status\n */\n\n/**\n * Get battery information\n * @returns {Promise<BatteryInfo>}\n * \n * @typedef {Object} BatteryInfo\n * @property {boolean} isCharging - Whether device is charging\n * @property {number} level - Battery percentage (0-100)\n * @property {number} chargingTime - Seconds until fully charged (Infinity if not charging)\n * @property {number} dischargingTime - Seconds until empty (Infinity if charging)\n */\nexport async function getBattery() {\n // Battery API may not be available in all browsers (e.g., Safari)\n if (!('getBattery' in navigator)) {\n return { isCharging: false, level: 0, chargingTime: Infinity, dischargingTime: Infinity };\n }\n\n try {\n const battery = await navigator.getBattery();\n return {\n isCharging: battery.charging,\n level: Math.round(battery.level * 100),\n chargingTime: battery.chargingTime,\n dischargingTime: battery.dischargingTime\n };\n } catch {\n return { isCharging: false, level: 0, chargingTime: Infinity, dischargingTime: Infinity };\n }\n}\n","/**\n * @fileoverview OS Detection Module\n * @description Detects operating system with Client Hints support\n */\n\n/**\n * Detect operating system from Client Hints or User Agent\n * @param {Object} hev - High Entropy Values from Client Hints\n * @param {Object|null} parsedOS - Parsed OS from user agent\n * @returns {OSInfo}\n * \n * @typedef {Object} OSInfo\n * @property {string} name - Short OS name (win, mac, linux, android, ios)\n * @property {number|string} version - OS version\n * @property {string} string - Human-readable OS string\n */\nexport function detectOS(hev, parsedOS) {\n // Prefer Client Hints if available (more accurate)\n if (hev.platform) {\n const platform = hev.platform;\n const platformVersion = hev.platformVersion || '';\n\n if (platform === 'Windows') {\n // Windows 11 reports platformVersion >= 13.0\n const majorVer = parseInt(platformVersion.split('.')[0], 10);\n const winVer = majorVer >= 13 ? 11 : 10;\n return { name: 'win', version: winVer, string: `Windows ${winVer}` };\n }\n\n if (platform === 'macOS') {\n return { name: 'mac', version: platformVersion, string: `macOS ${platformVersion}` };\n }\n\n if (platform === 'Android') {\n return { name: 'android', version: platformVersion, string: `Android ${platformVersion}` };\n }\n\n if (platform === 'iOS') {\n return { name: 'ios', version: platformVersion, string: `iOS ${platformVersion}` };\n }\n\n if (platform === 'Linux') {\n return { name: 'linux', version: '', string: 'Linux' };\n }\n\n if (platform === 'Chrome OS') {\n return { name: 'chromeos', version: platformVersion, string: `Chrome OS ${platformVersion}` };\n }\n\n // Unknown platform from Client Hints\n return { name: platform.toLowerCase(), version: platformVersion, string: `${platform} ${platformVersion}` };\n }\n\n // Fallback to parsed User Agent\n if (parsedOS) {\n const family = parsedOS.family || '';\n \n if (family.includes('Windows')) {\n const ver = parseFloat(parsedOS.version) || 0;\n return { name: 'win', version: ver, string: family };\n }\n if (family.includes('macOS') || family.includes('Mac OS X')) {\n return { name: 'mac', version: parsedOS.version, string: family };\n }\n if (family.includes('Android')) {\n return { name: 'android', version: parsedOS.version, string: family };\n }\n if (family.includes('iOS')) {\n return { name: 'ios', version: parsedOS.version, string: family };\n }\n if (family.includes('Linux')) {\n return { name: 'linux', version: '', string: 'Linux' };\n }\n\n return { name: family.toLowerCase(), version: parsedOS.version || '', string: family };\n }\n\n return { name: '', version: '', string: 'Unknown OS' };\n}\n","/**\n * @fileoverview Browser Detection Module\n * @description Detects browser name and version with Client Hints support\n */\n\n/**\n * Detect browser from Client Hints or User Agent\n * @param {Object} hev - High Entropy Values from Client Hints\n * @param {Object} parsedUA - Parsed user agent data\n * @returns {string} Browser name and version (e.g., \"Chrome 120\")\n */\nexport function detectBrowser(hev, parsedUA) {\n // Priority: Client Hints > User Agent\n const brands = hev.fullVersionList || hev.brands || [];\n \n if (brands.length) {\n // Known browsers to look for (in priority order)\n const knownBrowsers = ['Chrome', 'Firefox', 'Safari', 'Edge', 'Opera', 'Brave', 'Vivaldi', 'Arc'];\n \n for (const brand of brands) {\n // Skip placeholder brands\n if (/Not.?A.?Brand|Chromium/i.test(brand.brand)) continue;\n \n // Check if it's a known browser\n const match = knownBrowsers.find(b => brand.brand.includes(b));\n if (match) {\n const majorVersion = brand.version.split('.')[0];\n return `${brand.brand} ${majorVersion}`;\n }\n }\n\n // If no known browser found, use first non-placeholder brand\n for (const brand of brands) {\n if (!/Not.?A.?Brand|Chromium/i.test(brand.brand)) {\n return `${brand.brand} ${brand.version.split('.')[0]}`;\n }\n }\n }\n\n // Fallback to parsed User Agent\n if (parsedUA.name) {\n const majorVersion = parsedUA.version?.split('.')[0] || '';\n return `${parsedUA.name} ${majorVersion}`.trim();\n }\n\n return 'Unknown Browser';\n}\n","/**\n * @fileoverview aki-info-detect - Browser information detection library\n * @author lacvietanh\n * @version 2.0.0\n * @license MIT\n * @see https://github.com/lacvietanh/akiInfoDetect.js\n * \n * @description\n * Lightweight library for detecting device, browser, hardware, and network information.\n * Works in browser environments only. Supports ES Modules and UMD.\n * \n * @example\n * import akiInfoDetect from 'aki-info-detect';\n * const info = await akiInfoDetect();\n * console.log(info.browser, info.os.string, info.GPU);\n */\n\nimport { parseUserAgent, getHighEntropyValues } from './modules/ua-parser.js';\nimport { detectGPU, getHardwareInfo } from './modules/hardware.js';\nimport { getNetworkInfo, getIP, getISP, getCountry, getConnection, getLocation, networkCache } from './modules/network.js';\nimport { getScreen } from './modules/screen.js';\nimport { getBattery } from './modules/battery.js';\nimport { detectOS } from './modules/os.js';\nimport { detectBrowser } from './modules/browser.js';\n\n/**\n * @typedef {Object} AkiInfoResult\n * @property {string} browser - Browser name and version\n * @property {string} product - Device product name\n * @property {string} manufacturer - Device manufacturer\n * @property {boolean} isMobile - Is mobile device\n * @property {string} language - Preferred languages (space-separated 2-char codes)\n * @property {string} CPU - CPU/chip type\n * @property {number} CPUCore - Number of logical CPU cores\n * @property {string} arch - CPU architecture (x86_64, arm64)\n * @property {number} RAM - Device memory in GB\n * @property {string} GPU - GPU renderer string\n * @property {Object} battery - Battery status\n * @property {Object} os - Operating system info\n * @property {Object} network - Network info (reactive getters)\n * @property {Function} getIP - Fetch IP address\n * @property {Function} getISP - Fetch ISP info\n * @property {Function} getCountry - Fetch country code\n * @property {Function} getNetworkInfo - Fetch all network info\n * @property {Function} getLocation - Fetch geolocation\n * @property {Function} getConnection - Get connection quality\n * @property {Function} getScreen - Get screen info\n */\n\n/**\n * Main detection function\n * @param {boolean} [forceNetworkRefresh=false] - Force refresh network data\n * @returns {Promise<AkiInfoResult>} Complete system information\n */\nasync function akiInfoDetect(forceNetworkRefresh = false) {\n // Parallel async operations for performance\n const [hev, battery, gpu] = await Promise.all([\n getHighEntropyValues(),\n getBattery(),\n Promise.resolve(detectGPU())\n ]);\n\n // Parse user agent\n const parsedUA = parseUserAgent();\n \n // Get hardware info with GPU-based chip detection\n const hardware = getHardwareInfo(gpu);\n\n // Detect OS (prefer Client Hints)\n const os = detectOS(hev, parsedUA.os);\n\n // Detect browser (prefer Client Hints)\n const browser = detectBrowser(hev, parsedUA);\n\n // Detect mobile status\n const isMobile = hev.mobile ?? /Mobile|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);\n\n // Get language preferences (top 3, 2-char codes)\n const languages = (navigator.languages || [navigator.language])\n .slice(0, 3)\n .map(l => l.substring(0, 2).toLowerCase())\n .join(' ');\n\n // Trigger network fetch in background (non-blocking)\n if (forceNetworkRefresh || !networkCache.IP) {\n getNetworkInfo(forceNetworkRefresh).catch(() => {});\n }\n\n return {\n // Basic info\n browser,\n product: parsedUA.product,\n manufacturer: parsedUA.manufacturer,\n isMobile,\n language: languages,\n\n // Hardware\n CPU: hardware.CPU,\n CPUCore: hardware.CPUCore,\n arch: hev.architecture || hardware.arch,\n RAM: hardware.RAM,\n GPU: hardware.GPU,\n\n // Battery\n battery,\n\n // OS\n os,\n\n // Network (reactive getters from cache)\n network: {\n get IP() { return networkCache.IP; },\n get ISP() { return networkCache.ISP; },\n get country() { return networkCache.country; }\n },\n\n // Async methods\n getIP,\n getISP,\n getCountry,\n getNetworkInfo,\n getLocation,\n getConnection,\n getScreen\n };\n}\n\n// Named exports for tree-shaking\nexport {\n akiInfoDetect,\n getNetworkInfo,\n getIP,\n getISP,\n getCountry,\n getConnection,\n getLocation,\n getScreen,\n getBattery,\n detectGPU,\n parseUserAgent,\n getHighEntropyValues\n};\n\n// Default export\nexport default akiInfoDetect;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"aki-info-detect.js","sources":["../src/modules/ua-parser.js","../src/modules/hardware.js","../src/modules/network.js","../src/modules/screen.js","../src/modules/battery.js","../src/modules/os.js","../src/modules/browser.js","../src/index.js"],"sourcesContent":["/**\n * @fileoverview User Agent and Browser/OS Parser\n * @description Parses user agent string and Client Hints for browser and OS detection\n */\n\n/**\n * Parse user agent string to extract browser and OS info\n * @param {string} [ua] - User agent string (defaults to navigator.userAgent)\n * @returns {ParsedUA} Parsed platform information\n * \n * @typedef {Object} ParsedUA\n * @property {string} name - Browser name\n * @property {string} version - Browser version\n * @property {string} product - Device product name\n * @property {string} manufacturer - Device manufacturer\n * @property {Object|null} os - Operating system info\n * @property {string} layout - Rendering engine\n */\nexport function parseUserAgent(ua = navigator.userAgent) {\n let os = null;\n let name = '';\n let version = '';\n let product = '';\n let manufacturer = '';\n\n // Browser detection (order matters - more specific first)\n const browserPatterns = [\n { test: /Edg(?:e)?\\//, name: 'Edge', regex: /Edg(?:e)?\\/([\\d.]+)/ },\n { test: /OPR\\/|Opera\\//, name: 'Opera', regex: /(?:OPR|Opera)\\/([\\d.]+)/ },\n { test: /Firefox\\//, name: 'Firefox', regex: /Firefox\\/([\\d.]+)/, exclude: /Seamonkey/ },\n { test: /Chrome\\//, name: 'Chrome', regex: /Chrome\\/([\\d.]+)/, exclude: /Edge|Edg|OPR|Opera/ },\n { test: /Safari\\//, name: 'Safari', regex: /Version\\/([\\d.]+)/, exclude: /Chrome|Edge|Edg|OPR|Opera/ },\n { test: /MSIE|Trident/, name: 'IE', regex: /(?:MSIE |rv:)([\\d.]+)/ }\n ];\n\n for (const { test, name: browserName, regex, exclude } of browserPatterns) {\n if (test.test(ua) && (!exclude || !exclude.test(ua))) {\n name = browserName;\n const match = ua.match(regex);\n version = match?.[1] || '';\n break;\n }\n }\n\n // OS detection\n // [CRITICAL] iPhone/iPad/iPod MUST be tested before \"Mac OS X\". Real iOS user agents contain\n // the literal substring \"like Mac OS X\" (Apple's legacy WebKit compatibility convention, e.g.\n // \"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) ...\") — so a naive /Mac OS X/ test\n // matches genuine iPhones too, not just desktop Safari/Chrome. Getting this order wrong\n // silently mislabels every iPhone as \"macOS\" (verified on production data: 125/528 clients on\n // kinhdich.akinet.me — 2026-07-10). Do not reorder without re-reading this comment.\n const osPatterns = [\n {\n test: /Windows/,\n parse: () => {\n const verMap = { '10.0': '10', '6.3': '8.1', '6.2': '8', '6.1': '7', '6.0': 'Vista', '5.1': 'XP' };\n const match = ua.match(/Windows NT ([\\d.]+)/);\n const ntVer = match?.[1] || '';\n const winVer = verMap[ntVer] || ntVer;\n return {\n family: `Windows ${winVer}`,\n version: winVer,\n architecture: /WOW64|Win64|x64/.test(ua) ? 64 : 32\n };\n }\n },\n {\n test: /iPhone|iPad|iPod/,\n parse: () => {\n const match = ua.match(/OS ([\\d_]+)/);\n const ver = match?.[1]?.replace(/_/g, '.') || '';\n return { family: `iOS ${ver}`, version: ver, architecture: 64 };\n }\n },\n {\n test: /Mac OS X/,\n parse: () => {\n const match = ua.match(/Mac OS X ([\\d_.]+)/);\n const ver = match?.[1]?.replace(/_/g, '.') || '';\n return { family: `macOS ${ver}`, version: ver, architecture: 64 };\n }\n },\n {\n test: /Android/,\n parse: () => {\n const match = ua.match(/Android ([\\d.]+)/);\n const ver = match?.[1] || '';\n return { family: `Android ${ver}`, version: ver, architecture: 64 };\n }\n },\n {\n test: /Linux/,\n parse: () => ({\n family: 'Linux',\n version: '',\n architecture: /x86_64|amd64/.test(ua) ? 64 : 32\n })\n }\n ];\n\n for (const { test, parse } of osPatterns) {\n if (test.test(ua)) {\n os = parse();\n break;\n }\n }\n\n // Device detection\n if (/iPhone/.test(ua)) {\n product = 'iPhone';\n manufacturer = 'Apple';\n } else if (/iPad/.test(ua)) {\n product = 'iPad';\n manufacturer = 'Apple';\n } else if (/Android/.test(ua)) {\n const match = ua.match(/Android[^;]+;\\s*([^;)]+)/);\n if (match) {\n product = match[1].trim();\n // Detect manufacturer from product string\n const mfrMatch = product.match(/^(Samsung|LG|Motorola|HTC|Huawei|Xiaomi|OnePlus|Google|Sony|OPPO|Vivo|Realme)/i);\n manufacturer = mfrMatch?.[1] || '';\n }\n }\n\n // Rendering engine\n const layout = /AppleWebKit/.test(ua) ? 'WebKit' :\n /Gecko\\//.test(ua) ? 'Gecko' :\n /Trident/.test(ua) ? 'Trident' : '';\n\n return { name, version, product, manufacturer, os, layout };\n}\n\n/**\n * Get High Entropy Values from Client Hints API\n * @returns {Promise<Object>} High entropy values object\n */\nexport async function getHighEntropyValues() {\n if (!navigator.userAgentData?.getHighEntropyValues) {\n return {};\n }\n\n try {\n return await navigator.userAgentData.getHighEntropyValues([\n 'platform',\n 'platformVersion',\n 'architecture',\n 'model',\n 'mobile',\n 'bitness',\n 'brands',\n 'fullVersionList'\n ]);\n } catch {\n return {};\n }\n}\n","/**\n * @fileoverview Hardware Detection Module\n * @description Detects CPU, GPU, RAM, and other hardware information\n */\n\n/**\n * Detect GPU information using WebGL\n * @returns {string} GPU renderer string\n */\nexport function detectGPU() {\n try {\n const canvas = document.createElement('canvas');\n const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');\n \n if (!gl) return 'No WebGL support';\n\n const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');\n if (!debugInfo) return 'GPU info restricted';\n\n return gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) || 'Unknown GPU';\n } catch {\n return 'GPU detection failed';\n }\n}\n\n/**\n * Parse GPU string to detect Apple Silicon or other notable chips\n * Uses future-proof regex patterns (M1, M2, M3... MX)\n * @param {string} gpu - GPU renderer string\n * @returns {Object} Parsed chip info\n */\nexport function parseChipInfo(gpu) {\n const gpuLower = gpu.toLowerCase();\n \n // Apple Silicon detection - future-proof pattern for M1, M2, M3, M4... MX\n const appleSiliconMatch = gpu.match(/Apple\\s+M(\\d+)(?:\\s+(Pro|Max|Ultra))?/i);\n if (appleSiliconMatch) {\n const chipNum = appleSiliconMatch[1];\n const variant = appleSiliconMatch[2] || '';\n return {\n type: 'Apple Silicon',\n chip: `M${chipNum}${variant ? ' ' + variant : ''}`,\n architecture: 'arm64'\n };\n }\n\n // Generic Apple GPU (older or unidentified)\n if (gpuLower.includes('apple')) {\n return { type: 'Apple Silicon', chip: 'Apple GPU', architecture: 'arm64' };\n }\n\n // NVIDIA detection\n const nvidiaMatch = gpu.match(/NVIDIA\\s+(.+?)(?:\\s*\\/|$)/i) || gpu.match(/(GeForce|Quadro|RTX|GTX)\\s+[\\w\\s]+/i);\n if (nvidiaMatch) {\n return { type: 'NVIDIA', chip: nvidiaMatch[0].trim(), architecture: 'x86_64' };\n }\n\n // AMD detection\n const amdMatch = gpu.match(/(Radeon|AMD)\\s+[\\w\\s]+/i);\n if (amdMatch) {\n return { type: 'AMD', chip: amdMatch[0].trim(), architecture: 'x86_64' };\n }\n\n // Intel detection\n const intelMatch = gpu.match(/Intel.*?(UHD|Iris|HD)\\s*(?:Graphics)?\\s*(\\d*)/i);\n if (intelMatch || gpuLower.includes('intel')) {\n return { type: 'Intel', chip: intelMatch?.[0] || 'Intel Graphics', architecture: 'x86_64' };\n }\n\n return { type: 'Unknown', chip: gpu, architecture: '' };\n}\n\n/**\n * Get hardware information\n * @param {string} gpu - GPU string for chip detection\n * @returns {Object} Hardware specs\n */\nexport function getHardwareInfo(gpu) {\n const chipInfo = parseChipInfo(gpu);\n \n return {\n RAM: navigator.deviceMemory || 0,\n CPUCore: navigator.hardwareConcurrency || 0,\n GPU: gpu,\n CPU: chipInfo.type,\n arch: chipInfo.architecture || (navigator.userAgentData?.platform === 'macOS' ? 'arm64' : 'x86_64')\n };\n}\n","/**\n * @fileoverview Network Information Module\n * @description Handles IP, ISP, country detection with caching\n */\n\n/** Cache for network data */\nconst cache = {\n IP: '',\n ISP: '',\n country: '',\n lastUpdated: 0,\n TTL: 3600000 // 1 hour\n};\n\n/**\n * Fetch with timeout\n * @param {string} url - URL to fetch\n * @param {number} [timeout=2500] - Timeout in ms\n * @returns {Promise<Object|null>} JSON response or null\n */\nasync function fetchJSON(url, timeout = 2500) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const res = await fetch(url, { signal: controller.signal });\n clearTimeout(timeoutId);\n return res.ok ? await res.json() : null;\n } catch {\n clearTimeout(timeoutId);\n return null;\n }\n}\n\n/**\n * Get network information (IP, ISP, country)\n * @param {boolean} [forceRefresh=false] - Bypass cache\n * @returns {Promise<NetworkInfo>}\n * \n * @typedef {Object} NetworkInfo\n * @property {string} IP - Public IP address\n * @property {string} ISP - Internet Service Provider\n * @property {string} country - Country code (ISO 3166-1 alpha-2)\n */\nexport async function getNetworkInfo(forceRefresh = false) {\n // Return cached data if valid\n if (!forceRefresh && cache.IP && Date.now() - cache.lastUpdated < cache.TTL) {\n return { IP: cache.IP, ISP: cache.ISP, country: cache.country };\n }\n\n // Service providers in priority order\n const services = [\n {\n url: 'https://ipinfo.io/json',\n parse: (d) => ({ IP: d.ip, ISP: d.org || '', country: d.country || '' })\n },\n {\n url: 'https://ipwhois.app/json/',\n parse: (d) => ({ IP: d.ip, ISP: d.isp || '', country: d.country_code || '' })\n },\n {\n url: 'https://api.ipify.org?format=json',\n parse: (d) => ({ IP: d.ip, ISP: '', country: '' })\n }\n ];\n\n for (const { url, parse } of services) {\n const data = await fetchJSON(url);\n if (data) {\n const result = parse(data);\n if (result.IP) {\n cache.IP = result.IP;\n cache.ISP = result.ISP || cache.ISP;\n cache.country = result.country || cache.country;\n cache.lastUpdated = Date.now();\n return result;\n }\n }\n }\n\n // Return whatever we have in cache\n return { IP: cache.IP, ISP: cache.ISP, country: cache.country };\n}\n\n/**\n * Get public IP address\n * @param {boolean} [forceRefresh=false] - Bypass cache\n * @returns {Promise<string>}\n */\nexport async function getIP(forceRefresh = false) {\n const info = await getNetworkInfo(forceRefresh);\n return info.IP;\n}\n\n/**\n * Get ISP information\n * @param {boolean} [forceRefresh=false] - Bypass cache\n * @returns {Promise<string>}\n */\nexport async function getISP(forceRefresh = false) {\n const info = await getNetworkInfo(forceRefresh);\n return info.ISP;\n}\n\n/**\n * Get country code\n * @param {boolean} [forceRefresh=false] - Bypass cache\n * @returns {Promise<string>}\n */\nexport async function getCountry(forceRefresh = false) {\n const info = await getNetworkInfo(forceRefresh);\n return info.country;\n}\n\n/**\n * Get connection quality info (Network Information API)\n * @returns {ConnectionInfo|null}\n * \n * @typedef {Object} ConnectionInfo\n * @property {string} type - Connection type (wifi, cellular, etc.)\n * @property {string} effectiveType - Effective connection type (4g, 3g, etc.)\n * @property {number} downlink - Downlink speed in Mbps\n * @property {number} rtt - Round-trip time in ms\n * @property {boolean} saveData - Data saver mode enabled\n */\nexport function getConnection() {\n const conn = navigator.connection || navigator.mozConnection || navigator.webkitConnection;\n \n if (!conn) return null;\n\n return {\n type: conn.type || 'unknown',\n effectiveType: conn.effectiveType || 'unknown',\n downlink: conn.downlink || 0,\n rtt: conn.rtt || 0,\n saveData: conn.saveData || false\n };\n}\n\n/**\n * Get geolocation (requires user permission)\n * @param {Object} [options] - Geolocation options\n * @returns {Promise<GeolocationData|null>}\n * \n * @typedef {Object} GeolocationData\n * @property {number} latitude\n * @property {number} longitude\n * @property {number} accuracy - Accuracy in meters\n * @property {number} timestamp\n */\nexport function getLocation(options = { timeout: 10000, enableHighAccuracy: false }) {\n return new Promise((resolve) => {\n if (!navigator.geolocation) {\n resolve(null);\n return;\n }\n\n navigator.geolocation.getCurrentPosition(\n (pos) => resolve({\n latitude: pos.coords.latitude,\n longitude: pos.coords.longitude,\n accuracy: pos.coords.accuracy,\n timestamp: pos.timestamp\n }),\n () => resolve(null),\n options\n );\n });\n}\n\n/** Expose cache for reactive access */\nexport const networkCache = cache;\n","/**\n * @fileoverview Screen and Display Module\n * @description Detects screen resolution, pixel ratio, orientation\n */\n\n/**\n * Get screen and display information\n * @returns {ScreenInfo}\n * \n * @typedef {Object} ScreenInfo\n * @property {number} width - Screen width in pixels\n * @property {number} height - Screen height in pixels\n * @property {number} availWidth - Available width (excluding taskbars)\n * @property {number} availHeight - Available height\n * @property {number} colorDepth - Color depth in bits\n * @property {number} pixelRatio - Device pixel ratio\n * @property {string} orientation - Screen orientation\n */\nexport function getScreen() {\n const { screen } = window;\n \n return {\n width: screen.width,\n height: screen.height,\n availWidth: screen.availWidth,\n availHeight: screen.availHeight,\n colorDepth: screen.colorDepth,\n pixelRatio: window.devicePixelRatio || 1,\n orientation: screen.orientation?.type || \n (window.innerWidth > window.innerHeight ? 'landscape-primary' : 'portrait-primary')\n };\n}\n","/**\n * @fileoverview Battery Status Module\n * @description Detects battery level and charging status\n */\n\n/**\n * Get battery information\n * @returns {Promise<BatteryInfo>}\n * \n * @typedef {Object} BatteryInfo\n * @property {boolean} isCharging - Whether device is charging\n * @property {number} level - Battery percentage (0-100)\n * @property {number} chargingTime - Seconds until fully charged (Infinity if not charging)\n * @property {number} dischargingTime - Seconds until empty (Infinity if charging)\n */\nexport async function getBattery() {\n // Battery API may not be available in all browsers (e.g., Safari)\n if (!('getBattery' in navigator)) {\n return { isCharging: false, level: 0, chargingTime: Infinity, dischargingTime: Infinity };\n }\n\n try {\n const battery = await navigator.getBattery();\n return {\n isCharging: battery.charging,\n level: Math.round(battery.level * 100),\n chargingTime: battery.chargingTime,\n dischargingTime: battery.dischargingTime\n };\n } catch {\n return { isCharging: false, level: 0, chargingTime: Infinity, dischargingTime: Infinity };\n }\n}\n","/**\n * @fileoverview OS Detection Module\n * @description Detects operating system with Client Hints support\n */\n\n/**\n * Detect operating system from Client Hints or User Agent\n * @param {Object} hev - High Entropy Values from Client Hints\n * @param {Object|null} parsedOS - Parsed OS from user agent\n * @returns {OSInfo}\n * \n * @typedef {Object} OSInfo\n * @property {string} name - Short OS name (win, mac, linux, android, ios)\n * @property {number|string} version - OS version\n * @property {string} string - Human-readable OS string\n */\nexport function detectOS(hev, parsedOS) {\n // Prefer Client Hints if available (more accurate)\n if (hev.platform) {\n const platform = hev.platform;\n const platformVersion = hev.platformVersion || '';\n\n if (platform === 'Windows') {\n // Windows 11 reports platformVersion >= 13.0\n const majorVer = parseInt(platformVersion.split('.')[0], 10);\n const winVer = majorVer >= 13 ? 11 : 10;\n return { name: 'win', version: winVer, string: `Windows ${winVer}` };\n }\n\n if (platform === 'macOS') {\n return { name: 'mac', version: platformVersion, string: `macOS ${platformVersion}` };\n }\n\n if (platform === 'Android') {\n return { name: 'android', version: platformVersion, string: `Android ${platformVersion}` };\n }\n\n if (platform === 'iOS') {\n return { name: 'ios', version: platformVersion, string: `iOS ${platformVersion}` };\n }\n\n if (platform === 'Linux') {\n return { name: 'linux', version: '', string: 'Linux' };\n }\n\n if (platform === 'Chrome OS') {\n return { name: 'chromeos', version: platformVersion, string: `Chrome OS ${platformVersion}` };\n }\n\n // Unknown platform from Client Hints\n return { name: platform.toLowerCase(), version: platformVersion, string: `${platform} ${platformVersion}` };\n }\n\n // Fallback to parsed User Agent\n if (parsedOS) {\n const family = parsedOS.family || '';\n \n if (family.includes('Windows')) {\n const ver = parseFloat(parsedOS.version) || 0;\n return { name: 'win', version: ver, string: family };\n }\n if (family.includes('macOS') || family.includes('Mac OS X')) {\n return { name: 'mac', version: parsedOS.version, string: family };\n }\n if (family.includes('Android')) {\n return { name: 'android', version: parsedOS.version, string: family };\n }\n if (family.includes('iOS')) {\n return { name: 'ios', version: parsedOS.version, string: family };\n }\n if (family.includes('Linux')) {\n return { name: 'linux', version: '', string: 'Linux' };\n }\n\n return { name: family.toLowerCase(), version: parsedOS.version || '', string: family };\n }\n\n return { name: '', version: '', string: 'Unknown OS' };\n}\n","/**\n * @fileoverview Browser Detection Module\n * @description Detects browser name and version with Client Hints support\n */\n\n/**\n * Detect browser from Client Hints or User Agent\n * @param {Object} hev - High Entropy Values from Client Hints\n * @param {Object} parsedUA - Parsed user agent data\n * @returns {string} Browser name and version (e.g., \"Chrome 120\")\n */\nexport function detectBrowser(hev, parsedUA) {\n // Priority: Client Hints > User Agent\n const brands = hev.fullVersionList || hev.brands || [];\n \n if (brands.length) {\n // Known browsers to look for (in priority order)\n const knownBrowsers = ['Chrome', 'Firefox', 'Safari', 'Edge', 'Opera', 'Brave', 'Vivaldi', 'Arc'];\n \n for (const brand of brands) {\n // Skip placeholder brands\n if (/Not.?A.?Brand|Chromium/i.test(brand.brand)) continue;\n \n // Check if it's a known browser\n const match = knownBrowsers.find(b => brand.brand.includes(b));\n if (match) {\n const majorVersion = brand.version.split('.')[0];\n return `${brand.brand} ${majorVersion}`;\n }\n }\n\n // If no known browser found, use first non-placeholder brand\n for (const brand of brands) {\n if (!/Not.?A.?Brand|Chromium/i.test(brand.brand)) {\n return `${brand.brand} ${brand.version.split('.')[0]}`;\n }\n }\n }\n\n // Fallback to parsed User Agent\n if (parsedUA.name) {\n const majorVersion = parsedUA.version?.split('.')[0] || '';\n return `${parsedUA.name} ${majorVersion}`.trim();\n }\n\n return 'Unknown Browser';\n}\n","/**\n * @fileoverview aki-info-detect - Browser information detection library\n * @author lacvietanh\n * @version 2.0.0\n * @license MIT\n * @see https://github.com/lacvietanh/akiInfoDetect.js\n * \n * @description\n * Lightweight library for detecting device, browser, hardware, and network information.\n * Works in browser environments only. Supports ES Modules and UMD.\n * \n * @example\n * import akiInfoDetect from 'aki-info-detect';\n * const info = await akiInfoDetect();\n * console.log(info.browser, info.os.string, info.GPU);\n */\n\nimport { parseUserAgent, getHighEntropyValues } from './modules/ua-parser.js';\nimport { detectGPU, getHardwareInfo } from './modules/hardware.js';\nimport { getNetworkInfo, getIP, getISP, getCountry, getConnection, getLocation, networkCache } from './modules/network.js';\nimport { getScreen } from './modules/screen.js';\nimport { getBattery } from './modules/battery.js';\nimport { detectOS } from './modules/os.js';\nimport { detectBrowser } from './modules/browser.js';\n\n/**\n * @typedef {Object} AkiInfoResult\n * @property {string} browser - Browser name and version\n * @property {string} product - Device product name\n * @property {string} manufacturer - Device manufacturer\n * @property {boolean} isMobile - Is mobile device\n * @property {string} language - Preferred languages (space-separated 2-char codes)\n * @property {string} CPU - CPU/chip type\n * @property {number} CPUCore - Number of logical CPU cores\n * @property {string} arch - CPU architecture (x86_64, arm64)\n * @property {number} RAM - Device memory in GB\n * @property {string} GPU - GPU renderer string\n * @property {Object} battery - Battery status\n * @property {Object} os - Operating system info\n * @property {Object} network - Network info (reactive getters)\n * @property {Function} getIP - Fetch IP address\n * @property {Function} getISP - Fetch ISP info\n * @property {Function} getCountry - Fetch country code\n * @property {Function} getNetworkInfo - Fetch all network info\n * @property {Function} getLocation - Fetch geolocation\n * @property {Function} getConnection - Get connection quality\n * @property {Function} getScreen - Get screen info\n */\n\n/**\n * Main detection function\n * @param {boolean} [forceNetworkRefresh=false] - Force refresh network data\n * @returns {Promise<AkiInfoResult>} Complete system information\n */\nasync function akiInfoDetect(forceNetworkRefresh = false) {\n // Parallel async operations for performance\n const [hev, battery, gpu] = await Promise.all([\n getHighEntropyValues(),\n getBattery(),\n Promise.resolve(detectGPU())\n ]);\n\n // Parse user agent\n const parsedUA = parseUserAgent();\n \n // Get hardware info with GPU-based chip detection\n const hardware = getHardwareInfo(gpu);\n\n // Detect OS (prefer Client Hints)\n const os = detectOS(hev, parsedUA.os);\n\n // Detect browser (prefer Client Hints)\n const browser = detectBrowser(hev, parsedUA);\n\n // Detect mobile status\n const isMobile = hev.mobile ?? /Mobile|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);\n\n // Get language preferences (top 3, 2-char codes)\n const languages = (navigator.languages || [navigator.language])\n .slice(0, 3)\n .map(l => l.substring(0, 2).toLowerCase())\n .join(' ');\n\n // Trigger network fetch in background (non-blocking)\n if (forceNetworkRefresh || !networkCache.IP) {\n getNetworkInfo(forceNetworkRefresh).catch(() => {});\n }\n\n return {\n // Basic info\n browser,\n product: parsedUA.product,\n manufacturer: parsedUA.manufacturer,\n isMobile,\n language: languages,\n\n // Hardware\n CPU: hardware.CPU,\n CPUCore: hardware.CPUCore,\n arch: hev.architecture || hardware.arch,\n RAM: hardware.RAM,\n GPU: hardware.GPU,\n\n // Battery\n battery,\n\n // OS\n os,\n\n // Network (reactive getters from cache)\n network: {\n get IP() { return networkCache.IP; },\n get ISP() { return networkCache.ISP; },\n get country() { return networkCache.country; }\n },\n\n // Async methods\n getIP,\n getISP,\n getCountry,\n getNetworkInfo,\n getLocation,\n getConnection,\n getScreen\n };\n}\n\n// Named exports for tree-shaking\nexport {\n akiInfoDetect,\n getNetworkInfo,\n getIP,\n getISP,\n getCountry,\n getConnection,\n getLocation,\n getScreen,\n getBattery,\n detectGPU,\n parseUserAgent,\n getHighEntropyValues,\n // [CRITICAL] Pure, network-free — consumers that cannot call the default akiInfoDetect()\n // (e.g. because it auto-triggers getNetworkInfo()) MUST still be able to apply the\n // \"prefer Client Hints over UA string\" rule themselves. Exporting these lets them do so\n // without duplicating the OS/browser detection logic inline.\n detectOS,\n detectBrowser\n};\n\n// Default export\nexport default akiInfoDetect;\n"],"names":[],"mappings":"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"}
@@ -1,10 +1,10 @@
1
1
  !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).akiInfoDetect={})}(this,function(e){"use strict";
2
2
  /*!
3
- * aki-info-detect v2.0.2
4
- * (c) 2025 lacvietanh
3
+ * aki-info-detect v2.0.3
4
+ * (c) 2026 lacvietanh
5
5
  * Released under the MIT License
6
6
  * https://akiinfodetect-js.pages.dev/
7
- */function t(e=navigator.userAgent){let t=null,n="",r="",i="",o="";const a=[{test:/Edg(?:e)?\//,name:"Edge",regex:/Edg(?:e)?\/([\d.]+)/},{test:/OPR\/|Opera\//,name:"Opera",regex:/(?:OPR|Opera)\/([\d.]+)/},{test:/Firefox\//,name:"Firefox",regex:/Firefox\/([\d.]+)/,exclude:/Seamonkey/},{test:/Chrome\//,name:"Chrome",regex:/Chrome\/([\d.]+)/,exclude:/Edge|Edg|OPR|Opera/},{test:/Safari\//,name:"Safari",regex:/Version\/([\d.]+)/,exclude:/Chrome|Edge|Edg|OPR|Opera/},{test:/MSIE|Trident/,name:"IE",regex:/(?:MSIE |rv:)([\d.]+)/}];for(const{test:c,name:u,regex:l,exclude:d}of a)if(c.test(e)&&(!d||!d.test(e))){n=u;const t=e.match(l);r=(null==t?void 0:t[1])||"";break}const s=[{test:/Windows/,parse:()=>{const t=e.match(/Windows NT ([\d.]+)/),n=(null==t?void 0:t[1])||"",r={"10.0":"10",6.3:"8.1",6.2:"8",6.1:"7","6.0":"Vista",5.1:"XP"}[n]||n;return{family:`Windows ${r}`,version:r,architecture:/WOW64|Win64|x64/.test(e)?64:32}}},{test:/Mac OS X/,parse:()=>{var t;const n=e.match(/Mac OS X ([\d_.]+)/),r=(null==(t=null==n?void 0:n[1])?void 0:t.replace(/_/g,"."))||"";return{family:`macOS ${r}`,version:r,architecture:64}}},{test:/Android/,parse:()=>{const t=e.match(/Android ([\d.]+)/),n=(null==t?void 0:t[1])||"";return{family:`Android ${n}`,version:n,architecture:64}}},{test:/iPhone|iPad|iPod/,parse:()=>{var t;const n=e.match(/OS ([\d_]+)/),r=(null==(t=null==n?void 0:n[1])?void 0:t.replace(/_/g,"."))||"";return{family:`iOS ${r}`,version:r,architecture:64}}},{test:/Linux/,parse:()=>({family:"Linux",version:"",architecture:/x86_64|amd64/.test(e)?64:32})}];for(const{test:c,parse:u}of s)if(c.test(e)){t=u();break}if(/iPhone/.test(e))i="iPhone",o="Apple";else if(/iPad/.test(e))i="iPad",o="Apple";else if(/Android/.test(e)){const t=e.match(/Android[^;]+;\s*([^;)]+)/);if(t){i=t[1].trim();const e=i.match(/^(Samsung|LG|Motorola|HTC|Huawei|Xiaomi|OnePlus|Google|Sony|OPPO|Vivo|Realme)/i);o=(null==e?void 0:e[1])||""}}return{name:n,version:r,product:i,manufacturer:o,os:t,layout:/AppleWebKit/.test(e)?"WebKit":/Gecko\//.test(e)?"Gecko":/Trident/.test(e)?"Trident":""}}async function n(){var e;if(!(null==(e=navigator.userAgentData)?void 0:e.getHighEntropyValues))return{};try{return await navigator.userAgentData.getHighEntropyValues(["platform","platformVersion","architecture","model","mobile","bitness","brands","fullVersionList"])}catch{return{}}}function r(){try{const e=document.createElement("canvas"),t=e.getContext("webgl")||e.getContext("experimental-webgl");if(!t)return"No WebGL support";const n=t.getExtension("WEBGL_debug_renderer_info");return n?t.getParameter(n.UNMASKED_RENDERER_WEBGL)||"Unknown GPU":"GPU info restricted"}catch{return"GPU detection failed"}}const i={IP:"",ISP:"",country:"",lastUpdated:0,TTL:36e5};async function o(e,t=2500){const n=new AbortController,r=setTimeout(()=>n.abort(),t);try{const t=await fetch(e,{signal:n.signal});return clearTimeout(r),t.ok?await t.json():null}catch{return clearTimeout(r),null}}async function a(e=!1){if(!e&&i.IP&&Date.now()-i.lastUpdated<i.TTL)return{IP:i.IP,ISP:i.ISP,country:i.country};const t=[{url:"https://ipinfo.io/json",parse:e=>({IP:e.ip,ISP:e.org||"",country:e.country||""})},{url:"https://ipwhois.app/json/",parse:e=>({IP:e.ip,ISP:e.isp||"",country:e.country_code||""})},{url:"https://api.ipify.org?format=json",parse:e=>({IP:e.ip,ISP:"",country:""})}];for(const{url:n,parse:r}of t){const e=await o(n);if(e){const t=r(e);if(t.IP)return i.IP=t.IP,i.ISP=t.ISP||i.ISP,i.country=t.country||i.country,i.lastUpdated=Date.now(),t}}return{IP:i.IP,ISP:i.ISP,country:i.country}}async function s(e=!1){return(await a(e)).IP}async function c(e=!1){return(await a(e)).ISP}async function u(e=!1){return(await a(e)).country}function l(){const e=navigator.connection||navigator.mozConnection||navigator.webkitConnection;return e?{type:e.type||"unknown",effectiveType:e.effectiveType||"unknown",downlink:e.downlink||0,rtt:e.rtt||0,saveData:e.saveData||!1}:null}function d(e={timeout:1e4,enableHighAccuracy:!1}){return new Promise(t=>{navigator.geolocation?navigator.geolocation.getCurrentPosition(e=>t({latitude:e.coords.latitude,longitude:e.coords.longitude,accuracy:e.coords.accuracy,timestamp:e.timestamp}),()=>t(null),e):t(null)})}const g=i;function m(){var e;const{screen:t}=window;return{width:t.width,height:t.height,availWidth:t.availWidth,availHeight:t.availHeight,colorDepth:t.colorDepth,pixelRatio:window.devicePixelRatio||1,orientation:(null==(e=t.orientation)?void 0:e.type)||(window.innerWidth>window.innerHeight?"landscape-primary":"portrait-primary")}}async function p(){if(!("getBattery"in navigator))return{isCharging:!1,level:0,chargingTime:1/0,dischargingTime:1/0};try{const e=await navigator.getBattery();return{isCharging:e.charging,level:Math.round(100*e.level),chargingTime:e.chargingTime,dischargingTime:e.dischargingTime}}catch{return{isCharging:!1,level:0,chargingTime:1/0,dischargingTime:1/0}}}
7
+ */function t(e=navigator.userAgent){let t=null,n="",r="",i="",o="";const a=[{test:/Edg(?:e)?\//,name:"Edge",regex:/Edg(?:e)?\/([\d.]+)/},{test:/OPR\/|Opera\//,name:"Opera",regex:/(?:OPR|Opera)\/([\d.]+)/},{test:/Firefox\//,name:"Firefox",regex:/Firefox\/([\d.]+)/,exclude:/Seamonkey/},{test:/Chrome\//,name:"Chrome",regex:/Chrome\/([\d.]+)/,exclude:/Edge|Edg|OPR|Opera/},{test:/Safari\//,name:"Safari",regex:/Version\/([\d.]+)/,exclude:/Chrome|Edge|Edg|OPR|Opera/},{test:/MSIE|Trident/,name:"IE",regex:/(?:MSIE |rv:)([\d.]+)/}];for(const{test:c,name:u,regex:l,exclude:d}of a)if(c.test(e)&&(!d||!d.test(e))){n=u;const t=e.match(l);r=(null==t?void 0:t[1])||"";break}const s=[{test:/Windows/,parse:()=>{const t=e.match(/Windows NT ([\d.]+)/),n=(null==t?void 0:t[1])||"",r={"10.0":"10",6.3:"8.1",6.2:"8",6.1:"7","6.0":"Vista",5.1:"XP"}[n]||n;return{family:`Windows ${r}`,version:r,architecture:/WOW64|Win64|x64/.test(e)?64:32}}},{test:/iPhone|iPad|iPod/,parse:()=>{var t;const n=e.match(/OS ([\d_]+)/),r=(null==(t=null==n?void 0:n[1])?void 0:t.replace(/_/g,"."))||"";return{family:`iOS ${r}`,version:r,architecture:64}}},{test:/Mac OS X/,parse:()=>{var t;const n=e.match(/Mac OS X ([\d_.]+)/),r=(null==(t=null==n?void 0:n[1])?void 0:t.replace(/_/g,"."))||"";return{family:`macOS ${r}`,version:r,architecture:64}}},{test:/Android/,parse:()=>{const t=e.match(/Android ([\d.]+)/),n=(null==t?void 0:t[1])||"";return{family:`Android ${n}`,version:n,architecture:64}}},{test:/Linux/,parse:()=>({family:"Linux",version:"",architecture:/x86_64|amd64/.test(e)?64:32})}];for(const{test:c,parse:u}of s)if(c.test(e)){t=u();break}if(/iPhone/.test(e))i="iPhone",o="Apple";else if(/iPad/.test(e))i="iPad",o="Apple";else if(/Android/.test(e)){const t=e.match(/Android[^;]+;\s*([^;)]+)/);if(t){i=t[1].trim();const e=i.match(/^(Samsung|LG|Motorola|HTC|Huawei|Xiaomi|OnePlus|Google|Sony|OPPO|Vivo|Realme)/i);o=(null==e?void 0:e[1])||""}}return{name:n,version:r,product:i,manufacturer:o,os:t,layout:/AppleWebKit/.test(e)?"WebKit":/Gecko\//.test(e)?"Gecko":/Trident/.test(e)?"Trident":""}}async function n(){var e;if(!(null==(e=navigator.userAgentData)?void 0:e.getHighEntropyValues))return{};try{return await navigator.userAgentData.getHighEntropyValues(["platform","platformVersion","architecture","model","mobile","bitness","brands","fullVersionList"])}catch{return{}}}function r(){try{const e=document.createElement("canvas"),t=e.getContext("webgl")||e.getContext("experimental-webgl");if(!t)return"No WebGL support";const n=t.getExtension("WEBGL_debug_renderer_info");return n?t.getParameter(n.UNMASKED_RENDERER_WEBGL)||"Unknown GPU":"GPU info restricted"}catch{return"GPU detection failed"}}const i={IP:"",ISP:"",country:"",lastUpdated:0,TTL:36e5};async function o(e,t=2500){const n=new AbortController,r=setTimeout(()=>n.abort(),t);try{const t=await fetch(e,{signal:n.signal});return clearTimeout(r),t.ok?await t.json():null}catch{return clearTimeout(r),null}}async function a(e=!1){if(!e&&i.IP&&Date.now()-i.lastUpdated<i.TTL)return{IP:i.IP,ISP:i.ISP,country:i.country};const t=[{url:"https://ipinfo.io/json",parse:e=>({IP:e.ip,ISP:e.org||"",country:e.country||""})},{url:"https://ipwhois.app/json/",parse:e=>({IP:e.ip,ISP:e.isp||"",country:e.country_code||""})},{url:"https://api.ipify.org?format=json",parse:e=>({IP:e.ip,ISP:"",country:""})}];for(const{url:n,parse:r}of t){const e=await o(n);if(e){const t=r(e);if(t.IP)return i.IP=t.IP,i.ISP=t.ISP||i.ISP,i.country=t.country||i.country,i.lastUpdated=Date.now(),t}}return{IP:i.IP,ISP:i.ISP,country:i.country}}async function s(e=!1){return(await a(e)).IP}async function c(e=!1){return(await a(e)).ISP}async function u(e=!1){return(await a(e)).country}function l(){const e=navigator.connection||navigator.mozConnection||navigator.webkitConnection;return e?{type:e.type||"unknown",effectiveType:e.effectiveType||"unknown",downlink:e.downlink||0,rtt:e.rtt||0,saveData:e.saveData||!1}:null}function d(e={timeout:1e4,enableHighAccuracy:!1}){return new Promise(t=>{navigator.geolocation?navigator.geolocation.getCurrentPosition(e=>t({latitude:e.coords.latitude,longitude:e.coords.longitude,accuracy:e.coords.accuracy,timestamp:e.timestamp}),()=>t(null),e):t(null)})}const g=i;function m(){var e;const{screen:t}=window;return{width:t.width,height:t.height,availWidth:t.availWidth,availHeight:t.availHeight,colorDepth:t.colorDepth,pixelRatio:window.devicePixelRatio||1,orientation:(null==(e=t.orientation)?void 0:e.type)||(window.innerWidth>window.innerHeight?"landscape-primary":"portrait-primary")}}async function p(){if(!("getBattery"in navigator))return{isCharging:!1,level:0,chargingTime:1/0,dischargingTime:1/0};try{const e=await navigator.getBattery();return{isCharging:e.charging,level:Math.round(100*e.level),chargingTime:e.chargingTime,dischargingTime:e.dischargingTime}}catch{return{isCharging:!1,level:0,chargingTime:1/0,dischargingTime:1/0}}}function f(e,t){if(e.platform){const t=e.platform,n=e.platformVersion||"";if("Windows"===t){const e=parseInt(n.split(".")[0],10)>=13?11:10;return{name:"win",version:e,string:`Windows ${e}`}}return"macOS"===t?{name:"mac",version:n,string:`macOS ${n}`}:"Android"===t?{name:"android",version:n,string:`Android ${n}`}:"iOS"===t?{name:"ios",version:n,string:`iOS ${n}`}:"Linux"===t?{name:"linux",version:"",string:"Linux"}:"Chrome OS"===t?{name:"chromeos",version:n,string:`Chrome OS ${n}`}:{name:t.toLowerCase(),version:n,string:`${t} ${n}`}}if(t){const e=t.family||"";if(e.includes("Windows")){return{name:"win",version:parseFloat(t.version)||0,string:e}}return e.includes("macOS")||e.includes("Mac OS X")?{name:"mac",version:t.version,string:e}:e.includes("Android")?{name:"android",version:t.version,string:e}:e.includes("iOS")?{name:"ios",version:t.version,string:e}:e.includes("Linux")?{name:"linux",version:"",string:"Linux"}:{name:e.toLowerCase(),version:t.version||"",string:e}}return{name:"",version:"",string:"Unknown OS"}}function h(e,t){var n;const r=e.fullVersionList||e.brands||[];if(r.length){const e=["Chrome","Firefox","Safari","Edge","Opera","Brave","Vivaldi","Arc"];for(const t of r){if(/Not.?A.?Brand|Chromium/i.test(t.brand))continue;if(e.find(e=>t.brand.includes(e))){const e=t.version.split(".")[0];return`${t.brand} ${e}`}}for(const t of r)if(!/Not.?A.?Brand|Chromium/i.test(t.brand))return`${t.brand} ${t.version.split(".")[0]}`}if(t.name){const e=(null==(n=t.version)?void 0:n.split(".")[0])||"";return`${t.name} ${e}`.trim()}return"Unknown Browser"}
8
8
  /**
9
9
  * @fileoverview aki-info-detect - Browser information detection library
10
10
  * @author lacvietanh
@@ -20,6 +20,5 @@
20
20
  * import akiInfoDetect from 'aki-info-detect';
21
21
  * const info = await akiInfoDetect();
22
22
  * console.log(info.browser, info.os.string, info.GPU);
23
- */
24
- async function f(e=!1){const[i,o,f]=await Promise.all([n(),p(),Promise.resolve(r())]),h=t(),v=function(e){var t;const n=function(e){const t=e.toLowerCase(),n=e.match(/Apple\s+M(\d+)(?:\s+(Pro|Max|Ultra))?/i);if(n){const e=n[1],t=n[2]||"";return{type:"Apple Silicon",chip:`M${e}${t?" "+t:""}`,architecture:"arm64"}}if(t.includes("apple"))return{type:"Apple Silicon",chip:"Apple GPU",architecture:"arm64"};const r=e.match(/NVIDIA\s+(.+?)(?:\s*\/|$)/i)||e.match(/(GeForce|Quadro|RTX|GTX)\s+[\w\s]+/i);if(r)return{type:"NVIDIA",chip:r[0].trim(),architecture:"x86_64"};const i=e.match(/(Radeon|AMD)\s+[\w\s]+/i);if(i)return{type:"AMD",chip:i[0].trim(),architecture:"x86_64"};const o=e.match(/Intel.*?(UHD|Iris|HD)\s*(?:Graphics)?\s*(\d*)/i);return o||t.includes("intel")?{type:"Intel",chip:(null==o?void 0:o[0])||"Intel Graphics",architecture:"x86_64"}:{type:"Unknown",chip:e,architecture:""}}(e);return{RAM:navigator.deviceMemory||0,CPUCore:navigator.hardwareConcurrency||0,GPU:e,CPU:n.type,arch:n.architecture||("macOS"===(null==(t=navigator.userAgentData)?void 0:t.platform)?"arm64":"x86_64")}}(f),P=function(e,t){if(e.platform){const t=e.platform,n=e.platformVersion||"";if("Windows"===t){const e=parseInt(n.split(".")[0],10)>=13?11:10;return{name:"win",version:e,string:`Windows ${e}`}}return"macOS"===t?{name:"mac",version:n,string:`macOS ${n}`}:"Android"===t?{name:"android",version:n,string:`Android ${n}`}:"iOS"===t?{name:"ios",version:n,string:`iOS ${n}`}:"Linux"===t?{name:"linux",version:"",string:"Linux"}:"Chrome OS"===t?{name:"chromeos",version:n,string:`Chrome OS ${n}`}:{name:t.toLowerCase(),version:n,string:`${t} ${n}`}}if(t){const e=t.family||"";return e.includes("Windows")?{name:"win",version:parseFloat(t.version)||0,string:e}:e.includes("macOS")||e.includes("Mac OS X")?{name:"mac",version:t.version,string:e}:e.includes("Android")?{name:"android",version:t.version,string:e}:e.includes("iOS")?{name:"ios",version:t.version,string:e}:e.includes("Linux")?{name:"linux",version:"",string:"Linux"}:{name:e.toLowerCase(),version:t.version||"",string:e}}return{name:"",version:"",string:"Unknown OS"}}(i,h.os),y=function(e,t){var n;const r=e.fullVersionList||e.brands||[];if(r.length){const e=["Chrome","Firefox","Safari","Edge","Opera","Brave","Vivaldi","Arc"];for(const t of r)if(!/Not.?A.?Brand|Chromium/i.test(t.brand)&&e.find(e=>t.brand.includes(e))){const e=t.version.split(".")[0];return`${t.brand} ${e}`}for(const t of r)if(!/Not.?A.?Brand|Chromium/i.test(t.brand))return`${t.brand} ${t.version.split(".")[0]}`}if(t.name){const e=(null==(n=t.version)?void 0:n.split(".")[0])||"";return`${t.name} ${e}`.trim()}return"Unknown Browser"}(i,h),w=i.mobile??/Mobile|Android|iPhone|iPad|iPod/i.test(navigator.userAgent),I=(navigator.languages||[navigator.language]).slice(0,3).map(e=>e.substring(0,2).toLowerCase()).join(" ");return!e&&g.IP||a(e).catch(()=>{}),{browser:y,product:h.product,manufacturer:h.manufacturer,isMobile:w,language:I,CPU:v.CPU,CPUCore:v.CPUCore,arch:i.architecture||v.arch,RAM:v.RAM,GPU:v.GPU,battery:o,os:P,network:{get IP(){return g.IP},get ISP(){return g.ISP},get country(){return g.country}},getIP:s,getISP:c,getCountry:u,getNetworkInfo:a,getLocation:d,getConnection:l,getScreen:m}}e.akiInfoDetect=f,e.default=f,e.detectGPU=r,e.getBattery=p,e.getConnection=l,e.getCountry=u,e.getHighEntropyValues=n,e.getIP=s,e.getISP=c,e.getLocation=d,e.getNetworkInfo=a,e.getScreen=m,e.parseUserAgent=t,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
23
+ */async function v(e=!1){const[i,o,v]=await Promise.all([n(),p(),Promise.resolve(r())]),P=t(),y=function(e){var t;const n=function(e){const t=e.toLowerCase(),n=e.match(/Apple\s+M(\d+)(?:\s+(Pro|Max|Ultra))?/i);if(n){const e=n[1],t=n[2]||"";return{type:"Apple Silicon",chip:`M${e}${t?" "+t:""}`,architecture:"arm64"}}if(t.includes("apple"))return{type:"Apple Silicon",chip:"Apple GPU",architecture:"arm64"};const r=e.match(/NVIDIA\s+(.+?)(?:\s*\/|$)/i)||e.match(/(GeForce|Quadro|RTX|GTX)\s+[\w\s]+/i);if(r)return{type:"NVIDIA",chip:r[0].trim(),architecture:"x86_64"};const i=e.match(/(Radeon|AMD)\s+[\w\s]+/i);if(i)return{type:"AMD",chip:i[0].trim(),architecture:"x86_64"};const o=e.match(/Intel.*?(UHD|Iris|HD)\s*(?:Graphics)?\s*(\d*)/i);return o||t.includes("intel")?{type:"Intel",chip:(null==o?void 0:o[0])||"Intel Graphics",architecture:"x86_64"}:{type:"Unknown",chip:e,architecture:""}}(e);return{RAM:navigator.deviceMemory||0,CPUCore:navigator.hardwareConcurrency||0,GPU:e,CPU:n.type,arch:n.architecture||("macOS"===(null==(t=navigator.userAgentData)?void 0:t.platform)?"arm64":"x86_64")}}(v),w=f(i,P.os),I=h(i,P),S=i.mobile??/Mobile|Android|iPhone|iPad|iPod/i.test(navigator.userAgent),x=(navigator.languages||[navigator.language]).slice(0,3).map(e=>e.substring(0,2).toLowerCase()).join(" ");return!e&&g.IP||a(e).catch(()=>{}),{browser:I,product:P.product,manufacturer:P.manufacturer,isMobile:S,language:x,CPU:y.CPU,CPUCore:y.CPUCore,arch:i.architecture||y.arch,RAM:y.RAM,GPU:y.GPU,battery:o,os:w,network:{get IP(){return g.IP},get ISP(){return g.ISP},get country(){return g.country}},getIP:s,getISP:c,getCountry:u,getNetworkInfo:a,getLocation:d,getConnection:l,getScreen:m}}e.akiInfoDetect=v,e.default=v,e.detectBrowser=h,e.detectGPU=r,e.detectOS=f,e.getBattery=p,e.getConnection=l,e.getCountry=u,e.getHighEntropyValues=n,e.getIP=s,e.getISP=c,e.getLocation=d,e.getNetworkInfo=a,e.getScreen=m,e.parseUserAgent=t,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
25
24
  //# sourceMappingURL=aki-info-detect.umd.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"aki-info-detect.umd.cjs","sources":["../src/modules/ua-parser.js","../src/modules/hardware.js","../src/modules/network.js","../src/modules/screen.js","../src/modules/battery.js","../src/index.js","../src/modules/os.js","../src/modules/browser.js"],"sourcesContent":["/**\n * @fileoverview User Agent and Browser/OS Parser\n * @description Parses user agent string and Client Hints for browser and OS detection\n */\n\n/**\n * Parse user agent string to extract browser and OS info\n * @param {string} [ua] - User agent string (defaults to navigator.userAgent)\n * @returns {ParsedUA} Parsed platform information\n * \n * @typedef {Object} ParsedUA\n * @property {string} name - Browser name\n * @property {string} version - Browser version\n * @property {string} product - Device product name\n * @property {string} manufacturer - Device manufacturer\n * @property {Object|null} os - Operating system info\n * @property {string} layout - Rendering engine\n */\nexport function parseUserAgent(ua = navigator.userAgent) {\n let os = null;\n let name = '';\n let version = '';\n let product = '';\n let manufacturer = '';\n\n // Browser detection (order matters - more specific first)\n const browserPatterns = [\n { test: /Edg(?:e)?\\//, name: 'Edge', regex: /Edg(?:e)?\\/([\\d.]+)/ },\n { test: /OPR\\/|Opera\\//, name: 'Opera', regex: /(?:OPR|Opera)\\/([\\d.]+)/ },\n { test: /Firefox\\//, name: 'Firefox', regex: /Firefox\\/([\\d.]+)/, exclude: /Seamonkey/ },\n { test: /Chrome\\//, name: 'Chrome', regex: /Chrome\\/([\\d.]+)/, exclude: /Edge|Edg|OPR|Opera/ },\n { test: /Safari\\//, name: 'Safari', regex: /Version\\/([\\d.]+)/, exclude: /Chrome|Edge|Edg|OPR|Opera/ },\n { test: /MSIE|Trident/, name: 'IE', regex: /(?:MSIE |rv:)([\\d.]+)/ }\n ];\n\n for (const { test, name: browserName, regex, exclude } of browserPatterns) {\n if (test.test(ua) && (!exclude || !exclude.test(ua))) {\n name = browserName;\n const match = ua.match(regex);\n version = match?.[1] || '';\n break;\n }\n }\n\n // OS detection\n const osPatterns = [\n {\n test: /Windows/,\n parse: () => {\n const verMap = { '10.0': '10', '6.3': '8.1', '6.2': '8', '6.1': '7', '6.0': 'Vista', '5.1': 'XP' };\n const match = ua.match(/Windows NT ([\\d.]+)/);\n const ntVer = match?.[1] || '';\n const winVer = verMap[ntVer] || ntVer;\n return {\n family: `Windows ${winVer}`,\n version: winVer,\n architecture: /WOW64|Win64|x64/.test(ua) ? 64 : 32\n };\n }\n },\n {\n test: /Mac OS X/,\n parse: () => {\n const match = ua.match(/Mac OS X ([\\d_.]+)/);\n const ver = match?.[1]?.replace(/_/g, '.') || '';\n return { family: `macOS ${ver}`, version: ver, architecture: 64 };\n }\n },\n {\n test: /Android/,\n parse: () => {\n const match = ua.match(/Android ([\\d.]+)/);\n const ver = match?.[1] || '';\n return { family: `Android ${ver}`, version: ver, architecture: 64 };\n }\n },\n {\n test: /iPhone|iPad|iPod/,\n parse: () => {\n const match = ua.match(/OS ([\\d_]+)/);\n const ver = match?.[1]?.replace(/_/g, '.') || '';\n return { family: `iOS ${ver}`, version: ver, architecture: 64 };\n }\n },\n {\n test: /Linux/,\n parse: () => ({\n family: 'Linux',\n version: '',\n architecture: /x86_64|amd64/.test(ua) ? 64 : 32\n })\n }\n ];\n\n for (const { test, parse } of osPatterns) {\n if (test.test(ua)) {\n os = parse();\n break;\n }\n }\n\n // Device detection\n if (/iPhone/.test(ua)) {\n product = 'iPhone';\n manufacturer = 'Apple';\n } else if (/iPad/.test(ua)) {\n product = 'iPad';\n manufacturer = 'Apple';\n } else if (/Android/.test(ua)) {\n const match = ua.match(/Android[^;]+;\\s*([^;)]+)/);\n if (match) {\n product = match[1].trim();\n // Detect manufacturer from product string\n const mfrMatch = product.match(/^(Samsung|LG|Motorola|HTC|Huawei|Xiaomi|OnePlus|Google|Sony|OPPO|Vivo|Realme)/i);\n manufacturer = mfrMatch?.[1] || '';\n }\n }\n\n // Rendering engine\n const layout = /AppleWebKit/.test(ua) ? 'WebKit' :\n /Gecko\\//.test(ua) ? 'Gecko' :\n /Trident/.test(ua) ? 'Trident' : '';\n\n return { name, version, product, manufacturer, os, layout };\n}\n\n/**\n * Get High Entropy Values from Client Hints API\n * @returns {Promise<Object>} High entropy values object\n */\nexport async function getHighEntropyValues() {\n if (!navigator.userAgentData?.getHighEntropyValues) {\n return {};\n }\n\n try {\n return await navigator.userAgentData.getHighEntropyValues([\n 'platform',\n 'platformVersion',\n 'architecture',\n 'model',\n 'mobile',\n 'bitness',\n 'brands',\n 'fullVersionList'\n ]);\n } catch {\n return {};\n }\n}\n","/**\n * @fileoverview Hardware Detection Module\n * @description Detects CPU, GPU, RAM, and other hardware information\n */\n\n/**\n * Detect GPU information using WebGL\n * @returns {string} GPU renderer string\n */\nexport function detectGPU() {\n try {\n const canvas = document.createElement('canvas');\n const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');\n \n if (!gl) return 'No WebGL support';\n\n const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');\n if (!debugInfo) return 'GPU info restricted';\n\n return gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) || 'Unknown GPU';\n } catch {\n return 'GPU detection failed';\n }\n}\n\n/**\n * Parse GPU string to detect Apple Silicon or other notable chips\n * Uses future-proof regex patterns (M1, M2, M3... MX)\n * @param {string} gpu - GPU renderer string\n * @returns {Object} Parsed chip info\n */\nexport function parseChipInfo(gpu) {\n const gpuLower = gpu.toLowerCase();\n \n // Apple Silicon detection - future-proof pattern for M1, M2, M3, M4... MX\n const appleSiliconMatch = gpu.match(/Apple\\s+M(\\d+)(?:\\s+(Pro|Max|Ultra))?/i);\n if (appleSiliconMatch) {\n const chipNum = appleSiliconMatch[1];\n const variant = appleSiliconMatch[2] || '';\n return {\n type: 'Apple Silicon',\n chip: `M${chipNum}${variant ? ' ' + variant : ''}`,\n architecture: 'arm64'\n };\n }\n\n // Generic Apple GPU (older or unidentified)\n if (gpuLower.includes('apple')) {\n return { type: 'Apple Silicon', chip: 'Apple GPU', architecture: 'arm64' };\n }\n\n // NVIDIA detection\n const nvidiaMatch = gpu.match(/NVIDIA\\s+(.+?)(?:\\s*\\/|$)/i) || gpu.match(/(GeForce|Quadro|RTX|GTX)\\s+[\\w\\s]+/i);\n if (nvidiaMatch) {\n return { type: 'NVIDIA', chip: nvidiaMatch[0].trim(), architecture: 'x86_64' };\n }\n\n // AMD detection\n const amdMatch = gpu.match(/(Radeon|AMD)\\s+[\\w\\s]+/i);\n if (amdMatch) {\n return { type: 'AMD', chip: amdMatch[0].trim(), architecture: 'x86_64' };\n }\n\n // Intel detection\n const intelMatch = gpu.match(/Intel.*?(UHD|Iris|HD)\\s*(?:Graphics)?\\s*(\\d*)/i);\n if (intelMatch || gpuLower.includes('intel')) {\n return { type: 'Intel', chip: intelMatch?.[0] || 'Intel Graphics', architecture: 'x86_64' };\n }\n\n return { type: 'Unknown', chip: gpu, architecture: '' };\n}\n\n/**\n * Get hardware information\n * @param {string} gpu - GPU string for chip detection\n * @returns {Object} Hardware specs\n */\nexport function getHardwareInfo(gpu) {\n const chipInfo = parseChipInfo(gpu);\n \n return {\n RAM: navigator.deviceMemory || 0,\n CPUCore: navigator.hardwareConcurrency || 0,\n GPU: gpu,\n CPU: chipInfo.type,\n arch: chipInfo.architecture || (navigator.userAgentData?.platform === 'macOS' ? 'arm64' : 'x86_64')\n };\n}\n","/**\n * @fileoverview Network Information Module\n * @description Handles IP, ISP, country detection with caching\n */\n\n/** Cache for network data */\nconst cache = {\n IP: '',\n ISP: '',\n country: '',\n lastUpdated: 0,\n TTL: 3600000 // 1 hour\n};\n\n/**\n * Fetch with timeout\n * @param {string} url - URL to fetch\n * @param {number} [timeout=2500] - Timeout in ms\n * @returns {Promise<Object|null>} JSON response or null\n */\nasync function fetchJSON(url, timeout = 2500) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const res = await fetch(url, { signal: controller.signal });\n clearTimeout(timeoutId);\n return res.ok ? await res.json() : null;\n } catch {\n clearTimeout(timeoutId);\n return null;\n }\n}\n\n/**\n * Get network information (IP, ISP, country)\n * @param {boolean} [forceRefresh=false] - Bypass cache\n * @returns {Promise<NetworkInfo>}\n * \n * @typedef {Object} NetworkInfo\n * @property {string} IP - Public IP address\n * @property {string} ISP - Internet Service Provider\n * @property {string} country - Country code (ISO 3166-1 alpha-2)\n */\nexport async function getNetworkInfo(forceRefresh = false) {\n // Return cached data if valid\n if (!forceRefresh && cache.IP && Date.now() - cache.lastUpdated < cache.TTL) {\n return { IP: cache.IP, ISP: cache.ISP, country: cache.country };\n }\n\n // Service providers in priority order\n const services = [\n {\n url: 'https://ipinfo.io/json',\n parse: (d) => ({ IP: d.ip, ISP: d.org || '', country: d.country || '' })\n },\n {\n url: 'https://ipwhois.app/json/',\n parse: (d) => ({ IP: d.ip, ISP: d.isp || '', country: d.country_code || '' })\n },\n {\n url: 'https://api.ipify.org?format=json',\n parse: (d) => ({ IP: d.ip, ISP: '', country: '' })\n }\n ];\n\n for (const { url, parse } of services) {\n const data = await fetchJSON(url);\n if (data) {\n const result = parse(data);\n if (result.IP) {\n cache.IP = result.IP;\n cache.ISP = result.ISP || cache.ISP;\n cache.country = result.country || cache.country;\n cache.lastUpdated = Date.now();\n return result;\n }\n }\n }\n\n // Return whatever we have in cache\n return { IP: cache.IP, ISP: cache.ISP, country: cache.country };\n}\n\n/**\n * Get public IP address\n * @param {boolean} [forceRefresh=false] - Bypass cache\n * @returns {Promise<string>}\n */\nexport async function getIP(forceRefresh = false) {\n const info = await getNetworkInfo(forceRefresh);\n return info.IP;\n}\n\n/**\n * Get ISP information\n * @param {boolean} [forceRefresh=false] - Bypass cache\n * @returns {Promise<string>}\n */\nexport async function getISP(forceRefresh = false) {\n const info = await getNetworkInfo(forceRefresh);\n return info.ISP;\n}\n\n/**\n * Get country code\n * @param {boolean} [forceRefresh=false] - Bypass cache\n * @returns {Promise<string>}\n */\nexport async function getCountry(forceRefresh = false) {\n const info = await getNetworkInfo(forceRefresh);\n return info.country;\n}\n\n/**\n * Get connection quality info (Network Information API)\n * @returns {ConnectionInfo|null}\n * \n * @typedef {Object} ConnectionInfo\n * @property {string} type - Connection type (wifi, cellular, etc.)\n * @property {string} effectiveType - Effective connection type (4g, 3g, etc.)\n * @property {number} downlink - Downlink speed in Mbps\n * @property {number} rtt - Round-trip time in ms\n * @property {boolean} saveData - Data saver mode enabled\n */\nexport function getConnection() {\n const conn = navigator.connection || navigator.mozConnection || navigator.webkitConnection;\n \n if (!conn) return null;\n\n return {\n type: conn.type || 'unknown',\n effectiveType: conn.effectiveType || 'unknown',\n downlink: conn.downlink || 0,\n rtt: conn.rtt || 0,\n saveData: conn.saveData || false\n };\n}\n\n/**\n * Get geolocation (requires user permission)\n * @param {Object} [options] - Geolocation options\n * @returns {Promise<GeolocationData|null>}\n * \n * @typedef {Object} GeolocationData\n * @property {number} latitude\n * @property {number} longitude\n * @property {number} accuracy - Accuracy in meters\n * @property {number} timestamp\n */\nexport function getLocation(options = { timeout: 10000, enableHighAccuracy: false }) {\n return new Promise((resolve) => {\n if (!navigator.geolocation) {\n resolve(null);\n return;\n }\n\n navigator.geolocation.getCurrentPosition(\n (pos) => resolve({\n latitude: pos.coords.latitude,\n longitude: pos.coords.longitude,\n accuracy: pos.coords.accuracy,\n timestamp: pos.timestamp\n }),\n () => resolve(null),\n options\n );\n });\n}\n\n/** Expose cache for reactive access */\nexport const networkCache = cache;\n","/**\n * @fileoverview Screen and Display Module\n * @description Detects screen resolution, pixel ratio, orientation\n */\n\n/**\n * Get screen and display information\n * @returns {ScreenInfo}\n * \n * @typedef {Object} ScreenInfo\n * @property {number} width - Screen width in pixels\n * @property {number} height - Screen height in pixels\n * @property {number} availWidth - Available width (excluding taskbars)\n * @property {number} availHeight - Available height\n * @property {number} colorDepth - Color depth in bits\n * @property {number} pixelRatio - Device pixel ratio\n * @property {string} orientation - Screen orientation\n */\nexport function getScreen() {\n const { screen } = window;\n \n return {\n width: screen.width,\n height: screen.height,\n availWidth: screen.availWidth,\n availHeight: screen.availHeight,\n colorDepth: screen.colorDepth,\n pixelRatio: window.devicePixelRatio || 1,\n orientation: screen.orientation?.type || \n (window.innerWidth > window.innerHeight ? 'landscape-primary' : 'portrait-primary')\n };\n}\n","/**\n * @fileoverview Battery Status Module\n * @description Detects battery level and charging status\n */\n\n/**\n * Get battery information\n * @returns {Promise<BatteryInfo>}\n * \n * @typedef {Object} BatteryInfo\n * @property {boolean} isCharging - Whether device is charging\n * @property {number} level - Battery percentage (0-100)\n * @property {number} chargingTime - Seconds until fully charged (Infinity if not charging)\n * @property {number} dischargingTime - Seconds until empty (Infinity if charging)\n */\nexport async function getBattery() {\n // Battery API may not be available in all browsers (e.g., Safari)\n if (!('getBattery' in navigator)) {\n return { isCharging: false, level: 0, chargingTime: Infinity, dischargingTime: Infinity };\n }\n\n try {\n const battery = await navigator.getBattery();\n return {\n isCharging: battery.charging,\n level: Math.round(battery.level * 100),\n chargingTime: battery.chargingTime,\n dischargingTime: battery.dischargingTime\n };\n } catch {\n return { isCharging: false, level: 0, chargingTime: Infinity, dischargingTime: Infinity };\n }\n}\n","/**\n * @fileoverview aki-info-detect - Browser information detection library\n * @author lacvietanh\n * @version 2.0.0\n * @license MIT\n * @see https://github.com/lacvietanh/akiInfoDetect.js\n * \n * @description\n * Lightweight library for detecting device, browser, hardware, and network information.\n * Works in browser environments only. Supports ES Modules and UMD.\n * \n * @example\n * import akiInfoDetect from 'aki-info-detect';\n * const info = await akiInfoDetect();\n * console.log(info.browser, info.os.string, info.GPU);\n */\n\nimport { parseUserAgent, getHighEntropyValues } from './modules/ua-parser.js';\nimport { detectGPU, getHardwareInfo } from './modules/hardware.js';\nimport { getNetworkInfo, getIP, getISP, getCountry, getConnection, getLocation, networkCache } from './modules/network.js';\nimport { getScreen } from './modules/screen.js';\nimport { getBattery } from './modules/battery.js';\nimport { detectOS } from './modules/os.js';\nimport { detectBrowser } from './modules/browser.js';\n\n/**\n * @typedef {Object} AkiInfoResult\n * @property {string} browser - Browser name and version\n * @property {string} product - Device product name\n * @property {string} manufacturer - Device manufacturer\n * @property {boolean} isMobile - Is mobile device\n * @property {string} language - Preferred languages (space-separated 2-char codes)\n * @property {string} CPU - CPU/chip type\n * @property {number} CPUCore - Number of logical CPU cores\n * @property {string} arch - CPU architecture (x86_64, arm64)\n * @property {number} RAM - Device memory in GB\n * @property {string} GPU - GPU renderer string\n * @property {Object} battery - Battery status\n * @property {Object} os - Operating system info\n * @property {Object} network - Network info (reactive getters)\n * @property {Function} getIP - Fetch IP address\n * @property {Function} getISP - Fetch ISP info\n * @property {Function} getCountry - Fetch country code\n * @property {Function} getNetworkInfo - Fetch all network info\n * @property {Function} getLocation - Fetch geolocation\n * @property {Function} getConnection - Get connection quality\n * @property {Function} getScreen - Get screen info\n */\n\n/**\n * Main detection function\n * @param {boolean} [forceNetworkRefresh=false] - Force refresh network data\n * @returns {Promise<AkiInfoResult>} Complete system information\n */\nasync function akiInfoDetect(forceNetworkRefresh = false) {\n // Parallel async operations for performance\n const [hev, battery, gpu] = await Promise.all([\n getHighEntropyValues(),\n getBattery(),\n Promise.resolve(detectGPU())\n ]);\n\n // Parse user agent\n const parsedUA = parseUserAgent();\n \n // Get hardware info with GPU-based chip detection\n const hardware = getHardwareInfo(gpu);\n\n // Detect OS (prefer Client Hints)\n const os = detectOS(hev, parsedUA.os);\n\n // Detect browser (prefer Client Hints)\n const browser = detectBrowser(hev, parsedUA);\n\n // Detect mobile status\n const isMobile = hev.mobile ?? /Mobile|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);\n\n // Get language preferences (top 3, 2-char codes)\n const languages = (navigator.languages || [navigator.language])\n .slice(0, 3)\n .map(l => l.substring(0, 2).toLowerCase())\n .join(' ');\n\n // Trigger network fetch in background (non-blocking)\n if (forceNetworkRefresh || !networkCache.IP) {\n getNetworkInfo(forceNetworkRefresh).catch(() => {});\n }\n\n return {\n // Basic info\n browser,\n product: parsedUA.product,\n manufacturer: parsedUA.manufacturer,\n isMobile,\n language: languages,\n\n // Hardware\n CPU: hardware.CPU,\n CPUCore: hardware.CPUCore,\n arch: hev.architecture || hardware.arch,\n RAM: hardware.RAM,\n GPU: hardware.GPU,\n\n // Battery\n battery,\n\n // OS\n os,\n\n // Network (reactive getters from cache)\n network: {\n get IP() { return networkCache.IP; },\n get ISP() { return networkCache.ISP; },\n get country() { return networkCache.country; }\n },\n\n // Async methods\n getIP,\n getISP,\n getCountry,\n getNetworkInfo,\n getLocation,\n getConnection,\n getScreen\n };\n}\n\n// Named exports for tree-shaking\nexport {\n akiInfoDetect,\n getNetworkInfo,\n getIP,\n getISP,\n getCountry,\n getConnection,\n getLocation,\n getScreen,\n getBattery,\n detectGPU,\n parseUserAgent,\n getHighEntropyValues\n};\n\n// Default export\nexport default akiInfoDetect;\n","/**\n * @fileoverview OS Detection Module\n * @description Detects operating system with Client Hints support\n */\n\n/**\n * Detect operating system from Client Hints or User Agent\n * @param {Object} hev - High Entropy Values from Client Hints\n * @param {Object|null} parsedOS - Parsed OS from user agent\n * @returns {OSInfo}\n * \n * @typedef {Object} OSInfo\n * @property {string} name - Short OS name (win, mac, linux, android, ios)\n * @property {number|string} version - OS version\n * @property {string} string - Human-readable OS string\n */\nexport function detectOS(hev, parsedOS) {\n // Prefer Client Hints if available (more accurate)\n if (hev.platform) {\n const platform = hev.platform;\n const platformVersion = hev.platformVersion || '';\n\n if (platform === 'Windows') {\n // Windows 11 reports platformVersion >= 13.0\n const majorVer = parseInt(platformVersion.split('.')[0], 10);\n const winVer = majorVer >= 13 ? 11 : 10;\n return { name: 'win', version: winVer, string: `Windows ${winVer}` };\n }\n\n if (platform === 'macOS') {\n return { name: 'mac', version: platformVersion, string: `macOS ${platformVersion}` };\n }\n\n if (platform === 'Android') {\n return { name: 'android', version: platformVersion, string: `Android ${platformVersion}` };\n }\n\n if (platform === 'iOS') {\n return { name: 'ios', version: platformVersion, string: `iOS ${platformVersion}` };\n }\n\n if (platform === 'Linux') {\n return { name: 'linux', version: '', string: 'Linux' };\n }\n\n if (platform === 'Chrome OS') {\n return { name: 'chromeos', version: platformVersion, string: `Chrome OS ${platformVersion}` };\n }\n\n // Unknown platform from Client Hints\n return { name: platform.toLowerCase(), version: platformVersion, string: `${platform} ${platformVersion}` };\n }\n\n // Fallback to parsed User Agent\n if (parsedOS) {\n const family = parsedOS.family || '';\n \n if (family.includes('Windows')) {\n const ver = parseFloat(parsedOS.version) || 0;\n return { name: 'win', version: ver, string: family };\n }\n if (family.includes('macOS') || family.includes('Mac OS X')) {\n return { name: 'mac', version: parsedOS.version, string: family };\n }\n if (family.includes('Android')) {\n return { name: 'android', version: parsedOS.version, string: family };\n }\n if (family.includes('iOS')) {\n return { name: 'ios', version: parsedOS.version, string: family };\n }\n if (family.includes('Linux')) {\n return { name: 'linux', version: '', string: 'Linux' };\n }\n\n return { name: family.toLowerCase(), version: parsedOS.version || '', string: family };\n }\n\n return { name: '', version: '', string: 'Unknown OS' };\n}\n","/**\n * @fileoverview Browser Detection Module\n * @description Detects browser name and version with Client Hints support\n */\n\n/**\n * Detect browser from Client Hints or User Agent\n * @param {Object} hev - High Entropy Values from Client Hints\n * @param {Object} parsedUA - Parsed user agent data\n * @returns {string} Browser name and version (e.g., \"Chrome 120\")\n */\nexport function detectBrowser(hev, parsedUA) {\n // Priority: Client Hints > User Agent\n const brands = hev.fullVersionList || hev.brands || [];\n \n if (brands.length) {\n // Known browsers to look for (in priority order)\n const knownBrowsers = ['Chrome', 'Firefox', 'Safari', 'Edge', 'Opera', 'Brave', 'Vivaldi', 'Arc'];\n \n for (const brand of brands) {\n // Skip placeholder brands\n if (/Not.?A.?Brand|Chromium/i.test(brand.brand)) continue;\n \n // Check if it's a known browser\n const match = knownBrowsers.find(b => brand.brand.includes(b));\n if (match) {\n const majorVersion = brand.version.split('.')[0];\n return `${brand.brand} ${majorVersion}`;\n }\n }\n\n // If no known browser found, use first non-placeholder brand\n for (const brand of brands) {\n if (!/Not.?A.?Brand|Chromium/i.test(brand.brand)) {\n return `${brand.brand} ${brand.version.split('.')[0]}`;\n }\n }\n }\n\n // Fallback to parsed User Agent\n if (parsedUA.name) {\n const majorVersion = parsedUA.version?.split('.')[0] || '';\n return `${parsedUA.name} ${majorVersion}`.trim();\n }\n\n return 'Unknown Browser';\n}\n"],"names":["parseUserAgent","ua","navigator","userAgent","os","name","version","product","manufacturer","browserPatterns","test","regex","exclude","browserName","match","osPatterns","parse","ntVer","winVer","family","architecture","ver","_a","replace","trim","mfrMatch","layout","async","getHighEntropyValues","userAgentData","detectGPU","canvas","document","createElement","gl","getContext","debugInfo","getExtension","getParameter","UNMASKED_RENDERER_WEBGL","cache","IP","ISP","country","lastUpdated","TTL","fetchJSON","url","timeout","controller","AbortController","timeoutId","setTimeout","abort","res","fetch","signal","clearTimeout","ok","json","getNetworkInfo","forceRefresh","Date","now","services","d","ip","org","isp","country_code","data","result","getIP","getISP","getCountry","getConnection","conn","connection","mozConnection","webkitConnection","type","effectiveType","downlink","rtt","saveData","getLocation","options","enableHighAccuracy","Promise","resolve","geolocation","getCurrentPosition","pos","latitude","coords","longitude","accuracy","timestamp","networkCache","getScreen","screen","window","width","height","availWidth","availHeight","colorDepth","pixelRatio","devicePixelRatio","orientation","innerWidth","innerHeight","getBattery","isCharging","level","chargingTime","Infinity","dischargingTime","battery","charging","Math","round","akiInfoDetect","forceNetworkRefresh","hev","gpu","all","parsedUA","hardware","chipInfo","gpuLower","toLowerCase","appleSiliconMatch","chipNum","variant","chip","includes","nvidiaMatch","amdMatch","intelMatch","parseChipInfo","RAM","deviceMemory","CPUCore","hardwareConcurrency","GPU","CPU","arch","platform","getHardwareInfo","parsedOS","platformVersion","parseInt","split","string","parseFloat","detectOS","browser","brands","fullVersionList","length","knownBrowsers","brand","find","b","majorVersion","detectBrowser","isMobile","mobile","languages","language","slice","map","l","substring","join","catch","network"],"mappings":"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"}
1
+ {"version":3,"file":"aki-info-detect.umd.cjs","sources":["../src/modules/ua-parser.js","../src/modules/hardware.js","../src/modules/network.js","../src/modules/screen.js","../src/modules/battery.js","../src/modules/os.js","../src/modules/browser.js","../src/index.js"],"sourcesContent":["/**\n * @fileoverview User Agent and Browser/OS Parser\n * @description Parses user agent string and Client Hints for browser and OS detection\n */\n\n/**\n * Parse user agent string to extract browser and OS info\n * @param {string} [ua] - User agent string (defaults to navigator.userAgent)\n * @returns {ParsedUA} Parsed platform information\n * \n * @typedef {Object} ParsedUA\n * @property {string} name - Browser name\n * @property {string} version - Browser version\n * @property {string} product - Device product name\n * @property {string} manufacturer - Device manufacturer\n * @property {Object|null} os - Operating system info\n * @property {string} layout - Rendering engine\n */\nexport function parseUserAgent(ua = navigator.userAgent) {\n let os = null;\n let name = '';\n let version = '';\n let product = '';\n let manufacturer = '';\n\n // Browser detection (order matters - more specific first)\n const browserPatterns = [\n { test: /Edg(?:e)?\\//, name: 'Edge', regex: /Edg(?:e)?\\/([\\d.]+)/ },\n { test: /OPR\\/|Opera\\//, name: 'Opera', regex: /(?:OPR|Opera)\\/([\\d.]+)/ },\n { test: /Firefox\\//, name: 'Firefox', regex: /Firefox\\/([\\d.]+)/, exclude: /Seamonkey/ },\n { test: /Chrome\\//, name: 'Chrome', regex: /Chrome\\/([\\d.]+)/, exclude: /Edge|Edg|OPR|Opera/ },\n { test: /Safari\\//, name: 'Safari', regex: /Version\\/([\\d.]+)/, exclude: /Chrome|Edge|Edg|OPR|Opera/ },\n { test: /MSIE|Trident/, name: 'IE', regex: /(?:MSIE |rv:)([\\d.]+)/ }\n ];\n\n for (const { test, name: browserName, regex, exclude } of browserPatterns) {\n if (test.test(ua) && (!exclude || !exclude.test(ua))) {\n name = browserName;\n const match = ua.match(regex);\n version = match?.[1] || '';\n break;\n }\n }\n\n // OS detection\n // [CRITICAL] iPhone/iPad/iPod MUST be tested before \"Mac OS X\". Real iOS user agents contain\n // the literal substring \"like Mac OS X\" (Apple's legacy WebKit compatibility convention, e.g.\n // \"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) ...\") — so a naive /Mac OS X/ test\n // matches genuine iPhones too, not just desktop Safari/Chrome. Getting this order wrong\n // silently mislabels every iPhone as \"macOS\" (verified on production data: 125/528 clients on\n // kinhdich.akinet.me — 2026-07-10). Do not reorder without re-reading this comment.\n const osPatterns = [\n {\n test: /Windows/,\n parse: () => {\n const verMap = { '10.0': '10', '6.3': '8.1', '6.2': '8', '6.1': '7', '6.0': 'Vista', '5.1': 'XP' };\n const match = ua.match(/Windows NT ([\\d.]+)/);\n const ntVer = match?.[1] || '';\n const winVer = verMap[ntVer] || ntVer;\n return {\n family: `Windows ${winVer}`,\n version: winVer,\n architecture: /WOW64|Win64|x64/.test(ua) ? 64 : 32\n };\n }\n },\n {\n test: /iPhone|iPad|iPod/,\n parse: () => {\n const match = ua.match(/OS ([\\d_]+)/);\n const ver = match?.[1]?.replace(/_/g, '.') || '';\n return { family: `iOS ${ver}`, version: ver, architecture: 64 };\n }\n },\n {\n test: /Mac OS X/,\n parse: () => {\n const match = ua.match(/Mac OS X ([\\d_.]+)/);\n const ver = match?.[1]?.replace(/_/g, '.') || '';\n return { family: `macOS ${ver}`, version: ver, architecture: 64 };\n }\n },\n {\n test: /Android/,\n parse: () => {\n const match = ua.match(/Android ([\\d.]+)/);\n const ver = match?.[1] || '';\n return { family: `Android ${ver}`, version: ver, architecture: 64 };\n }\n },\n {\n test: /Linux/,\n parse: () => ({\n family: 'Linux',\n version: '',\n architecture: /x86_64|amd64/.test(ua) ? 64 : 32\n })\n }\n ];\n\n for (const { test, parse } of osPatterns) {\n if (test.test(ua)) {\n os = parse();\n break;\n }\n }\n\n // Device detection\n if (/iPhone/.test(ua)) {\n product = 'iPhone';\n manufacturer = 'Apple';\n } else if (/iPad/.test(ua)) {\n product = 'iPad';\n manufacturer = 'Apple';\n } else if (/Android/.test(ua)) {\n const match = ua.match(/Android[^;]+;\\s*([^;)]+)/);\n if (match) {\n product = match[1].trim();\n // Detect manufacturer from product string\n const mfrMatch = product.match(/^(Samsung|LG|Motorola|HTC|Huawei|Xiaomi|OnePlus|Google|Sony|OPPO|Vivo|Realme)/i);\n manufacturer = mfrMatch?.[1] || '';\n }\n }\n\n // Rendering engine\n const layout = /AppleWebKit/.test(ua) ? 'WebKit' :\n /Gecko\\//.test(ua) ? 'Gecko' :\n /Trident/.test(ua) ? 'Trident' : '';\n\n return { name, version, product, manufacturer, os, layout };\n}\n\n/**\n * Get High Entropy Values from Client Hints API\n * @returns {Promise<Object>} High entropy values object\n */\nexport async function getHighEntropyValues() {\n if (!navigator.userAgentData?.getHighEntropyValues) {\n return {};\n }\n\n try {\n return await navigator.userAgentData.getHighEntropyValues([\n 'platform',\n 'platformVersion',\n 'architecture',\n 'model',\n 'mobile',\n 'bitness',\n 'brands',\n 'fullVersionList'\n ]);\n } catch {\n return {};\n }\n}\n","/**\n * @fileoverview Hardware Detection Module\n * @description Detects CPU, GPU, RAM, and other hardware information\n */\n\n/**\n * Detect GPU information using WebGL\n * @returns {string} GPU renderer string\n */\nexport function detectGPU() {\n try {\n const canvas = document.createElement('canvas');\n const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');\n \n if (!gl) return 'No WebGL support';\n\n const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');\n if (!debugInfo) return 'GPU info restricted';\n\n return gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) || 'Unknown GPU';\n } catch {\n return 'GPU detection failed';\n }\n}\n\n/**\n * Parse GPU string to detect Apple Silicon or other notable chips\n * Uses future-proof regex patterns (M1, M2, M3... MX)\n * @param {string} gpu - GPU renderer string\n * @returns {Object} Parsed chip info\n */\nexport function parseChipInfo(gpu) {\n const gpuLower = gpu.toLowerCase();\n \n // Apple Silicon detection - future-proof pattern for M1, M2, M3, M4... MX\n const appleSiliconMatch = gpu.match(/Apple\\s+M(\\d+)(?:\\s+(Pro|Max|Ultra))?/i);\n if (appleSiliconMatch) {\n const chipNum = appleSiliconMatch[1];\n const variant = appleSiliconMatch[2] || '';\n return {\n type: 'Apple Silicon',\n chip: `M${chipNum}${variant ? ' ' + variant : ''}`,\n architecture: 'arm64'\n };\n }\n\n // Generic Apple GPU (older or unidentified)\n if (gpuLower.includes('apple')) {\n return { type: 'Apple Silicon', chip: 'Apple GPU', architecture: 'arm64' };\n }\n\n // NVIDIA detection\n const nvidiaMatch = gpu.match(/NVIDIA\\s+(.+?)(?:\\s*\\/|$)/i) || gpu.match(/(GeForce|Quadro|RTX|GTX)\\s+[\\w\\s]+/i);\n if (nvidiaMatch) {\n return { type: 'NVIDIA', chip: nvidiaMatch[0].trim(), architecture: 'x86_64' };\n }\n\n // AMD detection\n const amdMatch = gpu.match(/(Radeon|AMD)\\s+[\\w\\s]+/i);\n if (amdMatch) {\n return { type: 'AMD', chip: amdMatch[0].trim(), architecture: 'x86_64' };\n }\n\n // Intel detection\n const intelMatch = gpu.match(/Intel.*?(UHD|Iris|HD)\\s*(?:Graphics)?\\s*(\\d*)/i);\n if (intelMatch || gpuLower.includes('intel')) {\n return { type: 'Intel', chip: intelMatch?.[0] || 'Intel Graphics', architecture: 'x86_64' };\n }\n\n return { type: 'Unknown', chip: gpu, architecture: '' };\n}\n\n/**\n * Get hardware information\n * @param {string} gpu - GPU string for chip detection\n * @returns {Object} Hardware specs\n */\nexport function getHardwareInfo(gpu) {\n const chipInfo = parseChipInfo(gpu);\n \n return {\n RAM: navigator.deviceMemory || 0,\n CPUCore: navigator.hardwareConcurrency || 0,\n GPU: gpu,\n CPU: chipInfo.type,\n arch: chipInfo.architecture || (navigator.userAgentData?.platform === 'macOS' ? 'arm64' : 'x86_64')\n };\n}\n","/**\n * @fileoverview Network Information Module\n * @description Handles IP, ISP, country detection with caching\n */\n\n/** Cache for network data */\nconst cache = {\n IP: '',\n ISP: '',\n country: '',\n lastUpdated: 0,\n TTL: 3600000 // 1 hour\n};\n\n/**\n * Fetch with timeout\n * @param {string} url - URL to fetch\n * @param {number} [timeout=2500] - Timeout in ms\n * @returns {Promise<Object|null>} JSON response or null\n */\nasync function fetchJSON(url, timeout = 2500) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const res = await fetch(url, { signal: controller.signal });\n clearTimeout(timeoutId);\n return res.ok ? await res.json() : null;\n } catch {\n clearTimeout(timeoutId);\n return null;\n }\n}\n\n/**\n * Get network information (IP, ISP, country)\n * @param {boolean} [forceRefresh=false] - Bypass cache\n * @returns {Promise<NetworkInfo>}\n * \n * @typedef {Object} NetworkInfo\n * @property {string} IP - Public IP address\n * @property {string} ISP - Internet Service Provider\n * @property {string} country - Country code (ISO 3166-1 alpha-2)\n */\nexport async function getNetworkInfo(forceRefresh = false) {\n // Return cached data if valid\n if (!forceRefresh && cache.IP && Date.now() - cache.lastUpdated < cache.TTL) {\n return { IP: cache.IP, ISP: cache.ISP, country: cache.country };\n }\n\n // Service providers in priority order\n const services = [\n {\n url: 'https://ipinfo.io/json',\n parse: (d) => ({ IP: d.ip, ISP: d.org || '', country: d.country || '' })\n },\n {\n url: 'https://ipwhois.app/json/',\n parse: (d) => ({ IP: d.ip, ISP: d.isp || '', country: d.country_code || '' })\n },\n {\n url: 'https://api.ipify.org?format=json',\n parse: (d) => ({ IP: d.ip, ISP: '', country: '' })\n }\n ];\n\n for (const { url, parse } of services) {\n const data = await fetchJSON(url);\n if (data) {\n const result = parse(data);\n if (result.IP) {\n cache.IP = result.IP;\n cache.ISP = result.ISP || cache.ISP;\n cache.country = result.country || cache.country;\n cache.lastUpdated = Date.now();\n return result;\n }\n }\n }\n\n // Return whatever we have in cache\n return { IP: cache.IP, ISP: cache.ISP, country: cache.country };\n}\n\n/**\n * Get public IP address\n * @param {boolean} [forceRefresh=false] - Bypass cache\n * @returns {Promise<string>}\n */\nexport async function getIP(forceRefresh = false) {\n const info = await getNetworkInfo(forceRefresh);\n return info.IP;\n}\n\n/**\n * Get ISP information\n * @param {boolean} [forceRefresh=false] - Bypass cache\n * @returns {Promise<string>}\n */\nexport async function getISP(forceRefresh = false) {\n const info = await getNetworkInfo(forceRefresh);\n return info.ISP;\n}\n\n/**\n * Get country code\n * @param {boolean} [forceRefresh=false] - Bypass cache\n * @returns {Promise<string>}\n */\nexport async function getCountry(forceRefresh = false) {\n const info = await getNetworkInfo(forceRefresh);\n return info.country;\n}\n\n/**\n * Get connection quality info (Network Information API)\n * @returns {ConnectionInfo|null}\n * \n * @typedef {Object} ConnectionInfo\n * @property {string} type - Connection type (wifi, cellular, etc.)\n * @property {string} effectiveType - Effective connection type (4g, 3g, etc.)\n * @property {number} downlink - Downlink speed in Mbps\n * @property {number} rtt - Round-trip time in ms\n * @property {boolean} saveData - Data saver mode enabled\n */\nexport function getConnection() {\n const conn = navigator.connection || navigator.mozConnection || navigator.webkitConnection;\n \n if (!conn) return null;\n\n return {\n type: conn.type || 'unknown',\n effectiveType: conn.effectiveType || 'unknown',\n downlink: conn.downlink || 0,\n rtt: conn.rtt || 0,\n saveData: conn.saveData || false\n };\n}\n\n/**\n * Get geolocation (requires user permission)\n * @param {Object} [options] - Geolocation options\n * @returns {Promise<GeolocationData|null>}\n * \n * @typedef {Object} GeolocationData\n * @property {number} latitude\n * @property {number} longitude\n * @property {number} accuracy - Accuracy in meters\n * @property {number} timestamp\n */\nexport function getLocation(options = { timeout: 10000, enableHighAccuracy: false }) {\n return new Promise((resolve) => {\n if (!navigator.geolocation) {\n resolve(null);\n return;\n }\n\n navigator.geolocation.getCurrentPosition(\n (pos) => resolve({\n latitude: pos.coords.latitude,\n longitude: pos.coords.longitude,\n accuracy: pos.coords.accuracy,\n timestamp: pos.timestamp\n }),\n () => resolve(null),\n options\n );\n });\n}\n\n/** Expose cache for reactive access */\nexport const networkCache = cache;\n","/**\n * @fileoverview Screen and Display Module\n * @description Detects screen resolution, pixel ratio, orientation\n */\n\n/**\n * Get screen and display information\n * @returns {ScreenInfo}\n * \n * @typedef {Object} ScreenInfo\n * @property {number} width - Screen width in pixels\n * @property {number} height - Screen height in pixels\n * @property {number} availWidth - Available width (excluding taskbars)\n * @property {number} availHeight - Available height\n * @property {number} colorDepth - Color depth in bits\n * @property {number} pixelRatio - Device pixel ratio\n * @property {string} orientation - Screen orientation\n */\nexport function getScreen() {\n const { screen } = window;\n \n return {\n width: screen.width,\n height: screen.height,\n availWidth: screen.availWidth,\n availHeight: screen.availHeight,\n colorDepth: screen.colorDepth,\n pixelRatio: window.devicePixelRatio || 1,\n orientation: screen.orientation?.type || \n (window.innerWidth > window.innerHeight ? 'landscape-primary' : 'portrait-primary')\n };\n}\n","/**\n * @fileoverview Battery Status Module\n * @description Detects battery level and charging status\n */\n\n/**\n * Get battery information\n * @returns {Promise<BatteryInfo>}\n * \n * @typedef {Object} BatteryInfo\n * @property {boolean} isCharging - Whether device is charging\n * @property {number} level - Battery percentage (0-100)\n * @property {number} chargingTime - Seconds until fully charged (Infinity if not charging)\n * @property {number} dischargingTime - Seconds until empty (Infinity if charging)\n */\nexport async function getBattery() {\n // Battery API may not be available in all browsers (e.g., Safari)\n if (!('getBattery' in navigator)) {\n return { isCharging: false, level: 0, chargingTime: Infinity, dischargingTime: Infinity };\n }\n\n try {\n const battery = await navigator.getBattery();\n return {\n isCharging: battery.charging,\n level: Math.round(battery.level * 100),\n chargingTime: battery.chargingTime,\n dischargingTime: battery.dischargingTime\n };\n } catch {\n return { isCharging: false, level: 0, chargingTime: Infinity, dischargingTime: Infinity };\n }\n}\n","/**\n * @fileoverview OS Detection Module\n * @description Detects operating system with Client Hints support\n */\n\n/**\n * Detect operating system from Client Hints or User Agent\n * @param {Object} hev - High Entropy Values from Client Hints\n * @param {Object|null} parsedOS - Parsed OS from user agent\n * @returns {OSInfo}\n * \n * @typedef {Object} OSInfo\n * @property {string} name - Short OS name (win, mac, linux, android, ios)\n * @property {number|string} version - OS version\n * @property {string} string - Human-readable OS string\n */\nexport function detectOS(hev, parsedOS) {\n // Prefer Client Hints if available (more accurate)\n if (hev.platform) {\n const platform = hev.platform;\n const platformVersion = hev.platformVersion || '';\n\n if (platform === 'Windows') {\n // Windows 11 reports platformVersion >= 13.0\n const majorVer = parseInt(platformVersion.split('.')[0], 10);\n const winVer = majorVer >= 13 ? 11 : 10;\n return { name: 'win', version: winVer, string: `Windows ${winVer}` };\n }\n\n if (platform === 'macOS') {\n return { name: 'mac', version: platformVersion, string: `macOS ${platformVersion}` };\n }\n\n if (platform === 'Android') {\n return { name: 'android', version: platformVersion, string: `Android ${platformVersion}` };\n }\n\n if (platform === 'iOS') {\n return { name: 'ios', version: platformVersion, string: `iOS ${platformVersion}` };\n }\n\n if (platform === 'Linux') {\n return { name: 'linux', version: '', string: 'Linux' };\n }\n\n if (platform === 'Chrome OS') {\n return { name: 'chromeos', version: platformVersion, string: `Chrome OS ${platformVersion}` };\n }\n\n // Unknown platform from Client Hints\n return { name: platform.toLowerCase(), version: platformVersion, string: `${platform} ${platformVersion}` };\n }\n\n // Fallback to parsed User Agent\n if (parsedOS) {\n const family = parsedOS.family || '';\n \n if (family.includes('Windows')) {\n const ver = parseFloat(parsedOS.version) || 0;\n return { name: 'win', version: ver, string: family };\n }\n if (family.includes('macOS') || family.includes('Mac OS X')) {\n return { name: 'mac', version: parsedOS.version, string: family };\n }\n if (family.includes('Android')) {\n return { name: 'android', version: parsedOS.version, string: family };\n }\n if (family.includes('iOS')) {\n return { name: 'ios', version: parsedOS.version, string: family };\n }\n if (family.includes('Linux')) {\n return { name: 'linux', version: '', string: 'Linux' };\n }\n\n return { name: family.toLowerCase(), version: parsedOS.version || '', string: family };\n }\n\n return { name: '', version: '', string: 'Unknown OS' };\n}\n","/**\n * @fileoverview Browser Detection Module\n * @description Detects browser name and version with Client Hints support\n */\n\n/**\n * Detect browser from Client Hints or User Agent\n * @param {Object} hev - High Entropy Values from Client Hints\n * @param {Object} parsedUA - Parsed user agent data\n * @returns {string} Browser name and version (e.g., \"Chrome 120\")\n */\nexport function detectBrowser(hev, parsedUA) {\n // Priority: Client Hints > User Agent\n const brands = hev.fullVersionList || hev.brands || [];\n \n if (brands.length) {\n // Known browsers to look for (in priority order)\n const knownBrowsers = ['Chrome', 'Firefox', 'Safari', 'Edge', 'Opera', 'Brave', 'Vivaldi', 'Arc'];\n \n for (const brand of brands) {\n // Skip placeholder brands\n if (/Not.?A.?Brand|Chromium/i.test(brand.brand)) continue;\n \n // Check if it's a known browser\n const match = knownBrowsers.find(b => brand.brand.includes(b));\n if (match) {\n const majorVersion = brand.version.split('.')[0];\n return `${brand.brand} ${majorVersion}`;\n }\n }\n\n // If no known browser found, use first non-placeholder brand\n for (const brand of brands) {\n if (!/Not.?A.?Brand|Chromium/i.test(brand.brand)) {\n return `${brand.brand} ${brand.version.split('.')[0]}`;\n }\n }\n }\n\n // Fallback to parsed User Agent\n if (parsedUA.name) {\n const majorVersion = parsedUA.version?.split('.')[0] || '';\n return `${parsedUA.name} ${majorVersion}`.trim();\n }\n\n return 'Unknown Browser';\n}\n","/**\n * @fileoverview aki-info-detect - Browser information detection library\n * @author lacvietanh\n * @version 2.0.0\n * @license MIT\n * @see https://github.com/lacvietanh/akiInfoDetect.js\n * \n * @description\n * Lightweight library for detecting device, browser, hardware, and network information.\n * Works in browser environments only. Supports ES Modules and UMD.\n * \n * @example\n * import akiInfoDetect from 'aki-info-detect';\n * const info = await akiInfoDetect();\n * console.log(info.browser, info.os.string, info.GPU);\n */\n\nimport { parseUserAgent, getHighEntropyValues } from './modules/ua-parser.js';\nimport { detectGPU, getHardwareInfo } from './modules/hardware.js';\nimport { getNetworkInfo, getIP, getISP, getCountry, getConnection, getLocation, networkCache } from './modules/network.js';\nimport { getScreen } from './modules/screen.js';\nimport { getBattery } from './modules/battery.js';\nimport { detectOS } from './modules/os.js';\nimport { detectBrowser } from './modules/browser.js';\n\n/**\n * @typedef {Object} AkiInfoResult\n * @property {string} browser - Browser name and version\n * @property {string} product - Device product name\n * @property {string} manufacturer - Device manufacturer\n * @property {boolean} isMobile - Is mobile device\n * @property {string} language - Preferred languages (space-separated 2-char codes)\n * @property {string} CPU - CPU/chip type\n * @property {number} CPUCore - Number of logical CPU cores\n * @property {string} arch - CPU architecture (x86_64, arm64)\n * @property {number} RAM - Device memory in GB\n * @property {string} GPU - GPU renderer string\n * @property {Object} battery - Battery status\n * @property {Object} os - Operating system info\n * @property {Object} network - Network info (reactive getters)\n * @property {Function} getIP - Fetch IP address\n * @property {Function} getISP - Fetch ISP info\n * @property {Function} getCountry - Fetch country code\n * @property {Function} getNetworkInfo - Fetch all network info\n * @property {Function} getLocation - Fetch geolocation\n * @property {Function} getConnection - Get connection quality\n * @property {Function} getScreen - Get screen info\n */\n\n/**\n * Main detection function\n * @param {boolean} [forceNetworkRefresh=false] - Force refresh network data\n * @returns {Promise<AkiInfoResult>} Complete system information\n */\nasync function akiInfoDetect(forceNetworkRefresh = false) {\n // Parallel async operations for performance\n const [hev, battery, gpu] = await Promise.all([\n getHighEntropyValues(),\n getBattery(),\n Promise.resolve(detectGPU())\n ]);\n\n // Parse user agent\n const parsedUA = parseUserAgent();\n \n // Get hardware info with GPU-based chip detection\n const hardware = getHardwareInfo(gpu);\n\n // Detect OS (prefer Client Hints)\n const os = detectOS(hev, parsedUA.os);\n\n // Detect browser (prefer Client Hints)\n const browser = detectBrowser(hev, parsedUA);\n\n // Detect mobile status\n const isMobile = hev.mobile ?? /Mobile|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);\n\n // Get language preferences (top 3, 2-char codes)\n const languages = (navigator.languages || [navigator.language])\n .slice(0, 3)\n .map(l => l.substring(0, 2).toLowerCase())\n .join(' ');\n\n // Trigger network fetch in background (non-blocking)\n if (forceNetworkRefresh || !networkCache.IP) {\n getNetworkInfo(forceNetworkRefresh).catch(() => {});\n }\n\n return {\n // Basic info\n browser,\n product: parsedUA.product,\n manufacturer: parsedUA.manufacturer,\n isMobile,\n language: languages,\n\n // Hardware\n CPU: hardware.CPU,\n CPUCore: hardware.CPUCore,\n arch: hev.architecture || hardware.arch,\n RAM: hardware.RAM,\n GPU: hardware.GPU,\n\n // Battery\n battery,\n\n // OS\n os,\n\n // Network (reactive getters from cache)\n network: {\n get IP() { return networkCache.IP; },\n get ISP() { return networkCache.ISP; },\n get country() { return networkCache.country; }\n },\n\n // Async methods\n getIP,\n getISP,\n getCountry,\n getNetworkInfo,\n getLocation,\n getConnection,\n getScreen\n };\n}\n\n// Named exports for tree-shaking\nexport {\n akiInfoDetect,\n getNetworkInfo,\n getIP,\n getISP,\n getCountry,\n getConnection,\n getLocation,\n getScreen,\n getBattery,\n detectGPU,\n parseUserAgent,\n getHighEntropyValues,\n // [CRITICAL] Pure, network-free — consumers that cannot call the default akiInfoDetect()\n // (e.g. because it auto-triggers getNetworkInfo()) MUST still be able to apply the\n // \"prefer Client Hints over UA string\" rule themselves. Exporting these lets them do so\n // without duplicating the OS/browser detection logic inline.\n detectOS,\n detectBrowser\n};\n\n// Default export\nexport default akiInfoDetect;\n"],"names":["parseUserAgent","ua","navigator","userAgent","os","name","version","product","manufacturer","browserPatterns","test","regex","exclude","browserName","match","osPatterns","parse","ntVer","winVer","family","architecture","ver","_a","replace","trim","mfrMatch","layout","async","getHighEntropyValues","userAgentData","detectGPU","canvas","document","createElement","gl","getContext","debugInfo","getExtension","getParameter","UNMASKED_RENDERER_WEBGL","cache","IP","ISP","country","lastUpdated","TTL","fetchJSON","url","timeout","controller","AbortController","timeoutId","setTimeout","abort","res","fetch","signal","clearTimeout","ok","json","getNetworkInfo","forceRefresh","Date","now","services","d","ip","org","isp","country_code","data","result","getIP","getISP","getCountry","getConnection","conn","connection","mozConnection","webkitConnection","type","effectiveType","downlink","rtt","saveData","getLocation","options","enableHighAccuracy","Promise","resolve","geolocation","getCurrentPosition","pos","latitude","coords","longitude","accuracy","timestamp","networkCache","getScreen","screen","window","width","height","availWidth","availHeight","colorDepth","pixelRatio","devicePixelRatio","orientation","innerWidth","innerHeight","getBattery","isCharging","level","chargingTime","Infinity","dischargingTime","battery","charging","Math","round","detectOS","hev","parsedOS","platform","platformVersion","parseInt","split","string","toLowerCase","includes","parseFloat","detectBrowser","parsedUA","brands","fullVersionList","length","knownBrowsers","brand","find","b","majorVersion","akiInfoDetect","forceNetworkRefresh","gpu","all","hardware","chipInfo","gpuLower","appleSiliconMatch","chipNum","variant","chip","nvidiaMatch","amdMatch","intelMatch","parseChipInfo","RAM","deviceMemory","CPUCore","hardwareConcurrency","GPU","CPU","arch","getHardwareInfo","browser","isMobile","mobile","languages","language","slice","map","l","substring","join","catch","network"],"mappings":";;;;;;GAkBO,SAASA,EAAeC,EAAKC,UAAUC,WAC5C,IAAIC,EAAK,KACLC,EAAO,GACPC,EAAU,GACVC,EAAU,GACVC,EAAe,GAGnB,MAAMC,EAAkB,CACtB,CAAEC,KAAM,cAAeL,KAAM,OAAQM,MAAO,uBAC5C,CAAED,KAAM,gBAAiBL,KAAM,QAASM,MAAO,2BAC/C,CAAED,KAAM,YAAaL,KAAM,UAAWM,MAAO,oBAAqBC,QAAS,aAC3E,CAAEF,KAAM,WAAYL,KAAM,SAAUM,MAAO,mBAAoBC,QAAS,sBACxE,CAAEF,KAAM,WAAYL,KAAM,SAAUM,MAAO,oBAAqBC,QAAS,6BACzE,CAAEF,KAAM,eAAgBL,KAAM,KAAMM,MAAO,0BAG7C,IAAA,MAAWD,KAAEA,EAAML,KAAMQ,QAAaF,EAAAC,QAAOA,KAAaH,EACxD,GAAIC,EAAKA,KAAKT,MAASW,IAAYA,EAAQF,KAAKT,IAAM,CACpDI,EAAOQ,EACP,MAAMC,EAAQb,EAAGa,MAAMH,GACvBL,SAAUQ,WAAQ,KAAM,GACxB,KACF,CAUF,MAAMC,EAAa,CACjB,CACEL,KAAM,UACNM,MAAO,KACL,MACMF,EAAQb,EAAGa,MAAM,uBACjBG,SAAQH,WAAQ,KAAM,GACtBI,EAHS,CAAE,OAAQ,KAAM,IAAO,MAAO,IAAO,IAAK,IAAO,IAAK,MAAO,QAAS,IAAO,MAGtED,IAAUA,EAChC,MAAO,CACLE,OAAQ,WAAWD,IACnBZ,QAASY,EACTE,aAAc,kBAAkBV,KAAKT,GAAM,GAAK,MAItD,CACES,KAAM,mBACNM,MAAO,WACL,MAAMF,EAAQb,EAAGa,MAAM,eACjBO,GAAM,OAAAC,EAAA,MAAAR,OAAA,EAAAA,EAAQ,SAAR,EAAAQ,EAAYC,QAAQ,KAAM,OAAQ,GAC9C,MAAO,CAAEJ,OAAQ,OAAOE,IAAOf,QAASe,EAAKD,aAAc,MAG/D,CACEV,KAAM,WACNM,MAAO,WACL,MAAMF,EAAQb,EAAGa,MAAM,sBACjBO,GAAM,OAAAC,EAAA,MAAAR,OAAA,EAAAA,EAAQ,SAAR,EAAAQ,EAAYC,QAAQ,KAAM,OAAQ,GAC9C,MAAO,CAAEJ,OAAQ,SAASE,IAAOf,QAASe,EAAKD,aAAc,MAGjE,CACEV,KAAM,UACNM,MAAO,KACL,MAAMF,EAAQb,EAAGa,MAAM,oBACjBO,SAAMP,WAAQ,KAAM,GAC1B,MAAO,CAAEK,OAAQ,WAAWE,IAAOf,QAASe,EAAKD,aAAc,MAGnE,CACEV,KAAM,QACNM,MAAO,KAAA,CACLG,OAAQ,QACRb,QAAS,GACTc,aAAc,eAAeV,KAAKT,GAAM,GAAK,OAKnD,IAAA,MAAWS,KAAEA,EAAAM,MAAMA,KAAWD,EAC5B,GAAIL,EAAKA,KAAKT,GAAK,CACjBG,EAAKY,IACL,KACF,CAIF,GAAI,SAASN,KAAKT,GAChBM,EAAU,SACVC,EAAe,aACjB,GAAW,OAAOE,KAAKT,GACrBM,EAAU,OACVC,EAAe,aACjB,GAAW,UAAUE,KAAKT,GAAK,CAC7B,MAAMa,EAAQb,EAAGa,MAAM,4BACvB,GAAIA,EAAO,CACTP,EAAUO,EAAM,GAAGU,OAEnB,MAAMC,EAAWlB,EAAQO,MAAM,kFAC/BN,SAAeiB,WAAW,KAAM,EAClC,CACF,CAOA,MAAO,CAAEpB,OAAMC,UAASC,UAASC,eAAcJ,KAAIsB,OAJpC,cAAchB,KAAKT,GAAM,SACzB,UAAUS,KAAKT,GAAM,QACrB,UAAUS,KAAKT,GAAM,UAAY,GAGlD,CAMO0B,eAAeC,UACpB,KAAK,OAAAN,EAAApB,UAAU2B,oBAAV,EAAAP,EAAyBM,sBAC5B,MAAO,CAAA,EAGT,IACE,aAAa1B,UAAU2B,cAAcD,qBAAqB,CACxD,WACA,kBACA,eACA,QACA,SACA,UACA,SACA,mBAEJ,CAAA,MACE,MAAO,CAAA,CACT,CACF,CClJO,SAASE,IACd,IACE,MAAMC,EAASC,SAASC,cAAc,UAChCC,EAAKH,EAAOI,WAAW,UAAYJ,EAAOI,WAAW,sBAE3D,IAAKD,EAAI,MAAO,mBAEhB,MAAME,EAAYF,EAAGG,aAAa,6BAClC,OAAKD,EAEEF,EAAGI,aAAaF,EAAUG,0BAA4B,cAFtC,qBAGzB,CAAA,MACE,MAAO,sBACT,CACF,CCjBA,MAAMC,EAAQ,CACZC,GAAI,GACJC,IAAK,GACLC,QAAS,GACTC,YAAa,EACbC,IAAK,MASPlB,eAAemB,EAAUC,EAAKC,EAAU,MACtC,MAAMC,EAAa,IAAIC,gBACjBC,EAAYC,WAAW,IAAMH,EAAWI,QAASL,GAEvD,IACE,MAAMM,QAAYC,MAAMR,EAAK,CAAES,OAAQP,EAAWO,SAElD,OADAC,aAAaN,GACNG,EAAII,SAAWJ,EAAIK,OAAS,IACrC,CAAA,MAEE,OADAF,aAAaN,GACN,IACT,CACF,CAYOxB,eAAeiC,EAAeC,GAAe,GAElD,IAAKA,GAAgBrB,EAAMC,IAAMqB,KAAKC,MAAQvB,EAAMI,YAAcJ,EAAMK,IACtE,MAAO,CAAEJ,GAAID,EAAMC,GAAIC,IAAKF,EAAME,IAAKC,QAASH,EAAMG,SAIxD,MAAMqB,EAAW,CACf,CACEjB,IAAK,yBACL/B,MAAQiD,IAAA,CAASxB,GAAIwB,EAAEC,GAAIxB,IAAKuB,EAAEE,KAAO,GAAIxB,QAASsB,EAAEtB,SAAW,MAErE,CACEI,IAAK,4BACL/B,MAAQiD,IAAA,CAASxB,GAAIwB,EAAEC,GAAIxB,IAAKuB,EAAEG,KAAO,GAAIzB,QAASsB,EAAEI,cAAgB,MAE1E,CACEtB,IAAK,oCACL/B,MAAQiD,IAAA,CAASxB,GAAIwB,EAAEC,GAAIxB,IAAK,GAAIC,QAAS,OAIjD,IAAA,MAAWI,IAAEA,EAAA/B,MAAKA,KAAWgD,EAAU,CACrC,MAAMM,QAAaxB,EAAUC,GAC7B,GAAIuB,EAAM,CACR,MAAMC,EAASvD,EAAMsD,GACrB,GAAIC,EAAO9B,GAKT,OAJAD,EAAMC,GAAK8B,EAAO9B,GAClBD,EAAME,IAAM6B,EAAO7B,KAAOF,EAAME,IAChCF,EAAMG,QAAU4B,EAAO5B,SAAWH,EAAMG,QACxCH,EAAMI,YAAckB,KAAKC,MAClBQ,CAEX,CACF,CAGA,MAAO,CAAE9B,GAAID,EAAMC,GAAIC,IAAKF,EAAME,IAAKC,QAASH,EAAMG,QACxD,CAOOhB,eAAe6C,EAAMX,GAAe,GAEzC,aADmBD,EAAeC,IACtBpB,EACd,CAOOd,eAAe8C,EAAOZ,GAAe,GAE1C,aADmBD,EAAeC,IACtBnB,GACd,CAOOf,eAAe+C,EAAWb,GAAe,GAE9C,aADmBD,EAAeC,IACtBlB,OACd,CAaO,SAASgC,IACd,MAAMC,EAAO1E,UAAU2E,YAAc3E,UAAU4E,eAAiB5E,UAAU6E,iBAE1E,OAAKH,EAEE,CACLI,KAAMJ,EAAKI,MAAQ,UACnBC,cAAeL,EAAKK,eAAiB,UACrCC,SAAUN,EAAKM,UAAY,EAC3BC,IAAKP,EAAKO,KAAO,EACjBC,SAAUR,EAAKQ,WAAY,GAPX,IASpB,CAaO,SAASC,EAAYC,EAAU,CAAEtC,QAAS,IAAOuC,oBAAoB,IAC1E,OAAO,IAAIC,QAASC,IACbvF,UAAUwF,YAKfxF,UAAUwF,YAAYC,mBACnBC,GAAQH,EAAQ,CACfI,SAAUD,EAAIE,OAAOD,SACrBE,UAAWH,EAAIE,OAAOC,UACtBC,SAAUJ,EAAIE,OAAOE,SACrBC,UAAWL,EAAIK,YAEjB,IAAMR,EAAQ,MACdH,GAZAG,EAAQ,OAed,CAGO,MAAMS,EAAe1D,ECzJrB,SAAS2D,UACd,MAAMC,OAAEA,GAAWC,OAEnB,MAAO,CACLC,MAAOF,EAAOE,MACdC,OAAQH,EAAOG,OACfC,WAAYJ,EAAOI,WACnBC,YAAaL,EAAOK,YACpBC,WAAYN,EAAOM,WACnBC,WAAYN,OAAOO,kBAAoB,EACvCC,aAAa,OAAAvF,IAAOuF,kBAAP,EAAAvF,EAAoB0D,QACnBqB,OAAOS,WAAaT,OAAOU,YAAc,oBAAsB,oBAEjF,CChBOpF,eAAeqF,IAEpB,KAAM,eAAgB9G,WACpB,MAAO,CAAE+G,YAAY,EAAOC,MAAO,EAAGC,aAAcC,IAAUC,gBAAiBD,KAGjF,IACE,MAAME,QAAgBpH,UAAU8G,aAChC,MAAO,CACLC,WAAYK,EAAQC,SACpBL,MAAOM,KAAKC,MAAsB,IAAhBH,EAAQJ,OAC1BC,aAAcG,EAAQH,aACtBE,gBAAiBC,EAAQD,gBAE7B,CAAA,MACE,MAAO,CAAEJ,YAAY,EAAOC,MAAO,EAAGC,aAAcC,IAAUC,gBAAiBD,IACjF,CACF,CChBO,SAASM,EAASC,EAAKC,GAE5B,GAAID,EAAIE,SAAU,CAChB,MAAMA,EAAWF,EAAIE,SACfC,EAAkBH,EAAIG,iBAAmB,GAE/C,GAAiB,YAAbD,EAAwB,CAE1B,MACM3G,EADW6G,SAASD,EAAgBE,MAAM,KAAK,GAAI,KAC9B,GAAK,GAAK,GACrC,MAAO,CAAE3H,KAAM,MAAOC,QAASY,EAAQ+G,OAAQ,WAAW/G,IAC5D,CAEA,MAAiB,UAAb2G,EACK,CAAExH,KAAM,MAAOC,QAASwH,EAAiBG,OAAQ,SAASH,KAGlD,YAAbD,EACK,CAAExH,KAAM,UAAWC,QAASwH,EAAiBG,OAAQ,WAAWH,KAGxD,QAAbD,EACK,CAAExH,KAAM,MAAOC,QAASwH,EAAiBG,OAAQ,OAAOH,KAGhD,UAAbD,EACK,CAAExH,KAAM,QAASC,QAAS,GAAI2H,OAAQ,SAG9B,cAAbJ,EACK,CAAExH,KAAM,WAAYC,QAASwH,EAAiBG,OAAQ,aAAaH,KAIrE,CAAEzH,KAAMwH,EAASK,cAAe5H,QAASwH,EAAiBG,OAAQ,GAAGJ,KAAYC,IAC1F,CAGA,GAAIF,EAAU,CACZ,MAAMzG,EAASyG,EAASzG,QAAU,GAElC,GAAIA,EAAOgH,SAAS,WAAY,CAE9B,MAAO,CAAE9H,KAAM,MAAOC,QADV8H,WAAWR,EAAStH,UAAY,EACR2H,OAAQ9G,EAC9C,CACA,OAAIA,EAAOgH,SAAS,UAAYhH,EAAOgH,SAAS,YACvC,CAAE9H,KAAM,MAAOC,QAASsH,EAAStH,QAAS2H,OAAQ9G,GAEvDA,EAAOgH,SAAS,WACX,CAAE9H,KAAM,UAAWC,QAASsH,EAAStH,QAAS2H,OAAQ9G,GAE3DA,EAAOgH,SAAS,OACX,CAAE9H,KAAM,MAAOC,QAASsH,EAAStH,QAAS2H,OAAQ9G,GAEvDA,EAAOgH,SAAS,SACX,CAAE9H,KAAM,QAASC,QAAS,GAAI2H,OAAQ,SAGxC,CAAE5H,KAAMc,EAAO+G,cAAe5H,QAASsH,EAAStH,SAAW,GAAI2H,OAAQ9G,EAChF,CAEA,MAAO,CAAEd,KAAM,GAAIC,QAAS,GAAI2H,OAAQ,aAC1C,CCnEO,SAASI,EAAcV,EAAKW,SAEjC,MAAMC,EAASZ,EAAIa,iBAAmBb,EAAIY,QAAU,GAEpD,GAAIA,EAAOE,OAAQ,CAEjB,MAAMC,EAAgB,CAAC,SAAU,UAAW,SAAU,OAAQ,QAAS,QAAS,UAAW,OAE3F,IAAA,MAAWC,KAASJ,EAAQ,CAE1B,GAAI,0BAA0B7H,KAAKiI,EAAMA,OAAQ,SAIjD,GADcD,EAAcE,KAAKC,GAAKF,EAAMA,MAAMR,SAASU,IAChD,CACT,MAAMC,EAAeH,EAAMrI,QAAQ0H,MAAM,KAAK,GAC9C,MAAO,GAAGW,EAAMA,SAASG,GAC3B,CACF,CAGA,IAAA,MAAWH,KAASJ,EAClB,IAAK,0BAA0B7H,KAAKiI,EAAMA,OACxC,MAAO,GAAGA,EAAMA,SAASA,EAAMrI,QAAQ0H,MAAM,KAAK,IAGxD,CAGA,GAAIM,EAASjI,KAAM,CACjB,MAAMyI,GAAe,OAAAxH,EAAAgH,EAAShI,cAAT,EAAAgB,EAAkB0G,MAAM,KAAK,KAAM,GACxD,MAAO,GAAGM,EAASjI,QAAQyI,IAAetH,MAC5C,CAEA,MAAO,iBACT;;;;;;;;;;;;;;;;KCQAG,eAAeoH,EAAcC,GAAsB,GAEjD,MAAOrB,EAAKL,EAAS2B,SAAazD,QAAQ0D,IAAI,CAC5CtH,IACAoF,IACAxB,QAAQC,QAAQ3D,OAIZwG,EAAWtI,IAGXmJ,ENWD,SAAyBF,SAC9B,MAAMG,EA/CD,SAAuBH,GAC5B,MAAMI,EAAWJ,EAAIf,cAGfoB,EAAoBL,EAAInI,MAAM,0CACpC,GAAIwI,EAAmB,CACrB,MAAMC,EAAUD,EAAkB,GAC5BE,EAAUF,EAAkB,IAAM,GACxC,MAAO,CACLtE,KAAM,gBACNyE,KAAM,IAAIF,IAAUC,EAAU,IAAMA,EAAU,KAC9CpI,aAAc,QAElB,CAGA,GAAIiI,EAASlB,SAAS,SACpB,MAAO,CAAEnD,KAAM,gBAAiByE,KAAM,YAAarI,aAAc,SAInE,MAAMsI,EAAcT,EAAInI,MAAM,+BAAiCmI,EAAInI,MAAM,uCACzE,GAAI4I,EACF,MAAO,CAAE1E,KAAM,SAAUyE,KAAMC,EAAY,GAAGlI,OAAQJ,aAAc,UAItE,MAAMuI,EAAWV,EAAInI,MAAM,2BAC3B,GAAI6I,EACF,MAAO,CAAE3E,KAAM,MAAOyE,KAAME,EAAS,GAAGnI,OAAQJ,aAAc,UAIhE,MAAMwI,EAAaX,EAAInI,MAAM,kDAC7B,OAAI8I,GAAcP,EAASlB,SAAS,SAC3B,CAAEnD,KAAM,QAASyE,YAAMG,WAAa,KAAM,iBAAkBxI,aAAc,UAG5E,CAAE4D,KAAM,UAAWyE,KAAMR,EAAK7H,aAAc,GACrD,CAQmByI,CAAcZ,GAE/B,MAAO,CACLa,IAAK5J,UAAU6J,cAAgB,EAC/BC,QAAS9J,UAAU+J,qBAAuB,EAC1CC,IAAKjB,EACLkB,IAAKf,EAASpE,KACdoF,KAAMhB,EAAShI,eAAuD,WAAtC,OAAAE,EAAApB,UAAU2B,oBAAV,EAAAP,EAAyBuG,UAAuB,QAAU,UAE9F,CMrBmBwC,CAAgBpB,GAG3B7I,EAAKsH,EAASC,EAAKW,EAASlI,IAG5BkK,EAAUjC,EAAcV,EAAKW,GAG7BiC,EAAW5C,EAAI6C,QAAU,mCAAmC9J,KAAKR,UAAUC,WAG3EsK,GAAavK,UAAUuK,WAAa,CAACvK,UAAUwK,WAClDC,MAAM,EAAG,GACTC,IAAIC,GAAKA,EAAEC,UAAU,EAAG,GAAG5C,eAC3B6C,KAAK,KAOR,OAJI/B,GAAwB9C,EAAazD,IACvCmB,EAAeoF,GAAqBgC,MAAM,QAGrC,CAELV,UACA/J,QAAS+H,EAAS/H,QAClBC,aAAc8H,EAAS9H,aACvB+J,WACAG,SAAUD,EAGVN,IAAKhB,EAASgB,IACdH,QAASb,EAASa,QAClBI,KAAMzC,EAAIvG,cAAgB+H,EAASiB,KACnCN,IAAKX,EAASW,IACdI,IAAKf,EAASe,IAGd5C,UAGAlH,KAGA6K,QAAS,CACP,MAAIxI,GAAO,OAAOyD,EAAazD,EAAI,EACnC,OAAIC,GAAQ,OAAOwD,EAAaxD,GAAK,EACrC,WAAIC,GAAY,OAAOuD,EAAavD,OAAS,GAI/C6B,QACAC,SACAC,aACAd,iBACAyB,cACAV,gBACAwB,YAEJ"}
@@ -118,3 +118,13 @@ export declare function parseUserAgent(ua?: string): {
118
118
  layout: string;
119
119
  };
120
120
  export declare function getHighEntropyValues(): Promise<Record<string, unknown>>;
121
+ /**
122
+ * Detect OS, preferring Client Hints (hev) over the parsed User-Agent string when available.
123
+ * [CRITICAL] UA strings are frozen by browser vendors (Chromium UA Reduction pins Windows at
124
+ * "NT 10.0" and macOS at "10_15_7" forever) — always pass `hev` when you have it.
125
+ */
126
+ export declare function detectOS(hev: Record<string, unknown>, parsedOS: { family: string; version: string | number; architecture?: number } | null): OSInfo;
127
+ /**
128
+ * Detect browser name/version, preferring Client Hints brands over the parsed User-Agent string.
129
+ */
130
+ export declare function detectBrowser(hev: Record<string, unknown>, parsedUA: { name: string; version: string }): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aki-info-detect",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "Lightweight JavaScript library for detecting device, browser, hardware, and network information with UMD/ESM support",
5
5
  "author": "lacvietanh",
6
6
  "license": "MIT",