aki-info-detect 2.0.1 โ 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 +223 -0
- package/dist/aki-info-detect.js +13 -11
- package/dist/aki-info-detect.js.map +1 -1
- package/dist/aki-info-detect.umd.cjs +4 -5
- package/dist/aki-info-detect.umd.cjs.map +1 -1
- package/dist/index.html +305 -2
- package/dist/types/index.d.ts +10 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,198 @@ A lightweight JavaScript library for detecting device, browser, hardware, and ne
|
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
[](https://bundlephobia.com/package/aki-info-detect)
|
|
8
8
|
|
|
9
|
+
## Introduction
|
|
10
|
+
|
|
11
|
+
**aki-info-detect** is a modern, lightweight JavaScript library designed to provide comprehensive device, browser, and system information detection for web applications. Built with performance and developer experience in mind, it leverages modern browser APIs including the Client Hints API for accurate hardware detection while maintaining a small footprint (~3.8 kB gzipped).
|
|
12
|
+
|
|
13
|
+
Whether you need to customize user experiences based on device capabilities, gather analytics data, or implement platform-specific features, aki-info-detect provides a unified, easy-to-use API that works seamlessly across all modern browsers.
|
|
14
|
+
|
|
15
|
+
### Why aki-info-detect?
|
|
16
|
+
|
|
17
|
+
- **๐ฏ Comprehensive Detection**: Get detailed information about browser, OS, CPU, GPU, RAM, network, battery, and more from a single library
|
|
18
|
+
- **โก Performance-First**: Minimal bundle size with tree-shakeable exportsโimport only what you need
|
|
19
|
+
- **๐ฎ Future-Proof**: Built on modern Web APIs with graceful fallbacks for older browsers
|
|
20
|
+
- **๐ช Apple Silicon Ready**: Advanced detection for Apple M-series chips (M1, M2, M3, M4, and beyond)
|
|
21
|
+
- **๐ Privacy-Conscious**: Implements caching for network requests to minimize external API calls
|
|
22
|
+
- **๐ฆ Zero Dependencies**: No external runtime dependenciesโjust pure, optimized JavaScript
|
|
23
|
+
- **๐จ Developer-Friendly**: TypeScript declarations included, intuitive API design, comprehensive documentation
|
|
24
|
+
|
|
25
|
+
## Use Cases
|
|
26
|
+
|
|
27
|
+
### 1. **Adaptive User Interfaces**
|
|
28
|
+
Dynamically adjust your UI based on device capabilities and screen properties:
|
|
29
|
+
```javascript
|
|
30
|
+
const info = await akiInfoDetect();
|
|
31
|
+
if (info.isMobile) {
|
|
32
|
+
// Load mobile-optimized components
|
|
33
|
+
loadMobileUI();
|
|
34
|
+
} else if (info.RAM < 4) {
|
|
35
|
+
// Use lightweight version for low-memory devices
|
|
36
|
+
enableLowMemoryMode();
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. **Analytics & User Insights**
|
|
41
|
+
Gather detailed technical data to understand your user base:
|
|
42
|
+
```javascript
|
|
43
|
+
const info = await akiInfoDetect();
|
|
44
|
+
analytics.track('page_view', {
|
|
45
|
+
browser: info.browser,
|
|
46
|
+
os: info.os.string,
|
|
47
|
+
device: info.isMobile ? 'mobile' : 'desktop',
|
|
48
|
+
gpu: info.GPU,
|
|
49
|
+
country: await info.getCountry()
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 3. **Feature Detection & Progressive Enhancement**
|
|
54
|
+
Enable or disable features based on browser capabilities:
|
|
55
|
+
```javascript
|
|
56
|
+
const info = await akiInfoDetect();
|
|
57
|
+
if (info.browser.includes('Chrome') && parseInt(info.browser.split(' ')[1]) >= 90) {
|
|
58
|
+
// Enable features requiring Chrome 90+
|
|
59
|
+
enableAdvancedFeatures();
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 4. **Platform-Specific Optimization**
|
|
64
|
+
Optimize content delivery based on hardware capabilities:
|
|
65
|
+
```javascript
|
|
66
|
+
const info = await akiInfoDetect();
|
|
67
|
+
if (info.CPU === 'Apple Silicon') {
|
|
68
|
+
// Load optimized assets for Apple Silicon
|
|
69
|
+
loadWebPImages();
|
|
70
|
+
} else if (info.GPU.includes('NVIDIA')) {
|
|
71
|
+
// Enable GPU-accelerated features
|
|
72
|
+
enableHardwareAcceleration();
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 5. **Targeted User Support**
|
|
77
|
+
Provide context-aware help and troubleshooting:
|
|
78
|
+
```javascript
|
|
79
|
+
const info = await akiInfoDetect();
|
|
80
|
+
if (info.browser.includes('Safari') && info.os.name === 'ios') {
|
|
81
|
+
showMessage('For best experience on iOS Safari, please enable...');
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 6. **Network-Aware Loading**
|
|
86
|
+
Adapt content loading strategies based on connection quality:
|
|
87
|
+
```javascript
|
|
88
|
+
const info = await akiInfoDetect();
|
|
89
|
+
const conn = info.getConnection();
|
|
90
|
+
if (conn.type === '4g' && conn.downlink > 5) {
|
|
91
|
+
// High-speed connection: load high-quality assets
|
|
92
|
+
loadHDContent();
|
|
93
|
+
} else {
|
|
94
|
+
// Optimize for slower connections
|
|
95
|
+
loadCompressedContent();
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Comparison with Similar Libraries
|
|
100
|
+
|
|
101
|
+
| Feature | aki-info-detect | Platform.js | UA-Parser.js | Detect.js |
|
|
102
|
+
|---------|----------------|-------------|--------------|-----------|
|
|
103
|
+
| **Bundle Size (gzipped)** | ~3.8 kB | ~2.5 kB | ~9 kB | Varies |
|
|
104
|
+
| **Client Hints API** | โ
Full Support | โ No | โ No | โ No |
|
|
105
|
+
| **GPU Detection** | โ
Yes | โ No | โ No | โ No |
|
|
106
|
+
| **Apple Silicon Detection** | โ
Yes (M1-MX) | โ No | โ No | โ No |
|
|
107
|
+
| **Network Info (IP/ISP)** | โ
Yes (cached) | โ No | โ No | โ No |
|
|
108
|
+
| **Battery Status** | โ
Yes | โ No | โ No | โ No |
|
|
109
|
+
| **Geolocation** | โ
Yes | โ No | โ No | โ No |
|
|
110
|
+
| **Tree-shakeable** | โ
Yes | โ ๏ธ Partial | โ ๏ธ Partial | โ ๏ธ Partial |
|
|
111
|
+
| **TypeScript** | โ
Full declarations | โ ๏ธ Community types | โ
Yes | โ No |
|
|
112
|
+
| **Active Maintenance** | โ
Yes | โ ๏ธ Sporadic | โ
Yes | โ Archived |
|
|
113
|
+
| **Modern Build System** | โ
Vite | โ Legacy | โ
Webpack | โ Legacy |
|
|
114
|
+
|
|
115
|
+
### Key Differentiators
|
|
116
|
+
|
|
117
|
+
**vs. Platform.js**
|
|
118
|
+
- aki-info-detect is a modern evolution with Client Hints API support for more accurate detection
|
|
119
|
+
- Adds hardware detection (GPU, RAM) and network capabilities not available in Platform.js
|
|
120
|
+
- Built with modern tooling (Vite) for better tree-shaking and optimization
|
|
121
|
+
|
|
122
|
+
**vs. UA-Parser.js**
|
|
123
|
+
- Lighter bundle size while providing more comprehensive information
|
|
124
|
+
- Includes hardware and network detection beyond just user agent parsing
|
|
125
|
+
- Native async/await support with modern API design
|
|
126
|
+
|
|
127
|
+
**vs. Detect.js**
|
|
128
|
+
- Active maintenance (Detect.js is archived)
|
|
129
|
+
- Smaller, more focused API surface with better performance
|
|
130
|
+
- Modern Web API integration for enhanced detection capabilities
|
|
131
|
+
|
|
132
|
+
## Frequently Asked Questions (FAQ)
|
|
133
|
+
|
|
134
|
+
### General Questions
|
|
135
|
+
|
|
136
|
+
**Q: What browsers does aki-info-detect support?**
|
|
137
|
+
A: All modern browsers with ES2020+ support: Chrome/Edge 89+, Firefox 88+, Safari 14+, Opera 76+. Client Hints features work best in Chromium-based browsers.
|
|
138
|
+
|
|
139
|
+
**Q: Does it work in Node.js or server-side environments?**
|
|
140
|
+
A: No, aki-info-detect is specifically designed for browser environments. It relies on browser APIs like `navigator`, `screen`, and Web APIs that are not available in Node.js.
|
|
141
|
+
|
|
142
|
+
**Q: How accurate is the detection?**
|
|
143
|
+
A: Very accurate for modern browsers that support Client Hints API. For browsers without Client Hints, it falls back to user agent parsing which is still reliable but less detailed. GPU and CPU detection is most accurate in Chromium-based browsers.
|
|
144
|
+
|
|
145
|
+
**Q: Is user agent string spoofing a concern?**
|
|
146
|
+
A: While user agents can be spoofed, Client Hints API provides more reliable detection. For critical functionality, always combine with feature detection rather than relying solely on browser/platform detection.
|
|
147
|
+
|
|
148
|
+
### Privacy & Security
|
|
149
|
+
|
|
150
|
+
**Q: Does aki-info-detect collect or transmit user data?**
|
|
151
|
+
A: No. The library only detects information locally in the browser. The optional network info feature (`getNetworkInfo()`) makes a request to a public IP API, but this is only triggered when you explicitly call that method.
|
|
152
|
+
|
|
153
|
+
**Q: What about user privacy?**
|
|
154
|
+
A: aki-info-detect is privacy-conscious. Network requests are cached for 1 hour to minimize external API calls, and all detection happens client-side. No data is sent to our servers.
|
|
155
|
+
|
|
156
|
+
**Q: Is it GDPR compliant?**
|
|
157
|
+
A: Detection of browser/device information is generally considered functional data necessary for website operation. However, always review your local regulations and privacy policies, especially when using geolocation or storing detected data.
|
|
158
|
+
|
|
159
|
+
### Technical Questions
|
|
160
|
+
|
|
161
|
+
**Q: Why do I need to configure server headers?**
|
|
162
|
+
A: The Client Hints API requires the server to explicitly request detailed information via `Accept-CH` headers. Without these headers, browsers will only provide basic user agent data. This is a browser security feature designed to enhance user privacy.
|
|
163
|
+
|
|
164
|
+
**Q: Can I use it with React, Vue, or other frameworks?**
|
|
165
|
+
A: Absolutely! aki-info-detect is framework-agnostic. See the React Hook and Vue Composable examples in the documentation for integration patterns.
|
|
166
|
+
|
|
167
|
+
**Q: How do I reduce bundle size further?**
|
|
168
|
+
A: Use tree-shakeable imports to include only the functions you need:
|
|
169
|
+
```javascript
|
|
170
|
+
import { detectBrowser, detectOS } from 'aki-info-detect';
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Q: Does it support TypeScript?**
|
|
174
|
+
A: Yes, full TypeScript declarations are included in the package.
|
|
175
|
+
|
|
176
|
+
**Q: How often is the network information cached?**
|
|
177
|
+
A: Network info (IP, ISP, country) is cached for 1 hour by default. You can force a refresh by passing `true` to `getNetworkInfo(true)`.
|
|
178
|
+
|
|
179
|
+
### Troubleshooting
|
|
180
|
+
|
|
181
|
+
**Q: Why am I getting generic hardware info?**
|
|
182
|
+
A: Make sure your server is configured to send the required `Accept-CH` headers. Without these headers, detailed Client Hints data won't be available.
|
|
183
|
+
|
|
184
|
+
**Q: Why is GPU detection returning "Unknown"?**
|
|
185
|
+
A: GPU detection works best in Chromium browsers with Client Hints. Safari and Firefox have limited support. Also, some browsers may restrict this information for privacy reasons.
|
|
186
|
+
|
|
187
|
+
**Q: The library says "Unknown" for many fields. What's wrong?**
|
|
188
|
+
A: Check browser console for errors, ensure you're using a supported browser version, and verify that your server is sending proper Client Hints headers (see Server Configuration section).
|
|
189
|
+
|
|
190
|
+
### Usage & Development
|
|
191
|
+
|
|
192
|
+
**Q: Can I contribute to the project?**
|
|
193
|
+
A: Yes! Contributions are welcome. Please check the GitHub repository for contribution guidelines.
|
|
194
|
+
|
|
195
|
+
**Q: How do I report a bug or request a feature?**
|
|
196
|
+
A: Open an issue on the [GitHub repository](https://github.com/lacvietanh/akiInfoDetect.js/issues) with details about the bug or feature request.
|
|
197
|
+
|
|
198
|
+
**Q: Is there a demo I can try?**
|
|
199
|
+
A: Yes! Visit the [live demo](https://akiinfodetect-js.pages.dev/) to see the library in action.
|
|
200
|
+
|
|
9
201
|
## Features
|
|
10
202
|
|
|
11
203
|
- ๐ **Browser Detection** โ Name, version, rendering engine
|
|
@@ -19,6 +211,37 @@ A lightweight JavaScript library for detecting device, browser, hardware, and ne
|
|
|
19
211
|
- ๐ฆ **Tree-shakeable** โ Import only what you need
|
|
20
212
|
- ๐ฏ **Browser-only** โ Optimized for browser environments
|
|
21
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
|
+
|
|
22
245
|
## Installation
|
|
23
246
|
|
|
24
247
|
### npm / yarn / pnpm
|
package/dist/aki-info-detect.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* aki-info-detect v2.0.
|
|
3
|
-
* (c)
|
|
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.
|
|
4
|
-
* (c)
|
|
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:/
|
|
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":"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"}
|
package/dist/index.html
CHANGED
|
@@ -543,13 +543,316 @@
|
|
|
543
543
|
|
|
544
544
|
<!-- Tabs -->
|
|
545
545
|
<div class="tabs">
|
|
546
|
-
<button class="tab active" data-tab="
|
|
546
|
+
<button class="tab active" data-tab="intro">Intro</button>
|
|
547
|
+
<button class="tab" data-tab="demo">Demo</button>
|
|
547
548
|
<button class="tab" data-tab="docs">Documentation</button>
|
|
548
549
|
<button class="tab" data-tab="api">API Reference</button>
|
|
549
550
|
</div>
|
|
550
551
|
|
|
552
|
+
<!-- Intro Tab -->
|
|
553
|
+
<div class="tab-content active" id="intro">
|
|
554
|
+
<div class="docs-content">
|
|
555
|
+
<h2>Welcome to aki-info-detect</h2>
|
|
556
|
+
<p>
|
|
557
|
+
<strong>aki-info-detect</strong> is a modern, lightweight JavaScript library that provides comprehensive device, browser, and system information detection for web applications. Built with performance and developer experience in mind, it leverages modern browser APIs including the Client Hints API for accurate hardware detection while maintaining a minimal footprint (~3.8 kB gzipped).
|
|
558
|
+
</p>
|
|
559
|
+
<p>
|
|
560
|
+
Whether you need to customize user experiences based on device capabilities, gather analytics data, or implement platform-specific features, aki-info-detect provides a unified, easy-to-use API that works seamlessly across all modern browsers.
|
|
561
|
+
</p>
|
|
562
|
+
|
|
563
|
+
<div class="note-box">
|
|
564
|
+
<strong>โก Quick Start:</strong> Install with <code>npm install aki-info-detect</code> and start detecting in seconds!
|
|
565
|
+
</div>
|
|
566
|
+
|
|
567
|
+
<h2>Key Benefits</h2>
|
|
568
|
+
<div class="feature-grid">
|
|
569
|
+
<div class="feature-item">
|
|
570
|
+
<strong>๐ฏ Comprehensive Detection</strong>
|
|
571
|
+
<p style="font-size: 0.85rem; margin-top: 4px;">Browser, OS, CPU, GPU, RAM, network, battery, and more from a single library</p>
|
|
572
|
+
</div>
|
|
573
|
+
<div class="feature-item">
|
|
574
|
+
<strong>โก Performance-First</strong>
|
|
575
|
+
<p style="font-size: 0.85rem; margin-top: 4px;">Minimal bundle size with tree-shakeable exportsโimport only what you need</p>
|
|
576
|
+
</div>
|
|
577
|
+
<div class="feature-item">
|
|
578
|
+
<strong>๐ฎ Future-Proof</strong>
|
|
579
|
+
<p style="font-size: 0.85rem; margin-top: 4px;">Built on modern Web APIs with graceful fallbacks for older browsers</p>
|
|
580
|
+
</div>
|
|
581
|
+
<div class="feature-item">
|
|
582
|
+
<strong>๐ช Apple Silicon Ready</strong>
|
|
583
|
+
<p style="font-size: 0.85rem; margin-top: 4px;">Advanced detection for Apple M-series chips (M1, M2, M3, M4, and beyond)</p>
|
|
584
|
+
</div>
|
|
585
|
+
<div class="feature-item">
|
|
586
|
+
<strong>๐ Privacy-Conscious</strong>
|
|
587
|
+
<p style="font-size: 0.85rem; margin-top: 4px;">Implements caching for network requests to minimize external API calls</p>
|
|
588
|
+
</div>
|
|
589
|
+
<div class="feature-item">
|
|
590
|
+
<strong>๐ฆ Zero Dependencies</strong>
|
|
591
|
+
<p style="font-size: 0.85rem; margin-top: 4px;">No external runtime dependenciesโjust pure, optimized JavaScript</p>
|
|
592
|
+
</div>
|
|
593
|
+
</div>
|
|
594
|
+
|
|
595
|
+
<h2>Common Use Cases</h2>
|
|
596
|
+
|
|
597
|
+
<h3>1. Adaptive User Interfaces</h3>
|
|
598
|
+
<p>Dynamically adjust your UI based on device capabilities and screen properties:</p>
|
|
599
|
+
<pre><code>const info = await akiInfoDetect();
|
|
600
|
+
if (info.isMobile) {
|
|
601
|
+
loadMobileUI();
|
|
602
|
+
} else if (info.RAM < 4) {
|
|
603
|
+
enableLowMemoryMode();
|
|
604
|
+
}</code></pre>
|
|
605
|
+
|
|
606
|
+
<h3>2. Analytics & User Insights</h3>
|
|
607
|
+
<p>Gather detailed technical data to understand your user base:</p>
|
|
608
|
+
<pre><code>const info = await akiInfoDetect();
|
|
609
|
+
analytics.track('page_view', {
|
|
610
|
+
browser: info.browser,
|
|
611
|
+
os: info.os.string,
|
|
612
|
+
device: info.isMobile ? 'mobile' : 'desktop',
|
|
613
|
+
gpu: info.GPU
|
|
614
|
+
});</code></pre>
|
|
615
|
+
|
|
616
|
+
<h3>3. Feature Detection & Progressive Enhancement</h3>
|
|
617
|
+
<p>Enable or disable features based on browser capabilities:</p>
|
|
618
|
+
<pre><code>const info = await akiInfoDetect();
|
|
619
|
+
const chromeVersion = parseInt(info.browser.split(' ')[1]);
|
|
620
|
+
if (info.browser.includes('Chrome') && chromeVersion >= 90) {
|
|
621
|
+
enableAdvancedFeatures();
|
|
622
|
+
}</code></pre>
|
|
623
|
+
|
|
624
|
+
<h3>4. Platform-Specific Optimization</h3>
|
|
625
|
+
<p>Optimize content delivery based on hardware capabilities:</p>
|
|
626
|
+
<pre><code>const info = await akiInfoDetect();
|
|
627
|
+
if (info.CPU === 'Apple Silicon') {
|
|
628
|
+
loadWebPImages();
|
|
629
|
+
} else if (info.GPU.includes('NVIDIA')) {
|
|
630
|
+
enableHardwareAcceleration();
|
|
631
|
+
}</code></pre>
|
|
632
|
+
|
|
633
|
+
<h3>5. Network-Aware Loading</h3>
|
|
634
|
+
<p>Adapt content loading strategies based on connection quality:</p>
|
|
635
|
+
<pre><code>const info = await akiInfoDetect();
|
|
636
|
+
const conn = info.getConnection();
|
|
637
|
+
if (conn.type === '4g' && conn.downlink > 5) {
|
|
638
|
+
loadHDContent();
|
|
639
|
+
} else {
|
|
640
|
+
loadCompressedContent();
|
|
641
|
+
}</code></pre>
|
|
642
|
+
|
|
643
|
+
<h2>Comparison with Similar Libraries</h2>
|
|
644
|
+
<p>aki-info-detect stands out from other detection libraries with its modern approach and comprehensive feature set:</p>
|
|
645
|
+
|
|
646
|
+
<div style="overflow-x: auto; margin: 20px 0;">
|
|
647
|
+
<table style="width: 100%; border-collapse: collapse; font-size: 0.9rem;">
|
|
648
|
+
<thead>
|
|
649
|
+
<tr style="border-bottom: 2px solid var(--border);">
|
|
650
|
+
<th style="text-align: left; padding: 10px; color: var(--text-primary);">Feature</th>
|
|
651
|
+
<th style="text-align: center; padding: 10px; color: var(--accent);">aki-info-detect</th>
|
|
652
|
+
<th style="text-align: center; padding: 10px; color: var(--text-secondary);">Platform.js</th>
|
|
653
|
+
<th style="text-align: center; padding: 10px; color: var(--text-secondary);">UA-Parser.js</th>
|
|
654
|
+
<th style="text-align: center; padding: 10px; color: var(--text-secondary);">Detect.js</th>
|
|
655
|
+
</tr>
|
|
656
|
+
</thead>
|
|
657
|
+
<tbody>
|
|
658
|
+
<tr style="border-bottom: 1px solid var(--border);">
|
|
659
|
+
<td style="padding: 10px; color: var(--text-secondary);">Bundle Size (gzipped)</td>
|
|
660
|
+
<td style="padding: 10px; text-align: center; color: var(--success);">~3.8 kB</td>
|
|
661
|
+
<td style="padding: 10px; text-align: center;">~2.5 kB</td>
|
|
662
|
+
<td style="padding: 10px; text-align: center;">~9 kB</td>
|
|
663
|
+
<td style="padding: 10px; text-align: center;">Varies</td>
|
|
664
|
+
</tr>
|
|
665
|
+
<tr style="border-bottom: 1px solid var(--border);">
|
|
666
|
+
<td style="padding: 10px; color: var(--text-secondary);">Client Hints API</td>
|
|
667
|
+
<td style="padding: 10px; text-align: center; color: var(--success);">โ
</td>
|
|
668
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ</td>
|
|
669
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ</td>
|
|
670
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ</td>
|
|
671
|
+
</tr>
|
|
672
|
+
<tr style="border-bottom: 1px solid var(--border);">
|
|
673
|
+
<td style="padding: 10px; color: var(--text-secondary);">GPU Detection</td>
|
|
674
|
+
<td style="padding: 10px; text-align: center; color: var(--success);">โ
</td>
|
|
675
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ</td>
|
|
676
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ</td>
|
|
677
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ</td>
|
|
678
|
+
</tr>
|
|
679
|
+
<tr style="border-bottom: 1px solid var(--border);">
|
|
680
|
+
<td style="padding: 10px; color: var(--text-secondary);">Apple Silicon Detection</td>
|
|
681
|
+
<td style="padding: 10px; text-align: center; color: var(--success);">โ
(M1-MX)</td>
|
|
682
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ</td>
|
|
683
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ</td>
|
|
684
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ</td>
|
|
685
|
+
</tr>
|
|
686
|
+
<tr style="border-bottom: 1px solid var(--border);">
|
|
687
|
+
<td style="padding: 10px; color: var(--text-secondary);">Network Info (IP/ISP)</td>
|
|
688
|
+
<td style="padding: 10px; text-align: center; color: var(--success);">โ
</td>
|
|
689
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ</td>
|
|
690
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ</td>
|
|
691
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ</td>
|
|
692
|
+
</tr>
|
|
693
|
+
<tr style="border-bottom: 1px solid var(--border);">
|
|
694
|
+
<td style="padding: 10px; color: var(--text-secondary);">Battery & Geolocation</td>
|
|
695
|
+
<td style="padding: 10px; text-align: center; color: var(--success);">โ
</td>
|
|
696
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ</td>
|
|
697
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ</td>
|
|
698
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ</td>
|
|
699
|
+
</tr>
|
|
700
|
+
<tr style="border-bottom: 1px solid var(--border);">
|
|
701
|
+
<td style="padding: 10px; color: var(--text-secondary);">Tree-shakeable</td>
|
|
702
|
+
<td style="padding: 10px; text-align: center; color: var(--success);">โ
</td>
|
|
703
|
+
<td style="padding: 10px; text-align: center; color: var(--text-muted);">โ ๏ธ</td>
|
|
704
|
+
<td style="padding: 10px; text-align: center; color: var(--text-muted);">โ ๏ธ</td>
|
|
705
|
+
<td style="padding: 10px; text-align: center; color: var(--text-muted);">โ ๏ธ</td>
|
|
706
|
+
</tr>
|
|
707
|
+
<tr style="border-bottom: 1px solid var(--border);">
|
|
708
|
+
<td style="padding: 10px; color: var(--text-secondary);">TypeScript Support</td>
|
|
709
|
+
<td style="padding: 10px; text-align: center; color: var(--success);">โ
Full</td>
|
|
710
|
+
<td style="padding: 10px; text-align: center; color: var(--text-muted);">โ ๏ธ Community</td>
|
|
711
|
+
<td style="padding: 10px; text-align: center; color: var(--success);">โ
</td>
|
|
712
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ</td>
|
|
713
|
+
</tr>
|
|
714
|
+
<tr>
|
|
715
|
+
<td style="padding: 10px; color: var(--text-secondary);">Active Maintenance</td>
|
|
716
|
+
<td style="padding: 10px; text-align: center; color: var(--success);">โ
</td>
|
|
717
|
+
<td style="padding: 10px; text-align: center; color: var(--text-muted);">โ ๏ธ</td>
|
|
718
|
+
<td style="padding: 10px; text-align: center; color: var(--success);">โ
</td>
|
|
719
|
+
<td style="padding: 10px; text-align: center; color: var(--error);">โ Archived</td>
|
|
720
|
+
</tr>
|
|
721
|
+
</tbody>
|
|
722
|
+
</table>
|
|
723
|
+
</div>
|
|
724
|
+
|
|
725
|
+
<h3>Key Differentiators</h3>
|
|
726
|
+
<ul>
|
|
727
|
+
<li><strong>vs. Platform.js:</strong> Modern evolution with Client Hints API support, hardware detection (GPU, RAM), and network capabilities</li>
|
|
728
|
+
<li><strong>vs. UA-Parser.js:</strong> Lighter bundle while providing more comprehensive information including hardware and network detection</li>
|
|
729
|
+
<li><strong>vs. Detect.js:</strong> Active maintenance (Detect.js is archived), smaller focused API, modern Web API integration</li>
|
|
730
|
+
</ul>
|
|
731
|
+
|
|
732
|
+
<h2>Frequently Asked Questions</h2>
|
|
733
|
+
|
|
734
|
+
<h3>General Questions</h3>
|
|
735
|
+
|
|
736
|
+
<details style="margin: 10px 0; padding: 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; cursor: pointer;">
|
|
737
|
+
<summary style="font-weight: 500; color: var(--text-primary); cursor: pointer;">What browsers does aki-info-detect support?</summary>
|
|
738
|
+
<p style="margin-top: 10px; color: var(--text-secondary);">
|
|
739
|
+
All modern browsers with ES2020+ support: Chrome/Edge 89+, Firefox 88+, Safari 14+, Opera 76+. Client Hints features work best in Chromium-based browsers.
|
|
740
|
+
</p>
|
|
741
|
+
</details>
|
|
742
|
+
|
|
743
|
+
<details style="margin: 10px 0; padding: 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; cursor: pointer;">
|
|
744
|
+
<summary style="font-weight: 500; color: var(--text-primary); cursor: pointer;">Does it work in Node.js or server-side environments?</summary>
|
|
745
|
+
<p style="margin-top: 10px; color: var(--text-secondary);">
|
|
746
|
+
No, aki-info-detect is specifically designed for browser environments. It relies on browser APIs like <code>navigator</code>, <code>screen</code>, and Web APIs that are not available in Node.js.
|
|
747
|
+
</p>
|
|
748
|
+
</details>
|
|
749
|
+
|
|
750
|
+
<details style="margin: 10px 0; padding: 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; cursor: pointer;">
|
|
751
|
+
<summary style="font-weight: 500; color: var(--text-primary); cursor: pointer;">How accurate is the detection?</summary>
|
|
752
|
+
<p style="margin-top: 10px; color: var(--text-secondary);">
|
|
753
|
+
Very accurate for modern browsers that support Client Hints API. For browsers without Client Hints, it falls back to user agent parsing which is still reliable but less detailed. GPU and CPU detection is most accurate in Chromium-based browsers.
|
|
754
|
+
</p>
|
|
755
|
+
</details>
|
|
756
|
+
|
|
757
|
+
<details style="margin: 10px 0; padding: 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; cursor: pointer;">
|
|
758
|
+
<summary style="font-weight: 500; color: var(--text-primary); cursor: pointer;">Is user agent string spoofing a concern?</summary>
|
|
759
|
+
<p style="margin-top: 10px; color: var(--text-secondary);">
|
|
760
|
+
While user agents can be spoofed, Client Hints API provides more reliable detection. For critical functionality, always combine with feature detection rather than relying solely on browser/platform detection.
|
|
761
|
+
</p>
|
|
762
|
+
</details>
|
|
763
|
+
|
|
764
|
+
<h3>Privacy & Security</h3>
|
|
765
|
+
|
|
766
|
+
<details style="margin: 10px 0; padding: 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; cursor: pointer;">
|
|
767
|
+
<summary style="font-weight: 500; color: var(--text-primary); cursor: pointer;">Does aki-info-detect collect or transmit user data?</summary>
|
|
768
|
+
<p style="margin-top: 10px; color: var(--text-secondary);">
|
|
769
|
+
No. The library only detects information locally in the browser. The optional network info feature (<code>getNetworkInfo()</code>) makes a request to a public IP API, but this is only triggered when you explicitly call that method.
|
|
770
|
+
</p>
|
|
771
|
+
</details>
|
|
772
|
+
|
|
773
|
+
<details style="margin: 10px 0; padding: 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; cursor: pointer;">
|
|
774
|
+
<summary style="font-weight: 500; color: var(--text-primary); cursor: pointer;">What about user privacy?</summary>
|
|
775
|
+
<p style="margin-top: 10px; color: var(--text-secondary);">
|
|
776
|
+
aki-info-detect is privacy-conscious. Network requests are cached for 1 hour to minimize external API calls, and all detection happens client-side. No data is sent to our servers.
|
|
777
|
+
</p>
|
|
778
|
+
</details>
|
|
779
|
+
|
|
780
|
+
<details style="margin: 10px 0; padding: 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; cursor: pointer;">
|
|
781
|
+
<summary style="font-weight: 500; color: var(--text-primary); cursor: pointer;">Is it GDPR compliant?</summary>
|
|
782
|
+
<p style="margin-top: 10px; color: var(--text-secondary);">
|
|
783
|
+
Detection of browser/device information is generally considered functional data necessary for website operation. However, always review your local regulations and privacy policies, especially when using geolocation or storing detected data.
|
|
784
|
+
</p>
|
|
785
|
+
</details>
|
|
786
|
+
|
|
787
|
+
<h3>Technical Questions</h3>
|
|
788
|
+
|
|
789
|
+
<details style="margin: 10px 0; padding: 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; cursor: pointer;">
|
|
790
|
+
<summary style="font-weight: 500; color: var(--text-primary); cursor: pointer;">Why do I need to configure server headers?</summary>
|
|
791
|
+
<p style="margin-top: 10px; color: var(--text-secondary);">
|
|
792
|
+
The Client Hints API requires the server to explicitly request detailed information via <code>Accept-CH</code> headers. Without these headers, browsers will only provide basic user agent data. This is a browser security feature designed to enhance user privacy.
|
|
793
|
+
</p>
|
|
794
|
+
</details>
|
|
795
|
+
|
|
796
|
+
<details style="margin: 10px 0; padding: 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; cursor: pointer;">
|
|
797
|
+
<summary style="font-weight: 500; color: var(--text-primary); cursor: pointer;">Can I use it with React, Vue, or other frameworks?</summary>
|
|
798
|
+
<p style="margin-top: 10px; color: var(--text-secondary);">
|
|
799
|
+
Absolutely! aki-info-detect is framework-agnostic. Check the Documentation tab for React Hook and Vue Composable examples.
|
|
800
|
+
</p>
|
|
801
|
+
</details>
|
|
802
|
+
|
|
803
|
+
<details style="margin: 10px 0; padding: 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; cursor: pointer;">
|
|
804
|
+
<summary style="font-weight: 500; color: var(--text-primary); cursor: pointer;">How do I reduce bundle size further?</summary>
|
|
805
|
+
<p style="margin-top: 10px; color: var(--text-secondary);">
|
|
806
|
+
Use tree-shakeable imports to include only the functions you need:
|
|
807
|
+
<pre style="margin-top: 8px;"><code>import { detectBrowser, detectOS } from 'aki-info-detect';</code></pre>
|
|
808
|
+
</p>
|
|
809
|
+
</details>
|
|
810
|
+
|
|
811
|
+
<details style="margin: 10px 0; padding: 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; cursor: pointer;">
|
|
812
|
+
<summary style="font-weight: 500; color: var(--text-primary); cursor: pointer;">Does it support TypeScript?</summary>
|
|
813
|
+
<p style="margin-top: 10px; color: var(--text-secondary);">
|
|
814
|
+
Yes, full TypeScript declarations are included in the package.
|
|
815
|
+
</p>
|
|
816
|
+
</details>
|
|
817
|
+
|
|
818
|
+
<details style="margin: 10px 0; padding: 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; cursor: pointer;">
|
|
819
|
+
<summary style="font-weight: 500; color: var(--text-primary); cursor: pointer;">How often is the network information cached?</summary>
|
|
820
|
+
<p style="margin-top: 10px; color: var(--text-secondary);">
|
|
821
|
+
Network info (IP, ISP, country) is cached for 1 hour by default. You can force a refresh by passing <code>true</code> to <code>getNetworkInfo(true)</code>.
|
|
822
|
+
</p>
|
|
823
|
+
</details>
|
|
824
|
+
|
|
825
|
+
<h3>Troubleshooting</h3>
|
|
826
|
+
|
|
827
|
+
<details style="margin: 10px 0; padding: 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; cursor: pointer;">
|
|
828
|
+
<summary style="font-weight: 500; color: var(--text-primary); cursor: pointer;">Why am I getting generic hardware info?</summary>
|
|
829
|
+
<p style="margin-top: 10px; color: var(--text-secondary);">
|
|
830
|
+
Make sure your server is configured to send the required <code>Accept-CH</code> headers. Without these headers, detailed Client Hints data won't be available. Check the Documentation tab for server configuration examples.
|
|
831
|
+
</p>
|
|
832
|
+
</details>
|
|
833
|
+
|
|
834
|
+
<details style="margin: 10px 0; padding: 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; cursor: pointer;">
|
|
835
|
+
<summary style="font-weight: 500; color: var(--text-primary); cursor: pointer;">Why is GPU detection returning "Unknown"?</summary>
|
|
836
|
+
<p style="margin-top: 10px; color: var(--text-secondary);">
|
|
837
|
+
GPU detection works best in Chromium browsers with Client Hints. Safari and Firefox have limited support. Also, some browsers may restrict this information for privacy reasons.
|
|
838
|
+
</p>
|
|
839
|
+
</details>
|
|
840
|
+
|
|
841
|
+
<details style="margin: 10px 0; padding: 12px; background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; cursor: pointer;">
|
|
842
|
+
<summary style="font-weight: 500; color: var(--text-primary); cursor: pointer;">The library says "Unknown" for many fields. What's wrong?</summary>
|
|
843
|
+
<p style="margin-top: 10px; color: var(--text-secondary);">
|
|
844
|
+
Check browser console for errors, ensure you're using a supported browser version, and verify that your server is sending proper Client Hints headers (see the Documentation tab for Server Configuration).
|
|
845
|
+
</p>
|
|
846
|
+
</details>
|
|
847
|
+
|
|
848
|
+
<div class="note-box" style="margin-top: 24px;">
|
|
849
|
+
<strong>๐ Ready to get started?</strong> Check out the <strong>Demo</strong> tab to see the library in action, or visit the <strong>Documentation</strong> tab for installation instructions and code examples.
|
|
850
|
+
</div>
|
|
851
|
+
</div>
|
|
852
|
+
</div>
|
|
853
|
+
|
|
551
854
|
<!-- Demo Tab -->
|
|
552
|
-
<div class="tab-content
|
|
855
|
+
<div class="tab-content" id="demo">
|
|
553
856
|
<div class="note-box">
|
|
554
857
|
<strong>โ ๏ธ Server Headers Required:</strong> For deep hardware detection via Client Hints API, configure:
|
|
555
858
|
<code>Accept-CH: Sec-CH-UA, Sec-CH-UA-Mobile, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch, Sec-CH-UA-Bitness</code>
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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