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 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
+ [![npm version](https://img.shields.io/npm/v/aki-info-detect.svg)](https://www.npmjs.com/package/aki-info-detect)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
+ [![Bundle Size](https://img.shields.io/bundlephobia/minzip/aki-info-detect)](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
+ }