@posthog/core 1.8.0 → 1.8.1

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.
@@ -6,6 +6,7 @@ export * from './string-utils';
6
6
  export * from './type-utils';
7
7
  export * from './promise-queue';
8
8
  export * from './logger';
9
+ export * from './user-agent-utils';
9
10
  export declare const STRING_FORMAT = "utf8";
10
11
  export declare function assert(truthyValue: any, message: string): void;
11
12
  export declare function removeTrailingSlash(url: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,yBAAyB,CAAA;AACvC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,UAAU,CAAA;AAExB,eAAO,MAAM,aAAa,SAAS,CAAA;AAEnC,wBAAgB,MAAM,CAAC,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAI9D;AASD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAA;CACtC;AAED,wBAAsB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,CAAC,CAqB5F;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,GAAG,CAOnE;AAGD,eAAO,MAAM,SAAS,GAAI,KAAK,GAAG,KAAG,GAAG,IAAI,OAAO,CAAC,GAAG,CAEtD,CAAA;AAED,eAAO,MAAM,OAAO,GAAI,GAAG,OAAO,KAAG,CAAC,IAAI,KAEzC,CAAA;AAED,wBAAgB,QAAQ,IAAI,SAAS,GAAG,SAAS,CAEhD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAC1B,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAC1C,OAAO,CAAC,CAAC;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC,EAAE,CAAC,CAStF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,yBAAyB,CAAA;AACvC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,UAAU,CAAA;AACxB,cAAc,oBAAoB,CAAA;AAElC,eAAO,MAAM,aAAa,SAAS,CAAA;AAEnC,wBAAgB,MAAM,CAAC,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAI9D;AASD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAA;CACtC;AAED,wBAAsB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,CAAC,CAqB5F;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,GAAG,CAOnE;AAGD,eAAO,MAAM,SAAS,GAAI,KAAK,GAAG,KAAG,GAAG,IAAI,OAAO,CAAC,GAAG,CAEtD,CAAA;AAED,eAAO,MAAM,OAAO,GAAI,GAAG,OAAO,KAAG,CAAC,IAAI,KAEzC,CAAA;AAED,wBAAgB,QAAQ,IAAI,SAAS,GAAG,SAAS,CAEhD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAC1B,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAC1C,OAAO,CAAC,CAAC;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC,EAAE,CAAC,CAStF"}
@@ -20,6 +20,9 @@ var __webpack_modules__ = {
20
20
  },
21
21
  "./type-utils": function(module) {
22
22
  module.exports = require("./type-utils.js");
23
+ },
24
+ "./user-agent-utils": function(module) {
25
+ module.exports = require("./user-agent-utils.js");
23
26
  }
24
27
  };
25
28
  var __webpack_module_cache__ = {};
@@ -211,6 +214,25 @@ var __webpack_exports__ = {};
211
214
  return _logger__WEBPACK_IMPORTED_MODULE_6__[key];
212
215
  }).bind(0, __WEBPACK_IMPORT_KEY__);
213
216
  __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
