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.
Files changed (2) hide show
  1. package/README.md +430 -109
  2. 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="vue-virtual-scroller-kit"
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
- A lightweight utility for detecting the operating system and device type in browsers, Node.js, and SSR. With React hooks and Vue composables. No dependencies.
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
- ## Usage
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, detectIsMacOS, detectIsAndroid,
26
- detectIsWindows, detectIsLinux, detectIsChromeOS,
27
- detectIsWindows11,
28
- isMobileDevice, isDesktopDevice,
29
- } from 'os-detect';
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
- // Get OS as a string
32
- getOS(); // 'ios' | 'macos' | 'android' | 'windows' | 'linux' | 'chromeos' | 'unknown'
93
+ ## getOS
33
94
 
34
- // Boolean detection
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
- // Async: Windows 11 check
43
- const isWin11 = await detectIsWindows11(); // true | false
97
+ ```ts
98
+ import { getOS } from 'os-detect'
44
99
 
45
- // Device category
46
- isMobileDevice(); // true if iOS or Android
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
- CommonJS:
51
- ```js
52
- const { getOS, detectIsIOS, isMobileDevice } = require('os-detect');
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
- UMD via `<script>` tag:
56
- ```html
57
- <script src="https://unpkg.com/os-detect/dist/index.umd.js"></script>
58
- <script>
59
- console.log(OsDetect.getOS());
60
- console.log(OsDetect.detectIsIOS());
61
- console.log(OsDetect.isMobileDevice());
62
- </script>
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
- ## React
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
- import { useOS, useIsWindows11 } from 'os-detect/react';
71
-
72
- function App() {
73
- const os = useOS(); // 'windows' | 'macos' | 'ios' | ...
74
- const isWin11 = useIsWindows11(); // null (loading) true | false
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
- Requires React 17+.
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 = useOS(); // Readonly<Ref<OS>>
98
- const isWin11 = useIsWindows11(); // Readonly<Ref<boolean | null>>
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>OS: {{ os }}</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
- Requires Vue 3+.
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 via `process.platform`:
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
- // Reads process.platform no browser required
121
- getOS(); // 'macos' | 'windows' | 'linux' | 'android' | 'unknown'
122
- detectIsWindows(); // true on Windows
311
+ getOS() // 'macos' | 'windows' | 'linux' | 'android' | 'unknown'
312
+ detectIsWindows() // true on Windows
123
313
 
124
- // Windows 11 via os.release() build number (>= 22000)
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
- Functions that require a browser environment (`detectIsIOS()`, `detectIsChromeOS()`) always return `false` in Node.js.
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
- > **SSR hydration note**: In Next.js / Nuxt SSR, `getOS()` returns the **server's** OS, not the client's. To avoid hydration mismatches, wrap detection in `useEffect` (React) or `onMounted` (Vue) so it runs only on the client.
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
- ## API Reference
373
+ ## UMD / CDN
142
374
 
143
- ### OS String Detection
375
+ A prebuilt UMD bundle is available via unpkg and jsDelivr — no build step required.
144
376
 
145
- | Function | Returns |
146
- |---|---|
147
- | `getOS()` | `'ios'` \| `'macos'` \| `'android'` \| `'windows'` \| `'linux'` \| `'chromeos'` \| `'unknown'` |
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
- ### Boolean Detection
384
+ OsDetect.detectIsWindows11().then((isWin11) => {
385
+ console.log('Windows 11:', isWin11)
386
+ })
387
+ </script>
388
+ ```
150
389
 
151
- | Function | Returns `true` when | Node.js |
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
- ### Device Category
392
+ ---
162
393
 
163
- | Function | Returns `true` when |
164
- |---|---|
165
- | `isMobileDevice()` | iOS or Android |
166
- | `isDesktopDevice()` | macOS, Windows, Linux, or ChromeOS |
394
+ ## TypeScript types
167
395
 
168
- > `isMobileDevice()` and `isDesktopDevice()` are **not mutually exclusive** a device with an unrecognized OS returns `false` for both.
396
+ All public types are exported from the package root:
169
397
 
170
- ### React Hooks
398
+ ```ts
399
+ import type { OS } from 'os-detect'
171
400
 
172
- Import from `os-detect/react`:
401
+ // OS is a string union:
402
+ // 'ios' | 'macos' | 'android' | 'windows' | 'linux' | 'chromeos' | 'unknown'
403
+ ```
173
404
 
174
- | Hook | Returns |
175
- |---|---|
176
- | `useOS()` | `OS` string — synchronous, cached |
177
- | `useIsWindows11()` | `boolean \| null` — `null` while detecting |
405
+ ### Narrowing with `getOS()`
178
406
 
179
- ### Vue Composables
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
- Import from `os-detect/vue`:
430
+ TypeScript knows all branches are exhausted after the switch — no need for a `default` case if you handle `'unknown'`.
182
431
 
183
- | Composable | Returns |
184
- |---|---|
185
- | `useOS()` | `Readonly<Ref<OS>>` synchronous, cached |
186
- | `useIsWindows11()` | `Readonly<Ref<boolean \| null>>` — `null` while detecting |
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 Strategy
450
+ ## Detection strategy
191
451
 
192
- **Browser**: prefers `navigator.userAgentData` (Chrome 90+ / Edge 90+), falls back to `navigator.userAgent` for all other browsers.
452
+ ### Browser
193
453
 
194
- **Node.js**: reads `process.platform` when `navigator` is absent.
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
- Results are cached after the first call — subsequent calls have zero overhead.
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
- `detectIsWindows11()` in the browser uses `userAgentData.getHighEntropyValues(['platformVersion'])` — Windows 11 reports `platformVersion >= 13.0.0`. In Node.js it uses `os.release()` — Windows 11 has build number `>= 22000`. Returns `false` if the API is unavailable or the call fails.
459
+ ### Node.js
199
460
 
200
- ### iPadOS 13+ note
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
- Since iPadOS 13, Safari sends `Macintosh` in the userAgent string. `detectIsIOS()` correctly identifies these devices using `navigator.maxTouchPoints > 1`, and `detectIsMacOS()` excludes them accordingly.
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()`. The old name still works but logs a deprecation warning and will be removed in v3.0.
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. ❤️
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "os-detect",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "Simple OS and device type detection for browsers (no dependencies)",
5
5
  "author": "macrulez",
6
6
  "license": "MIT",