Skip to content

Справочник ​

Архитектура ​

os-detect
│
├── src/utils/platform.ts
│     getUADataPlatform()    → navigator.userAgentData.platform (Chrome/Edge)
│     isNodeNavigator()      → отличает собственный синтетический navigator
│                                Node.js (Node 21+) от настоящего браузерного navigator
│     getNodePlatform()      → process.platform (только Node.js)
│
├── src/utils/cache.ts
│     memoizeBoolean()       → оборачивает синхронный детектор одноразовым кэшем
│     memoizeAsyncBoolean()  → то же самое для асинхронной detectIsWindows11();
│                                также делит один промис "в полёте" между
│                                параллельными вызовами до его разрешения
│     resetDetectionCache()  → очищает каждый кэш, зарегистрированный любым из хелперов
│
├── src/detectors/ios.ts        detectIsIOS()       → UA + maxTouchPoints (учитывает iPadOS 13+)
├── src/detectors/macos.ts      detectIsMacOS()      → UAData / UA / process.platform=darwin
├── src/detectors/android.ts    detectIsAndroid()    → UAData / UA / process.platform=android
├── src/detectors/chromeos.ts   detectIsChromeOS()   → UAData / UA (только браузер)
├── src/detectors/linux.ts      detectIsLinux()      → UAData / UA / process.platform=linux
│                                                        (исключает Android и ChromeOS)
├── src/detectors/windows.ts    detectIsWindows()    → UAData / UA / process.platform=win32
│                                detectIsWindows11()  → асинхронно, кэшируется через memoizeAsyncBoolean()
│                                  браузер: userAgentData.getHighEntropyValues(['platformVersion'])
│                                           platformVersion >= 13.0.0 → Windows 11
│                                  Node.js: import('os').release() номер сборки >= 22000
│
├── src/detectors/touch.ts      detectHasTouch()     → maxTouchPoints / msMaxTouchPoints / ontouchstart
├── src/detectors/tv.ts         detectIsTV()         → UAData platform='tv' / UA (Tizen, webOS и т. д.)
├── src/detectors/node.ts       detectIsNode()       → process.versions.node
├── src/detectors/browser.ts    detectIsBrowser()    → typeof window/document !== 'undefined'
├── src/detectors/webworker.ts  detectIsWebWorker()  → self && !window && importScripts
├── src/detectors/electron.ts   detectIsElectron()   → process.versions.electron / UA 'Electron/'
├── src/detectors/pwa.ts        detectIsPWA()        → matchMedia display-mode / navigator.standalone
│                                                        / referrer android-app://
│
├── src/index.ts  (базовая точка входа — реэкспортирует детекторы выше, плюс:)
│     getOS()               → вызывает детекторы в порядке приоритета
│     isMobileDevice()      → detectIsIOS() || detectIsAndroid()
│     isDesktopDevice()     → detectIsMacOS() || detectIsWindows()
│                               || detectIsLinux() || detectIsChromeOS()
│     getFormFactor()       → detectIsTV() / ОС + размеры экрана
│     getRuntime()          → detectIsWebWorker() → detectIsBrowser() → detectIsNode()
│     getPrimaryInput()     → matchMedia('(pointer: …)'), НЕ кэшируется
│     getPixelRatio()       → window.devicePixelRatio, НЕ кэшируется
│     detectIsiOS()         → устаревший алиас для detectIsIOS()
│
├── src/react.ts  (os-detect/react)
│     useOS()              → useState(() => getOS())
│     useIsWindows11()     → useState(null) + useEffect → detectIsWindows11()
│     useFormFactor()      → useState(() => getFormFactor())
│     useRuntime()         → useState(() => getRuntime())
│     usePrimaryInput()    → useState('unknown') + useEffect → слушатели matchMedia
│
└── src/vue.ts  (os-detect/vue)
      useOS()              → readonly(ref(getOS()))
      useIsWindows11()     → readonly(ref(null)) + onMounted → detectIsWindows11()
      useFormFactor()      → readonly(ref(getFormFactor()))
      useRuntime()         → readonly(ref(getRuntime()))
      usePrimaryInput()    → readonly(ref('unknown')) + onMounted/onUnmounted →
                                слушатели matchMedia

Совместимость с SSR ​

Базовая точка входа безопасна для вызова во время серверного рендеринга — определение Node.js читает process.platform напрямую (в том числе на Node.js 21+, где собственный синтетический глобал navigator рантайма корректно распознаётся и не перекрывает Node.js-путь).

За чем стоит следить SSR-фреймворкам — это несовпадения при гидратации, а не ошибочное определение Node.js: getOS() во время серверного рендеринга возвращает ОС сервера, которая может отличаться от клиентской. Любой UI, зависящий от getOS()/useOS(), стоит откладывать до клиента, если требуется точное совпадение — точный паттерн см. в разделах SSR для React-хуков и Composables для Vue. detectIsWindows11()/useIsWindows11() это не затрагивает — они асинхронны и разрешаются только на клиенте.

Функции, требующие настоящего браузерного окружения (detectIsIOS(), detectIsChromeOS(), detectHasTouch(), detectIsTV(), detectIsPWA()), в Node.js всегда возвращают false — там нет ни userAgent, ни maxTouchPoints, ни matchMedia для чтения. getFormFactor() и getRuntime() в Node.js всё же разрешаются в реальное значение ('unknown' и 'node' соответственно, в обычном скрипте), поскольку не для каждой ветки зависят от браузерных глобалов.

getPrimaryInput() и getPixelRatio() — два исключения из «всё кэшируется» выше — почему именно, см. Как это работает, а заметки о SSR во Vue описывают ту же ловушку с гидратацией, что уже есть у useOS() — и её же несут useFormFactor()/useRuntime().

Размер бандла и peer-зависимости ​

Точка входаPeer-зависимостиФорматЗаметки
os-detect—ESM, CJS, UMDБазовая часть — все функции определения и типы OS/FormFactor/Runtime/PrimaryInput
os-detect/reactreact >=17ESM, CJSХуки useOS, useIsWindows11, useFormFactor, useRuntime, usePrimaryInput
os-detect/vuevue >=3ESM, CJSТе же пять, как composables

Все три точки входа перечислены в exports пакета package.json, у каждой есть собственный настоящий .d.ts. Адаптеры для React и Vue не добавляют никакой рантайм-логики сверх самих хуков/composables — они вызывают те же кэшируемые базовые функции.

У пакета нулевые runtime-зависимости. React и Vue — опциональные peer-зависимости, они нужны только при использовании соответствующей точки входа.

Миграция ​

detectIsiOS() переименована в detectIsIOS() (заглавная OS) для согласованности с остальным API.

Старое имя всё ещё работает в v2, но выводит предупреждение об устаревании (один раз за сессию, начиная с v2.2.1) и будет удалено в v3.0:

ts
detectIsiOS() // ⚠ устарело — выводит console.warn один раз за сессию
detectIsIOS() // ✓ используйте это вместо

Других breaking-изменений между v1 и v2 нет.

Лицензия ​

MIT