os-detect 2.0.2 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +430 -109
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,12 +4,51 @@
|
|
|
4
4
|
</h1>
|
|
5
5
|
<img
|
|
6
6
|
src="https://s3.twcstorage.ru/c9a2cc89-780f97fd-311d-4a1a-b86f-c25665c9dc46/images/npm/os-detect.webp"
|
|
7
|
-
alt="
|
|
7
|
+
alt="os-detect"
|
|
8
8
|
style="max-width:100%;width:auto;height:300px;border-radius:12px"
|
|
9
9
|
/>
|
|
10
10
|
</div>
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Lightweight OS and device-type detection for browsers, Node.js, and SSR — with React hooks and Vue composables. No dependencies.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Contents
|
|
17
|
+
|
|
18
|
+
- [Features](#features)
|
|
19
|
+
- [Installation](#installation)
|
|
20
|
+
- [Quick start](#quick-start)
|
|
21
|
+
- [getOS](#getos)
|
|
22
|
+
- [Boolean detection](#boolean-detection)
|
|
23
|
+
- [detectIsWindows11](#detectiswindows11)
|
|
24
|
+
- [Device category](#device-category)
|
|
25
|
+
- [React hooks](#react-hooks)
|
|
26
|
+
- [Vue composables](#vue-composables)
|
|
27
|
+
- [Node.js & SSR](#nodejs--ssr)
|
|
28
|
+
- [UMD / CDN](#umd--cdn)
|
|
29
|
+
- [TypeScript types](#typescript-types)
|
|
30
|
+
- [Detection strategy](#detection-strategy)
|
|
31
|
+
- [Architecture](#architecture)
|
|
32
|
+
- [Bundle size & entry points](#bundle-size--entry-points)
|
|
33
|
+
- [Migration from v1.x](#migration-from-v1x)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- **`getOS()`** — returns a typed string identifier for the current OS; detection priority ensures ChromeOS is never misidentified as Linux
|
|
40
|
+
- **Boolean functions** — `detectIsIOS()`, `detectIsMacOS()`, `detectIsAndroid()`, `detectIsWindows()`, `detectIsLinux()`, `detectIsChromeOS()` — all synchronous and cached
|
|
41
|
+
- **`detectIsWindows11()`** — async; uses `navigator.userAgentData.getHighEntropyValues()` in the browser and `os.release()` in Node.js
|
|
42
|
+
- **Device category** — `isMobileDevice()` and `isDesktopDevice()` for quick coarse checks
|
|
43
|
+
- **React hooks** — `useOS()` and `useIsWindows11()` from `os-detect/react`
|
|
44
|
+
- **Vue composables** — `useOS()` and `useIsWindows11()` from `os-detect/vue` as readonly refs
|
|
45
|
+
- **Node.js support** — reads `process.platform` when `navigator` is absent; `detectIsWindows11()` uses `os.release()` build number
|
|
46
|
+
- **iPadOS 13+ detection** — correctly identifies iPads that send `Macintosh` in their userAgent via `navigator.maxTouchPoints`
|
|
47
|
+
- **Result cache** — every function caches its result after the first call; zero overhead on subsequent calls
|
|
48
|
+
- **Zero runtime dependencies** — no external packages; React and Vue are optional peer deps
|
|
49
|
+
- **Tree-shakeable ESM** — import only what you use; UMD and CJS bundles also included
|
|
50
|
+
|
|
51
|
+
---
|
|
13
52
|
|
|
14
53
|
## Installation
|
|
15
54
|
|
|
@@ -17,201 +56,483 @@ A lightweight utility for detecting the operating system and device type — in
|
|
|
17
56
|
npm install os-detect
|
|
18
57
|
```
|
|
19
58
|
|
|
20
|
-
|
|
59
|
+
React hooks (optional peer dependency):
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm install react@>=17
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Vue composables (optional peer dependency):
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm install vue@>=3
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Quick start
|
|
21
74
|
|
|
22
75
|
```ts
|
|
23
76
|
import {
|
|
24
77
|
getOS,
|
|
25
|
-
detectIsIOS,
|
|
26
|
-
detectIsWindows,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
78
|
+
detectIsIOS,
|
|
79
|
+
detectIsWindows,
|
|
80
|
+
isMobileDevice,
|
|
81
|
+
} from 'os-detect'
|
|
82
|
+
|
|
83
|
+
console.log(getOS()) // 'windows' | 'macos' | 'ios' | 'android' | 'linux' | 'chromeos' | 'unknown'
|
|
84
|
+
console.log(detectIsIOS()) // true on iPhone / iPad
|
|
85
|
+
console.log(detectIsWindows()) // true on Windows desktop
|
|
86
|
+
console.log(isMobileDevice()) // true on iOS or Android
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
All functions are synchronous and cached — safe to call on every render or in any reactive context.
|
|
90
|
+
|
|
91
|
+
---
|
|
30
92
|
|
|
31
|
-
|
|
32
|
-
getOS(); // 'ios' | 'macos' | 'android' | 'windows' | 'linux' | 'chromeos' | 'unknown'
|
|
93
|
+
## getOS
|
|
33
94
|
|
|
34
|
-
|
|
35
|
-
detectIsIOS(); // true on iPhone, iPod, iPad (including iPadOS 13+)
|
|
36
|
-
detectIsMacOS(); // true on macOS desktop (correctly excludes iPadOS 13+)
|
|
37
|
-
detectIsAndroid(); // true on Android phones and tablets
|
|
38
|
-
detectIsWindows(); // true on Windows desktop or laptop
|
|
39
|
-
detectIsLinux(); // true on Linux desktop (excludes Android and ChromeOS)
|
|
40
|
-
detectIsChromeOS(); // true on ChromeOS devices
|
|
95
|
+
Returns a single string identifying the current operating system.
|
|
41
96
|
|
|
42
|
-
|
|
43
|
-
|
|
97
|
+
```ts
|
|
98
|
+
import { getOS } from 'os-detect'
|
|
44
99
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
isDesktopDevice(); // true if macOS, Windows, Linux, or ChromeOS
|
|
100
|
+
const os = getOS()
|
|
101
|
+
// 'ios' | 'macos' | 'android' | 'windows' | 'linux' | 'chromeos' | 'unknown'
|
|
48
102
|
```
|
|
49
103
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
104
|
+
Detection priority (first match wins):
|
|
105
|
+
|
|
106
|
+
1. iOS / iPadOS
|
|
107
|
+
2. Android
|
|
108
|
+
3. ChromeOS
|
|
109
|
+
4. Linux
|
|
110
|
+
5. macOS
|
|
111
|
+
6. Windows
|
|
112
|
+
7. `'unknown'`
|
|
113
|
+
|
|
114
|
+
ChromeOS is checked before Linux because ChromeOS userAgent strings contain `"Linux"` — checking in the wrong order would misidentify Chromebooks.
|
|
115
|
+
|
|
116
|
+
### `OS` type
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
type OS = 'ios' | 'macos' | 'android' | 'windows' | 'linux' | 'chromeos' | 'unknown'
|
|
53
120
|
```
|
|
54
121
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Boolean detection
|
|
125
|
+
|
|
126
|
+
All functions are synchronous, side-effect-free, and cache their result after the first call.
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import {
|
|
130
|
+
detectIsIOS,
|
|
131
|
+
detectIsMacOS,
|
|
132
|
+
detectIsAndroid,
|
|
133
|
+
detectIsWindows,
|
|
134
|
+
detectIsLinux,
|
|
135
|
+
detectIsChromeOS,
|
|
136
|
+
} from 'os-detect'
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
| Function | Returns `true` when | Node.js behaviour |
|
|
140
|
+
|---|---|---|
|
|
141
|
+
| `detectIsIOS()` | iPhone, iPod, or iPad (including iPadOS 13+) | always `false` |
|
|
142
|
+
| `detectIsMacOS()` | macOS desktop (correctly excludes iPadOS 13+) | `process.platform === 'darwin'` |
|
|
143
|
+
| `detectIsAndroid()` | Android phones and tablets | `process.platform === 'android'` |
|
|
144
|
+
| `detectIsWindows()` | Windows desktop or laptop (32-bit and 64-bit) | `process.platform === 'win32'` |
|
|
145
|
+
| `detectIsLinux()` | Linux desktop (excludes Android and ChromeOS) | `process.platform === 'linux'` |
|
|
146
|
+
| `detectIsChromeOS()` | ChromeOS devices | always `false` |
|
|
147
|
+
|
|
148
|
+
### iPadOS 13+ note
|
|
149
|
+
|
|
150
|
+
Since iPadOS 13, Safari reports `Macintosh` in the userAgent string. `detectIsIOS()` handles this correctly by checking `navigator.maxTouchPoints > 1`. `detectIsMacOS()` excludes iPadOS devices accordingly — you will never get `true` from both functions on the same device.
|
|
151
|
+
|
|
152
|
+
### Example — conditional rendering
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import { detectIsIOS, detectIsAndroid, detectIsWindows } from 'os-detect'
|
|
156
|
+
|
|
157
|
+
if (detectIsIOS()) {
|
|
158
|
+
// show App Store badge
|
|
159
|
+
} else if (detectIsAndroid()) {
|
|
160
|
+
// show Google Play badge
|
|
161
|
+
} else if (detectIsWindows()) {
|
|
162
|
+
// show Windows Store badge
|
|
163
|
+
}
|
|
63
164
|
```
|
|
64
165
|
|
|
65
166
|
---
|
|
66
167
|
|
|
67
|
-
##
|
|
168
|
+
## detectIsWindows11
|
|
169
|
+
|
|
170
|
+
Asynchronous. Returns `true` only when both `detectIsWindows()` is `true` and the Windows 11 check succeeds.
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
import { detectIsWindows11 } from 'os-detect'
|
|
174
|
+
|
|
175
|
+
const isWin11 = await detectIsWindows11() // Promise<boolean>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Browser** — uses `navigator.userAgentData.getHighEntropyValues(['platformVersion'])` (Chrome 90+ / Edge 90+). Windows 11 reports `platformVersion >= 13.0.0`. Returns `false` if the API is unavailable or the call fails.
|
|
179
|
+
|
|
180
|
+
**Node.js** — uses `os.release()`. Windows 11 has build number `>= 22000`. Returns `false` if the import fails.
|
|
181
|
+
|
|
182
|
+
Returns `false` immediately if `detectIsWindows()` is `false` — no async work is done.
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
// Differentiate Windows 10 from Windows 11
|
|
186
|
+
const isWindows = detectIsWindows()
|
|
187
|
+
const isWin11 = await detectIsWindows11()
|
|
188
|
+
|
|
189
|
+
if (isWin11) {
|
|
190
|
+
console.log('Windows 11')
|
|
191
|
+
} else if (isWindows) {
|
|
192
|
+
console.log('Windows 10 or older')
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Device category
|
|
199
|
+
|
|
200
|
+
Quick coarse checks that group OS values into mobile and desktop buckets.
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
import { isMobileDevice, isDesktopDevice } from 'os-detect'
|
|
204
|
+
|
|
205
|
+
isMobileDevice() // true if iOS or Android
|
|
206
|
+
isDesktopDevice() // true if macOS, Windows, Linux, or ChromeOS
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
> **Note:** `isMobileDevice()` and `isDesktopDevice()` are **not mutually exclusive**. A device with an unrecognized OS returns `false` for both. There is no overlap on the currently recognised OS values.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## React hooks
|
|
214
|
+
|
|
215
|
+
Import from `os-detect/react`. Requires React 17+.
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
import { useOS, useIsWindows11 } from 'os-detect/react'
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### `useOS()`
|
|
222
|
+
|
|
223
|
+
Returns the current OS string synchronously. The value is computed once and stable across re-renders.
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
function Banner() {
|
|
227
|
+
const os = useOS() // 'windows' | 'macos' | 'ios' | ...
|
|
228
|
+
|
|
229
|
+
return <p>Running on {os}</p>
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### `useIsWindows11()`
|
|
234
|
+
|
|
235
|
+
Starts the async detection inside `useEffect` and updates state when it resolves. Returns `null` while the detection is in progress.
|
|
68
236
|
|
|
69
237
|
```tsx
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return (
|
|
77
|
-
<div>
|
|
78
|
-
<p>OS: {os}</p>
|
|
79
|
-
{isWin11 === null && <p>Detecting Windows version...</p>}
|
|
80
|
-
{isWin11 === true && <p>Windows 11</p>}
|
|
81
|
-
{isWin11 === false && os === 'windows' && <p>Windows 10 or older</p>}
|
|
82
|
-
</div>
|
|
83
|
-
);
|
|
238
|
+
function WindowsBadge() {
|
|
239
|
+
const isWin11 = useIsWindows11() // null → true | false
|
|
240
|
+
|
|
241
|
+
if (isWin11 === null) return <Spinner />
|
|
242
|
+
return <p>{isWin11 ? 'Windows 11' : 'Windows 10 or older'}</p>
|
|
84
243
|
}
|
|
85
244
|
```
|
|
86
245
|
|
|
87
|
-
|
|
246
|
+
| Hook | Returns | Notes |
|
|
247
|
+
|---|---|---|
|
|
248
|
+
| `useOS()` | `OS` | Synchronous, cached, stable |
|
|
249
|
+
| `useIsWindows11()` | `boolean \| null` | `null` while detecting |
|
|
88
250
|
|
|
89
251
|
---
|
|
90
252
|
|
|
91
|
-
## Vue
|
|
253
|
+
## Vue composables
|
|
254
|
+
|
|
255
|
+
Import from `os-detect/vue`. Requires Vue 3+.
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
import { useOS, useIsWindows11 } from 'os-detect/vue'
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### `useOS()`
|
|
262
|
+
|
|
263
|
+
Returns a readonly `Ref` with the current OS string. Detection is synchronous and cached.
|
|
264
|
+
|
|
265
|
+
```vue
|
|
266
|
+
<script setup lang="ts">
|
|
267
|
+
import { useOS } from 'os-detect/vue'
|
|
268
|
+
|
|
269
|
+
const os = useOS() // Readonly<Ref<OS>>
|
|
270
|
+
</script>
|
|
271
|
+
|
|
272
|
+
<template>
|
|
273
|
+
<p>Running on {{ os }}</p>
|
|
274
|
+
</template>
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### `useIsWindows11()`
|
|
278
|
+
|
|
279
|
+
Returns a readonly `Ref` that starts as `null` and resolves to `true` or `false` after the async detection completes inside `onMounted`.
|
|
92
280
|
|
|
93
281
|
```vue
|
|
94
282
|
<script setup lang="ts">
|
|
95
|
-
import { useOS, useIsWindows11 } from 'os-detect/vue'
|
|
283
|
+
import { useOS, useIsWindows11 } from 'os-detect/vue'
|
|
96
284
|
|
|
97
|
-
const os
|
|
98
|
-
const isWin11 = useIsWindows11()
|
|
285
|
+
const os = useOS() // Readonly<Ref<OS>>
|
|
286
|
+
const isWin11 = useIsWindows11() // Readonly<Ref<boolean | null>>
|
|
99
287
|
</script>
|
|
100
288
|
|
|
101
289
|
<template>
|
|
102
|
-
<p>
|
|
103
|
-
<p v-if="isWin11 === null">Detecting Windows version...</p>
|
|
290
|
+
<p v-if="isWin11 === null">Detecting Windows version…</p>
|
|
104
291
|
<p v-else-if="isWin11">Windows 11</p>
|
|
105
292
|
<p v-else-if="os === 'windows'">Windows 10 or older</p>
|
|
293
|
+
<p v-else>OS: {{ os }}</p>
|
|
106
294
|
</template>
|
|
107
295
|
```
|
|
108
296
|
|
|
109
|
-
|
|
297
|
+
| Composable | Returns | Notes |
|
|
298
|
+
|---|---|---|
|
|
299
|
+
| `useOS()` | `Readonly<Ref<OS>>` | Synchronous, cached |
|
|
300
|
+
| `useIsWindows11()` | `Readonly<Ref<boolean \| null>>` | `null` while detecting |
|
|
110
301
|
|
|
111
302
|
---
|
|
112
303
|
|
|
113
304
|
## Node.js & SSR
|
|
114
305
|
|
|
115
|
-
All functions work in Node.js
|
|
306
|
+
All core functions work in Node.js. When `navigator` is absent the library reads `process.platform` instead of the userAgent.
|
|
116
307
|
|
|
117
308
|
```ts
|
|
118
|
-
import { getOS, detectIsWindows, detectIsWindows11 } from 'os-detect'
|
|
309
|
+
import { getOS, detectIsWindows, detectIsWindows11 } from 'os-detect'
|
|
119
310
|
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
detectIsWindows(); // true on Windows
|
|
311
|
+
getOS() // 'macos' | 'windows' | 'linux' | 'android' | 'unknown'
|
|
312
|
+
detectIsWindows() // true on Windows
|
|
123
313
|
|
|
124
|
-
|
|
125
|
-
const isWin11 = await detectIsWindows11();
|
|
314
|
+
const isWin11 = await detectIsWindows11() // uses os.release() build number
|
|
126
315
|
```
|
|
127
316
|
|
|
317
|
+
### `process.platform` mapping
|
|
318
|
+
|
|
128
319
|
| `process.platform` | Detected as |
|
|
129
320
|
|---|---|
|
|
130
321
|
| `darwin` | macOS |
|
|
131
322
|
| `win32` | Windows (32-bit and 64-bit) |
|
|
132
323
|
| `linux` | Linux |
|
|
133
324
|
| `android` | Android |
|
|
325
|
+
| anything else | `'unknown'` |
|
|
326
|
+
|
|
327
|
+
Functions that require a browser environment (`detectIsIOS()`, `detectIsChromeOS()`) always return `false` in Node.js — there is no userAgent or `maxTouchPoints` to read.
|
|
328
|
+
|
|
329
|
+
### SSR hydration note
|
|
330
|
+
|
|
331
|
+
In Next.js or Nuxt SSR, `getOS()` returns the **server's** OS during server-side rendering — not the client's. To avoid hydration mismatches, run detection only on the client:
|
|
332
|
+
|
|
333
|
+
**React (Next.js):**
|
|
334
|
+
|
|
335
|
+
```tsx
|
|
336
|
+
import { useEffect, useState } from 'react'
|
|
337
|
+
import { getOS } from 'os-detect'
|
|
338
|
+
import type { OS } from 'os-detect'
|
|
339
|
+
|
|
340
|
+
function OSBanner() {
|
|
341
|
+
const [os, setOS] = useState<OS | null>(null)
|
|
342
|
+
|
|
343
|
+
useEffect(() => {
|
|
344
|
+
setOS(getOS())
|
|
345
|
+
}, [])
|
|
346
|
+
|
|
347
|
+
if (!os) return null
|
|
348
|
+
return <p>OS: {os}</p>
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Vue (Nuxt):**
|
|
353
|
+
|
|
354
|
+
```vue
|
|
355
|
+
<script setup lang="ts">
|
|
356
|
+
import { ref, onMounted } from 'vue'
|
|
357
|
+
import { getOS } from 'os-detect'
|
|
358
|
+
import type { OS } from 'os-detect'
|
|
134
359
|
|
|
135
|
-
|
|
360
|
+
const os = ref<OS | null>(null)
|
|
361
|
+
onMounted(() => { os.value = getOS() })
|
|
362
|
+
</script>
|
|
363
|
+
|
|
364
|
+
<template>
|
|
365
|
+
<p v-if="os">OS: {{ os }}</p>
|
|
366
|
+
</template>
|
|
367
|
+
```
|
|
136
368
|
|
|
137
|
-
|
|
369
|
+
Alternatively, use the Vue composable `useOS()` from `os-detect/vue` — it calls `getOS()` inside `ref()` which is evaluated only on the client when used with `<script setup>` and SSR-safe composition.
|
|
138
370
|
|
|
139
371
|
---
|
|
140
372
|
|
|
141
|
-
##
|
|
373
|
+
## UMD / CDN
|
|
142
374
|
|
|
143
|
-
|
|
375
|
+
A prebuilt UMD bundle is available via unpkg and jsDelivr — no build step required.
|
|
144
376
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
377
|
+
```html
|
|
378
|
+
<script src="https://unpkg.com/os-detect/dist/index.umd.js"></script>
|
|
379
|
+
<script>
|
|
380
|
+
console.log(OsDetect.getOS())
|
|
381
|
+
console.log(OsDetect.detectIsIOS())
|
|
382
|
+
console.log(OsDetect.isMobileDevice())
|
|
148
383
|
|
|
149
|
-
|
|
384
|
+
OsDetect.detectIsWindows11().then((isWin11) => {
|
|
385
|
+
console.log('Windows 11:', isWin11)
|
|
386
|
+
})
|
|
387
|
+
</script>
|
|
388
|
+
```
|
|
150
389
|
|
|
151
|
-
|
|
152
|
-
|---|---|---|
|
|
153
|
-
| `detectIsIOS()` | iPhone, iPod, or iPad (including iPadOS 13+) | always `false` |
|
|
154
|
-
| `detectIsMacOS()` | macOS desktop (excludes iPadOS 13+) | `process.platform === 'darwin'` |
|
|
155
|
-
| `detectIsAndroid()` | Android phones and tablets | `process.platform === 'android'` |
|
|
156
|
-
| `detectIsWindows()` | Windows desktop or laptop | `process.platform === 'win32'` |
|
|
157
|
-
| `detectIsLinux()` | Linux desktop (excludes Android and ChromeOS) | `process.platform === 'linux'` |
|
|
158
|
-
| `detectIsChromeOS()` | ChromeOS devices | always `false` |
|
|
159
|
-
| `detectIsWindows11()` | Windows 11 — **async**, `Promise<boolean>` | via `os.release()` |
|
|
390
|
+
The global name is `OsDetect`. All functions from the core entry point are exposed on it.
|
|
160
391
|
|
|
161
|
-
|
|
392
|
+
---
|
|
162
393
|
|
|
163
|
-
|
|
164
|
-
|---|---|
|
|
165
|
-
| `isMobileDevice()` | iOS or Android |
|
|
166
|
-
| `isDesktopDevice()` | macOS, Windows, Linux, or ChromeOS |
|
|
394
|
+
## TypeScript types
|
|
167
395
|
|
|
168
|
-
|
|
396
|
+
All public types are exported from the package root:
|
|
169
397
|
|
|
170
|
-
|
|
398
|
+
```ts
|
|
399
|
+
import type { OS } from 'os-detect'
|
|
171
400
|
|
|
172
|
-
|
|
401
|
+
// OS is a string union:
|
|
402
|
+
// 'ios' | 'macos' | 'android' | 'windows' | 'linux' | 'chromeos' | 'unknown'
|
|
403
|
+
```
|
|
173
404
|
|
|
174
|
-
|
|
175
|
-
|---|---|
|
|
176
|
-
| `useOS()` | `OS` string — synchronous, cached |
|
|
177
|
-
| `useIsWindows11()` | `boolean \| null` — `null` while detecting |
|
|
405
|
+
### Narrowing with `getOS()`
|
|
178
406
|
|
|
179
|
-
|
|
407
|
+
```ts
|
|
408
|
+
import { getOS } from 'os-detect'
|
|
409
|
+
import type { OS } from 'os-detect'
|
|
410
|
+
|
|
411
|
+
const os: OS = getOS()
|
|
412
|
+
|
|
413
|
+
switch (os) {
|
|
414
|
+
case 'ios':
|
|
415
|
+
case 'android':
|
|
416
|
+
console.log('Mobile device')
|
|
417
|
+
break
|
|
418
|
+
case 'windows':
|
|
419
|
+
case 'macos':
|
|
420
|
+
case 'linux':
|
|
421
|
+
case 'chromeos':
|
|
422
|
+
console.log('Desktop device')
|
|
423
|
+
break
|
|
424
|
+
case 'unknown':
|
|
425
|
+
console.log('Unrecognised OS')
|
|
426
|
+
break
|
|
427
|
+
}
|
|
428
|
+
```
|
|
180
429
|
|
|
181
|
-
|
|
430
|
+
TypeScript knows all branches are exhausted after the switch — no need for a `default` case if you handle `'unknown'`.
|
|
182
431
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
432
|
+
### React types
|
|
433
|
+
|
|
434
|
+
`useOS()` returns `OS`. `useIsWindows11()` returns `boolean | null`. Both are fully typed with no extra imports needed.
|
|
435
|
+
|
|
436
|
+
### Vue types
|
|
437
|
+
|
|
438
|
+
`useOS()` returns `Readonly<Ref<OS>>`. `useIsWindows11()` returns `Readonly<Ref<boolean | null>>`.
|
|
439
|
+
|
|
440
|
+
```ts
|
|
441
|
+
import type { OS } from 'os-detect'
|
|
442
|
+
import { useOS } from 'os-detect/vue'
|
|
443
|
+
import type { Ref } from 'vue'
|
|
444
|
+
|
|
445
|
+
const os: Readonly<Ref<OS>> = useOS()
|
|
446
|
+
```
|
|
187
447
|
|
|
188
448
|
---
|
|
189
449
|
|
|
190
|
-
## Detection
|
|
450
|
+
## Detection strategy
|
|
191
451
|
|
|
192
|
-
|
|
452
|
+
### Browser
|
|
193
453
|
|
|
194
|
-
|
|
454
|
+
1. **`navigator.userAgentData.platform`** (Chrome 90+ / Edge 90+) — the modern, high-entropy-free platform hint. Reliable and not spoofable by userAgent overrides.
|
|
455
|
+
2. **`navigator.userAgent`** — legacy fallback for all other browsers (Firefox, Safari, older Chrome). Regex patterns are kept conservative to avoid false positives.
|
|
195
456
|
|
|
196
|
-
|
|
457
|
+
For Windows 11, an additional async call to `navigator.userAgentData.getHighEntropyValues(['platformVersion'])` is required — this is gated behind `detectIsWindows11()` and never called automatically.
|
|
197
458
|
|
|
198
|
-
|
|
459
|
+
### Node.js
|
|
199
460
|
|
|
200
|
-
|
|
461
|
+
Reads `process.platform` when `navigator` is absent. No userAgent parsing is done server-side. `detectIsWindows11()` dynamically imports the built-in `os` module and parses the build number from `os.release()`.
|
|
201
462
|
|
|
202
|
-
|
|
463
|
+
### Caching
|
|
464
|
+
|
|
465
|
+
Results are stored in module-level variables after the first call. Subsequent calls return the cached value directly with no DOM or process access. This makes repeated calls in reactive contexts (render functions, computed properties) effectively free.
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
## Architecture
|
|
470
|
+
|
|
471
|
+
```
|
|
472
|
+
os-detect
|
|
473
|
+
│
|
|
474
|
+
├── src/index.ts (core entry point)
|
|
475
|
+
│ getUADataPlatform() → navigator.userAgentData.platform (Chrome/Edge)
|
|
476
|
+
│ getNodePlatform() → process.platform (Node.js only)
|
|
477
|
+
│
|
|
478
|
+
│ detectIsIOS() → UA + maxTouchPoints (iPadOS 13+ aware)
|
|
479
|
+
│ detectIsMacOS() → UAData / UA / process.platform=darwin
|
|
480
|
+
│ detectIsAndroid() → UAData / UA / process.platform=android
|
|
481
|
+
│ detectIsWindows() → UAData / UA / process.platform=win32
|
|
482
|
+
│ detectIsChromeOS() → UAData / UA (browser only)
|
|
483
|
+
│ detectIsLinux() → UAData / UA / process.platform=linux
|
|
484
|
+
│ (excludes Android and ChromeOS)
|
|
485
|
+
│
|
|
486
|
+
│ detectIsWindows11() → async
|
|
487
|
+
│ browser: userAgentData.getHighEntropyValues(['platformVersion'])
|
|
488
|
+
│ platformVersion >= 13.0.0 → Windows 11
|
|
489
|
+
│ Node.js: import('os').release() build number >= 22000
|
|
490
|
+
│
|
|
491
|
+
│ getOS() → calls detectors in priority order
|
|
492
|
+
│ isMobileDevice() → detectIsIOS() || detectIsAndroid()
|
|
493
|
+
│ isDesktopDevice() → detectIsMacOS() || detectIsWindows()
|
|
494
|
+
│ || detectIsLinux() || detectIsChromeOS()
|
|
495
|
+
│
|
|
496
|
+
│ detectIsiOS() → deprecated alias for detectIsIOS()
|
|
497
|
+
│
|
|
498
|
+
├── src/react.ts (os-detect/react)
|
|
499
|
+
│ useOS() → useState(() => getOS())
|
|
500
|
+
│ useIsWindows11() → useState(null) + useEffect → detectIsWindows11()
|
|
501
|
+
│
|
|
502
|
+
└── src/vue.ts (os-detect/vue)
|
|
503
|
+
useOS() → readonly(ref(getOS()))
|
|
504
|
+
useIsWindows11() → readonly(ref(null)) + onMounted → detectIsWindows11()
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
## Bundle size & entry points
|
|
510
|
+
|
|
511
|
+
| Entry point | Peer deps | Format | Notes |
|
|
512
|
+
|---|---|---|---|
|
|
513
|
+
| `os-detect` | — | ESM, CJS, UMD | Core — all detection functions and the `OS` type |
|
|
514
|
+
| `os-detect/react` | `react >=17` | ESM, CJS | `useOS` and `useIsWindows11` hooks |
|
|
515
|
+
| `os-detect/vue` | `vue >=3` | ESM, CJS | `useOS` and `useIsWindows11` composables |
|
|
516
|
+
|
|
517
|
+
All three entry points are listed in `package.json` exports. The React and Vue adapters add no runtime logic beyond the hooks/composables themselves — they call the same cached core functions.
|
|
518
|
+
|
|
519
|
+
The package has **zero runtime dependencies**. React and Vue are optional peer dependencies — you only need them if you use the corresponding entry point.
|
|
203
520
|
|
|
204
521
|
---
|
|
205
522
|
|
|
206
523
|
## Migration from v1.x
|
|
207
524
|
|
|
208
|
-
`detectIsiOS()` was renamed to `detectIsIOS()
|
|
525
|
+
`detectIsiOS()` was renamed to `detectIsIOS()` (capital `OS`) for consistency with the rest of the API.
|
|
526
|
+
|
|
527
|
+
The old name still works in v2 but logs a deprecation warning and will be removed in v3.0:
|
|
209
528
|
|
|
210
529
|
```ts
|
|
211
|
-
detectIsiOS() // deprecated — logs console.warn
|
|
212
|
-
detectIsIOS() // use this instead
|
|
530
|
+
detectIsiOS() // ⚠ deprecated — logs console.warn in all environments
|
|
531
|
+
detectIsIOS() // ✓ use this instead
|
|
213
532
|
```
|
|
214
533
|
|
|
534
|
+
No other breaking changes between v1 and v2.
|
|
535
|
+
|
|
215
536
|
---
|
|
216
537
|
|
|
217
538
|
## License
|
|
@@ -238,4 +559,4 @@ Open source takes time and effort. If my work saves you time or brings value, co
|
|
|
238
559
|
<img src="https://img.shields.io/badge/Donate-CryptoCloud-8A2BE2?style=for-the-badge&logo=cryptocurrency&logoColor=white" alt="Donate via CryptoCloud">
|
|
239
560
|
</a>
|
|
240
561
|
|
|
241
|
-
Thank you for being part of this journey. ❤️
|
|
562
|
+
Thank you for being part of this journey. ❤️
|