detecctio 1.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/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # DeteccioJS
2
+
3
+ Lightweight, zero-dependency detection library for OS, browser, and device type.
4
+ Works in every JS environment — React, Vue, Angular, vanilla, SSR (Node-safe).
5
+
6
+ ## Install
7
+
8
+ ```sh
9
+ npm install detecctio
10
+ ```
11
+
12
+ ## Named exports (tree-shakeable)
13
+
14
+ ```ts
15
+ import { isIOS, isMobile, isTouchDevice, isSafari } from "detecctio";
16
+
17
+ if (isIOS) { /* ... */ }
18
+ if (isTouchDevice) { /* ... */ }
19
+ ```
20
+
21
+ ### Full list
22
+
23
+ | Export | Description |
24
+ |---|---|
25
+ | `isIOS` | iPhone, iPad, iPod (incl. iPadOS 13+) |
26
+ | `isAndroid` | Android |
27
+ | `isWindows` | Windows |
28
+ | `isMacOS` | macOS (excludes iOS) |
29
+ | `isLinux` | Linux (excludes Android) |
30
+ | `isSafari` | Safari (excludes Chromium-based) |
31
+ | `isChrome` | Chrome / Chromium / CriOS |
32
+ | `isFirefox` | Firefox / FxiOS |
33
+ | `isOpera` | Opera |
34
+ | `isEdge` | Edge (Chromium & legacy) |
35
+ | `isIE` | Any Internet Explorer |
36
+ | `isIE9` | Internet Explorer 9 specifically |
37
+ | `isIPhone` | iPhone |
38
+ | `isIPad` | iPad (incl. iPadOS 13+) |
39
+ | `isMobile` | Mobile phones (not tablets) |
40
+ | `isTablet` | Tablets |
41
+ | `isDesktop` | Not mobile and not tablet |
42
+ | `isTouchDevice` | Has touch capability |
43
+
44
+ ## Class-based API
45
+
46
+ ```ts
47
+ import { DetecctioJS } from "detecctio";
48
+
49
+ new DetecctioJS({
50
+ windowObject: true, // creates window.detecctio object with all booleans
51
+ documentClasses: "html", // adds classes to <html> e.g. "is-ios", "is-touch-device"
52
+ // also accepts: "body" | any CSS selector | false
53
+ });
54
+ ```
55
+
56
+ ### `window.detecctio`
57
+
58
+ When `windowObject: true` is set, `window.detecctio` is populated with all boolean flags:
59
+
60
+ ```js
61
+ console.log(window.detecctio.isDesktop); // true | false
62
+ ```
63
+
64
+ ### CSS classes
65
+
66
+ When `documentClasses` targets an element, active detections are added as kebab-case classes:
67
+
68
+ ```html
69
+ <html class="is-desktop is-chrome is-windows">
70
+ ```
71
+
72
+ ## CDN / UMD
73
+
74
+ ```html
75
+ <script src="https://unpkg.com/detecctio/dist/index.umd.js"></script>
76
+ <script>
77
+ const { isIOS, DetecctioJS } = DetecctioJS;
78
+ </script>
79
+ ```
80
+
81
+ ## SSR / Node
82
+
83
+ All detections are **SSR-safe** — no crash when `window` or `navigator` is absent; values default to `false`.
84
+
85
+ ## Development
86
+
87
+ ```sh
88
+ nvm use # uses .nvmrc (Node 20)
89
+ npm install
90
+ npm run build # ESM + CJS + UMD
91
+ npm test # Vitest
92
+ npm run typecheck
93
+ ```
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";var m=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames,y=Object.getOwnPropertySymbols;var P=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var A=(o,e,t)=>e in o?m(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t,S=(o,e)=>{for(var t in e||(e={}))P.call(e,t)&&A(o,t,e[t]);if(y)for(var t of y(e))W.call(e,t)&&A(o,t,e[t]);return o};var F=(o,e)=>{for(var t in e)m(o,t,{get:e[t],enumerable:!0})},U=(o,e,t,f)=>{if(e&&typeof e=="object"||typeof e=="function")for(let d of L(e))!P.call(o,d)&&d!==t&&m(o,d,{get:()=>e[d],enumerable:!(f=C(e,d))||f.enumerable});return o};var j=o=>U(m({},"__esModule",{value:!0}),o);var _={};F(_,{DetecctioJS:()=>M,isAndroid:()=>l,isChrome:()=>O,isDesktop:()=>E,isEdge:()=>s,isFirefox:()=>D,isIE:()=>w,isIE9:()=>g,isIOS:()=>n,isIPad:()=>a,isIPhone:()=>v,isLinux:()=>h,isMacOS:()=>x,isMobile:()=>p,isOpera:()=>r,isSafari:()=>I,isTablet:()=>c,isTouchDevice:()=>T,isWindows:()=>b});module.exports=j(_);function R(){return typeof navigator=="undefined"?"":navigator.userAgent||""}function u(){var o;return typeof navigator=="undefined"?0:(o=navigator.maxTouchPoints)!=null?o:0}function i(o){return o.test(R())}var n=(()=>{var o;return i(/iphone|ipad|ipod/i)?!0:/macintosh/i.test((o=navigator==null?void 0:navigator.userAgent)!=null?o:"")&&u()>1})(),l=i(/android/i),b=i(/windows/i),x=n?!1:i(/macintosh|mac os x/i),h=l?!1:i(/linux/i);var s=i(/edg(e|\/)/i),r=i(/opr\/|opera/i),g=i(/msie 9/i),w=i(/msie|trident/i),D=i(/firefox|fxios/i),I=s||r?!1:i(/safari/i)&&!i(/chrome|chromium|crios/i),O=s||r?!1:i(/chrome|chromium|crios/i);var v=i(/iphone/i),a=(()=>{var o;return i(/ipad/i)?!0:/macintosh/i.test((o=navigator==null?void 0:navigator.userAgent)!=null?o:"")&&u()>1})(),T=(()=>{var o;return typeof window=="undefined"?!1:"ontouchstart"in window||u()>0||!!window.DocumentTouch&&typeof document!="undefined"&&document instanceof((o=window.DocumentTouch)!=null?o:class{})})(),c=!!(a||i(/android/i)&&!i(/mobile/i)||i(/windows/i)&&i(/touch/i)&&!i(/tablet pc/i)),p=c?!1:i(/android.*mobile|iphone|ipod|windows phone|blackberry|bb\d+|meego|silk|bada\//i)||n&&!a,E=!p&&!c;var k={isIOS:n,isAndroid:l,isWindows:b,isMacOS:x,isLinux:h,isSafari:I,isChrome:O,isFirefox:D,isOpera:r,isEdge:s,isIE:w,isIE9:g,isIPad:a,isIPhone:v,isDesktop:E,isMobile:p,isTablet:c,isTouchDevice:T},N={isIOS:"is-ios",isAndroid:"is-android",isWindows:"is-windows",isMacOS:"is-macos",isLinux:"is-linux",isSafari:"is-safari",isChrome:"is-chrome",isFirefox:"is-firefox",isOpera:"is-opera",isEdge:"is-edge",isIE:"is-ie",isIE9:"is-ie9",isIPad:"is-ipad",isIPhone:"is-iphone",isDesktop:"is-desktop",isMobile:"is-mobile",isTablet:"is-tablet",isTouchDevice:"is-touch-device"},M=class{constructor(e={}){if(this.detections=S({},k),e.windowObject&&typeof window!="undefined"&&(window.detecctio=S({},k)),e.documentClasses&&typeof document!="undefined"){let t=this.resolveTarget(e.documentClasses);t&&this.applyClasses(t)}}resolveTarget(e){return e==="html"?document.documentElement:e==="body"?document.body:document.querySelector(e)}applyClasses(e){for(let[t,f]of Object.entries(this.detections))f&&e.classList.add(N[t])}};0&&(module.exports={DetecctioJS,isAndroid,isChrome,isDesktop,isEdge,isFirefox,isIE,isIE9,isIOS,isIPad,isIPhone,isLinux,isMacOS,isMobile,isOpera,isSafari,isTablet,isTouchDevice,isWindows});
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/utils/ua.ts","../src/detections/os.ts","../src/detections/browser.ts","../src/detections/device.ts","../src/DetecctioJS.ts"],"sourcesContent":["export { isIOS, isAndroid, isWindows, isMacOS, isLinux } from \"./detections/os\";\nexport { isChrome, isEdge, isFirefox, isIE, isIE9, isOpera, isSafari } from \"./detections/browser\";\nexport { isDesktop, isIPad, isIPhone, isMobile, isTablet, isTouchDevice } from \"./detections/device\";\nexport { DetecctioJS } from \"./DetecctioJS\";\nexport type { DetecctioOptions, DetectionMap } from \"./DetecctioJS\";\n","/**\n * Safe user-agent string — returns empty string in non-browser / SSR envs.\n */\nexport function getUA(): string {\n if (typeof navigator === \"undefined\") return \"\";\n return navigator.userAgent || \"\";\n}\n\n/**\n * Safe platform string.\n */\nexport function getPlatform(): string {\n if (typeof navigator === \"undefined\") return \"\";\n // navigator.platform is deprecated but still the most reliable for iPad detection\n return (navigator as Navigator & { userAgentData?: { platform?: string } })\n .userAgentData?.platform ?? navigator.platform ?? \"\";\n}\n\n/**\n * Returns max touch points count (0 in non-browser env).\n */\nexport function getMaxTouchPoints(): number {\n if (typeof navigator === \"undefined\") return 0;\n return navigator.maxTouchPoints ?? 0;\n}\n\nexport function testUA(pattern: RegExp): boolean {\n return pattern.test(getUA());\n}\n","import { getMaxTouchPoints, getPlatform, testUA } from \"../utils/ua\";\n\n/**\n * iOS — iPhone, iPad, iPod.\n * Uses both UA and the iPad-on-desktop trick (macOS + touch points).\n */\nexport const isIOS: boolean = (() => {\n if (testUA(/iphone|ipad|ipod/i)) return true;\n // iPadOS 13+ reports as macOS in UA\n return /macintosh/i.test(navigator?.userAgent ?? \"\") && getMaxTouchPoints() > 1;\n})();\n\nexport const isAndroid: boolean = testUA(/android/i);\n\n/**\n * Windows — desktop or mobile.\n */\nexport const isWindows: boolean = testUA(/windows/i);\n\nexport const isMacOS: boolean = (() => {\n if (isIOS) return false;\n return testUA(/macintosh|mac os x/i);\n})();\n\nexport const isLinux: boolean = (() => {\n if (isAndroid) return false;\n return testUA(/linux/i);\n})();\n","import { testUA } from \"../utils/ua\";\n\n/**\n * Edge — Chromium-based Edge (Edg/) and legacy Edge (Edge/).\n * Must be checked before Chrome since Edg UA also contains \"Chrome\".\n */\nexport const isEdge: boolean = testUA(/edg(e|\\/)/i);\n\n/**\n * Opera — modern (OPR/) and legacy (Opera/).\n * Must be checked before Chrome for the same reason.\n */\nexport const isOpera: boolean = testUA(/opr\\/|opera/i);\n\n/**\n * IE9 — strict IE 9 match.\n */\nexport const isIE9: boolean = testUA(/msie 9/i);\n\n/**\n * Any Internet Explorer (IE 6–11).\n */\nexport const isIE: boolean = testUA(/msie|trident/i);\n\n/**\n * Firefox.\n */\nexport const isFirefox: boolean = testUA(/firefox|fxios/i);\n\n/**\n * Safari — must exclude Chrome and Android WebView which also include \"Safari\".\n */\nexport const isSafari: boolean = (() => {\n if (isEdge || isOpera) return false;\n return testUA(/safari/i) && !testUA(/chrome|chromium|crios/i);\n})();\n\n/**\n * Chrome — includes Chromium, Chrome for iOS (CriOS).\n * Excludes Edge and Opera which also carry the Chrome token.\n */\nexport const isChrome: boolean = (() => {\n if (isEdge || isOpera) return false;\n return testUA(/chrome|chromium|crios/i);\n})();\n","import { getMaxTouchPoints, testUA } from \"../utils/ua\";\nimport { isIOS } from \"./os\";\n\n/**\n * iPhone — not iPad.\n */\nexport const isIPhone: boolean = testUA(/iphone/i);\n\n/**\n * iPad — includes iPadOS 13+ which disguises itself as macOS.\n */\nexport const isIPad: boolean = (() => {\n if (testUA(/ipad/i)) return true;\n return /macintosh/i.test(navigator?.userAgent ?? \"\") && getMaxTouchPoints() > 1;\n})();\n\n/**\n * Touch device — has touch capability.\n * Works in browser; returns false in SSR / Node.\n */\nexport const isTouchDevice: boolean = (() => {\n if (typeof window === \"undefined\") return false;\n return (\n \"ontouchstart\" in window ||\n getMaxTouchPoints() > 0 ||\n // legacy IE/Edge\n !!(window as Window & { DocumentTouch?: unknown }).DocumentTouch &&\n typeof document !== \"undefined\" &&\n document instanceof ((window as Window & { DocumentTouch?: unknown }).DocumentTouch as { new(): unknown } ?? class {})\n );\n})();\n\n/**\n * Tablet — iPad, Android tablets (no \"Mobile\" in UA), or Windows tablets.\n */\nexport const isTablet: boolean = (() => {\n if (isIPad) return true;\n // Android tablet: has \"Android\" but not \"Mobile\"\n if (testUA(/android/i) && !testUA(/mobile/i)) return true;\n // Windows tablet\n if (testUA(/windows/i) && testUA(/touch/i) && !testUA(/tablet pc/i)) return true;\n return false;\n})();\n\n/**\n * Mobile — phones only (not tablets).\n */\nexport const isMobile: boolean = (() => {\n if (isTablet) return false;\n return testUA(/android.*mobile|iphone|ipod|windows phone|blackberry|bb\\d+|meego|silk|bada\\//i) || isIOS && !isIPad;\n})();\n\n/**\n * Desktop — not mobile, not tablet.\n */\nexport const isDesktop: boolean = !isMobile && !isTablet;\n","import { isAndroid, isIOS, isLinux, isMacOS, isWindows } from \"./detections/os\";\nimport { isChrome, isEdge, isFirefox, isIE, isIE9, isOpera, isSafari } from \"./detections/browser\";\nimport {\n isDesktop,\n isIPad,\n isIPhone,\n isMobile,\n isTablet,\n isTouchDevice,\n} from \"./detections/device\";\n\nexport interface DetectionMap {\n isIOS: boolean;\n isAndroid: boolean;\n isWindows: boolean;\n isMacOS: boolean;\n isLinux: boolean;\n isSafari: boolean;\n isChrome: boolean;\n isFirefox: boolean;\n isOpera: boolean;\n isEdge: boolean;\n isIE: boolean;\n isIE9: boolean;\n isIPad: boolean;\n isIPhone: boolean;\n isDesktop: boolean;\n isMobile: boolean;\n isTablet: boolean;\n isTouchDevice: boolean;\n}\n\nexport interface DetecctioOptions {\n /**\n * Attach detections to `window.detecctio` as a plain object of booleans.\n * @default false\n */\n windowObject?: boolean;\n /**\n * Target element for class injection. Accepts `\"html\"`, `\"body\"`, or a CSS selector string.\n * Pass `false` to disable class injection.\n * @default false\n */\n documentClasses?: \"html\" | \"body\" | string | false;\n}\n\nconst ALL_DETECTIONS: DetectionMap = {\n isIOS,\n isAndroid,\n isWindows,\n isMacOS,\n isLinux,\n isSafari,\n isChrome,\n isFirefox,\n isOpera,\n isEdge,\n isIE,\n isIE9,\n isIPad,\n isIPhone,\n isDesktop,\n isMobile,\n isTablet,\n isTouchDevice,\n};\n\nconst CLASS_MAP: Record<keyof DetectionMap, string> = {\n isIOS: \"is-ios\",\n isAndroid: \"is-android\",\n isWindows: \"is-windows\",\n isMacOS: \"is-macos\",\n isLinux: \"is-linux\",\n isSafari: \"is-safari\",\n isChrome: \"is-chrome\",\n isFirefox: \"is-firefox\",\n isOpera: \"is-opera\",\n isEdge: \"is-edge\",\n isIE: \"is-ie\",\n isIE9: \"is-ie9\",\n isIPad: \"is-ipad\",\n isIPhone: \"is-iphone\",\n isDesktop: \"is-desktop\",\n isMobile: \"is-mobile\",\n isTablet: \"is-tablet\",\n isTouchDevice: \"is-touch-device\",\n};\n\nexport class DetecctioJS {\n readonly detections: Readonly<DetectionMap>;\n\n constructor(options: DetecctioOptions = {}) {\n this.detections = { ...ALL_DETECTIONS };\n\n if (options.windowObject && typeof window !== \"undefined\") {\n (window as Window & { detecctio?: DetectionMap }).detecctio = { ...ALL_DETECTIONS };\n }\n\n if (options.documentClasses && typeof document !== \"undefined\") {\n const target = this.resolveTarget(options.documentClasses);\n if (target) {\n this.applyClasses(target);\n }\n }\n }\n\n private resolveTarget(selector: string): Element | null {\n if (selector === \"html\") return document.documentElement;\n if (selector === \"body\") return document.body;\n return document.querySelector(selector);\n }\n\n private applyClasses(el: Element): void {\n for (const [key, value] of Object.entries(this.detections) as [keyof DetectionMap, boolean][]) {\n if (value) {\n el.classList.add(CLASS_MAP[key]);\n }\n }\n }\n}\n"],"mappings":"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","names":["index_exports","__export","DetecctioJS","isAndroid","isChrome","isDesktop","isEdge","isFirefox","isIE","isIE9","isIOS","isIPad","isIPhone","isLinux","isMacOS","isMobile","isOpera","isSafari","isTablet","isTouchDevice","isWindows","__toCommonJS","getUA","getMaxTouchPoints","_a","testUA","pattern","getUA","isIOS","_a","testUA","getMaxTouchPoints","isAndroid","isWindows","isMacOS","isLinux","isEdge","testUA","isOpera","isIE9","isIE","isFirefox","isSafari","isChrome","isIPhone","testUA","isIPad","_a","getMaxTouchPoints","isTouchDevice","isTablet","isMobile","isIOS","isDesktop","ALL_DETECTIONS","isIOS","isAndroid","isWindows","isMacOS","isLinux","isSafari","isChrome","isFirefox","isOpera","isEdge","isIE","isIE9","isIPad","isIPhone","isDesktop","isMobile","isTablet","isTouchDevice","CLASS_MAP","DetecctioJS","options","__spreadValues","target","selector","el","key","value"]}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * iOS — iPhone, iPad, iPod.
3
+ * Uses both UA and the iPad-on-desktop trick (macOS + touch points).
4
+ */
5
+ declare const isIOS: boolean;
6
+ declare const isAndroid: boolean;
7
+ /**
8
+ * Windows — desktop or mobile.
9
+ */
10
+ declare const isWindows: boolean;
11
+ declare const isMacOS: boolean;
12
+ declare const isLinux: boolean;
13
+
14
+ /**
15
+ * Edge — Chromium-based Edge (Edg/) and legacy Edge (Edge/).
16
+ * Must be checked before Chrome since Edg UA also contains "Chrome".
17
+ */
18
+ declare const isEdge: boolean;
19
+ /**
20
+ * Opera — modern (OPR/) and legacy (Opera/).
21
+ * Must be checked before Chrome for the same reason.
22
+ */
23
+ declare const isOpera: boolean;
24
+ /**
25
+ * IE9 — strict IE 9 match.
26
+ */
27
+ declare const isIE9: boolean;
28
+ /**
29
+ * Any Internet Explorer (IE 6–11).
30
+ */
31
+ declare const isIE: boolean;
32
+ /**
33
+ * Firefox.
34
+ */
35
+ declare const isFirefox: boolean;
36
+ /**
37
+ * Safari — must exclude Chrome and Android WebView which also include "Safari".
38
+ */
39
+ declare const isSafari: boolean;
40
+ /**
41
+ * Chrome — includes Chromium, Chrome for iOS (CriOS).
42
+ * Excludes Edge and Opera which also carry the Chrome token.
43
+ */
44
+ declare const isChrome: boolean;
45
+
46
+ /**
47
+ * iPhone — not iPad.
48
+ */
49
+ declare const isIPhone: boolean;
50
+ /**
51
+ * iPad — includes iPadOS 13+ which disguises itself as macOS.
52
+ */
53
+ declare const isIPad: boolean;
54
+ /**
55
+ * Touch device — has touch capability.
56
+ * Works in browser; returns false in SSR / Node.
57
+ */
58
+ declare const isTouchDevice: boolean;
59
+ /**
60
+ * Tablet — iPad, Android tablets (no "Mobile" in UA), or Windows tablets.
61
+ */
62
+ declare const isTablet: boolean;
63
+ /**
64
+ * Mobile — phones only (not tablets).
65
+ */
66
+ declare const isMobile: boolean;
67
+ /**
68
+ * Desktop — not mobile, not tablet.
69
+ */
70
+ declare const isDesktop: boolean;
71
+
72
+ interface DetectionMap {
73
+ isIOS: boolean;
74
+ isAndroid: boolean;
75
+ isWindows: boolean;
76
+ isMacOS: boolean;
77
+ isLinux: boolean;
78
+ isSafari: boolean;
79
+ isChrome: boolean;
80
+ isFirefox: boolean;
81
+ isOpera: boolean;
82
+ isEdge: boolean;
83
+ isIE: boolean;
84
+ isIE9: boolean;
85
+ isIPad: boolean;
86
+ isIPhone: boolean;
87
+ isDesktop: boolean;
88
+ isMobile: boolean;
89
+ isTablet: boolean;
90
+ isTouchDevice: boolean;
91
+ }
92
+ interface DetecctioOptions {
93
+ /**
94
+ * Attach detections to `window.detecctio` as a plain object of booleans.
95
+ * @default false
96
+ */
97
+ windowObject?: boolean;
98
+ /**
99
+ * Target element for class injection. Accepts `"html"`, `"body"`, or a CSS selector string.
100
+ * Pass `false` to disable class injection.
101
+ * @default false
102
+ */
103
+ documentClasses?: "html" | "body" | string | false;
104
+ }
105
+ declare class DetecctioJS {
106
+ readonly detections: Readonly<DetectionMap>;
107
+ constructor(options?: DetecctioOptions);
108
+ private resolveTarget;
109
+ private applyClasses;
110
+ }
111
+
112
+ export { DetecctioJS, type DetecctioOptions, type DetectionMap, isAndroid, isChrome, isDesktop, isEdge, isFirefox, isIE, isIE9, isIOS, isIPad, isIPhone, isLinux, isMacOS, isMobile, isOpera, isSafari, isTablet, isTouchDevice, isWindows };
@@ -0,0 +1,112 @@
1
+ /**
2
+ * iOS — iPhone, iPad, iPod.
3
+ * Uses both UA and the iPad-on-desktop trick (macOS + touch points).
4
+ */
5
+ declare const isIOS: boolean;
6
+ declare const isAndroid: boolean;
7
+ /**
8
+ * Windows — desktop or mobile.
9
+ */
10
+ declare const isWindows: boolean;
11
+ declare const isMacOS: boolean;
12
+ declare const isLinux: boolean;
13
+
14
+ /**
15
+ * Edge — Chromium-based Edge (Edg/) and legacy Edge (Edge/).
16
+ * Must be checked before Chrome since Edg UA also contains "Chrome".
17
+ */
18
+ declare const isEdge: boolean;
19
+ /**
20
+ * Opera — modern (OPR/) and legacy (Opera/).
21
+ * Must be checked before Chrome for the same reason.
22
+ */
23
+ declare const isOpera: boolean;
24
+ /**
25
+ * IE9 — strict IE 9 match.
26
+ */
27
+ declare const isIE9: boolean;
28
+ /**
29
+ * Any Internet Explorer (IE 6–11).
30
+ */
31
+ declare const isIE: boolean;
32
+ /**
33
+ * Firefox.
34
+ */
35
+ declare const isFirefox: boolean;
36
+ /**
37
+ * Safari — must exclude Chrome and Android WebView which also include "Safari".
38
+ */
39
+ declare const isSafari: boolean;
40
+ /**
41
+ * Chrome — includes Chromium, Chrome for iOS (CriOS).
42
+ * Excludes Edge and Opera which also carry the Chrome token.
43
+ */
44
+ declare const isChrome: boolean;
45
+
46
+ /**
47
+ * iPhone — not iPad.
48
+ */
49
+ declare const isIPhone: boolean;
50
+ /**
51
+ * iPad — includes iPadOS 13+ which disguises itself as macOS.
52
+ */
53
+ declare const isIPad: boolean;
54
+ /**
55
+ * Touch device — has touch capability.
56
+ * Works in browser; returns false in SSR / Node.
57
+ */
58
+ declare const isTouchDevice: boolean;
59
+ /**
60
+ * Tablet — iPad, Android tablets (no "Mobile" in UA), or Windows tablets.
61
+ */
62
+ declare const isTablet: boolean;
63
+ /**
64
+ * Mobile — phones only (not tablets).
65
+ */
66
+ declare const isMobile: boolean;
67
+ /**
68
+ * Desktop — not mobile, not tablet.
69
+ */
70
+ declare const isDesktop: boolean;
71
+
72
+ interface DetectionMap {
73
+ isIOS: boolean;
74
+ isAndroid: boolean;
75
+ isWindows: boolean;
76
+ isMacOS: boolean;
77
+ isLinux: boolean;
78
+ isSafari: boolean;
79
+ isChrome: boolean;
80
+ isFirefox: boolean;
81
+ isOpera: boolean;
82
+ isEdge: boolean;
83
+ isIE: boolean;
84
+ isIE9: boolean;
85
+ isIPad: boolean;
86
+ isIPhone: boolean;
87
+ isDesktop: boolean;
88
+ isMobile: boolean;
89
+ isTablet: boolean;
90
+ isTouchDevice: boolean;
91
+ }
92
+ interface DetecctioOptions {
93
+ /**
94
+ * Attach detections to `window.detecctio` as a plain object of booleans.
95
+ * @default false
96
+ */
97
+ windowObject?: boolean;
98
+ /**
99
+ * Target element for class injection. Accepts `"html"`, `"body"`, or a CSS selector string.
100
+ * Pass `false` to disable class injection.
101
+ * @default false
102
+ */
103
+ documentClasses?: "html" | "body" | string | false;
104
+ }
105
+ declare class DetecctioJS {
106
+ readonly detections: Readonly<DetectionMap>;
107
+ constructor(options?: DetecctioOptions);
108
+ private resolveTarget;
109
+ private applyClasses;
110
+ }
111
+
112
+ export { DetecctioJS, type DetecctioOptions, type DetectionMap, isAndroid, isChrome, isDesktop, isEdge, isFirefox, isIE, isIE9, isIOS, isIPad, isIPhone, isLinux, isMacOS, isMobile, isOpera, isSafari, isTablet, isTouchDevice, isWindows };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ var A=Object.defineProperty;var E=Object.getOwnPropertySymbols;var P=Object.prototype.hasOwnProperty,k=Object.prototype.propertyIsEnumerable;var M=(e,i,t)=>i in e?A(e,i,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[i]=t,p=(e,i)=>{for(var t in i||(i={}))P.call(i,t)&&M(e,t,i[t]);if(E)for(var t of E(i))k.call(i,t)&&M(e,t,i[t]);return e};function C(){return typeof navigator=="undefined"?"":navigator.userAgent||""}function s(){var e;return typeof navigator=="undefined"?0:(e=navigator.maxTouchPoints)!=null?e:0}function o(e){return e.test(C())}var n=(()=>{var e;return o(/iphone|ipad|ipod/i)?!0:/macintosh/i.test((e=navigator==null?void 0:navigator.userAgent)!=null?e:"")&&s()>1})(),u=o(/android/i),f=o(/windows/i),m=n?!1:o(/macintosh|mac os x/i),b=u?!1:o(/linux/i);var r=o(/edg(e|\/)/i),a=o(/opr\/|opera/i),x=o(/msie 9/i),h=o(/msie|trident/i),g=o(/firefox|fxios/i),w=r||a?!1:o(/safari/i)&&!o(/chrome|chromium|crios/i),D=r||a?!1:o(/chrome|chromium|crios/i);var I=o(/iphone/i),c=(()=>{var e;return o(/ipad/i)?!0:/macintosh/i.test((e=navigator==null?void 0:navigator.userAgent)!=null?e:"")&&s()>1})(),O=(()=>{var e;return typeof window=="undefined"?!1:"ontouchstart"in window||s()>0||!!window.DocumentTouch&&typeof document!="undefined"&&document instanceof((e=window.DocumentTouch)!=null?e:class{})})(),d=!!(c||o(/android/i)&&!o(/mobile/i)||o(/windows/i)&&o(/touch/i)&&!o(/tablet pc/i)),l=d?!1:o(/android.*mobile|iphone|ipod|windows phone|blackberry|bb\d+|meego|silk|bada\//i)||n&&!c,v=!l&&!d;var S={isIOS:n,isAndroid:u,isWindows:f,isMacOS:m,isLinux:b,isSafari:w,isChrome:D,isFirefox:g,isOpera:a,isEdge:r,isIE:h,isIE9:x,isIPad:c,isIPhone:I,isDesktop:v,isMobile:l,isTablet:d,isTouchDevice:O},L={isIOS:"is-ios",isAndroid:"is-android",isWindows:"is-windows",isMacOS:"is-macos",isLinux:"is-linux",isSafari:"is-safari",isChrome:"is-chrome",isFirefox:"is-firefox",isOpera:"is-opera",isEdge:"is-edge",isIE:"is-ie",isIE9:"is-ie9",isIPad:"is-ipad",isIPhone:"is-iphone",isDesktop:"is-desktop",isMobile:"is-mobile",isTablet:"is-tablet",isTouchDevice:"is-touch-device"},T=class{constructor(i={}){if(this.detections=p({},S),i.windowObject&&typeof window!="undefined"&&(window.detecctio=p({},S)),i.documentClasses&&typeof document!="undefined"){let t=this.resolveTarget(i.documentClasses);t&&this.applyClasses(t)}}resolveTarget(i){return i==="html"?document.documentElement:i==="body"?document.body:document.querySelector(i)}applyClasses(i){for(let[t,y]of Object.entries(this.detections))y&&i.classList.add(L[t])}};export{T as DetecctioJS,u as isAndroid,D as isChrome,v as isDesktop,r as isEdge,g as isFirefox,h as isIE,x as isIE9,n as isIOS,c as isIPad,I as isIPhone,b as isLinux,m as isMacOS,l as isMobile,a as isOpera,w as isSafari,d as isTablet,O as isTouchDevice,f as isWindows};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/ua.ts","../src/detections/os.ts","../src/detections/browser.ts","../src/detections/device.ts","../src/DetecctioJS.ts"],"sourcesContent":["/**\n * Safe user-agent string — returns empty string in non-browser / SSR envs.\n */\nexport function getUA(): string {\n if (typeof navigator === \"undefined\") return \"\";\n return navigator.userAgent || \"\";\n}\n\n/**\n * Safe platform string.\n */\nexport function getPlatform(): string {\n if (typeof navigator === \"undefined\") return \"\";\n // navigator.platform is deprecated but still the most reliable for iPad detection\n return (navigator as Navigator & { userAgentData?: { platform?: string } })\n .userAgentData?.platform ?? navigator.platform ?? \"\";\n}\n\n/**\n * Returns max touch points count (0 in non-browser env).\n */\nexport function getMaxTouchPoints(): number {\n if (typeof navigator === \"undefined\") return 0;\n return navigator.maxTouchPoints ?? 0;\n}\n\nexport function testUA(pattern: RegExp): boolean {\n return pattern.test(getUA());\n}\n","import { getMaxTouchPoints, getPlatform, testUA } from \"../utils/ua\";\n\n/**\n * iOS — iPhone, iPad, iPod.\n * Uses both UA and the iPad-on-desktop trick (macOS + touch points).\n */\nexport const isIOS: boolean = (() => {\n if (testUA(/iphone|ipad|ipod/i)) return true;\n // iPadOS 13+ reports as macOS in UA\n return /macintosh/i.test(navigator?.userAgent ?? \"\") && getMaxTouchPoints() > 1;\n})();\n\nexport const isAndroid: boolean = testUA(/android/i);\n\n/**\n * Windows — desktop or mobile.\n */\nexport const isWindows: boolean = testUA(/windows/i);\n\nexport const isMacOS: boolean = (() => {\n if (isIOS) return false;\n return testUA(/macintosh|mac os x/i);\n})();\n\nexport const isLinux: boolean = (() => {\n if (isAndroid) return false;\n return testUA(/linux/i);\n})();\n","import { testUA } from \"../utils/ua\";\n\n/**\n * Edge — Chromium-based Edge (Edg/) and legacy Edge (Edge/).\n * Must be checked before Chrome since Edg UA also contains \"Chrome\".\n */\nexport const isEdge: boolean = testUA(/edg(e|\\/)/i);\n\n/**\n * Opera — modern (OPR/) and legacy (Opera/).\n * Must be checked before Chrome for the same reason.\n */\nexport const isOpera: boolean = testUA(/opr\\/|opera/i);\n\n/**\n * IE9 — strict IE 9 match.\n */\nexport const isIE9: boolean = testUA(/msie 9/i);\n\n/**\n * Any Internet Explorer (IE 6–11).\n */\nexport const isIE: boolean = testUA(/msie|trident/i);\n\n/**\n * Firefox.\n */\nexport const isFirefox: boolean = testUA(/firefox|fxios/i);\n\n/**\n * Safari — must exclude Chrome and Android WebView which also include \"Safari\".\n */\nexport const isSafari: boolean = (() => {\n if (isEdge || isOpera) return false;\n return testUA(/safari/i) && !testUA(/chrome|chromium|crios/i);\n})();\n\n/**\n * Chrome — includes Chromium, Chrome for iOS (CriOS).\n * Excludes Edge and Opera which also carry the Chrome token.\n */\nexport const isChrome: boolean = (() => {\n if (isEdge || isOpera) return false;\n return testUA(/chrome|chromium|crios/i);\n})();\n","import { getMaxTouchPoints, testUA } from \"../utils/ua\";\nimport { isIOS } from \"./os\";\n\n/**\n * iPhone — not iPad.\n */\nexport const isIPhone: boolean = testUA(/iphone/i);\n\n/**\n * iPad — includes iPadOS 13+ which disguises itself as macOS.\n */\nexport const isIPad: boolean = (() => {\n if (testUA(/ipad/i)) return true;\n return /macintosh/i.test(navigator?.userAgent ?? \"\") && getMaxTouchPoints() > 1;\n})();\n\n/**\n * Touch device — has touch capability.\n * Works in browser; returns false in SSR / Node.\n */\nexport const isTouchDevice: boolean = (() => {\n if (typeof window === \"undefined\") return false;\n return (\n \"ontouchstart\" in window ||\n getMaxTouchPoints() > 0 ||\n // legacy IE/Edge\n !!(window as Window & { DocumentTouch?: unknown }).DocumentTouch &&\n typeof document !== \"undefined\" &&\n document instanceof ((window as Window & { DocumentTouch?: unknown }).DocumentTouch as { new(): unknown } ?? class {})\n );\n})();\n\n/**\n * Tablet — iPad, Android tablets (no \"Mobile\" in UA), or Windows tablets.\n */\nexport const isTablet: boolean = (() => {\n if (isIPad) return true;\n // Android tablet: has \"Android\" but not \"Mobile\"\n if (testUA(/android/i) && !testUA(/mobile/i)) return true;\n // Windows tablet\n if (testUA(/windows/i) && testUA(/touch/i) && !testUA(/tablet pc/i)) return true;\n return false;\n})();\n\n/**\n * Mobile — phones only (not tablets).\n */\nexport const isMobile: boolean = (() => {\n if (isTablet) return false;\n return testUA(/android.*mobile|iphone|ipod|windows phone|blackberry|bb\\d+|meego|silk|bada\\//i) || isIOS && !isIPad;\n})();\n\n/**\n * Desktop — not mobile, not tablet.\n */\nexport const isDesktop: boolean = !isMobile && !isTablet;\n","import { isAndroid, isIOS, isLinux, isMacOS, isWindows } from \"./detections/os\";\nimport { isChrome, isEdge, isFirefox, isIE, isIE9, isOpera, isSafari } from \"./detections/browser\";\nimport {\n isDesktop,\n isIPad,\n isIPhone,\n isMobile,\n isTablet,\n isTouchDevice,\n} from \"./detections/device\";\n\nexport interface DetectionMap {\n isIOS: boolean;\n isAndroid: boolean;\n isWindows: boolean;\n isMacOS: boolean;\n isLinux: boolean;\n isSafari: boolean;\n isChrome: boolean;\n isFirefox: boolean;\n isOpera: boolean;\n isEdge: boolean;\n isIE: boolean;\n isIE9: boolean;\n isIPad: boolean;\n isIPhone: boolean;\n isDesktop: boolean;\n isMobile: boolean;\n isTablet: boolean;\n isTouchDevice: boolean;\n}\n\nexport interface DetecctioOptions {\n /**\n * Attach detections to `window.detecctio` as a plain object of booleans.\n * @default false\n */\n windowObject?: boolean;\n /**\n * Target element for class injection. Accepts `\"html\"`, `\"body\"`, or a CSS selector string.\n * Pass `false` to disable class injection.\n * @default false\n */\n documentClasses?: \"html\" | \"body\" | string | false;\n}\n\nconst ALL_DETECTIONS: DetectionMap = {\n isIOS,\n isAndroid,\n isWindows,\n isMacOS,\n isLinux,\n isSafari,\n isChrome,\n isFirefox,\n isOpera,\n isEdge,\n isIE,\n isIE9,\n isIPad,\n isIPhone,\n isDesktop,\n isMobile,\n isTablet,\n isTouchDevice,\n};\n\nconst CLASS_MAP: Record<keyof DetectionMap, string> = {\n isIOS: \"is-ios\",\n isAndroid: \"is-android\",\n isWindows: \"is-windows\",\n isMacOS: \"is-macos\",\n isLinux: \"is-linux\",\n isSafari: \"is-safari\",\n isChrome: \"is-chrome\",\n isFirefox: \"is-firefox\",\n isOpera: \"is-opera\",\n isEdge: \"is-edge\",\n isIE: \"is-ie\",\n isIE9: \"is-ie9\",\n isIPad: \"is-ipad\",\n isIPhone: \"is-iphone\",\n isDesktop: \"is-desktop\",\n isMobile: \"is-mobile\",\n isTablet: \"is-tablet\",\n isTouchDevice: \"is-touch-device\",\n};\n\nexport class DetecctioJS {\n readonly detections: Readonly<DetectionMap>;\n\n constructor(options: DetecctioOptions = {}) {\n this.detections = { ...ALL_DETECTIONS };\n\n if (options.windowObject && typeof window !== \"undefined\") {\n (window as Window & { detecctio?: DetectionMap }).detecctio = { ...ALL_DETECTIONS };\n }\n\n if (options.documentClasses && typeof document !== \"undefined\") {\n const target = this.resolveTarget(options.documentClasses);\n if (target) {\n this.applyClasses(target);\n }\n }\n }\n\n private resolveTarget(selector: string): Element | null {\n if (selector === \"html\") return document.documentElement;\n if (selector === \"body\") return document.body;\n return document.querySelector(selector);\n }\n\n private applyClasses(el: Element): void {\n for (const [key, value] of Object.entries(this.detections) as [keyof DetectionMap, boolean][]) {\n if (value) {\n el.classList.add(CLASS_MAP[key]);\n }\n }\n }\n}\n"],"mappings":"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","names":["getUA","getMaxTouchPoints","_a","testUA","pattern","getUA","isIOS","_a","testUA","getMaxTouchPoints","isAndroid","isWindows","isMacOS","isLinux","isEdge","testUA","isOpera","isIE9","isIE","isFirefox","isSafari","isChrome","isIPhone","testUA","isIPad","_a","getMaxTouchPoints","isTouchDevice","isTablet","isMobile","isIOS","isDesktop","ALL_DETECTIONS","isIOS","isAndroid","isWindows","isMacOS","isLinux","isSafari","isChrome","isFirefox","isOpera","isEdge","isIE","isIE9","isIPad","isIPhone","isDesktop","isMobile","isTablet","isTouchDevice","CLASS_MAP","DetecctioJS","options","__spreadValues","target","selector","el","key","value"]}
@@ -0,0 +1,2 @@
1
+ "use strict";var DetecctioJS=(()=>{var m=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames,y=Object.getOwnPropertySymbols;var P=Object.prototype.hasOwnProperty,W=Object.prototype.propertyIsEnumerable;var A=(o,e,t)=>e in o?m(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t,S=(o,e)=>{for(var t in e||(e={}))P.call(e,t)&&A(o,t,e[t]);if(y)for(var t of y(e))W.call(e,t)&&A(o,t,e[t]);return o};var F=(o,e)=>{for(var t in e)m(o,t,{get:e[t],enumerable:!0})},U=(o,e,t,f)=>{if(e&&typeof e=="object"||typeof e=="function")for(let d of L(e))!P.call(o,d)&&d!==t&&m(o,d,{get:()=>e[d],enumerable:!(f=C(e,d))||f.enumerable});return o};var j=o=>U(m({},"__esModule",{value:!0}),o);var _={};F(_,{DetecctioJS:()=>M,isAndroid:()=>l,isChrome:()=>O,isDesktop:()=>E,isEdge:()=>s,isFirefox:()=>D,isIE:()=>w,isIE9:()=>g,isIOS:()=>n,isIPad:()=>a,isIPhone:()=>v,isLinux:()=>h,isMacOS:()=>x,isMobile:()=>p,isOpera:()=>r,isSafari:()=>I,isTablet:()=>c,isTouchDevice:()=>T,isWindows:()=>b});function R(){return typeof navigator=="undefined"?"":navigator.userAgent||""}function u(){var o;return typeof navigator=="undefined"?0:(o=navigator.maxTouchPoints)!=null?o:0}function i(o){return o.test(R())}var n=(()=>{var o;return i(/iphone|ipad|ipod/i)?!0:/macintosh/i.test((o=navigator==null?void 0:navigator.userAgent)!=null?o:"")&&u()>1})(),l=i(/android/i),b=i(/windows/i),x=n?!1:i(/macintosh|mac os x/i),h=l?!1:i(/linux/i);var s=i(/edg(e|\/)/i),r=i(/opr\/|opera/i),g=i(/msie 9/i),w=i(/msie|trident/i),D=i(/firefox|fxios/i),I=s||r?!1:i(/safari/i)&&!i(/chrome|chromium|crios/i),O=s||r?!1:i(/chrome|chromium|crios/i);var v=i(/iphone/i),a=(()=>{var o;return i(/ipad/i)?!0:/macintosh/i.test((o=navigator==null?void 0:navigator.userAgent)!=null?o:"")&&u()>1})(),T=(()=>{var o;return typeof window=="undefined"?!1:"ontouchstart"in window||u()>0||!!window.DocumentTouch&&typeof document!="undefined"&&document instanceof((o=window.DocumentTouch)!=null?o:class{})})(),c=!!(a||i(/android/i)&&!i(/mobile/i)||i(/windows/i)&&i(/touch/i)&&!i(/tablet pc/i)),p=c?!1:i(/android.*mobile|iphone|ipod|windows phone|blackberry|bb\d+|meego|silk|bada\//i)||n&&!a,E=!p&&!c;var k={isIOS:n,isAndroid:l,isWindows:b,isMacOS:x,isLinux:h,isSafari:I,isChrome:O,isFirefox:D,isOpera:r,isEdge:s,isIE:w,isIE9:g,isIPad:a,isIPhone:v,isDesktop:E,isMobile:p,isTablet:c,isTouchDevice:T},N={isIOS:"is-ios",isAndroid:"is-android",isWindows:"is-windows",isMacOS:"is-macos",isLinux:"is-linux",isSafari:"is-safari",isChrome:"is-chrome",isFirefox:"is-firefox",isOpera:"is-opera",isEdge:"is-edge",isIE:"is-ie",isIE9:"is-ie9",isIPad:"is-ipad",isIPhone:"is-iphone",isDesktop:"is-desktop",isMobile:"is-mobile",isTablet:"is-tablet",isTouchDevice:"is-touch-device"},M=class{constructor(e={}){if(this.detections=S({},k),e.windowObject&&typeof window!="undefined"&&(window.detecctio=S({},k)),e.documentClasses&&typeof document!="undefined"){let t=this.resolveTarget(e.documentClasses);t&&this.applyClasses(t)}}resolveTarget(e){return e==="html"?document.documentElement:e==="body"?document.body:document.querySelector(e)}applyClasses(e){for(let[t,f]of Object.entries(this.detections))f&&e.classList.add(N[t])}};return j(_);})();
2
+ //# sourceMappingURL=index.umd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/utils/ua.ts","../src/detections/os.ts","../src/detections/browser.ts","../src/detections/device.ts","../src/DetecctioJS.ts"],"sourcesContent":["export { isIOS, isAndroid, isWindows, isMacOS, isLinux } from \"./detections/os\";\nexport { isChrome, isEdge, isFirefox, isIE, isIE9, isOpera, isSafari } from \"./detections/browser\";\nexport { isDesktop, isIPad, isIPhone, isMobile, isTablet, isTouchDevice } from \"./detections/device\";\nexport { DetecctioJS } from \"./DetecctioJS\";\nexport type { DetecctioOptions, DetectionMap } from \"./DetecctioJS\";\n","/**\n * Safe user-agent string — returns empty string in non-browser / SSR envs.\n */\nexport function getUA(): string {\n if (typeof navigator === \"undefined\") return \"\";\n return navigator.userAgent || \"\";\n}\n\n/**\n * Safe platform string.\n */\nexport function getPlatform(): string {\n if (typeof navigator === \"undefined\") return \"\";\n // navigator.platform is deprecated but still the most reliable for iPad detection\n return (navigator as Navigator & { userAgentData?: { platform?: string } })\n .userAgentData?.platform ?? navigator.platform ?? \"\";\n}\n\n/**\n * Returns max touch points count (0 in non-browser env).\n */\nexport function getMaxTouchPoints(): number {\n if (typeof navigator === \"undefined\") return 0;\n return navigator.maxTouchPoints ?? 0;\n}\n\nexport function testUA(pattern: RegExp): boolean {\n return pattern.test(getUA());\n}\n","import { getMaxTouchPoints, getPlatform, testUA } from \"../utils/ua\";\n\n/**\n * iOS — iPhone, iPad, iPod.\n * Uses both UA and the iPad-on-desktop trick (macOS + touch points).\n */\nexport const isIOS: boolean = (() => {\n if (testUA(/iphone|ipad|ipod/i)) return true;\n // iPadOS 13+ reports as macOS in UA\n return /macintosh/i.test(navigator?.userAgent ?? \"\") && getMaxTouchPoints() > 1;\n})();\n\nexport const isAndroid: boolean = testUA(/android/i);\n\n/**\n * Windows — desktop or mobile.\n */\nexport const isWindows: boolean = testUA(/windows/i);\n\nexport const isMacOS: boolean = (() => {\n if (isIOS) return false;\n return testUA(/macintosh|mac os x/i);\n})();\n\nexport const isLinux: boolean = (() => {\n if (isAndroid) return false;\n return testUA(/linux/i);\n})();\n","import { testUA } from \"../utils/ua\";\n\n/**\n * Edge — Chromium-based Edge (Edg/) and legacy Edge (Edge/).\n * Must be checked before Chrome since Edg UA also contains \"Chrome\".\n */\nexport const isEdge: boolean = testUA(/edg(e|\\/)/i);\n\n/**\n * Opera — modern (OPR/) and legacy (Opera/).\n * Must be checked before Chrome for the same reason.\n */\nexport const isOpera: boolean = testUA(/opr\\/|opera/i);\n\n/**\n * IE9 — strict IE 9 match.\n */\nexport const isIE9: boolean = testUA(/msie 9/i);\n\n/**\n * Any Internet Explorer (IE 6–11).\n */\nexport const isIE: boolean = testUA(/msie|trident/i);\n\n/**\n * Firefox.\n */\nexport const isFirefox: boolean = testUA(/firefox|fxios/i);\n\n/**\n * Safari — must exclude Chrome and Android WebView which also include \"Safari\".\n */\nexport const isSafari: boolean = (() => {\n if (isEdge || isOpera) return false;\n return testUA(/safari/i) && !testUA(/chrome|chromium|crios/i);\n})();\n\n/**\n * Chrome — includes Chromium, Chrome for iOS (CriOS).\n * Excludes Edge and Opera which also carry the Chrome token.\n */\nexport const isChrome: boolean = (() => {\n if (isEdge || isOpera) return false;\n return testUA(/chrome|chromium|crios/i);\n})();\n","import { getMaxTouchPoints, testUA } from \"../utils/ua\";\nimport { isIOS } from \"./os\";\n\n/**\n * iPhone — not iPad.\n */\nexport const isIPhone: boolean = testUA(/iphone/i);\n\n/**\n * iPad — includes iPadOS 13+ which disguises itself as macOS.\n */\nexport const isIPad: boolean = (() => {\n if (testUA(/ipad/i)) return true;\n return /macintosh/i.test(navigator?.userAgent ?? \"\") && getMaxTouchPoints() > 1;\n})();\n\n/**\n * Touch device — has touch capability.\n * Works in browser; returns false in SSR / Node.\n */\nexport const isTouchDevice: boolean = (() => {\n if (typeof window === \"undefined\") return false;\n return (\n \"ontouchstart\" in window ||\n getMaxTouchPoints() > 0 ||\n // legacy IE/Edge\n !!(window as Window & { DocumentTouch?: unknown }).DocumentTouch &&\n typeof document !== \"undefined\" &&\n document instanceof ((window as Window & { DocumentTouch?: unknown }).DocumentTouch as { new(): unknown } ?? class {})\n );\n})();\n\n/**\n * Tablet — iPad, Android tablets (no \"Mobile\" in UA), or Windows tablets.\n */\nexport const isTablet: boolean = (() => {\n if (isIPad) return true;\n // Android tablet: has \"Android\" but not \"Mobile\"\n if (testUA(/android/i) && !testUA(/mobile/i)) return true;\n // Windows tablet\n if (testUA(/windows/i) && testUA(/touch/i) && !testUA(/tablet pc/i)) return true;\n return false;\n})();\n\n/**\n * Mobile — phones only (not tablets).\n */\nexport const isMobile: boolean = (() => {\n if (isTablet) return false;\n return testUA(/android.*mobile|iphone|ipod|windows phone|blackberry|bb\\d+|meego|silk|bada\\//i) || isIOS && !isIPad;\n})();\n\n/**\n * Desktop — not mobile, not tablet.\n */\nexport const isDesktop: boolean = !isMobile && !isTablet;\n","import { isAndroid, isIOS, isLinux, isMacOS, isWindows } from \"./detections/os\";\nimport { isChrome, isEdge, isFirefox, isIE, isIE9, isOpera, isSafari } from \"./detections/browser\";\nimport {\n isDesktop,\n isIPad,\n isIPhone,\n isMobile,\n isTablet,\n isTouchDevice,\n} from \"./detections/device\";\n\nexport interface DetectionMap {\n isIOS: boolean;\n isAndroid: boolean;\n isWindows: boolean;\n isMacOS: boolean;\n isLinux: boolean;\n isSafari: boolean;\n isChrome: boolean;\n isFirefox: boolean;\n isOpera: boolean;\n isEdge: boolean;\n isIE: boolean;\n isIE9: boolean;\n isIPad: boolean;\n isIPhone: boolean;\n isDesktop: boolean;\n isMobile: boolean;\n isTablet: boolean;\n isTouchDevice: boolean;\n}\n\nexport interface DetecctioOptions {\n /**\n * Attach detections to `window.detecctio` as a plain object of booleans.\n * @default false\n */\n windowObject?: boolean;\n /**\n * Target element for class injection. Accepts `\"html\"`, `\"body\"`, or a CSS selector string.\n * Pass `false` to disable class injection.\n * @default false\n */\n documentClasses?: \"html\" | \"body\" | string | false;\n}\n\nconst ALL_DETECTIONS: DetectionMap = {\n isIOS,\n isAndroid,\n isWindows,\n isMacOS,\n isLinux,\n isSafari,\n isChrome,\n isFirefox,\n isOpera,\n isEdge,\n isIE,\n isIE9,\n isIPad,\n isIPhone,\n isDesktop,\n isMobile,\n isTablet,\n isTouchDevice,\n};\n\nconst CLASS_MAP: Record<keyof DetectionMap, string> = {\n isIOS: \"is-ios\",\n isAndroid: \"is-android\",\n isWindows: \"is-windows\",\n isMacOS: \"is-macos\",\n isLinux: \"is-linux\",\n isSafari: \"is-safari\",\n isChrome: \"is-chrome\",\n isFirefox: \"is-firefox\",\n isOpera: \"is-opera\",\n isEdge: \"is-edge\",\n isIE: \"is-ie\",\n isIE9: \"is-ie9\",\n isIPad: \"is-ipad\",\n isIPhone: \"is-iphone\",\n isDesktop: \"is-desktop\",\n isMobile: \"is-mobile\",\n isTablet: \"is-tablet\",\n isTouchDevice: \"is-touch-device\",\n};\n\nexport class DetecctioJS {\n readonly detections: Readonly<DetectionMap>;\n\n constructor(options: DetecctioOptions = {}) {\n this.detections = { ...ALL_DETECTIONS };\n\n if (options.windowObject && typeof window !== \"undefined\") {\n (window as Window & { detecctio?: DetectionMap }).detecctio = { ...ALL_DETECTIONS };\n }\n\n if (options.documentClasses && typeof document !== \"undefined\") {\n const target = this.resolveTarget(options.documentClasses);\n if (target) {\n this.applyClasses(target);\n }\n }\n }\n\n private resolveTarget(selector: string): Element | null {\n if (selector === \"html\") return document.documentElement;\n if (selector === \"body\") return document.body;\n return document.querySelector(selector);\n }\n\n private applyClasses(el: Element): void {\n for (const [key, value] of Object.entries(this.detections) as [keyof DetectionMap, boolean][]) {\n if (value) {\n el.classList.add(CLASS_MAP[key]);\n }\n }\n }\n}\n"],"mappings":"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","names":["index_exports","__export","DetecctioJS","isAndroid","isChrome","isDesktop","isEdge","isFirefox","isIE","isIE9","isIOS","isIPad","isIPhone","isLinux","isMacOS","isMobile","isOpera","isSafari","isTablet","isTouchDevice","isWindows","getUA","getMaxTouchPoints","_a","testUA","pattern","getUA","isIOS","_a","testUA","getMaxTouchPoints","isAndroid","isWindows","isMacOS","isLinux","isEdge","testUA","isOpera","isIE9","isIE","isFirefox","isSafari","isChrome","isIPhone","testUA","isIPad","_a","getMaxTouchPoints","isTouchDevice","isTablet","isMobile","isIOS","isDesktop","ALL_DETECTIONS","isIOS","isAndroid","isWindows","isMacOS","isLinux","isSafari","isChrome","isFirefox","isOpera","isEdge","isIE","isIE9","isIPad","isIPhone","isDesktop","isMobile","isTablet","isTouchDevice","CLASS_MAP","DetecctioJS","options","__spreadValues","target","selector","el","key","value"]}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "detecctio",
3
+ "version": "1.0.0",
4
+ "description": "Lightweight detection library for OS, browser, and device type across every JS environment",
5
+ "author": "Tomislav Jezidzic",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "detection",
9
+ "device",
10
+ "browser",
11
+ "os",
12
+ "mobile",
13
+ "ios",
14
+ "android",
15
+ "touch"
16
+ ],
17
+ "main": "./dist/index.cjs",
18
+ "module": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js",
24
+ "require": "./dist/index.cjs"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsup",
32
+ "dev": "tsup --watch",
33
+ "demo": "vite demo",
34
+ "test": "vitest run",
35
+ "test:watch": "vitest",
36
+ "test:coverage": "vitest run --coverage",
37
+ "lint": "eslint src --ext .ts",
38
+ "typecheck": "tsc --noEmit",
39
+ "prepublishOnly": "npm run build"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^20.14.0",
43
+ "@typescript-eslint/eslint-plugin": "^7.13.0",
44
+ "@typescript-eslint/parser": "^7.13.0",
45
+ "@vitest/coverage-v8": "^1.6.1",
46
+ "eslint": "^8.57.0",
47
+ "jsdom": "^29.1.1",
48
+ "tsup": "^8.1.0",
49
+ "typescript": "^5.5.2",
50
+ "vite": "^8.2.1",
51
+ "vitest": "^1.6.0"
52
+ }
53
+ }