aki-info-detect 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +354 -0
- package/dist/aki-info-detect.js +457 -0
- package/dist/aki-info-detect.js.map +1 -0
- package/dist/aki-info-detect.umd.cjs +25 -0
- package/dist/aki-info-detect.umd.cjs.map +1 -0
- package/dist/types/index.d.ts +120 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 lacvietanh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
# aki-info-detect
|
|
2
|
+
|
|
3
|
+
A lightweight JavaScript library for detecting device, browser, hardware, and network information in the browser.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/aki-info-detect)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://bundlephobia.com/package/aki-info-detect)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- 🌐 **Browser Detection** — Name, version, rendering engine
|
|
12
|
+
- 💻 **OS Detection** — Windows, macOS, Linux, iOS, Android with version
|
|
13
|
+
- ⚙️ **Hardware Info** — CPU cores, RAM, GPU (with Apple Silicon detection)
|
|
14
|
+
- 🌍 **Network Info** — Public IP, ISP, country code (with caching)
|
|
15
|
+
- 📱 **Screen Info** — Resolution, pixel ratio, orientation
|
|
16
|
+
- 🔋 **Battery Status** — Charging state, level percentage
|
|
17
|
+
- 📍 **Geolocation** — Coordinates with user permission
|
|
18
|
+
- 🚀 **Modern APIs** — Uses Client Hints for deeper detection
|
|
19
|
+
- 📦 **Tree-shakeable** — Import only what you need
|
|
20
|
+
- 🎯 **Browser-only** — Optimized for browser environments
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
### npm / yarn / pnpm
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install aki-info-detect
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### CDN
|
|
31
|
+
|
|
32
|
+
```html
|
|
33
|
+
<!-- ES Module -->
|
|
34
|
+
<script type="module">
|
|
35
|
+
import akiInfoDetect from 'https://unpkg.com/aki-info-detect/dist/aki-info-detect.js';
|
|
36
|
+
const info = await akiInfoDetect();
|
|
37
|
+
console.log(info);
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<!-- UMD (global variable) -->
|
|
41
|
+
<script src="https://unpkg.com/aki-info-detect/dist/aki-info-detect.umd.cjs"></script>
|
|
42
|
+
<script>
|
|
43
|
+
akiInfoDetect().then(info => console.log(info));
|
|
44
|
+
</script>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## ⚠️ IMPORTANT: Server Headers Required
|
|
50
|
+
|
|
51
|
+
To enable **deep hardware detection** via [Client Hints API](https://developer.mozilla.org/en-US/docs/Web/API/User-Agent_Client_Hints_API), your server **must** send these HTTP headers:
|
|
52
|
+
|
|
53
|
+
```http
|
|
54
|
+
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, Sec-CH-UA-Model, Sec-CH-UA-Full-Version-List
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Example Configurations
|
|
58
|
+
|
|
59
|
+
<details>
|
|
60
|
+
<summary><strong>Express.js</strong></summary>
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
app.use((req, res, next) => {
|
|
64
|
+
res.setHeader('Accept-CH',
|
|
65
|
+
'Sec-CH-UA, Sec-CH-UA-Mobile, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, ' +
|
|
66
|
+
'Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Model, Sec-CH-UA-Full-Version-List'
|
|
67
|
+
);
|
|
68
|
+
next();
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
</details>
|
|
72
|
+
|
|
73
|
+
<details>
|
|
74
|
+
<summary><strong>Nginx</strong></summary>
|
|
75
|
+
|
|
76
|
+
```nginx
|
|
77
|
+
add_header 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, Sec-CH-UA-Model, Sec-CH-UA-Full-Version-List";
|
|
78
|
+
```
|
|
79
|
+
</details>
|
|
80
|
+
|
|
81
|
+
<details>
|
|
82
|
+
<summary><strong>Vite</strong></summary>
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
// vite.config.js
|
|
86
|
+
export default {
|
|
87
|
+
server: {
|
|
88
|
+
headers: {
|
|
89
|
+
'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, Sec-CH-UA-Model, Sec-CH-UA-Full-Version-List'
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
```
|
|
94
|
+
</details>
|
|
95
|
+
|
|
96
|
+
<details>
|
|
97
|
+
<summary><strong>Vercel (vercel.json)</strong></summary>
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"headers": [
|
|
102
|
+
{
|
|
103
|
+
"source": "/(.*)",
|
|
104
|
+
"headers": [
|
|
105
|
+
{
|
|
106
|
+
"key": "Accept-CH",
|
|
107
|
+
"value": "Sec-CH-UA, Sec-CH-UA-Mobile, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Model, Sec-CH-UA-Full-Version-List"
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
</details>
|
|
115
|
+
|
|
116
|
+
<details>
|
|
117
|
+
<summary><strong>Netlify (_headers)</strong></summary>
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
/*
|
|
121
|
+
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, Sec-CH-UA-Model, Sec-CH-UA-Full-Version-List
|
|
122
|
+
```
|
|
123
|
+
</details>
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Quick Start
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
import akiInfoDetect from 'aki-info-detect';
|
|
131
|
+
|
|
132
|
+
const info = await akiInfoDetect();
|
|
133
|
+
|
|
134
|
+
console.log(info.browser); // "Chrome 120"
|
|
135
|
+
console.log(info.os.string); // "macOS 14.2.0"
|
|
136
|
+
console.log(info.CPU); // "Apple Silicon"
|
|
137
|
+
console.log(info.GPU); // "Apple M2 Pro"
|
|
138
|
+
console.log(info.RAM); // 16
|
|
139
|
+
console.log(info.isMobile); // false
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## API Reference
|
|
143
|
+
|
|
144
|
+
### `akiInfoDetect(forceNetworkRefresh?: boolean): Promise<AkiInfoResult>`
|
|
145
|
+
|
|
146
|
+
Main detection function. Returns a Promise with complete system information.
|
|
147
|
+
|
|
148
|
+
| Parameter | Type | Default | Description |
|
|
149
|
+
|-----------|------|---------|-------------|
|
|
150
|
+
| `forceNetworkRefresh` | `boolean` | `false` | Bypass network info cache |
|
|
151
|
+
|
|
152
|
+
### Result Object
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
interface AkiInfoResult {
|
|
156
|
+
// Basic
|
|
157
|
+
browser: string; // "Chrome 120", "Safari 17", "Firefox 121"
|
|
158
|
+
product: string; // "iPhone", "Galaxy S21", ""
|
|
159
|
+
manufacturer: string; // "Apple", "Samsung", ""
|
|
160
|
+
isMobile: boolean;
|
|
161
|
+
language: string; // "en vi zh" (space-separated 2-char codes)
|
|
162
|
+
|
|
163
|
+
// Hardware
|
|
164
|
+
CPU: string; // "Apple Silicon", "Intel", "AMD", "Unknown"
|
|
165
|
+
CPUCore: number; // 8, 10, 16, etc.
|
|
166
|
+
arch: string; // "arm64", "x86_64"
|
|
167
|
+
RAM: number; // GB (0 if unavailable)
|
|
168
|
+
GPU: string; // "Apple M2 Pro", "NVIDIA GeForce RTX 4090"
|
|
169
|
+
|
|
170
|
+
// Battery
|
|
171
|
+
battery: {
|
|
172
|
+
isCharging: boolean;
|
|
173
|
+
level: number; // 0-100
|
|
174
|
+
chargingTime: number;
|
|
175
|
+
dischargingTime: number;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// OS
|
|
179
|
+
os: {
|
|
180
|
+
name: string; // "win", "mac", "linux", "android", "ios"
|
|
181
|
+
version: string; // "11", "14.2.0", "14"
|
|
182
|
+
string: string; // "Windows 11", "macOS 14.2.0"
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// Network (reactive getters - updated after fetch)
|
|
186
|
+
network: {
|
|
187
|
+
IP: string;
|
|
188
|
+
ISP: string;
|
|
189
|
+
country: string; // ISO 3166-1 alpha-2 code
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// Methods
|
|
193
|
+
getIP(force?: boolean): Promise<string>;
|
|
194
|
+
getISP(force?: boolean): Promise<string>;
|
|
195
|
+
getCountry(force?: boolean): Promise<string>;
|
|
196
|
+
getNetworkInfo(force?: boolean): Promise<NetworkInfo>;
|
|
197
|
+
getLocation(options?: PositionOptions): Promise<GeolocationData | null>;
|
|
198
|
+
getConnection(): ConnectionInfo | null;
|
|
199
|
+
getScreen(): ScreenInfo;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Individual Exports (Tree-shakeable)
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
import {
|
|
207
|
+
getNetworkInfo,
|
|
208
|
+
getIP,
|
|
209
|
+
getScreen,
|
|
210
|
+
getBattery,
|
|
211
|
+
detectGPU
|
|
212
|
+
} from 'aki-info-detect';
|
|
213
|
+
|
|
214
|
+
// Use only what you need
|
|
215
|
+
const screen = getScreen();
|
|
216
|
+
const gpu = detectGPU();
|
|
217
|
+
const network = await getNetworkInfo();
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Usage Examples
|
|
221
|
+
|
|
222
|
+
### Get Network Info
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
const info = await akiInfoDetect();
|
|
226
|
+
|
|
227
|
+
// Network is fetched in background, access via methods:
|
|
228
|
+
const ip = await info.getIP();
|
|
229
|
+
const network = await info.getNetworkInfo();
|
|
230
|
+
|
|
231
|
+
console.log(network.IP); // "203.113.xxx.xxx"
|
|
232
|
+
console.log(network.ISP); // "Viettel Group"
|
|
233
|
+
console.log(network.country); // "VN"
|
|
234
|
+
|
|
235
|
+
// Force refresh (bypass 1-hour cache)
|
|
236
|
+
const fresh = await info.getNetworkInfo(true);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Get Geolocation
|
|
240
|
+
|
|
241
|
+
```javascript
|
|
242
|
+
const info = await akiInfoDetect();
|
|
243
|
+
const location = await info.getLocation();
|
|
244
|
+
|
|
245
|
+
if (location) {
|
|
246
|
+
console.log(`Lat: ${location.latitude}`);
|
|
247
|
+
console.log(`Long: ${location.longitude}`);
|
|
248
|
+
console.log(`Accuracy: ${location.accuracy}m`);
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Detect Apple Silicon
|
|
253
|
+
|
|
254
|
+
```javascript
|
|
255
|
+
const info = await akiInfoDetect();
|
|
256
|
+
|
|
257
|
+
// Future-proof: works with M1, M2, M3, M4... MX
|
|
258
|
+
if (info.CPU === 'Apple Silicon') {
|
|
259
|
+
console.log(`Running on ${info.GPU}`); // "Apple M2 Pro"
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### React Hook
|
|
264
|
+
|
|
265
|
+
```jsx
|
|
266
|
+
import { useState, useEffect } from 'react';
|
|
267
|
+
import akiInfoDetect from 'aki-info-detect';
|
|
268
|
+
|
|
269
|
+
function useSystemInfo() {
|
|
270
|
+
const [info, setInfo] = useState(null);
|
|
271
|
+
|
|
272
|
+
useEffect(() => {
|
|
273
|
+
akiInfoDetect().then(setInfo);
|
|
274
|
+
}, []);
|
|
275
|
+
|
|
276
|
+
return info;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function App() {
|
|
280
|
+
const info = useSystemInfo();
|
|
281
|
+
|
|
282
|
+
if (!info) return <div>Loading...</div>;
|
|
283
|
+
|
|
284
|
+
return (
|
|
285
|
+
<div>
|
|
286
|
+
<p>Browser: {info.browser}</p>
|
|
287
|
+
<p>OS: {info.os.string}</p>
|
|
288
|
+
<p>GPU: {info.GPU}</p>
|
|
289
|
+
</div>
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Vue 3 Composable
|
|
295
|
+
|
|
296
|
+
```javascript
|
|
297
|
+
import { ref, onMounted } from 'vue';
|
|
298
|
+
import akiInfoDetect from 'aki-info-detect';
|
|
299
|
+
|
|
300
|
+
export function useSystemInfo() {
|
|
301
|
+
const info = ref(null);
|
|
302
|
+
|
|
303
|
+
onMounted(async () => {
|
|
304
|
+
info.value = await akiInfoDetect();
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
return { info };
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Development
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
# Install dependencies
|
|
315
|
+
npm install
|
|
316
|
+
|
|
317
|
+
# Start dev server (with Client Hints headers)
|
|
318
|
+
npm run dev
|
|
319
|
+
|
|
320
|
+
# Build for production
|
|
321
|
+
npm run build
|
|
322
|
+
|
|
323
|
+
# Preview production build
|
|
324
|
+
npm run preview
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Browser Support
|
|
328
|
+
|
|
329
|
+
| Browser | Version | Notes |
|
|
330
|
+
|---------|---------|-------|
|
|
331
|
+
| Chrome | 89+ | Full Client Hints support |
|
|
332
|
+
| Edge | 89+ | Full Client Hints support |
|
|
333
|
+
| Opera | 75+ | Full Client Hints support |
|
|
334
|
+
| Firefox | 90+ | Limited Client Hints |
|
|
335
|
+
| Safari | 15+ | No Client Hints, fallback to UA |
|
|
336
|
+
|
|
337
|
+
## Changelog
|
|
338
|
+
|
|
339
|
+
### v2.0.0
|
|
340
|
+
- Complete rewrite with modular architecture
|
|
341
|
+
- Vite build system with ES/UMD outputs
|
|
342
|
+
- TypeScript declarations
|
|
343
|
+
- Client Hints API integration
|
|
344
|
+
- Future-proof Apple Silicon detection (M1→MX)
|
|
345
|
+
- Tree-shakeable exports
|
|
346
|
+
- 1-hour network info caching
|
|
347
|
+
- Removed SSR guards (browser-only library)
|
|
348
|
+
|
|
349
|
+
### v1.0.0
|
|
350
|
+
- Initial release
|
|
351
|
+
|
|
352
|
+
## License
|
|
353
|
+
|
|
354
|
+
MIT © [lacvietanh](https://github.com/lacvietanh)
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* aki-info-detect v2.0.0
|
|
3
|
+
* (c) 2025 lacvietanh
|
|
4
|
+
* Released under the MIT License
|
|
5
|
+
* https://github.com/lacvietanh/akiInfoDetect.js
|
|
6
|
+
*/
|
|
7
|
+
function parseUserAgent(ua = navigator.userAgent) {
|
|
8
|
+
let os = null;
|
|
9
|
+
let name = "";
|
|
10
|
+
let version = "";
|
|
11
|
+
let product = "";
|
|
12
|
+
let manufacturer = "";
|
|
13
|
+
const browserPatterns = [
|
|
14
|
+
{ test: /Edg(?:e)?\//, name: "Edge", regex: /Edg(?:e)?\/([\d.]+)/ },
|
|
15
|
+
{ test: /OPR\/|Opera\//, name: "Opera", regex: /(?:OPR|Opera)\/([\d.]+)/ },
|
|
16
|
+
{ test: /Firefox\//, name: "Firefox", regex: /Firefox\/([\d.]+)/, exclude: /Seamonkey/ },
|
|
17
|
+
{ test: /Chrome\//, name: "Chrome", regex: /Chrome\/([\d.]+)/, exclude: /Edge|Edg|OPR|Opera/ },
|
|
18
|
+
{ test: /Safari\//, name: "Safari", regex: /Version\/([\d.]+)/, exclude: /Chrome|Edge|Edg|OPR|Opera/ },
|
|
19
|
+
{ test: /MSIE|Trident/, name: "IE", regex: /(?:MSIE |rv:)([\d.]+)/ }
|
|
20
|
+
];
|
|
21
|
+
for (const { test, name: browserName, regex, exclude } of browserPatterns) {
|
|
22
|
+
if (test.test(ua) && (!exclude || !exclude.test(ua))) {
|
|
23
|
+
name = browserName;
|
|
24
|
+
const match = ua.match(regex);
|
|
25
|
+
version = (match == null ? void 0 : match[1]) || "";
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const osPatterns = [
|
|
30
|
+
{
|
|
31
|
+
test: /Windows/,
|
|
32
|
+
parse: () => {
|
|
33
|
+
const verMap = { "10.0": "10", "6.3": "8.1", "6.2": "8", "6.1": "7", "6.0": "Vista", "5.1": "XP" };
|
|
34
|
+
const match = ua.match(/Windows NT ([\d.]+)/);
|
|
35
|
+
const ntVer = (match == null ? void 0 : match[1]) || "";
|
|
36
|
+
const winVer = verMap[ntVer] || ntVer;
|
|
37
|
+
return {
|
|
38
|
+
family: `Windows ${winVer}`,
|
|
39
|
+
version: winVer,
|
|
40
|
+
architecture: /WOW64|Win64|x64/.test(ua) ? 64 : 32
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
test: /Mac OS X/,
|
|
46
|
+
parse: () => {
|
|
47
|
+
var _a;
|
|
48
|
+
const match = ua.match(/Mac OS X ([\d_.]+)/);
|
|
49
|
+
const ver = ((_a = match == null ? void 0 : match[1]) == null ? void 0 : _a.replace(/_/g, ".")) || "";
|
|
50
|
+
return { family: `macOS ${ver}`, version: ver, architecture: 64 };
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
test: /Android/,
|
|
55
|
+
parse: () => {
|
|
56
|
+
const match = ua.match(/Android ([\d.]+)/);
|
|
57
|
+
const ver = (match == null ? void 0 : match[1]) || "";
|
|
58
|
+
return { family: `Android ${ver}`, version: ver, architecture: 64 };
|
|
59
|
+
}
|
|
60
|
+
},
|
|
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
|
+
{
|
|
71
|
+
test: /Linux/,
|
|
72
|
+
parse: () => ({
|
|
73
|
+
family: "Linux",
|
|
74
|
+
version: "",
|
|
75
|
+
architecture: /x86_64|amd64/.test(ua) ? 64 : 32
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
];
|
|
79
|
+
for (const { test, parse } of osPatterns) {
|
|
80
|
+
if (test.test(ua)) {
|
|
81
|
+
os = parse();
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (/iPhone/.test(ua)) {
|
|
86
|
+
product = "iPhone";
|
|
87
|
+
manufacturer = "Apple";
|
|
88
|
+
} else if (/iPad/.test(ua)) {
|
|
89
|
+
product = "iPad";
|
|
90
|
+
manufacturer = "Apple";
|
|
91
|
+
} else if (/Android/.test(ua)) {
|
|
92
|
+
const match = ua.match(/Android[^;]+;\s*([^;)]+)/);
|
|
93
|
+
if (match) {
|
|
94
|
+
product = match[1].trim();
|
|
95
|
+
const mfrMatch = product.match(/^(Samsung|LG|Motorola|HTC|Huawei|Xiaomi|OnePlus|Google|Sony|OPPO|Vivo|Realme)/i);
|
|
96
|
+
manufacturer = (mfrMatch == null ? void 0 : mfrMatch[1]) || "";
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const layout = /AppleWebKit/.test(ua) ? "WebKit" : /Gecko\//.test(ua) ? "Gecko" : /Trident/.test(ua) ? "Trident" : "";
|
|
100
|
+
return { name, version, product, manufacturer, os, layout };
|
|
101
|
+
}
|
|
102
|
+
async function getHighEntropyValues() {
|
|
103
|
+
var _a;
|
|
104
|
+
if (!((_a = navigator.userAgentData) == null ? void 0 : _a.getHighEntropyValues)) {
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
return await navigator.userAgentData.getHighEntropyValues([
|
|
109
|
+
"platform",
|
|
110
|
+
"platformVersion",
|
|
111
|
+
"architecture",
|
|
112
|
+
"model",
|
|
113
|
+
"mobile",
|
|
114
|
+
"bitness",
|
|
115
|
+
"brands",
|
|
116
|
+
"fullVersionList"
|
|
117
|
+
]);
|
|
118
|
+
} catch {
|
|
119
|
+
return {};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function detectGPU() {
|
|
123
|
+
try {
|
|
124
|
+
const canvas = document.createElement("canvas");
|
|
125
|
+
const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
|
|
126
|
+
if (!gl) return "No WebGL support";
|
|
127
|
+
const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
|
|
128
|
+
if (!debugInfo) return "GPU info restricted";
|
|
129
|
+
return gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) || "Unknown GPU";
|
|
130
|
+
} catch {
|
|
131
|
+
return "GPU detection failed";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function parseChipInfo(gpu) {
|
|
135
|
+
const gpuLower = gpu.toLowerCase();
|
|
136
|
+
const appleSiliconMatch = gpu.match(/Apple\s+M(\d+)(?:\s+(Pro|Max|Ultra))?/i);
|
|
137
|
+
if (appleSiliconMatch) {
|
|
138
|
+
const chipNum = appleSiliconMatch[1];
|
|
139
|
+
const variant = appleSiliconMatch[2] || "";
|
|
140
|
+
return {
|
|
141
|
+
type: "Apple Silicon",
|
|
142
|
+
chip: `M${chipNum}${variant ? " " + variant : ""}`,
|
|
143
|
+
architecture: "arm64"
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if (gpuLower.includes("apple")) {
|
|
147
|
+
return { type: "Apple Silicon", chip: "Apple GPU", architecture: "arm64" };
|
|
148
|
+
}
|
|
149
|
+
const nvidiaMatch = gpu.match(/NVIDIA\s+(.+?)(?:\s*\/|$)/i) || gpu.match(/(GeForce|Quadro|RTX|GTX)\s+[\w\s]+/i);
|
|
150
|
+
if (nvidiaMatch) {
|
|
151
|
+
return { type: "NVIDIA", chip: nvidiaMatch[0].trim(), architecture: "x86_64" };
|
|
152
|
+
}
|
|
153
|
+
const amdMatch = gpu.match(/(Radeon|AMD)\s+[\w\s]+/i);
|
|
154
|
+
if (amdMatch) {
|
|
155
|
+
return { type: "AMD", chip: amdMatch[0].trim(), architecture: "x86_64" };
|
|
156
|
+
}
|
|
157
|
+
const intelMatch = gpu.match(/Intel.*?(UHD|Iris|HD)\s*(?:Graphics)?\s*(\d*)/i);
|
|
158
|
+
if (intelMatch || gpuLower.includes("intel")) {
|
|
159
|
+
return { type: "Intel", chip: (intelMatch == null ? void 0 : intelMatch[0]) || "Intel Graphics", architecture: "x86_64" };
|
|
160
|
+
}
|
|
161
|
+
return { type: "Unknown", chip: gpu, architecture: "" };
|
|
162
|
+
}
|
|
163
|
+
function getHardwareInfo(gpu) {
|
|
164
|
+
var _a;
|
|
165
|
+
const chipInfo = parseChipInfo(gpu);
|
|
166
|
+
return {
|
|
167
|
+
RAM: navigator.deviceMemory || 0,
|
|
168
|
+
CPUCore: navigator.hardwareConcurrency || 0,
|
|
169
|
+
GPU: gpu,
|
|
170
|
+
CPU: chipInfo.type,
|
|
171
|
+
arch: chipInfo.architecture || (((_a = navigator.userAgentData) == null ? void 0 : _a.platform) === "macOS" ? "arm64" : "x86_64")
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const cache = {
|
|
175
|
+
IP: "",
|
|
176
|
+
ISP: "",
|
|
177
|
+
country: "",
|
|
178
|
+
lastUpdated: 0,
|
|
179
|
+
TTL: 36e5
|
|
180
|
+
// 1 hour
|
|
181
|
+
};
|
|
182
|
+
async function fetchJSON(url, timeout = 2500) {
|
|
183
|
+
const controller = new AbortController();
|
|
184
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
185
|
+
try {
|
|
186
|
+
const res = await fetch(url, { signal: controller.signal });
|
|
187
|
+
clearTimeout(timeoutId);
|
|
188
|
+
return res.ok ? await res.json() : null;
|
|
189
|
+
} catch {
|
|
190
|
+
clearTimeout(timeoutId);
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async function getNetworkInfo(forceRefresh = false) {
|
|
195
|
+
if (!forceRefresh && cache.IP && Date.now() - cache.lastUpdated < cache.TTL) {
|
|
196
|
+
return { IP: cache.IP, ISP: cache.ISP, country: cache.country };
|
|
197
|
+
}
|
|
198
|
+
const services = [
|
|
199
|
+
{
|
|
200
|
+
url: "https://ipinfo.io/json",
|
|
201
|
+
parse: (d) => ({ IP: d.ip, ISP: d.org || "", country: d.country || "" })
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
url: "https://ipwhois.app/json/",
|
|
205
|
+
parse: (d) => ({ IP: d.ip, ISP: d.isp || "", country: d.country_code || "" })
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
url: "https://api.ipify.org?format=json",
|
|
209
|
+
parse: (d) => ({ IP: d.ip, ISP: "", country: "" })
|
|
210
|
+
}
|
|
211
|
+
];
|
|
212
|
+
for (const { url, parse } of services) {
|
|
213
|
+
const data = await fetchJSON(url);
|
|
214
|
+
if (data) {
|
|
215
|
+
const result = parse(data);
|
|
216
|
+
if (result.IP) {
|
|
217
|
+
cache.IP = result.IP;
|
|
218
|
+
cache.ISP = result.ISP || cache.ISP;
|
|
219
|
+
cache.country = result.country || cache.country;
|
|
220
|
+
cache.lastUpdated = Date.now();
|
|
221
|
+
return result;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return { IP: cache.IP, ISP: cache.ISP, country: cache.country };
|
|
226
|
+
}
|
|
227
|
+
async function getIP(forceRefresh = false) {
|
|
228
|
+
const info = await getNetworkInfo(forceRefresh);
|
|
229
|
+
return info.IP;
|
|
230
|
+
}
|
|
231
|
+
async function getISP(forceRefresh = false) {
|
|
232
|
+
const info = await getNetworkInfo(forceRefresh);
|
|
233
|
+
return info.ISP;
|
|
234
|
+
}
|
|
235
|
+
async function getCountry(forceRefresh = false) {
|
|
236
|
+
const info = await getNetworkInfo(forceRefresh);
|
|
237
|
+
return info.country;
|
|
238
|
+
}
|
|
239
|
+
function getConnection() {
|
|
240
|
+
const conn = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
|
|
241
|
+
if (!conn) return null;
|
|
242
|
+
return {
|
|
243
|
+
type: conn.type || "unknown",
|
|
244
|
+
effectiveType: conn.effectiveType || "unknown",
|
|
245
|
+
downlink: conn.downlink || 0,
|
|
246
|
+
rtt: conn.rtt || 0,
|
|
247
|
+
saveData: conn.saveData || false
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function getLocation(options = { timeout: 1e4, enableHighAccuracy: false }) {
|
|
251
|
+
return new Promise((resolve) => {
|
|
252
|
+
if (!navigator.geolocation) {
|
|
253
|
+
resolve(null);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
navigator.geolocation.getCurrentPosition(
|
|
257
|
+
(pos) => resolve({
|
|
258
|
+
latitude: pos.coords.latitude,
|
|
259
|
+
longitude: pos.coords.longitude,
|
|
260
|
+
accuracy: pos.coords.accuracy,
|
|
261
|
+
timestamp: pos.timestamp
|
|
262
|
+
}),
|
|
263
|
+
() => resolve(null),
|
|
264
|
+
options
|
|
265
|
+
);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
const networkCache = cache;
|
|
269
|
+
function getScreen() {
|
|
270
|
+
var _a;
|
|
271
|
+
const { screen } = window;
|
|
272
|
+
return {
|
|
273
|
+
width: screen.width,
|
|
274
|
+
height: screen.height,
|
|
275
|
+
availWidth: screen.availWidth,
|
|
276
|
+
availHeight: screen.availHeight,
|
|
277
|
+
colorDepth: screen.colorDepth,
|
|
278
|
+
pixelRatio: window.devicePixelRatio || 1,
|
|
279
|
+
orientation: ((_a = screen.orientation) == null ? void 0 : _a.type) || (window.innerWidth > window.innerHeight ? "landscape-primary" : "portrait-primary")
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
async function getBattery() {
|
|
283
|
+
if (!("getBattery" in navigator)) {
|
|
284
|
+
return { isCharging: false, level: 0, chargingTime: Infinity, dischargingTime: Infinity };
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
const battery = await navigator.getBattery();
|
|
288
|
+
return {
|
|
289
|
+
isCharging: battery.charging,
|
|
290
|
+
level: Math.round(battery.level * 100),
|
|
291
|
+
chargingTime: battery.chargingTime,
|
|
292
|
+
dischargingTime: battery.dischargingTime
|
|
293
|
+
};
|
|
294
|
+
} catch {
|
|
295
|
+
return { isCharging: false, level: 0, chargingTime: Infinity, dischargingTime: Infinity };
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function detectOS(hev, parsedOS) {
|
|
299
|
+
if (hev.platform) {
|
|
300
|
+
const platform = hev.platform;
|
|
301
|
+
const platformVersion = hev.platformVersion || "";
|
|
302
|
+
if (platform === "Windows") {
|
|
303
|
+
const majorVer = parseInt(platformVersion.split(".")[0], 10);
|
|
304
|
+
const winVer = majorVer >= 13 ? 11 : 10;
|
|
305
|
+
return { name: "win", version: winVer, string: `Windows ${winVer}` };
|
|
306
|
+
}
|
|
307
|
+
if (platform === "macOS") {
|
|
308
|
+
return { name: "mac", version: platformVersion, string: `macOS ${platformVersion}` };
|
|
309
|
+
}
|
|
310
|
+
if (platform === "Android") {
|
|
311
|
+
return { name: "android", version: platformVersion, string: `Android ${platformVersion}` };
|
|
312
|
+
}
|
|
313
|
+
if (platform === "iOS") {
|
|
314
|
+
return { name: "ios", version: platformVersion, string: `iOS ${platformVersion}` };
|
|
315
|
+
}
|
|
316
|
+
if (platform === "Linux") {
|
|
317
|
+
return { name: "linux", version: "", string: "Linux" };
|
|
318
|
+
}
|
|
319
|
+
if (platform === "Chrome OS") {
|
|
320
|
+
return { name: "chromeos", version: platformVersion, string: `Chrome OS ${platformVersion}` };
|
|
321
|
+
}
|
|
322
|
+
return { name: platform.toLowerCase(), version: platformVersion, string: `${platform} ${platformVersion}` };
|
|
323
|
+
}
|
|
324
|
+
if (parsedOS) {
|
|
325
|
+
const family = parsedOS.family || "";
|
|
326
|
+
if (family.includes("Windows")) {
|
|
327
|
+
const ver = parseFloat(parsedOS.version) || 0;
|
|
328
|
+
return { name: "win", version: ver, string: family };
|
|
329
|
+
}
|
|
330
|
+
if (family.includes("macOS") || family.includes("Mac OS X")) {
|
|
331
|
+
return { name: "mac", version: parsedOS.version, string: family };
|
|
332
|
+
}
|
|
333
|
+
if (family.includes("Android")) {
|
|
334
|
+
return { name: "android", version: parsedOS.version, string: family };
|
|
335
|
+
}
|
|
336
|
+
if (family.includes("iOS")) {
|
|
337
|
+
return { name: "ios", version: parsedOS.version, string: family };
|
|
338
|
+
}
|
|
339
|
+
if (family.includes("Linux")) {
|
|
340
|
+
return { name: "linux", version: "", string: "Linux" };
|
|
341
|
+
}
|
|
342
|
+
return { name: family.toLowerCase(), version: parsedOS.version || "", string: family };
|
|
343
|
+
}
|
|
344
|
+
return { name: "", version: "", string: "Unknown OS" };
|
|
345
|
+
}
|
|
346
|
+
function detectBrowser(hev, parsedUA) {
|
|
347
|
+
var _a;
|
|
348
|
+
const brands = hev.fullVersionList || hev.brands || [];
|
|
349
|
+
if (brands.length) {
|
|
350
|
+
const knownBrowsers = ["Chrome", "Firefox", "Safari", "Edge", "Opera", "Brave", "Vivaldi", "Arc"];
|
|
351
|
+
for (const brand of brands) {
|
|
352
|
+
if (/Not.?A.?Brand|Chromium/i.test(brand.brand)) continue;
|
|
353
|
+
const match = knownBrowsers.find((b) => brand.brand.includes(b));
|
|
354
|
+
if (match) {
|
|
355
|
+
const majorVersion = brand.version.split(".")[0];
|
|
356
|
+
return `${brand.brand} ${majorVersion}`;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
for (const brand of brands) {
|
|
360
|
+
if (!/Not.?A.?Brand|Chromium/i.test(brand.brand)) {
|
|
361
|
+
return `${brand.brand} ${brand.version.split(".")[0]}`;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (parsedUA.name) {
|
|
366
|
+
const majorVersion = ((_a = parsedUA.version) == null ? void 0 : _a.split(".")[0]) || "";
|
|
367
|
+
return `${parsedUA.name} ${majorVersion}`.trim();
|
|
368
|
+
}
|
|
369
|
+
return "Unknown Browser";
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* @fileoverview aki-info-detect - Browser information detection library
|
|
373
|
+
* @author lacvietanh
|
|
374
|
+
* @version 2.0.0
|
|
375
|
+
* @license MIT
|
|
376
|
+
* @see https://github.com/lacvietanh/akiInfoDetect.js
|
|
377
|
+
*
|
|
378
|
+
* @description
|
|
379
|
+
* Lightweight library for detecting device, browser, hardware, and network information.
|
|
380
|
+
* Works in browser environments only. Supports ES Modules and UMD.
|
|
381
|
+
*
|
|
382
|
+
* @example
|
|
383
|
+
* import akiInfoDetect from 'aki-info-detect';
|
|
384
|
+
* const info = await akiInfoDetect();
|
|
385
|
+
* console.log(info.browser, info.os.string, info.GPU);
|
|
386
|
+
*/
|
|
387
|
+
async function akiInfoDetect(forceNetworkRefresh = false) {
|
|
388
|
+
const [hev, battery, gpu] = await Promise.all([
|
|
389
|
+
getHighEntropyValues(),
|
|
390
|
+
getBattery(),
|
|
391
|
+
Promise.resolve(detectGPU())
|
|
392
|
+
]);
|
|
393
|
+
const parsedUA = parseUserAgent();
|
|
394
|
+
const hardware = getHardwareInfo(gpu);
|
|
395
|
+
const os = detectOS(hev, parsedUA.os);
|
|
396
|
+
const browser = detectBrowser(hev, parsedUA);
|
|
397
|
+
const isMobile = hev.mobile ?? /Mobile|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
398
|
+
const languages = (navigator.languages || [navigator.language]).slice(0, 3).map((l) => l.substring(0, 2).toLowerCase()).join(" ");
|
|
399
|
+
if (forceNetworkRefresh || !networkCache.IP) {
|
|
400
|
+
getNetworkInfo(forceNetworkRefresh).catch(() => {
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
return {
|
|
404
|
+
// Basic info
|
|
405
|
+
browser,
|
|
406
|
+
product: parsedUA.product,
|
|
407
|
+
manufacturer: parsedUA.manufacturer,
|
|
408
|
+
isMobile,
|
|
409
|
+
language: languages,
|
|
410
|
+
// Hardware
|
|
411
|
+
CPU: hardware.CPU,
|
|
412
|
+
CPUCore: hardware.CPUCore,
|
|
413
|
+
arch: hev.architecture || hardware.arch,
|
|
414
|
+
RAM: hardware.RAM,
|
|
415
|
+
GPU: hardware.GPU,
|
|
416
|
+
// Battery
|
|
417
|
+
battery,
|
|
418
|
+
// OS
|
|
419
|
+
os,
|
|
420
|
+
// Network (reactive getters from cache)
|
|
421
|
+
network: {
|
|
422
|
+
get IP() {
|
|
423
|
+
return networkCache.IP;
|
|
424
|
+
},
|
|
425
|
+
get ISP() {
|
|
426
|
+
return networkCache.ISP;
|
|
427
|
+
},
|
|
428
|
+
get country() {
|
|
429
|
+
return networkCache.country;
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
// Async methods
|
|
433
|
+
getIP,
|
|
434
|
+
getISP,
|
|
435
|
+
getCountry,
|
|
436
|
+
getNetworkInfo,
|
|
437
|
+
getLocation,
|
|
438
|
+
getConnection,
|
|
439
|
+
getScreen
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
export {
|
|
443
|
+
akiInfoDetect,
|
|
444
|
+
akiInfoDetect as default,
|
|
445
|
+
detectGPU,
|
|
446
|
+
getBattery,
|
|
447
|
+
getConnection,
|
|
448
|
+
getCountry,
|
|
449
|
+
getHighEntropyValues,
|
|
450
|
+
getIP,
|
|
451
|
+
getISP,
|
|
452
|
+
getLocation,
|
|
453
|
+
getNetworkInfo,
|
|
454
|
+
getScreen,
|
|
455
|
+
parseUserAgent
|
|
456
|
+
};
|
|
457
|
+
//# sourceMappingURL=aki-info-detect.js.map
|
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
/*!
|
|
3
|
+
* aki-info-detect v2.0.0
|
|
4
|
+
* (c) 2025 lacvietanh
|
|
5
|
+
* Released under the MIT License
|
|
6
|
+
* https://github.com/lacvietanh/akiInfoDetect.js
|
|
7
|
+
*/function t(e=navigator.userAgent){let t=null,n="",r="",i="",o="";const a=[{test:/Edg(?:e)?\//,name:"Edge",regex:/Edg(?:e)?\/([\d.]+)/},{test:/OPR\/|Opera\//,name:"Opera",regex:/(?:OPR|Opera)\/([\d.]+)/},{test:/Firefox\//,name:"Firefox",regex:/Firefox\/([\d.]+)/,exclude:/Seamonkey/},{test:/Chrome\//,name:"Chrome",regex:/Chrome\/([\d.]+)/,exclude:/Edge|Edg|OPR|Opera/},{test:/Safari\//,name:"Safari",regex:/Version\/([\d.]+)/,exclude:/Chrome|Edge|Edg|OPR|Opera/},{test:/MSIE|Trident/,name:"IE",regex:/(?:MSIE |rv:)([\d.]+)/}];for(const{test:c,name:u,regex:l,exclude:d}of a)if(c.test(e)&&(!d||!d.test(e))){n=u;const t=e.match(l);r=(null==t?void 0:t[1])||"";break}const s=[{test:/Windows/,parse:()=>{const t=e.match(/Windows NT ([\d.]+)/),n=(null==t?void 0:t[1])||"",r={"10.0":"10",6.3:"8.1",6.2:"8",6.1:"7","6.0":"Vista",5.1:"XP"}[n]||n;return{family:`Windows ${r}`,version:r,architecture:/WOW64|Win64|x64/.test(e)?64:32}}},{test:/Mac OS X/,parse:()=>{var t;const n=e.match(/Mac OS X ([\d_.]+)/),r=(null==(t=null==n?void 0:n[1])?void 0:t.replace(/_/g,"."))||"";return{family:`macOS ${r}`,version:r,architecture:64}}},{test:/Android/,parse:()=>{const t=e.match(/Android ([\d.]+)/),n=(null==t?void 0:t[1])||"";return{family:`Android ${n}`,version:n,architecture:64}}},{test:/iPhone|iPad|iPod/,parse:()=>{var t;const n=e.match(/OS ([\d_]+)/),r=(null==(t=null==n?void 0:n[1])?void 0:t.replace(/_/g,"."))||"";return{family:`iOS ${r}`,version:r,architecture:64}}},{test:/Linux/,parse:()=>({family:"Linux",version:"",architecture:/x86_64|amd64/.test(e)?64:32})}];for(const{test:c,parse:u}of s)if(c.test(e)){t=u();break}if(/iPhone/.test(e))i="iPhone",o="Apple";else if(/iPad/.test(e))i="iPad",o="Apple";else if(/Android/.test(e)){const t=e.match(/Android[^;]+;\s*([^;)]+)/);if(t){i=t[1].trim();const e=i.match(/^(Samsung|LG|Motorola|HTC|Huawei|Xiaomi|OnePlus|Google|Sony|OPPO|Vivo|Realme)/i);o=(null==e?void 0:e[1])||""}}return{name:n,version:r,product:i,manufacturer:o,os:t,layout:/AppleWebKit/.test(e)?"WebKit":/Gecko\//.test(e)?"Gecko":/Trident/.test(e)?"Trident":""}}async function n(){var e;if(!(null==(e=navigator.userAgentData)?void 0:e.getHighEntropyValues))return{};try{return await navigator.userAgentData.getHighEntropyValues(["platform","platformVersion","architecture","model","mobile","bitness","brands","fullVersionList"])}catch{return{}}}function r(){try{const e=document.createElement("canvas"),t=e.getContext("webgl")||e.getContext("experimental-webgl");if(!t)return"No WebGL support";const n=t.getExtension("WEBGL_debug_renderer_info");return n?t.getParameter(n.UNMASKED_RENDERER_WEBGL)||"Unknown GPU":"GPU info restricted"}catch{return"GPU detection failed"}}const i={IP:"",ISP:"",country:"",lastUpdated:0,TTL:36e5};async function o(e,t=2500){const n=new AbortController,r=setTimeout(()=>n.abort(),t);try{const t=await fetch(e,{signal:n.signal});return clearTimeout(r),t.ok?await t.json():null}catch{return clearTimeout(r),null}}async function a(e=!1){if(!e&&i.IP&&Date.now()-i.lastUpdated<i.TTL)return{IP:i.IP,ISP:i.ISP,country:i.country};const t=[{url:"https://ipinfo.io/json",parse:e=>({IP:e.ip,ISP:e.org||"",country:e.country||""})},{url:"https://ipwhois.app/json/",parse:e=>({IP:e.ip,ISP:e.isp||"",country:e.country_code||""})},{url:"https://api.ipify.org?format=json",parse:e=>({IP:e.ip,ISP:"",country:""})}];for(const{url:n,parse:r}of t){const e=await o(n);if(e){const t=r(e);if(t.IP)return i.IP=t.IP,i.ISP=t.ISP||i.ISP,i.country=t.country||i.country,i.lastUpdated=Date.now(),t}}return{IP:i.IP,ISP:i.ISP,country:i.country}}async function s(e=!1){return(await a(e)).IP}async function c(e=!1){return(await a(e)).ISP}async function u(e=!1){return(await a(e)).country}function l(){const e=navigator.connection||navigator.mozConnection||navigator.webkitConnection;return e?{type:e.type||"unknown",effectiveType:e.effectiveType||"unknown",downlink:e.downlink||0,rtt:e.rtt||0,saveData:e.saveData||!1}:null}function d(e={timeout:1e4,enableHighAccuracy:!1}){return new Promise(t=>{navigator.geolocation?navigator.geolocation.getCurrentPosition(e=>t({latitude:e.coords.latitude,longitude:e.coords.longitude,accuracy:e.coords.accuracy,timestamp:e.timestamp}),()=>t(null),e):t(null)})}const g=i;function m(){var e;const{screen:t}=window;return{width:t.width,height:t.height,availWidth:t.availWidth,availHeight:t.availHeight,colorDepth:t.colorDepth,pixelRatio:window.devicePixelRatio||1,orientation:(null==(e=t.orientation)?void 0:e.type)||(window.innerWidth>window.innerHeight?"landscape-primary":"portrait-primary")}}async function p(){if(!("getBattery"in navigator))return{isCharging:!1,level:0,chargingTime:1/0,dischargingTime:1/0};try{const e=await navigator.getBattery();return{isCharging:e.charging,level:Math.round(100*e.level),chargingTime:e.chargingTime,dischargingTime:e.dischargingTime}}catch{return{isCharging:!1,level:0,chargingTime:1/0,dischargingTime:1/0}}}
|
|
8
|
+
/**
|
|
9
|
+
* @fileoverview aki-info-detect - Browser information detection library
|
|
10
|
+
* @author lacvietanh
|
|
11
|
+
* @version 2.0.0
|
|
12
|
+
* @license MIT
|
|
13
|
+
* @see https://github.com/lacvietanh/akiInfoDetect.js
|
|
14
|
+
*
|
|
15
|
+
* @description
|
|
16
|
+
* Lightweight library for detecting device, browser, hardware, and network information.
|
|
17
|
+
* Works in browser environments only. Supports ES Modules and UMD.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* import akiInfoDetect from 'aki-info-detect';
|
|
21
|
+
* const info = await akiInfoDetect();
|
|
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"}})});
|
|
25
|
+
//# sourceMappingURL=aki-info-detect.umd.cjs.map
|
|
@@ -0,0 +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":";;;;;;GAkBO,SAASA,EAAeC,EAAKC,UAAUC,WAC5C,IAAIC,EAAK,KACLC,EAAO,GACPC,EAAU,GACVC,EAAU,GACVC,EAAe,GAGnB,MAAMC,EAAkB,CACtB,CAAEC,KAAM,cAAeL,KAAM,OAAQM,MAAO,uBAC5C,CAAED,KAAM,gBAAiBL,KAAM,QAASM,MAAO,2BAC/C,CAAED,KAAM,YAAaL,KAAM,UAAWM,MAAO,oBAAqBC,QAAS,aAC3E,CAAEF,KAAM,WAAYL,KAAM,SAAUM,MAAO,mBAAoBC,QAAS,sBACxE,CAAEF,KAAM,WAAYL,KAAM,SAAUM,MAAO,oBAAqBC,QAAS,6BACzE,CAAEF,KAAM,eAAgBL,KAAM,KAAMM,MAAO,0BAG7C,IAAA,MAAWD,KAAEA,EAAML,KAAMQ,QAAaF,EAAAC,QAAOA,KAAaH,EACxD,GAAIC,EAAKA,KAAKT,MAASW,IAAYA,EAAQF,KAAKT,IAAM,CACpDI,EAAOQ,EACP,MAAMC,EAAQb,EAAGa,MAAMH,GACvBL,SAAUQ,WAAQ,KAAM,GACxB,KACF,CAIF,MAAMC,EAAa,CACjB,CACEL,KAAM,UACNM,MAAO,KACL,MACMF,EAAQb,EAAGa,MAAM,uBACjBG,SAAQH,WAAQ,KAAM,GACtBI,EAHS,CAAE,OAAQ,KAAM,IAAO,MAAO,IAAO,IAAK,IAAO,IAAK,MAAO,QAAS,IAAO,MAGtED,IAAUA,EAChC,MAAO,CACLE,OAAQ,WAAWD,IACnBZ,QAASY,EACTE,aAAc,kBAAkBV,KAAKT,GAAM,GAAK,MAItD,CACES,KAAM,WACNM,MAAO,WACL,MAAMF,EAAQb,EAAGa,MAAM,sBACjBO,GAAM,OAAAC,EAAA,MAAAR,OAAA,EAAAA,EAAQ,SAAR,EAAAQ,EAAYC,QAAQ,KAAM,OAAQ,GAC9C,MAAO,CAAEJ,OAAQ,SAASE,IAAOf,QAASe,EAAKD,aAAc,MAGjE,CACEV,KAAM,UACNM,MAAO,KACL,MAAMF,EAAQb,EAAGa,MAAM,oBACjBO,SAAMP,WAAQ,KAAM,GAC1B,MAAO,CAAEK,OAAQ,WAAWE,IAAOf,QAASe,EAAKD,aAAc,MAGnE,CACEV,KAAM,mBACNM,MAAO,WACL,MAAMF,EAAQb,EAAGa,MAAM,eACjBO,GAAM,OAAAC,EAAA,MAAAR,OAAA,EAAAA,EAAQ,SAAR,EAAAQ,EAAYC,QAAQ,KAAM,OAAQ,GAC9C,MAAO,CAAEJ,OAAQ,OAAOE,IAAOf,QAASe,EAAKD,aAAc,MAG/D,CACEV,KAAM,QACNM,MAAO,KAAA,CACLG,OAAQ,QACRb,QAAS,GACTc,aAAc,eAAeV,KAAKT,GAAM,GAAK,OAKnD,IAAA,MAAWS,KAAEA,EAAAM,MAAMA,KAAWD,EAC5B,GAAIL,EAAKA,KAAKT,GAAK,CACjBG,EAAKY,IACL,KACF,CAIF,GAAI,SAASN,KAAKT,GAChBM,EAAU,SACVC,EAAe,aACjB,GAAW,OAAOE,KAAKT,GACrBM,EAAU,OACVC,EAAe,aACjB,GAAW,UAAUE,KAAKT,GAAK,CAC7B,MAAMa,EAAQb,EAAGa,MAAM,4BACvB,GAAIA,EAAO,CACTP,EAAUO,EAAM,GAAGU,OAEnB,MAAMC,EAAWlB,EAAQO,MAAM,kFAC/BN,SAAeiB,WAAW,KAAM,EAClC,CACF,CAOA,MAAO,CAAEpB,OAAMC,UAASC,UAASC,eAAcJ,KAAIsB,OAJpC,cAAchB,KAAKT,GAAM,SACzB,UAAUS,KAAKT,GAAM,QACrB,UAAUS,KAAKT,GAAM,UAAY,GAGlD,CAMO0B,eAAeC,UACpB,KAAK,OAAAN,EAAApB,UAAU2B,oBAAV,EAAAP,EAAyBM,sBAC5B,MAAO,CAAA,EAGT,IACE,aAAa1B,UAAU2B,cAAcD,qBAAqB,CACxD,WACA,kBACA,eACA,QACA,SACA,UACA,SACA,mBAEJ,CAAA,MACE,MAAO,CAAA,CACT,CACF,CC5IO,SAASE,IACd,IACE,MAAMC,EAASC,SAASC,cAAc,UAChCC,EAAKH,EAAOI,WAAW,UAAYJ,EAAOI,WAAW,sBAE3D,IAAKD,EAAI,MAAO,mBAEhB,MAAME,EAAYF,EAAGG,aAAa,6BAClC,OAAKD,EAEEF,EAAGI,aAAaF,EAAUG,0BAA4B,cAFtC,qBAGzB,CAAA,MACE,MAAO,sBACT,CACF,CCjBA,MAAMC,EAAQ,CACZC,GAAI,GACJC,IAAK,GACLC,QAAS,GACTC,YAAa,EACbC,IAAK,MASPlB,eAAemB,EAAUC,EAAKC,EAAU,MACtC,MAAMC,EAAa,IAAIC,gBACjBC,EAAYC,WAAW,IAAMH,EAAWI,QAASL,GAEvD,IACE,MAAMM,QAAYC,MAAMR,EAAK,CAAES,OAAQP,EAAWO,SAElD,OADAC,aAAaN,GACNG,EAAII,SAAWJ,EAAIK,OAAS,IACrC,CAAA,MAEE,OADAF,aAAaN,GACN,IACT,CACF,CAYOxB,eAAeiC,EAAeC,GAAe,GAElD,IAAKA,GAAgBrB,EAAMC,IAAMqB,KAAKC,MAAQvB,EAAMI,YAAcJ,EAAMK,IACtE,MAAO,CAAEJ,GAAID,EAAMC,GAAIC,IAAKF,EAAME,IAAKC,QAASH,EAAMG,SAIxD,MAAMqB,EAAW,CACf,CACEjB,IAAK,yBACL/B,MAAQiD,IAAA,CAASxB,GAAIwB,EAAEC,GAAIxB,IAAKuB,EAAEE,KAAO,GAAIxB,QAASsB,EAAEtB,SAAW,MAErE,CACEI,IAAK,4BACL/B,MAAQiD,IAAA,CAASxB,GAAIwB,EAAEC,GAAIxB,IAAKuB,EAAEG,KAAO,GAAIzB,QAASsB,EAAEI,cAAgB,MAE1E,CACEtB,IAAK,oCACL/B,MAAQiD,IAAA,CAASxB,GAAIwB,EAAEC,GAAIxB,IAAK,GAAIC,QAAS,OAIjD,IAAA,MAAWI,IAAEA,EAAA/B,MAAKA,KAAWgD,EAAU,CACrC,MAAMM,QAAaxB,EAAUC,GAC7B,GAAIuB,EAAM,CACR,MAAMC,EAASvD,EAAMsD,GACrB,GAAIC,EAAO9B,GAKT,OAJAD,EAAMC,GAAK8B,EAAO9B,GAClBD,EAAME,IAAM6B,EAAO7B,KAAOF,EAAME,IAChCF,EAAMG,QAAU4B,EAAO5B,SAAWH,EAAMG,QACxCH,EAAMI,YAAckB,KAAKC,MAClBQ,CAEX,CACF,CAGA,MAAO,CAAE9B,GAAID,EAAMC,GAAIC,IAAKF,EAAME,IAAKC,QAASH,EAAMG,QACxD,CAOOhB,eAAe6C,EAAMX,GAAe,GAEzC,aADmBD,EAAeC,IACtBpB,EACd,CAOOd,eAAe8C,EAAOZ,GAAe,GAE1C,aADmBD,EAAeC,IACtBnB,GACd,CAOOf,eAAe+C,EAAWb,GAAe,GAE9C,aADmBD,EAAeC,IACtBlB,OACd,CAaO,SAASgC,IACd,MAAMC,EAAO1E,UAAU2E,YAAc3E,UAAU4E,eAAiB5E,UAAU6E,iBAE1E,OAAKH,EAEE,CACLI,KAAMJ,EAAKI,MAAQ,UACnBC,cAAeL,EAAKK,eAAiB,UACrCC,SAAUN,EAAKM,UAAY,EAC3BC,IAAKP,EAAKO,KAAO,EACjBC,SAAUR,EAAKQ,WAAY,GAPX,IASpB,CAaO,SAASC,EAAYC,EAAU,CAAEtC,QAAS,IAAOuC,oBAAoB,IAC1E,OAAO,IAAIC,QAASC,IACbvF,UAAUwF,YAKfxF,UAAUwF,YAAYC,mBACnBC,GAAQH,EAAQ,CACfI,SAAUD,EAAIE,OAAOD,SACrBE,UAAWH,EAAIE,OAAOC,UACtBC,SAAUJ,EAAIE,OAAOE,SACrBC,UAAWL,EAAIK,YAEjB,IAAMR,EAAQ,MACdH,GAZAG,EAAQ,OAed,CAGO,MAAMS,EAAe1D,ECzJrB,SAAS2D,UACd,MAAMC,OAAEA,GAAWC,OAEnB,MAAO,CACLC,MAAOF,EAAOE,MACdC,OAAQH,EAAOG,OACfC,WAAYJ,EAAOI,WACnBC,YAAaL,EAAOK,YACpBC,WAAYN,EAAOM,WACnBC,WAAYN,OAAOO,kBAAoB,EACvCC,aAAa,OAAAvF,IAAOuF,kBAAP,EAAAvF,EAAoB0D,QACnBqB,OAAOS,WAAaT,OAAOU,YAAc,oBAAsB,oBAEjF,CChBOpF,eAAeqF,IAEpB,KAAM,eAAgB9G,WACpB,MAAO,CAAE+G,YAAY,EAAOC,MAAO,EAAGC,aAAcC,IAAUC,gBAAiBD,KAGjF,IACE,MAAME,QAAgBpH,UAAU8G,aAChC,MAAO,CACLC,WAAYK,EAAQC,SACpBL,MAAOM,KAAKC,MAAsB,IAAhBH,EAAQJ,OAC1BC,aAAcG,EAAQH,aACtBE,gBAAiBC,EAAQD,gBAE7B,CAAA,MACE,MAAO,CAAEJ,YAAY,EAAOC,MAAO,EAAGC,aAAcC,IAAUC,gBAAiBD,IACjF,CACF;;;;;;;;;;;;;;;;;ACsBAzF,eAAe+F,EAAcC,GAAsB,GAEjD,MAAOC,EAAKN,EAASO,SAAarC,QAAQsC,IAAI,CAC5ClG,IACAoF,IACAxB,QAAQC,QAAQ3D,OAIZiG,EAAW/H,IAGXgI,EJWD,SAAyBH,SAC9B,MAAMI,EA/CD,SAAuBJ,GAC5B,MAAMK,EAAWL,EAAIM,cAGfC,EAAoBP,EAAI/G,MAAM,0CACpC,GAAIsH,EAAmB,CACrB,MAAMC,EAAUD,EAAkB,GAC5BE,EAAUF,EAAkB,IAAM,GACxC,MAAO,CACLpD,KAAM,gBACNuD,KAAM,IAAIF,IAAUC,EAAU,IAAMA,EAAU,KAC9ClH,aAAc,QAElB,CAGA,GAAI8G,EAASM,SAAS,SACpB,MAAO,CAAExD,KAAM,gBAAiBuD,KAAM,YAAanH,aAAc,SAInE,MAAMqH,EAAcZ,EAAI/G,MAAM,+BAAiC+G,EAAI/G,MAAM,uCACzE,GAAI2H,EACF,MAAO,CAAEzD,KAAM,SAAUuD,KAAME,EAAY,GAAGjH,OAAQJ,aAAc,UAItE,MAAMsH,EAAWb,EAAI/G,MAAM,2BAC3B,GAAI4H,EACF,MAAO,CAAE1D,KAAM,MAAOuD,KAAMG,EAAS,GAAGlH,OAAQJ,aAAc,UAIhE,MAAMuH,EAAad,EAAI/G,MAAM,kDAC7B,OAAI6H,GAAcT,EAASM,SAAS,SAC3B,CAAExD,KAAM,QAASuD,YAAMI,WAAa,KAAM,iBAAkBvH,aAAc,UAG5E,CAAE4D,KAAM,UAAWuD,KAAMV,EAAKzG,aAAc,GACrD,CAQmBwH,CAAcf,GAE/B,MAAO,CACLgB,IAAK3I,UAAU4I,cAAgB,EAC/BC,QAAS7I,UAAU8I,qBAAuB,EAC1CC,IAAKpB,EACLqB,IAAKjB,EAASjD,KACdmE,KAAMlB,EAAS7G,eAAuD,WAAtC,OAAAE,EAAApB,UAAU2B,oBAAV,EAAAP,EAAyB8H,UAAuB,QAAU,UAE9F,CIrBmBC,CAAgBxB,GAG3BzH,ECrDD,SAAkBwH,EAAK0B,GAE5B,GAAI1B,EAAIwB,SAAU,CAChB,MAAMA,EAAWxB,EAAIwB,SACfG,EAAkB3B,EAAI2B,iBAAmB,GAE/C,GAAiB,YAAbH,EAAwB,CAE1B,MACMlI,EADWsI,SAASD,EAAgBE,MAAM,KAAK,GAAI,KAC9B,GAAK,GAAK,GACrC,MAAO,CAAEpJ,KAAM,MAAOC,QAASY,EAAQwI,OAAQ,WAAWxI,IAC5D,CAEA,MAAiB,UAAbkI,EACK,CAAE/I,KAAM,MAAOC,QAASiJ,EAAiBG,OAAQ,SAASH,KAGlD,YAAbH,EACK,CAAE/I,KAAM,UAAWC,QAASiJ,EAAiBG,OAAQ,WAAWH,KAGxD,QAAbH,EACK,CAAE/I,KAAM,MAAOC,QAASiJ,EAAiBG,OAAQ,OAAOH,KAGhD,UAAbH,EACK,CAAE/I,KAAM,QAASC,QAAS,GAAIoJ,OAAQ,SAG9B,cAAbN,EACK,CAAE/I,KAAM,WAAYC,QAASiJ,EAAiBG,OAAQ,aAAaH,KAIrE,CAAElJ,KAAM+I,EAASjB,cAAe7H,QAASiJ,EAAiBG,OAAQ,GAAGN,KAAYG,IAC1F,CAGA,GAAID,EAAU,CACZ,MAAMnI,EAASmI,EAASnI,QAAU,GAElC,OAAIA,EAAOqH,SAAS,WAEX,CAAEnI,KAAM,MAAOC,QADVqJ,WAAWL,EAAShJ,UAAY,EACRoJ,OAAQvI,GAE1CA,EAAOqH,SAAS,UAAYrH,EAAOqH,SAAS,YACvC,CAAEnI,KAAM,MAAOC,QAASgJ,EAAShJ,QAASoJ,OAAQvI,GAEvDA,EAAOqH,SAAS,WACX,CAAEnI,KAAM,UAAWC,QAASgJ,EAAShJ,QAASoJ,OAAQvI,GAE3DA,EAAOqH,SAAS,OACX,CAAEnI,KAAM,MAAOC,QAASgJ,EAAShJ,QAASoJ,OAAQvI,GAEvDA,EAAOqH,SAAS,SACX,CAAEnI,KAAM,QAASC,QAAS,GAAIoJ,OAAQ,SAGxC,CAAErJ,KAAMc,EAAOgH,cAAe7H,QAASgJ,EAAShJ,SAAW,GAAIoJ,OAAQvI,EAChF,CAEA,MAAO,CAAEd,KAAM,GAAIC,QAAS,GAAIoJ,OAAQ,aAC1C,CDTaE,CAAShC,EAAKG,EAAS3H,IAG5ByJ,EE7DD,SAAuBjC,EAAKG,SAEjC,MAAM+B,EAASlC,EAAImC,iBAAmBnC,EAAIkC,QAAU,GAEpD,GAAIA,EAAOE,OAAQ,CAEjB,MAAMC,EAAgB,CAAC,SAAU,UAAW,SAAU,OAAQ,QAAS,QAAS,UAAW,OAE3F,IAAA,MAAWC,KAASJ,EAElB,IAAI,0BAA0BpJ,KAAKwJ,EAAMA,QAG3BD,EAAcE,KAAKC,GAAKF,EAAMA,MAAM1B,SAAS4B,IAChD,CACT,MAAMC,EAAeH,EAAM5J,QAAQmJ,MAAM,KAAK,GAC9C,MAAO,GAAGS,EAAMA,SAASG,GAC3B,CAIF,IAAA,MAAWH,KAASJ,EAClB,IAAK,0BAA0BpJ,KAAKwJ,EAAMA,OACxC,MAAO,GAAGA,EAAMA,SAASA,EAAM5J,QAAQmJ,MAAM,KAAK,IAGxD,CAGA,GAAI1B,EAAS1H,KAAM,CACjB,MAAMgK,GAAe,OAAA/I,EAAAyG,EAASzH,cAAT,EAAAgB,EAAkBmI,MAAM,KAAK,KAAM,GACxD,MAAO,GAAG1B,EAAS1H,QAAQgK,IAAe7I,MAC5C,CAEA,MAAO,iBACT,CF0BkB8I,CAAc1C,EAAKG,GAG7BwC,EAAW3C,EAAI4C,QAAU,mCAAmC9J,KAAKR,UAAUC,WAG3EsK,GAAavK,UAAUuK,WAAa,CAACvK,UAAUwK,WAClDC,MAAM,EAAG,GACTC,IAAIC,GAAKA,EAAEC,UAAU,EAAG,GAAG3C,eAC3B4C,KAAK,KAOR,OAJIpD,GAAwBzB,EAAazD,IACvCmB,EAAe+D,GAAqBqD,MAAM,QAGrC,CAELnB,UACAtJ,QAASwH,EAASxH,QAClBC,aAAcuH,EAASvH,aACvB+J,WACAG,SAAUD,EAGVvB,IAAKlB,EAASkB,IACdH,QAASf,EAASe,QAClBI,KAAMvB,EAAIxG,cAAgB4G,EAASmB,KACnCN,IAAKb,EAASa,IACdI,IAAKjB,EAASiB,IAGd3B,UAGAlH,KAGA6K,QAAS,CACP,MAAIxI,GAAO,OAAOyD,EAAazD,EAAI,EACnC,OAAIC,GAAQ,OAAOwD,EAAaxD,GAAK,EACrC,WAAIC,GAAY,OAAOuD,EAAavD,OAAS,GAI/C6B,QACAC,SACAC,aACAd,iBACAyB,cACAV,gBACAwB,YAEJ"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* aki-info-detect - TypeScript declarations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface BatteryInfo {
|
|
6
|
+
isCharging: boolean;
|
|
7
|
+
level: number;
|
|
8
|
+
chargingTime: number;
|
|
9
|
+
dischargingTime: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface OSInfo {
|
|
13
|
+
name: string;
|
|
14
|
+
version: string | number;
|
|
15
|
+
string: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface NetworkInfo {
|
|
19
|
+
IP: string;
|
|
20
|
+
ISP: string;
|
|
21
|
+
country: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ScreenInfo {
|
|
25
|
+
width: number;
|
|
26
|
+
height: number;
|
|
27
|
+
availWidth: number;
|
|
28
|
+
availHeight: number;
|
|
29
|
+
colorDepth: number;
|
|
30
|
+
pixelRatio: number;
|
|
31
|
+
orientation: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ConnectionInfo {
|
|
35
|
+
type: string;
|
|
36
|
+
effectiveType: string;
|
|
37
|
+
downlink: number;
|
|
38
|
+
rtt: number;
|
|
39
|
+
saveData: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface GeolocationData {
|
|
43
|
+
latitude: number;
|
|
44
|
+
longitude: number;
|
|
45
|
+
accuracy: number;
|
|
46
|
+
timestamp: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface AkiInfoResult {
|
|
50
|
+
/** Browser name and version (e.g., "Chrome 120") */
|
|
51
|
+
browser: string;
|
|
52
|
+
/** Device product name (e.g., "iPhone", "Galaxy S21") */
|
|
53
|
+
product: string;
|
|
54
|
+
/** Device manufacturer (e.g., "Apple", "Samsung") */
|
|
55
|
+
manufacturer: string;
|
|
56
|
+
/** Whether the device is mobile */
|
|
57
|
+
isMobile: boolean;
|
|
58
|
+
/** Preferred languages (space-separated 2-char codes) */
|
|
59
|
+
language: string;
|
|
60
|
+
/** CPU/chip type (e.g., "Apple Silicon", "Intel") */
|
|
61
|
+
CPU: string;
|
|
62
|
+
/** Number of logical CPU cores */
|
|
63
|
+
CPUCore: number;
|
|
64
|
+
/** CPU architecture (e.g., "x86_64", "arm64") */
|
|
65
|
+
arch: string;
|
|
66
|
+
/** Device memory in GB */
|
|
67
|
+
RAM: number;
|
|
68
|
+
/** GPU renderer string */
|
|
69
|
+
GPU: string;
|
|
70
|
+
/** Battery status */
|
|
71
|
+
battery: BatteryInfo;
|
|
72
|
+
/** Operating system info */
|
|
73
|
+
os: OSInfo;
|
|
74
|
+
/** Network info (reactive getters) */
|
|
75
|
+
network: NetworkInfo;
|
|
76
|
+
/** Fetch public IP address */
|
|
77
|
+
getIP(forceRefresh?: boolean): Promise<string>;
|
|
78
|
+
/** Fetch ISP information */
|
|
79
|
+
getISP(forceRefresh?: boolean): Promise<string>;
|
|
80
|
+
/** Fetch country code */
|
|
81
|
+
getCountry(forceRefresh?: boolean): Promise<string>;
|
|
82
|
+
/** Fetch all network info */
|
|
83
|
+
getNetworkInfo(forceRefresh?: boolean): Promise<NetworkInfo>;
|
|
84
|
+
/** Fetch geolocation (requires user permission) */
|
|
85
|
+
getLocation(options?: PositionOptions): Promise<GeolocationData | null>;
|
|
86
|
+
/** Get network connection quality info */
|
|
87
|
+
getConnection(): ConnectionInfo | null;
|
|
88
|
+
/** Get screen/display info */
|
|
89
|
+
getScreen(): ScreenInfo;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Main detection function
|
|
94
|
+
* @param forceNetworkRefresh - Force refresh of cached network data
|
|
95
|
+
* @returns Promise resolving to complete system information
|
|
96
|
+
*/
|
|
97
|
+
declare function akiInfoDetect(forceNetworkRefresh?: boolean): Promise<AkiInfoResult>;
|
|
98
|
+
|
|
99
|
+
export { akiInfoDetect };
|
|
100
|
+
export default akiInfoDetect;
|
|
101
|
+
|
|
102
|
+
// Individual exports for tree-shaking
|
|
103
|
+
export declare function getNetworkInfo(forceRefresh?: boolean): Promise<NetworkInfo>;
|
|
104
|
+
export declare function getIP(forceRefresh?: boolean): Promise<string>;
|
|
105
|
+
export declare function getISP(forceRefresh?: boolean): Promise<string>;
|
|
106
|
+
export declare function getCountry(forceRefresh?: boolean): Promise<string>;
|
|
107
|
+
export declare function getConnection(): ConnectionInfo | null;
|
|
108
|
+
export declare function getLocation(options?: PositionOptions): Promise<GeolocationData | null>;
|
|
109
|
+
export declare function getScreen(): ScreenInfo;
|
|
110
|
+
export declare function getBattery(): Promise<BatteryInfo>;
|
|
111
|
+
export declare function detectGPU(): string;
|
|
112
|
+
export declare function parseUserAgent(ua?: string): {
|
|
113
|
+
name: string;
|
|
114
|
+
version: string;
|
|
115
|
+
product: string;
|
|
116
|
+
manufacturer: string;
|
|
117
|
+
os: { family: string; version: string; architecture: number } | null;
|
|
118
|
+
layout: string;
|
|
119
|
+
};
|
|
120
|
+
export declare function getHighEntropyValues(): Promise<Record<string, unknown>>;
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aki-info-detect",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Lightweight JavaScript library for detecting device, browser, hardware, and network information with UMD/ESM support",
|
|
5
|
+
"author": "lacvietanh",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/lacvietanh/akiInfoDetect.js.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/lacvietanh/akiInfoDetect.js/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/lacvietanh/akiInfoDetect.js#readme",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"device-detection",
|
|
17
|
+
"browser-detection",
|
|
18
|
+
"platform-detection",
|
|
19
|
+
"user-agent",
|
|
20
|
+
"client-hints",
|
|
21
|
+
"hardware-info",
|
|
22
|
+
"gpu-detection",
|
|
23
|
+
"network-info",
|
|
24
|
+
"ip-detection",
|
|
25
|
+
"system-info"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"main": "./dist/aki-info-detect.umd.cjs",
|
|
29
|
+
"module": "./dist/aki-info-detect.js",
|
|
30
|
+
"types": "./dist/types/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"import": "./dist/aki-info-detect.js",
|
|
34
|
+
"require": "./dist/aki-info-detect.umd.cjs",
|
|
35
|
+
"types": "./dist/types/index.d.ts"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"README.md",
|
|
41
|
+
"LICENSE"
|
|
42
|
+
],
|
|
43
|
+
"scripts": {
|
|
44
|
+
"dev": "vite",
|
|
45
|
+
"build": "vite build && npm run build:types",
|
|
46
|
+
"build:types": "mkdir -p dist/types && cp src/index.d.ts dist/types/index.d.ts",
|
|
47
|
+
"preview": "vite preview",
|
|
48
|
+
"serve": "node server.js",
|
|
49
|
+
"lint": "eslint src/**/*.js",
|
|
50
|
+
"prepublishOnly": "npm run build"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"eslint": "^8.56.0",
|
|
54
|
+
"terser": "^5.44.1",
|
|
55
|
+
"typescript": "^5.3.3",
|
|
56
|
+
"vite": "^5.0.10"
|
|
57
|
+
},
|
|
58
|
+
"engines": {
|
|
59
|
+
"node": ">=16.0.0"
|
|
60
|
+
},
|
|
61
|
+
"sideEffects": false
|
|
62
|
+
}
|