217
+ var _user_agent_utils__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__("./user-agent-utils");
218
+ var __WEBPACK_REEXPORT_OBJECT__ = {};
219
+ for(var __WEBPACK_IMPORT_KEY__ in _user_agent_utils__WEBPACK_IMPORTED_MODULE_7__)if ([
220
+ "removeTrailingSlash",
221
+ "retriable",
222
+ "default",
223
+ "currentISOTime",
224
+ "currentTimestamp",
225
+ "STRING_FORMAT",
226
+ "isError",
227
+ "safeSetTimeout",
228
+ "getFetch",
229
+ "isPromise",
230
+ "assert",
231
+ "allSettled"
232
+ ].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = (function(key) {
233
+ return _user_agent_utils__WEBPACK_IMPORTED_MODULE_7__[key];
234
+ }).bind(0, __WEBPACK_IMPORT_KEY__);
235
+ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
214
236
  const STRING_FORMAT = 'utf8';
215
237
  function assert(truthyValue, message) {
216
238
  if (!truthyValue || 'string' != typeof truthyValue || isEmpty(truthyValue)) throw new Error(message);
@@ -5,6 +5,7 @@ export * from "./string-utils.mjs";
5
5
  export * from "./type-utils.mjs";
6
6
  export * from "./promise-queue.mjs";
7
7
  export * from "./logger.mjs";
8
+ export * from "./user-agent-utils.mjs";
8
9
  const STRING_FORMAT = 'utf8';
9
10
  function assert(truthyValue, message) {
10
11
  if (!truthyValue || 'string' != typeof truthyValue || isEmpty(truthyValue)) throw new Error(message);
@@ -0,0 +1,19 @@
1
+ /**
2
+ * This function detects which browser is running this script.
3
+ * The order of the checks are important since many user agents
4
+ * include keywords used in later checks.
5
+ */
6
+ export declare const detectBrowser: (user_agent: string, vendor: string | undefined) => string;
7
+ /**
8
+ * This function detects which browser version is running this script,
9
+ * parsing major and minor version (e.g., 42.1). User agent strings from:
10
+ * http://www.useragentstring.com/pages/useragentstring.php
11
+ *
12
+ * `navigator.vendor` is passed in and used to help with detecting certain browsers
13
+ * NB `navigator.vendor` is deprecated and not present in every browser
14
+ */
15
+ export declare const detectBrowserVersion: (userAgent: string, vendor: string | undefined) => number | null;
16
+ export declare const detectOS: (user_agent: string) => [string, string];
17
+ export declare const detectDevice: (user_agent: string) => string;
18
+ export declare const detectDeviceType: (user_agent: string) => string;
19
+ //# sourceMappingURL=user-agent-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-agent-utils.d.ts","sourceRoot":"","sources":["../../src/utils/user-agent-utils.ts"],"names":[],"mappings":"AAqFA;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAa,YAAY,MAAM,EAAE,QAAQ,MAAM,GAAG,SAAS,KAAG,MA4CvF,CAAA;AAsBD;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAa,WAAW,MAAM,EAAE,QAAQ,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,IAetG,CAAA;AA0FD,eAAO,MAAM,QAAQ,GAAa,YAAY,MAAM,KAAG,CAAC,MAAM,EAAE,MAAM,CAUrE,CAAA;AAED,eAAO,MAAM,YAAY,GAAa,YAAY,MAAM,KAAG,MAuD1D,CAAA;AAED,eAAO,MAAM,gBAAgB,GAAa,YAAY,MAAM,KAAG,MAmB9D,CAAA"}
@@ -0,0 +1,384 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ detectDeviceType: ()=>detectDeviceType,
28
+ detectOS: ()=>detectOS,
29
+ detectDevice: ()=>detectDevice,
30
+ detectBrowser: ()=>detectBrowser,
31
+ detectBrowserVersion: ()=>detectBrowserVersion
32
+ });
33
+ const external_string_utils_js_namespaceObject = require("./string-utils.js");
34
+ const external_type_utils_js_namespaceObject = require("./type-utils.js");
35
+ const FACEBOOK = 'Facebook';
36
+ const MOBILE = 'Mobile';
37
+ const IOS = 'iOS';
38
+ const ANDROID = 'Android';
39
+ const TABLET = 'Tablet';
40
+ const ANDROID_TABLET = ANDROID + ' ' + TABLET;
41
+ const IPAD = 'iPad';
42
+ const APPLE = 'Apple';
43
+ const APPLE_WATCH = APPLE + ' Watch';
44
+ const SAFARI = 'Safari';
45
+ const BLACKBERRY = 'BlackBerry';
46
+ const SAMSUNG = 'Samsung';
47
+ const SAMSUNG_BROWSER = SAMSUNG + 'Browser';
48
+ const SAMSUNG_INTERNET = SAMSUNG + ' Internet';
49
+ const CHROME = 'Chrome';
50
+ const CHROME_OS = CHROME + ' OS';
51
+ const CHROME_IOS = CHROME + ' ' + IOS;
52
+ const INTERNET_EXPLORER = 'Internet Explorer';
53
+ const INTERNET_EXPLORER_MOBILE = INTERNET_EXPLORER + ' ' + MOBILE;
54
+ const OPERA = 'Opera';
55
+ const OPERA_MINI = OPERA + ' Mini';
56
+ const EDGE = 'Edge';
57
+ const MICROSOFT_EDGE = 'Microsoft ' + EDGE;
58
+ const FIREFOX = 'Firefox';
59
+ const FIREFOX_IOS = FIREFOX + ' ' + IOS;
60
+ const NINTENDO = 'Nintendo';
61
+ const PLAYSTATION = 'PlayStation';
62
+ const XBOX = 'Xbox';
63
+ const ANDROID_MOBILE = ANDROID + ' ' + MOBILE;
64
+ const MOBILE_SAFARI = MOBILE + ' ' + SAFARI;
65
+ const WINDOWS = 'Windows';
66
+ const WINDOWS_PHONE = WINDOWS + ' Phone';
67
+ const NOKIA = 'Nokia';
68
+ const OUYA = 'Ouya';
69
+ const GENERIC = 'Generic';
70
+ const GENERIC_MOBILE = GENERIC + ' ' + MOBILE.toLowerCase();
71
+ const GENERIC_TABLET = GENERIC + ' ' + TABLET.toLowerCase();
72
+ const KONQUEROR = 'Konqueror';
73
+ const BROWSER_VERSION_REGEX_SUFFIX = '(\\d+(\\.\\d+)?)';
74
+ const DEFAULT_BROWSER_VERSION_REGEX = new RegExp('Version/' + BROWSER_VERSION_REGEX_SUFFIX);
75
+ const XBOX_REGEX = new RegExp(XBOX, 'i');
76
+ const PLAYSTATION_REGEX = new RegExp(PLAYSTATION + ' \\w+', 'i');
77
+ const NINTENDO_REGEX = new RegExp(NINTENDO + ' \\w+', 'i');
78
+ const BLACKBERRY_REGEX = new RegExp(BLACKBERRY + '|PlayBook|BB10', 'i');
79
+ const windowsVersionMap = {
80
+ 'NT3.51': 'NT 3.11',
81
+ 'NT4.0': 'NT 4.0',
82
+ '5.0': '2000',
83
+ '5.1': 'XP',
84
+ '5.2': 'XP',
85
+ '6.0': 'Vista',
86
+ '6.1': '7',
87
+ '6.2': '8',
88
+ '6.3': '8.1',
89
+ '6.4': '10',
90
+ '10.0': '10'
91
+ };
92
+ function isSafari(userAgent) {
93
+ return (0, external_string_utils_js_namespaceObject.includes)(userAgent, SAFARI) && !(0, external_string_utils_js_namespaceObject.includes)(userAgent, CHROME) && !(0, external_string_utils_js_namespaceObject.includes)(userAgent, ANDROID);
94
+ }
95
+ const safariCheck = (ua, vendor)=>vendor && (0, external_string_utils_js_namespaceObject.includes)(vendor, APPLE) || isSafari(ua);
96
+ const detectBrowser = function(user_agent, vendor) {
97
+ vendor = vendor || '';
98
+ if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, ' OPR/') && (0, external_string_utils_js_namespaceObject.includes)(user_agent, 'Mini')) return OPERA_MINI;
99
+ if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, ' OPR/')) return OPERA;
100
+ if (BLACKBERRY_REGEX.test(user_agent)) return BLACKBERRY;
101
+ if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'IE' + MOBILE) || (0, external_string_utils_js_namespaceObject.includes)(user_agent, 'WPDesktop')) return INTERNET_EXPLORER_MOBILE;
102
+ if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, SAMSUNG_BROWSER)) return SAMSUNG_INTERNET;
103
+ else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, EDGE) || (0, external_string_utils_js_namespaceObject.includes)(user_agent, 'Edg/')) return MICROSOFT_EDGE;
104
+ else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'FBIOS')) return FACEBOOK + ' ' + MOBILE;
105
+ else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'UCWEB') || (0, external_string_utils_js_namespaceObject.includes)(user_agent, 'UCBrowser')) return 'UC Browser';
106
+ else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'CriOS')) return CHROME_IOS;
107
+ else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'CrMo')) return CHROME;
108
+ else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, CHROME)) return CHROME;
109
+ else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, ANDROID) && (0, external_string_utils_js_namespaceObject.includes)(user_agent, SAFARI)) return ANDROID_MOBILE;
110
+ else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'FxiOS')) return FIREFOX_IOS;
111
+ else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent.toLowerCase(), KONQUEROR.toLowerCase())) return KONQUEROR;
112
+ else if (safariCheck(user_agent, vendor)) return (0, external_string_utils_js_namespaceObject.includes)(user_agent, MOBILE) ? MOBILE_SAFARI : SAFARI;
113
+ else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, FIREFOX)) return FIREFOX;
114
+ else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'MSIE') || (0, external_string_utils_js_namespaceObject.includes)(user_agent, 'Trident/')) return INTERNET_EXPLORER;
115
+ else if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, 'Gecko')) return FIREFOX;
116
+ return '';
117
+ };
118
+ const versionRegexes = {
119
+ [INTERNET_EXPLORER_MOBILE]: [
120
+ new RegExp('rv:' + BROWSER_VERSION_REGEX_SUFFIX)
121
+ ],
122
+ [MICROSOFT_EDGE]: [
123
+ new RegExp(EDGE + '?\\/' + BROWSER_VERSION_REGEX_SUFFIX)
124
+ ],
125
+ [CHROME]: [
126
+ new RegExp('(' + CHROME + '|CrMo)\\/' + BROWSER_VERSION_REGEX_SUFFIX)
127
+ ],
128
+ [CHROME_IOS]: [
129
+ new RegExp('CriOS\\/' + BROWSER_VERSION_REGEX_SUFFIX)
130
+ ],
131
+ 'UC Browser': [
132
+ new RegExp('(UCBrowser|UCWEB)\\/' + BROWSER_VERSION_REGEX_SUFFIX)
133
+ ],
134
+ [SAFARI]: [
135
+ DEFAULT_BROWSER_VERSION_REGEX
136
+ ],
137
+ [MOBILE_SAFARI]: [
138
+ DEFAULT_BROWSER_VERSION_REGEX
139
+ ],
140
+ [OPERA]: [
141
+ new RegExp('(' + OPERA + '|OPR)\\/' + BROWSER_VERSION_REGEX_SUFFIX)
142
+ ],
143
+ [FIREFOX]: [
144
+ new RegExp(FIREFOX + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)
145
+ ],
146
+ [FIREFOX_IOS]: [
147
+ new RegExp('FxiOS\\/' + BROWSER_VERSION_REGEX_SUFFIX)
148
+ ],
149
+ [KONQUEROR]: [
150
+ new RegExp('Konqueror[:/]?' + BROWSER_VERSION_REGEX_SUFFIX, 'i')
151
+ ],
152
+ [BLACKBERRY]: [
153
+ new RegExp(BLACKBERRY + ' ' + BROWSER_VERSION_REGEX_SUFFIX),
154
+ DEFAULT_BROWSER_VERSION_REGEX
155
+ ],
156
+ [ANDROID_MOBILE]: [
157
+ new RegExp('android\\s' + BROWSER_VERSION_REGEX_SUFFIX, 'i')
158
+ ],
159
+ [SAMSUNG_INTERNET]: [
160
+ new RegExp(SAMSUNG_BROWSER + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)
161
+ ],
162
+ [INTERNET_EXPLORER]: [
163
+ new RegExp('(rv:|MSIE )' + BROWSER_VERSION_REGEX_SUFFIX)
164
+ ],
165
+ Mozilla: [
166
+ new RegExp('rv:' + BROWSER_VERSION_REGEX_SUFFIX)
167
+ ]
168
+ };
169
+ const detectBrowserVersion = function(userAgent, vendor) {
170
+ const browser = detectBrowser(userAgent, vendor);
171
+ const regexes = versionRegexes[browser];
172
+ if ((0, external_type_utils_js_namespaceObject.isUndefined)(regexes)) return null;
173
+ for(let i = 0; i < regexes.length; i++){
174
+ const regex = regexes[i];
175
+ const matches = userAgent.match(regex);
176
+ if (matches) return parseFloat(matches[matches.length - 2]);
177
+ }
178
+ return null;
179
+ };
180
+ const osMatchers = [
181
+ [
182
+ new RegExp(XBOX + '; ' + XBOX + ' (.*?)[);]', 'i'),
183
+ (match)=>[
184
+ XBOX,
185
+ match && match[1] || ''
186
+ ]
187
+ ],
188
+ [
189
+ new RegExp(NINTENDO, 'i'),
190
+ [
191
+ NINTENDO,
192
+ ''
193
+ ]
194
+ ],
195
+ [
196
+ new RegExp(PLAYSTATION, 'i'),
197
+ [
198
+ PLAYSTATION,
199
+ ''
200
+ ]
201
+ ],
202
+ [
203
+ BLACKBERRY_REGEX,
204
+ [
205
+ BLACKBERRY,
206
+ ''
207
+ ]
208
+ ],
209
+ [
210
+ new RegExp(WINDOWS, 'i'),
211
+ (_, user_agent)=>{
212
+ if (/Phone/.test(user_agent) || /WPDesktop/.test(user_agent)) return [
213
+ WINDOWS_PHONE,
214
+ ''
215
+ ];
216
+ if (new RegExp(MOBILE).test(user_agent) && !/IEMobile\b/.test(user_agent)) return [
217
+ WINDOWS + ' ' + MOBILE,
218
+ ''
219
+ ];
220
+ const match = /Windows NT ([0-9.]+)/i.exec(user_agent);
221
+ if (match && match[1]) {
222
+ const version = match[1];
223
+ let osVersion = windowsVersionMap[version] || '';
224
+ if (/arm/i.test(user_agent)) osVersion = 'RT';
225
+ return [
226
+ WINDOWS,
227
+ osVersion
228
+ ];
229
+ }
230
+ return [
231
+ WINDOWS,
232
+ ''
233
+ ];
234
+ }
235
+ ],
236
+ [
237
+ /((iPhone|iPad|iPod).*?OS (\d+)_(\d+)_?(\d+)?|iPhone)/,
238
+ (match)=>{
239
+ if (match && match[3]) {
240
+ const versionParts = [
241
+ match[3],
242
+ match[4],
243
+ match[5] || '0'
244
+ ];
245
+ return [
246
+ IOS,
247
+ versionParts.join('.')
248
+ ];
249
+ }
250
+ return [
251
+ IOS,
252
+ ''
253
+ ];
254
+ }
255
+ ],
256
+ [
257
+ /(watch.*\/(\d+\.\d+\.\d+)|watch os,(\d+\.\d+),)/i,
258
+ (match)=>{
259
+ let version = '';
260
+ if (match && match.length >= 3) version = (0, external_type_utils_js_namespaceObject.isUndefined)(match[2]) ? match[3] : match[2];
261
+ return [
262
+ 'watchOS',
263
+ version
264
+ ];
265
+ }
266
+ ],
267
+ [
268
+ new RegExp('(' + ANDROID + ' (\\d+)\\.(\\d+)\\.?(\\d+)?|' + ANDROID + ')', 'i'),
269
+ (match)=>{
270
+ if (match && match[2]) {
271
+ const versionParts = [
272
+ match[2],
273
+ match[3],
274
+ match[4] || '0'
275
+ ];
276
+ return [
277
+ ANDROID,
278
+ versionParts.join('.')
279
+ ];
280
+ }
281
+ return [
282
+ ANDROID,
283
+ ''
284
+ ];
285
+ }
286
+ ],
287
+ [
288
+ /Mac OS X (\d+)[_.](\d+)[_.]?(\d+)?/i,
289
+ (match)=>{
290
+ const result = [
291
+ 'Mac OS X',
292
+ ''
293
+ ];
294
+ if (match && match[1]) {
295
+ const versionParts = [
296
+ match[1],
297
+ match[2],
298
+ match[3] || '0'
299
+ ];
300
+ result[1] = versionParts.join('.');
301
+ }
302
+ return result;
303
+ }
304
+ ],
305
+ [
306
+ /Mac/i,
307
+ [
308
+ 'Mac OS X',
309
+ ''
310
+ ]
311
+ ],
312
+ [
313
+ /CrOS/,
314
+ [
315
+ CHROME_OS,
316
+ ''
317
+ ]
318
+ ],
319
+ [
320
+ /Linux|debian/i,
321
+ [
322
+ 'Linux',
323
+ ''
324
+ ]
325
+ ]
326
+ ];
327
+ const detectOS = function(user_agent) {
328
+ for(let i = 0; i < osMatchers.length; i++){
329
+ const [rgex, resultOrFn] = osMatchers[i];
330
+ const match = rgex.exec(user_agent);
331
+ const result = match && ((0, external_type_utils_js_namespaceObject.isFunction)(resultOrFn) ? resultOrFn(match, user_agent) : resultOrFn);
332
+ if (result) return result;
333
+ }
334
+ return [
335
+ '',
336
+ ''
337
+ ];
338
+ };
339
+ const detectDevice = function(user_agent) {
340
+ if (NINTENDO_REGEX.test(user_agent)) return NINTENDO;
341
+ if (PLAYSTATION_REGEX.test(user_agent)) return PLAYSTATION;
342
+ if (XBOX_REGEX.test(user_agent)) return XBOX;
343
+ if (new RegExp(OUYA, 'i').test(user_agent)) return OUYA;
344
+ if (new RegExp('(' + WINDOWS_PHONE + '|WPDesktop)', 'i').test(user_agent)) return WINDOWS_PHONE;
345
+ else if (/iPad/.test(user_agent)) return IPAD;
346
+ else if (/iPod/.test(user_agent)) return 'iPod Touch';
347
+ else if (/iPhone/.test(user_agent)) return 'iPhone';
348
+ else if (/(watch)(?: ?os[,/]|\d,\d\/)[\d.]+/i.test(user_agent)) return APPLE_WATCH;
349
+ else if (BLACKBERRY_REGEX.test(user_agent)) return BLACKBERRY;
350
+ else if (/(kobo)\s(ereader|touch)/i.test(user_agent)) return 'Kobo';
351
+ else if (new RegExp(NOKIA, 'i').test(user_agent)) return NOKIA;
352
+ else if (/(kf[a-z]{2}wi|aeo[c-r]{2})( bui|\))/i.test(user_agent) || /(kf[a-z]+)( bui|\)).+silk\//i.test(user_agent)) return 'Kindle Fire';
353
+ else if (/(Android|ZTE)/i.test(user_agent)) if (!(!new RegExp(MOBILE).test(user_agent) || /(9138B|TB782B|Nexus [97]|pixel c|HUAWEISHT|BTV|noble nook|smart ultra 6)/i.test(user_agent))) return ANDROID;
354
+ else {
355
+ if (/pixel[\daxl ]{1,6}/i.test(user_agent) && !/pixel c/i.test(user_agent) || /(huaweimed-al00|tah-|APA|SM-G92|i980|zte|U304AA)/i.test(user_agent) || /lmy47v/i.test(user_agent) && !/QTAQZ3/i.test(user_agent)) return ANDROID;
356
+ return ANDROID_TABLET;
357
+ }
358
+ else if (new RegExp('(pda|' + MOBILE + ')', 'i').test(user_agent)) return GENERIC_MOBILE;
359
+ else if (new RegExp(TABLET, 'i').test(user_agent) && !new RegExp(TABLET + ' pc', 'i').test(user_agent)) return GENERIC_TABLET;
360
+ else return '';
361
+ };
362
+ const detectDeviceType = function(user_agent) {
363
+ const device = detectDevice(user_agent);
364
+ if (device === IPAD || device === ANDROID_TABLET || 'Kobo' === device || 'Kindle Fire' === device || device === GENERIC_TABLET) return TABLET;
365
+ if (device === NINTENDO || device === XBOX || device === PLAYSTATION || device === OUYA) return 'Console';
366
+ if (device === APPLE_WATCH) return 'Wearable';
367
+ if (device) return MOBILE;
368
+ return 'Desktop';
369
+ };
370
+ exports.detectBrowser = __webpack_exports__.detectBrowser;
371
+ exports.detectBrowserVersion = __webpack_exports__.detectBrowserVersion;
372
+ exports.detectDevice = __webpack_exports__.detectDevice;
373
+ exports.detectDeviceType = __webpack_exports__.detectDeviceType;
374
+ exports.detectOS = __webpack_exports__.detectOS;
375
+ for(var __webpack_i__ in __webpack_exports__)if (-1 === [
376
+ "detectBrowser",
377
+ "detectBrowserVersion",
378
+ "detectDevice",
379
+ "detectDeviceType",
380
+ "detectOS"
381
+ ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
382
+ Object.defineProperty(exports, '__esModule', {
383
+ value: true
384
+ });
@@ -0,0 +1,338 @@
1
+ import { includes } from "./string-utils.mjs";
2
+ import { isFunction, isUndefined } from "./type-utils.mjs";
3
+ const FACEBOOK = 'Facebook';
4
+ const MOBILE = 'Mobile';
5
+ const IOS = 'iOS';
6
+ const ANDROID = 'Android';
7
+ const TABLET = 'Tablet';
8
+ const ANDROID_TABLET = ANDROID + ' ' + TABLET;
9
+ const IPAD = 'iPad';
10
+ const APPLE = 'Apple';
11
+ const APPLE_WATCH = APPLE + ' Watch';
12
+ const SAFARI = 'Safari';
13
+ const BLACKBERRY = 'BlackBerry';
14
+ const SAMSUNG = 'Samsung';
15
+ const SAMSUNG_BROWSER = SAMSUNG + 'Browser';
16
+ const SAMSUNG_INTERNET = SAMSUNG + ' Internet';
17
+ const CHROME = 'Chrome';
18
+ const CHROME_OS = CHROME + ' OS';
19
+ const CHROME_IOS = CHROME + ' ' + IOS;
20
+ const INTERNET_EXPLORER = 'Internet Explorer';
21
+ const INTERNET_EXPLORER_MOBILE = INTERNET_EXPLORER + ' ' + MOBILE;
22
+ const OPERA = 'Opera';
23
+ const OPERA_MINI = OPERA + ' Mini';
24
+ const EDGE = 'Edge';
25
+ const MICROSOFT_EDGE = 'Microsoft ' + EDGE;
26
+ const FIREFOX = 'Firefox';
27
+ const FIREFOX_IOS = FIREFOX + ' ' + IOS;
28
+ const NINTENDO = 'Nintendo';
29
+ const PLAYSTATION = 'PlayStation';
30
+ const XBOX = 'Xbox';
31
+ const ANDROID_MOBILE = ANDROID + ' ' + MOBILE;
32
+ const MOBILE_SAFARI = MOBILE + ' ' + SAFARI;
33
+ const WINDOWS = 'Windows';
34
+ const WINDOWS_PHONE = WINDOWS + ' Phone';
35
+ const NOKIA = 'Nokia';
36
+ const OUYA = 'Ouya';
37
+ const GENERIC = 'Generic';
38
+ const GENERIC_MOBILE = GENERIC + ' ' + MOBILE.toLowerCase();
39
+ const GENERIC_TABLET = GENERIC + ' ' + TABLET.toLowerCase();
40
+ const KONQUEROR = 'Konqueror';
41
+ const BROWSER_VERSION_REGEX_SUFFIX = '(\\d+(\\.\\d+)?)';
42
+ const DEFAULT_BROWSER_VERSION_REGEX = new RegExp('Version/' + BROWSER_VERSION_REGEX_SUFFIX);
43
+ const XBOX_REGEX = new RegExp(XBOX, 'i');
44
+ const PLAYSTATION_REGEX = new RegExp(PLAYSTATION + ' \\w+', 'i');
45
+ const NINTENDO_REGEX = new RegExp(NINTENDO + ' \\w+', 'i');
46
+ const BLACKBERRY_REGEX = new RegExp(BLACKBERRY + '|PlayBook|BB10', 'i');
47
+ const windowsVersionMap = {
48
+ 'NT3.51': 'NT 3.11',
49
+ 'NT4.0': 'NT 4.0',
50
+ '5.0': '2000',
51
+ '5.1': 'XP',
52
+ '5.2': 'XP',
53
+ '6.0': 'Vista',
54
+ '6.1': '7',
55
+ '6.2': '8',
56
+ '6.3': '8.1',
57
+ '6.4': '10',
58
+ '10.0': '10'
59
+ };
60
+ function isSafari(userAgent) {
61
+ return includes(userAgent, SAFARI) && !includes(userAgent, CHROME) && !includes(userAgent, ANDROID);
62
+ }
63
+ const safariCheck = (ua, vendor)=>vendor && includes(vendor, APPLE) || isSafari(ua);
64
+ const detectBrowser = function(user_agent, vendor) {
65
+ vendor = vendor || '';
66
+ if (includes(user_agent, ' OPR/') && includes(user_agent, 'Mini')) return OPERA_MINI;
67
+ if (includes(user_agent, ' OPR/')) return OPERA;
68
+ if (BLACKBERRY_REGEX.test(user_agent)) return BLACKBERRY;
69
+ if (includes(user_agent, 'IE' + MOBILE) || includes(user_agent, 'WPDesktop')) return INTERNET_EXPLORER_MOBILE;
70
+ if (includes(user_agent, SAMSUNG_BROWSER)) return SAMSUNG_INTERNET;
71
+ else if (includes(user_agent, EDGE) || includes(user_agent, 'Edg/')) return MICROSOFT_EDGE;
72
+ else if (includes(user_agent, 'FBIOS')) return FACEBOOK + ' ' + MOBILE;
73
+ else if (includes(user_agent, 'UCWEB') || includes(user_agent, 'UCBrowser')) return 'UC Browser';
74
+ else if (includes(user_agent, 'CriOS')) return CHROME_IOS;
75
+ else if (includes(user_agent, 'CrMo')) return CHROME;
76
+ else if (includes(user_agent, CHROME)) return CHROME;
77
+ else if (includes(user_agent, ANDROID) && includes(user_agent, SAFARI)) return ANDROID_MOBILE;
78
+ else if (includes(user_agent, 'FxiOS')) return FIREFOX_IOS;
79
+ else if (includes(user_agent.toLowerCase(), KONQUEROR.toLowerCase())) return KONQUEROR;
80
+ else if (safariCheck(user_agent, vendor)) return includes(user_agent, MOBILE) ? MOBILE_SAFARI : SAFARI;
81
+ else if (includes(user_agent, FIREFOX)) return FIREFOX;
82
+ else if (includes(user_agent, 'MSIE') || includes(user_agent, 'Trident/')) return INTERNET_EXPLORER;
83
+ else if (includes(user_agent, 'Gecko')) return FIREFOX;
84
+ return '';
85
+ };
86
+ const versionRegexes = {
87
+ [INTERNET_EXPLORER_MOBILE]: [
88
+ new RegExp('rv:' + BROWSER_VERSION_REGEX_SUFFIX)
89
+ ],
90
+ [MICROSOFT_EDGE]: [
91
+ new RegExp(EDGE + '?\\/' + BROWSER_VERSION_REGEX_SUFFIX)
92
+ ],
93
+ [CHROME]: [
94
+ new RegExp('(' + CHROME + '|CrMo)\\/' + BROWSER_VERSION_REGEX_SUFFIX)
95
+ ],
96
+ [CHROME_IOS]: [
97
+ new RegExp('CriOS\\/' + BROWSER_VERSION_REGEX_SUFFIX)
98
+ ],
99
+ 'UC Browser': [
100
+ new RegExp('(UCBrowser|UCWEB)\\/' + BROWSER_VERSION_REGEX_SUFFIX)
101
+ ],
102
+ [SAFARI]: [
103
+ DEFAULT_BROWSER_VERSION_REGEX
104
+ ],
105
+ [MOBILE_SAFARI]: [
106
+ DEFAULT_BROWSER_VERSION_REGEX
107
+ ],
108
+ [OPERA]: [
109
+ new RegExp('(' + OPERA + '|OPR)\\/' + BROWSER_VERSION_REGEX_SUFFIX)
110
+ ],
111
+ [FIREFOX]: [
112
+ new RegExp(FIREFOX + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)
113
+ ],
114
+ [FIREFOX_IOS]: [
115
+ new RegExp('FxiOS\\/' + BROWSER_VERSION_REGEX_SUFFIX)
116
+ ],
117
+ [KONQUEROR]: [
118
+ new RegExp('Konqueror[:/]?' + BROWSER_VERSION_REGEX_SUFFIX, 'i')
119
+ ],
120
+ [BLACKBERRY]: [
121
+ new RegExp(BLACKBERRY + ' ' + BROWSER_VERSION_REGEX_SUFFIX),
122
+ DEFAULT_BROWSER_VERSION_REGEX
123
+ ],
124
+ [ANDROID_MOBILE]: [
125
+ new RegExp('android\\s' + BROWSER_VERSION_REGEX_SUFFIX, 'i')
126
+ ],
127
+ [SAMSUNG_INTERNET]: [
128
+ new RegExp(SAMSUNG_BROWSER + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)
129
+ ],
130
+ [INTERNET_EXPLORER]: [
131
+ new RegExp('(rv:|MSIE )' + BROWSER_VERSION_REGEX_SUFFIX)
132
+ ],
133
+ Mozilla: [
134
+ new RegExp('rv:' + BROWSER_VERSION_REGEX_SUFFIX)
135
+ ]
136
+ };
137
+ const detectBrowserVersion = function(userAgent, vendor) {
138
+ const browser = detectBrowser(userAgent, vendor);
139
+ const regexes = versionRegexes[browser];
140
+ if (isUndefined(regexes)) return null;
141
+ for(let i = 0; i < regexes.length; i++){
142
+ const regex = regexes[i];
143
+ const matches = userAgent.match(regex);
144
+ if (matches) return parseFloat(matches[matches.length - 2]);
145
+ }
146
+ return null;
147
+ };
148
+ const osMatchers = [
149
+ [
150
+ new RegExp(XBOX + '; ' + XBOX + ' (.*?)[);]', 'i'),
151
+ (match)=>[
152
+ XBOX,
153
+ match && match[1] || ''
154
+ ]
155
+ ],
156
+ [
157
+ new RegExp(NINTENDO, 'i'),
158
+ [
159
+ NINTENDO,
160
+ ''
161
+ ]
162
+ ],
163
+ [
164
+ new RegExp(PLAYSTATION, 'i'),
165
+ [
166
+ PLAYSTATION,
167
+ ''
168
+ ]
169
+ ],
170
+ [
171
+ BLACKBERRY_REGEX,
172
+ [
173
+ BLACKBERRY,
174
+ ''
175
+ ]
176
+ ],
177
+ [
178
+ new RegExp(WINDOWS, 'i'),
179
+ (_, user_agent)=>{
180
+ if (/Phone/.test(user_agent) || /WPDesktop/.test(user_agent)) return [
181
+ WINDOWS_PHONE,
182
+ ''
183
+ ];
184
+ if (new RegExp(MOBILE).test(user_agent) && !/IEMobile\b/.test(user_agent)) return [
185
+ WINDOWS + ' ' + MOBILE,
186
+ ''
187
+ ];
188
+ const match = /Windows NT ([0-9.]+)/i.exec(user_agent);
189
+ if (match && match[1]) {
190
+ const version = match[1];
191
+ let osVersion = windowsVersionMap[version] || '';
192
+ if (/arm/i.test(user_agent)) osVersion = 'RT';
193
+ return [
194
+ WINDOWS,
195
+ osVersion
196
+ ];
197
+ }
198
+ return [
199
+ WINDOWS,
200
+ ''
201
+ ];
202
+ }
203
+ ],
204
+ [
205
+ /((iPhone|iPad|iPod).*?OS (\d+)_(\d+)_?(\d+)?|iPhone)/,
206
+ (match)=>{
207
+ if (match && match[3]) {
208
+ const versionParts = [
209
+ match[3],
210
+ match[4],
211
+ match[5] || '0'
212
+ ];
213
+ return [
214
+ IOS,
215
+ versionParts.join('.')
216
+ ];
217
+ }
218
+ return [
219
+ IOS,
220
+ ''
221
+ ];
222
+ }
223
+ ],
224
+ [
225
+ /(watch.*\/(\d+\.\d+\.\d+)|watch os,(\d+\.\d+),)/i,
226
+ (match)=>{
227
+ let version = '';
228
+ if (match && match.length >= 3) version = isUndefined(match[2]) ? match[3] : match[2];
229
+ return [
230
+ 'watchOS',
231
+ version
232
+ ];
233
+ }
234
+ ],
235
+ [
236
+ new RegExp('(' + ANDROID + ' (\\d+)\\.(\\d+)\\.?(\\d+)?|' + ANDROID + ')', 'i'),
237
+ (match)=>{
238
+ if (match && match[2]) {
239
+ const versionParts = [
240
+ match[2],
241
+ match[3],
242
+ match[4] || '0'
243
+ ];
244
+ return [
245
+ ANDROID,
246
+ versionParts.join('.')
247
+ ];
248
+ }
249
+ return [
250
+ ANDROID,
251
+ ''
252
+ ];
253
+ }
254
+ ],
255
+ [
256
+ /Mac OS X (\d+)[_.](\d+)[_.]?(\d+)?/i,
257
+ (match)=>{
258
+ const result = [
259
+ 'Mac OS X',
260
+ ''
261
+ ];
262
+ if (match && match[1]) {
263
+ const versionParts = [
264
+ match[1],
265
+ match[2],
266
+ match[3] || '0'
267
+ ];
268
+ result[1] = versionParts.join('.');
269
+ }
270
+ return result;
271
+ }
272
+ ],
273
+ [
274
+ /Mac/i,
275
+ [
276
+ 'Mac OS X',
277
+ ''
278
+ ]
279
+ ],
280
+ [
281
+ /CrOS/,
282
+ [
283
+ CHROME_OS,
284
+ ''
285
+ ]
286
+ ],
287
+ [
288
+ /Linux|debian/i,
289
+ [
290
+ 'Linux',
291
+ ''
292
+ ]
293
+ ]
294
+ ];
295
+ const detectOS = function(user_agent) {
296
+ for(let i = 0; i < osMatchers.length; i++){
297
+ const [rgex, resultOrFn] = osMatchers[i];
298
+ const match = rgex.exec(user_agent);
299
+ const result = match && (isFunction(resultOrFn) ? resultOrFn(match, user_agent) : resultOrFn);
300
+ if (result) return result;
301
+ }
302
+ return [
303
+ '',
304
+ ''
305
+ ];
306
+ };
307
+ const detectDevice = function(user_agent) {
308
+ if (NINTENDO_REGEX.test(user_agent)) return NINTENDO;
309
+ if (PLAYSTATION_REGEX.test(user_agent)) return PLAYSTATION;
310
+ if (XBOX_REGEX.test(user_agent)) return XBOX;
311
+ if (new RegExp(OUYA, 'i').test(user_agent)) return OUYA;
312
+ if (new RegExp('(' + WINDOWS_PHONE + '|WPDesktop)', 'i').test(user_agent)) return WINDOWS_PHONE;
313
+ else if (/iPad/.test(user_agent)) return IPAD;
314
+ else if (/iPod/.test(user_agent)) return 'iPod Touch';
315
+ else if (/iPhone/.test(user_agent)) return 'iPhone';
316
+ else if (/(watch)(?: ?os[,/]|\d,\d\/)[\d.]+/i.test(user_agent)) return APPLE_WATCH;
317
+ else if (BLACKBERRY_REGEX.test(user_agent)) return BLACKBERRY;
318
+ else if (/(kobo)\s(ereader|touch)/i.test(user_agent)) return 'Kobo';
319
+ else if (new RegExp(NOKIA, 'i').test(user_agent)) return NOKIA;
320
+ else if (/(kf[a-z]{2}wi|aeo[c-r]{2})( bui|\))/i.test(user_agent) || /(kf[a-z]+)( bui|\)).+silk\//i.test(user_agent)) return 'Kindle Fire';
321
+ else if (/(Android|ZTE)/i.test(user_agent)) if (!(!new RegExp(MOBILE).test(user_agent) || /(9138B|TB782B|Nexus [97]|pixel c|HUAWEISHT|BTV|noble nook|smart ultra 6)/i.test(user_agent))) return ANDROID;
322
+ else {
323
+ if (/pixel[\daxl ]{1,6}/i.test(user_agent) && !/pixel c/i.test(user_agent) || /(huaweimed-al00|tah-|APA|SM-G92|i980|zte|U304AA)/i.test(user_agent) || /lmy47v/i.test(user_agent) && !/QTAQZ3/i.test(user_agent)) return ANDROID;
324
+ return ANDROID_TABLET;
325
+ }
326
+ else if (new RegExp('(pda|' + MOBILE + ')', 'i').test(user_agent)) return GENERIC_MOBILE;
327
+ else if (new RegExp(TABLET, 'i').test(user_agent) && !new RegExp(TABLET + ' pc', 'i').test(user_agent)) return GENERIC_TABLET;
328
+ else return '';
329
+ };
330
+ const detectDeviceType = function(user_agent) {
331
+ const device = detectDevice(user_agent);
332
+ if (device === IPAD || device === ANDROID_TABLET || 'Kobo' === device || 'Kindle Fire' === device || device === GENERIC_TABLET) return TABLET;
333
+ if (device === NINTENDO || device === XBOX || device === PLAYSTATION || device === OUYA) return 'Console';
334
+ if (device === APPLE_WATCH) return 'Wearable';
335
+ if (device) return MOBILE;
336
+ return 'Desktop';
337
+ };
338
+ export { detectBrowser, detectBrowserVersion, detectDevice, detectDeviceType, detectOS };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posthog/core",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -7,6 +7,7 @@ export * from './string-utils'
7
7
  export * from './type-utils'
8
8
  export * from './promise-queue'
9
9
  export * from './logger'
10
+ export * from './user-agent-utils'
10
11
 
11
12
  export const STRING_FORMAT = 'utf8'
12
13
 
@@ -0,0 +1,358 @@
1
+ import { includes } from './string-utils'
2
+ import { isFunction, isUndefined } from './type-utils'
3
+
4
+ /**
5
+ * this device detection code is (at time of writing) about 3% of the size of the entire library
6
+ * this is mostly because the identifiers user in regexes and results can't be minified away since
7
+ * they have meaning
8
+ *
9
+ * so, there are some pre-uglifying choices in the code to help reduce the size
10
+ * e.g. many repeated strings are stored as variables and then old-fashioned concatenated together
11
+ *
12
+ * TL;DR here be dragons
13
+ */
14
+ const FACEBOOK = 'Facebook'
15
+ const MOBILE = 'Mobile'
16
+ const IOS = 'iOS'
17
+ const ANDROID = 'Android'
18
+ const TABLET = 'Tablet'
19
+ const ANDROID_TABLET = ANDROID + ' ' + TABLET
20
+ const IPAD = 'iPad'
21
+ const APPLE = 'Apple'
22
+ const APPLE_WATCH = APPLE + ' Watch'
23
+ const SAFARI = 'Safari'
24
+ const BLACKBERRY = 'BlackBerry'
25
+ const SAMSUNG = 'Samsung'
26
+ const SAMSUNG_BROWSER = SAMSUNG + 'Browser'
27
+ const SAMSUNG_INTERNET = SAMSUNG + ' Internet'
28
+ const CHROME = 'Chrome'
29
+ const CHROME_OS = CHROME + ' OS'
30
+ const CHROME_IOS = CHROME + ' ' + IOS
31
+ const INTERNET_EXPLORER = 'Internet Explorer'
32
+ const INTERNET_EXPLORER_MOBILE = INTERNET_EXPLORER + ' ' + MOBILE
33
+ const OPERA = 'Opera'
34
+ const OPERA_MINI = OPERA + ' Mini'
35
+ const EDGE = 'Edge'
36
+ const MICROSOFT_EDGE = 'Microsoft ' + EDGE
37
+ const FIREFOX = 'Firefox'
38
+ const FIREFOX_IOS = FIREFOX + ' ' + IOS
39
+ const NINTENDO = 'Nintendo'
40
+ const PLAYSTATION = 'PlayStation'
41
+ const XBOX = 'Xbox'
42
+ const ANDROID_MOBILE = ANDROID + ' ' + MOBILE
43
+ const MOBILE_SAFARI = MOBILE + ' ' + SAFARI
44
+ const WINDOWS = 'Windows'
45
+ const WINDOWS_PHONE = WINDOWS + ' Phone'
46
+ const NOKIA = 'Nokia'
47
+ const OUYA = 'Ouya'
48
+ const GENERIC = 'Generic'
49
+ const GENERIC_MOBILE = GENERIC + ' ' + MOBILE.toLowerCase()
50
+ const GENERIC_TABLET = GENERIC + ' ' + TABLET.toLowerCase()
51
+ const KONQUEROR = 'Konqueror'
52
+
53
+ const BROWSER_VERSION_REGEX_SUFFIX = '(\\d+(\\.\\d+)?)'
54
+ const DEFAULT_BROWSER_VERSION_REGEX = new RegExp('Version/' + BROWSER_VERSION_REGEX_SUFFIX)
55
+
56
+ const XBOX_REGEX = new RegExp(XBOX, 'i')
57
+ const PLAYSTATION_REGEX = new RegExp(PLAYSTATION + ' \\w+', 'i')
58
+ const NINTENDO_REGEX = new RegExp(NINTENDO + ' \\w+', 'i')
59
+ const BLACKBERRY_REGEX = new RegExp(BLACKBERRY + '|PlayBook|BB10', 'i')
60
+
61
+ const windowsVersionMap: Record<string, string> = {
62
+ 'NT3.51': 'NT 3.11',
63
+ 'NT4.0': 'NT 4.0',
64
+ '5.0': '2000',
65
+ '5.1': 'XP',
66
+ '5.2': 'XP',
67
+ '6.0': 'Vista',
68
+ '6.1': '7',
69
+ '6.2': '8',
70
+ '6.3': '8.1',
71
+ '6.4': '10',
72
+ '10.0': '10',
73
+ }
74
+
75
+ /**
76
+ * Safari detection turns out to be complicated. For e.g. https://stackoverflow.com/a/29696509
77
+ * We can be slightly loose because some options have been ruled out (e.g. firefox on iOS)
78
+ * before this check is made
79
+ */
80
+ function isSafari(userAgent: string): boolean {
81
+ return includes(userAgent, SAFARI) && !includes(userAgent, CHROME) && !includes(userAgent, ANDROID)
82
+ }
83
+
84
+ const safariCheck = (ua: string, vendor?: string) => (vendor && includes(vendor, APPLE)) || isSafari(ua)
85
+
86
+ /**
87
+ * This function detects which browser is running this script.
88
+ * The order of the checks are important since many user agents
89
+ * include keywords used in later checks.
90
+ */
91
+ export const detectBrowser = function (user_agent: string, vendor: string | undefined): string {
92
+ vendor = vendor || '' // vendor is undefined for at least IE9
93
+
94
+ if (includes(user_agent, ' OPR/') && includes(user_agent, 'Mini')) {
95
+ return OPERA_MINI
96
+ } else if (includes(user_agent, ' OPR/')) {
97
+ return OPERA
98
+ } else if (BLACKBERRY_REGEX.test(user_agent)) {
99
+ return BLACKBERRY
100
+ } else if (includes(user_agent, 'IE' + MOBILE) || includes(user_agent, 'WPDesktop')) {
101
+ return INTERNET_EXPLORER_MOBILE
102
+ }
103
+ // https://developer.samsung.com/internet/user-agent-string-format
104
+ else if (includes(user_agent, SAMSUNG_BROWSER)) {
105
+ return SAMSUNG_INTERNET
106
+ } else if (includes(user_agent, EDGE) || includes(user_agent, 'Edg/')) {
107
+ return MICROSOFT_EDGE
108
+ } else if (includes(user_agent, 'FBIOS')) {
109
+ return FACEBOOK + ' ' + MOBILE
110
+ } else if (includes(user_agent, 'UCWEB') || includes(user_agent, 'UCBrowser')) {
111
+ return 'UC Browser'
112
+ } else if (includes(user_agent, 'CriOS')) {
113
+ return CHROME_IOS // why not just Chrome?
114
+ } else if (includes(user_agent, 'CrMo')) {
115
+ return CHROME
116
+ } else if (includes(user_agent, CHROME)) {
117
+ return CHROME
118
+ } else if (includes(user_agent, ANDROID) && includes(user_agent, SAFARI)) {
119
+ return ANDROID_MOBILE
120
+ } else if (includes(user_agent, 'FxiOS')) {
121
+ return FIREFOX_IOS
122
+ } else if (includes(user_agent.toLowerCase(), KONQUEROR.toLowerCase())) {
123
+ return KONQUEROR
124
+ } else if (safariCheck(user_agent, vendor)) {
125
+ return includes(user_agent, MOBILE) ? MOBILE_SAFARI : SAFARI
126
+ } else if (includes(user_agent, FIREFOX)) {
127
+ return FIREFOX
128
+ } else if (includes(user_agent, 'MSIE') || includes(user_agent, 'Trident/')) {
129
+ return INTERNET_EXPLORER
130
+ } else if (includes(user_agent, 'Gecko')) {
131
+ return FIREFOX
132
+ }
133
+
134
+ return ''
135
+ }
136
+
137
+ const versionRegexes: Record<string, RegExp[]> = {
138
+ [INTERNET_EXPLORER_MOBILE]: [new RegExp('rv:' + BROWSER_VERSION_REGEX_SUFFIX)],
139
+ [MICROSOFT_EDGE]: [new RegExp(EDGE + '?\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
140
+ [CHROME]: [new RegExp('(' + CHROME + '|CrMo)\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
141
+ [CHROME_IOS]: [new RegExp('CriOS\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
142
+ 'UC Browser': [new RegExp('(UCBrowser|UCWEB)\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
143
+ [SAFARI]: [DEFAULT_BROWSER_VERSION_REGEX],
144
+ [MOBILE_SAFARI]: [DEFAULT_BROWSER_VERSION_REGEX],
145
+ [OPERA]: [new RegExp('(' + OPERA + '|OPR)\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
146
+ [FIREFOX]: [new RegExp(FIREFOX + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
147
+ [FIREFOX_IOS]: [new RegExp('FxiOS\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
148
+ [KONQUEROR]: [new RegExp('Konqueror[:/]?' + BROWSER_VERSION_REGEX_SUFFIX, 'i')],
149
+ // not every blackberry user agent has the version after the name
150
+ [BLACKBERRY]: [new RegExp(BLACKBERRY + ' ' + BROWSER_VERSION_REGEX_SUFFIX), DEFAULT_BROWSER_VERSION_REGEX],
151
+ [ANDROID_MOBILE]: [new RegExp('android\\s' + BROWSER_VERSION_REGEX_SUFFIX, 'i')],
152
+ [SAMSUNG_INTERNET]: [new RegExp(SAMSUNG_BROWSER + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
153
+ [INTERNET_EXPLORER]: [new RegExp('(rv:|MSIE )' + BROWSER_VERSION_REGEX_SUFFIX)],
154
+ Mozilla: [new RegExp('rv:' + BROWSER_VERSION_REGEX_SUFFIX)],
155
+ }
156
+
157
+ /**
158
+ * This function detects which browser version is running this script,
159
+ * parsing major and minor version (e.g., 42.1). User agent strings from:
160
+ * http://www.useragentstring.com/pages/useragentstring.php
161
+ *
162
+ * `navigator.vendor` is passed in and used to help with detecting certain browsers
163
+ * NB `navigator.vendor` is deprecated and not present in every browser
164
+ */
165
+ export const detectBrowserVersion = function (userAgent: string, vendor: string | undefined): number | null {
166
+ const browser = detectBrowser(userAgent, vendor)
167
+ const regexes: RegExp[] | undefined = versionRegexes[browser as keyof typeof versionRegexes]
168
+ if (isUndefined(regexes)) {
169
+ return null
170
+ }
171
+
172
+ for (let i = 0; i < regexes.length; i++) {
173
+ const regex = regexes[i]
174
+ const matches = userAgent.match(regex)
175
+ if (matches) {
176
+ return parseFloat(matches[matches.length - 2])
177
+ }
178
+ }
179
+ return null
180
+ }
181
+
182
+ // to avoid repeating regexes or calling them twice, we have an array of matches
183
+ // the first regex that matches uses its matcher function to return the result
184
+ const osMatchers: [
185
+ RegExp,
186
+ [string, string] | ((match: RegExpMatchArray | null, user_agent: string) => [string, string]),
187
+ ][] = [
188
+ [
189
+ new RegExp(XBOX + '; ' + XBOX + ' (.*?)[);]', 'i'),
190
+ (match) => {
191
+ return [XBOX, (match && match[1]) || '']
192
+ },
193
+ ],
194
+ [new RegExp(NINTENDO, 'i'), [NINTENDO, '']],
195
+ [new RegExp(PLAYSTATION, 'i'), [PLAYSTATION, '']],
196
+ [BLACKBERRY_REGEX, [BLACKBERRY, '']],
197
+ [
198
+ new RegExp(WINDOWS, 'i'),
199
+ (_, user_agent) => {
200
+ if (/Phone/.test(user_agent) || /WPDesktop/.test(user_agent)) {
201
+ return [WINDOWS_PHONE, '']
202
+ }
203
+ // not all JS versions support negative lookbehind, so we need two checks here
204
+ if (new RegExp(MOBILE).test(user_agent) && !/IEMobile\b/.test(user_agent)) {
205
+ return [WINDOWS + ' ' + MOBILE, '']
206
+ }
207
+ const match = /Windows NT ([0-9.]+)/i.exec(user_agent)
208
+ if (match && match[1]) {
209
+ const version = match[1]
210
+ let osVersion = windowsVersionMap[version] || ''
211
+ if (/arm/i.test(user_agent)) {
212
+ osVersion = 'RT'
213
+ }
214
+ return [WINDOWS, osVersion]
215
+ }
216
+ return [WINDOWS, '']
217
+ },
218
+ ],
219
+ [
220
+ /((iPhone|iPad|iPod).*?OS (\d+)_(\d+)_?(\d+)?|iPhone)/,
221
+ (match) => {
222
+ if (match && match[3]) {
223
+ const versionParts = [match[3], match[4], match[5] || '0']
224
+ return [IOS, versionParts.join('.')]
225
+ }
226
+ return [IOS, '']
227
+ },
228
+ ],
229
+ [
230
+ /(watch.*\/(\d+\.\d+\.\d+)|watch os,(\d+\.\d+),)/i,
231
+ (match) => {
232
+ // e.g. Watch4,3/5.3.8 (16U680)
233
+ let version = ''
234
+ if (match && match.length >= 3) {
235
+ version = isUndefined(match[2]) ? match[3] : match[2]
236
+ }
237
+ return ['watchOS', version]
238
+ },
239
+ ],
240
+ [
241
+ new RegExp('(' + ANDROID + ' (\\d+)\\.(\\d+)\\.?(\\d+)?|' + ANDROID + ')', 'i'),
242
+ (match) => {
243
+ if (match && match[2]) {
244
+ const versionParts = [match[2], match[3], match[4] || '0']
245
+ return [ANDROID, versionParts.join('.')]
246
+ }
247
+ return [ANDROID, '']
248
+ },
249
+ ],
250
+ [
251
+ /Mac OS X (\d+)[_.](\d+)[_.]?(\d+)?/i,
252
+ (match) => {
253
+ const result: [string, string] = ['Mac OS X', '']
254
+ if (match && match[1]) {
255
+ const versionParts = [match[1], match[2], match[3] || '0']
256
+ result[1] = versionParts.join('.')
257
+ }
258
+ return result
259
+ },
260
+ ],
261
+ [
262
+ /Mac/i,
263
+ // mop up a few non-standard UAs that should match mac
264
+ ['Mac OS X', ''],
265
+ ],
266
+ [/CrOS/, [CHROME_OS, '']],
267
+ [/Linux|debian/i, ['Linux', '']],
268
+ ]
269
+
270
+ export const detectOS = function (user_agent: string): [string, string] {
271
+ for (let i = 0; i < osMatchers.length; i++) {
272
+ const [rgex, resultOrFn] = osMatchers[i]
273
+ const match = rgex.exec(user_agent)
274
+ const result = match && (isFunction(resultOrFn) ? resultOrFn(match, user_agent) : resultOrFn)
275
+ if (result) {
276
+ return result
277
+ }
278
+ }
279
+ return ['', '']
280
+ }
281
+
282
+ export const detectDevice = function (user_agent: string): string {
283
+ if (NINTENDO_REGEX.test(user_agent)) {
284
+ return NINTENDO
285
+ } else if (PLAYSTATION_REGEX.test(user_agent)) {
286
+ return PLAYSTATION
287
+ } else if (XBOX_REGEX.test(user_agent)) {
288
+ return XBOX
289
+ } else if (new RegExp(OUYA, 'i').test(user_agent)) {
290
+ return OUYA
291
+ } else if (new RegExp('(' + WINDOWS_PHONE + '|WPDesktop)', 'i').test(user_agent)) {
292
+ return WINDOWS_PHONE
293
+ } else if (/iPad/.test(user_agent)) {
294
+ return IPAD
295
+ } else if (/iPod/.test(user_agent)) {
296
+ return 'iPod Touch'
297
+ } else if (/iPhone/.test(user_agent)) {
298
+ return 'iPhone'
299
+ } else if (/(watch)(?: ?os[,/]|\d,\d\/)[\d.]+/i.test(user_agent)) {
300
+ return APPLE_WATCH
301
+ } else if (BLACKBERRY_REGEX.test(user_agent)) {
302
+ return BLACKBERRY
303
+ } else if (/(kobo)\s(ereader|touch)/i.test(user_agent)) {
304
+ return 'Kobo'
305
+ } else if (new RegExp(NOKIA, 'i').test(user_agent)) {
306
+ return NOKIA
307
+ } else if (
308
+ // Kindle Fire without Silk / Echo Show
309
+ /(kf[a-z]{2}wi|aeo[c-r]{2})( bui|\))/i.test(user_agent) ||
310
+ // Kindle Fire HD
311
+ /(kf[a-z]+)( bui|\)).+silk\//i.test(user_agent)
312
+ ) {
313
+ return 'Kindle Fire'
314
+ } else if (/(Android|ZTE)/i.test(user_agent)) {
315
+ if (
316
+ !new RegExp(MOBILE).test(user_agent) ||
317
+ /(9138B|TB782B|Nexus [97]|pixel c|HUAWEISHT|BTV|noble nook|smart ultra 6)/i.test(user_agent)
318
+ ) {
319
+ if (
320
+ (/pixel[\daxl ]{1,6}/i.test(user_agent) && !/pixel c/i.test(user_agent)) ||
321
+ /(huaweimed-al00|tah-|APA|SM-G92|i980|zte|U304AA)/i.test(user_agent) ||
322
+ (/lmy47v/i.test(user_agent) && !/QTAQZ3/i.test(user_agent))
323
+ ) {
324
+ return ANDROID
325
+ }
326
+ return ANDROID_TABLET
327
+ } else {
328
+ return ANDROID
329
+ }
330
+ } else if (new RegExp('(pda|' + MOBILE + ')', 'i').test(user_agent)) {
331
+ return GENERIC_MOBILE
332
+ } else if (new RegExp(TABLET, 'i').test(user_agent) && !new RegExp(TABLET + ' pc', 'i').test(user_agent)) {
333
+ return GENERIC_TABLET
334
+ } else {
335
+ return ''
336
+ }
337
+ }
338
+
339
+ export const detectDeviceType = function (user_agent: string): string {
340
+ const device = detectDevice(user_agent)
341
+ if (
342
+ device === IPAD ||
343
+ device === ANDROID_TABLET ||
344
+ device === 'Kobo' ||
345
+ device === 'Kindle Fire' ||
346
+ device === GENERIC_TABLET
347
+ ) {
348
+ return TABLET
349
+ } else if (device === NINTENDO || device === XBOX || device === PLAYSTATION || device === OUYA) {
350
+ return 'Console'
351
+ } else if (device === APPLE_WATCH) {
352
+ return 'Wearable'
353
+ } else if (device) {
354
+ return MOBILE
355
+ } else {
356
+ return 'Desktop'
357
+ }
358
+ }