@posthog/core 1.30.14 → 1.31.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.
@@ -7,6 +7,15 @@
7
7
  export interface BrowserDetectionHints {
8
8
  brave?: boolean;
9
9
  }
10
+ /**
11
+ * Opt-in tweaks to UA-string detection. These change how existing traffic is
12
+ * attributed, so the host SDK gates them (behind its `2026-05-30` config
13
+ * defaults) rather than enabling them unconditionally — turning one on
14
+ * reattributes browsers that were previously reported as something else.
15
+ */
16
+ export interface BrowserDetectionOptions {
17
+ detectGoogleSearchApp?: boolean;
18
+ }
10
19
  /**
11
20
  * This function detects which browser is running this script.
12
21
  * The order of the checks are important since many user agents
@@ -16,8 +25,10 @@ export interface BrowserDetectionHints {
16
25
  * detect browsers that intentionally do not identify themselves in the UA
17
26
  * string. When omitted, only UA-string detection runs — preserving the previous
18
27
  * behaviour.
28
+ *
29
+ * `options` toggles opt-in UA-detection tweaks (see `BrowserDetectionOptions`).
19
30
  */
20
- export declare const detectBrowser: (user_agent: string, vendor: string | undefined, hints?: BrowserDetectionHints) => string;
31
+ export declare const detectBrowser: (user_agent: string, vendor: string | undefined, hints?: BrowserDetectionHints, options?: BrowserDetectionOptions) => string;
21
32
  /**
22
33
  * This function detects which browser version is running this script,
23
34
  * parsing major and minor version (e.g., 42.1). User agent strings from:
@@ -26,7 +37,7 @@ export declare const detectBrowser: (user_agent: string, vendor: string | undefi
26
37
  * `navigator.vendor` is passed in and used to help with detecting certain browsers
27
38
  * NB `navigator.vendor` is deprecated and not present in every browser
28
39
  */
29
- export declare const detectBrowserVersion: (userAgent: string, vendor: string | undefined, hints?: BrowserDetectionHints) => number | null;
40
+ export declare const detectBrowserVersion: (userAgent: string, vendor: string | undefined, hints?: BrowserDetectionHints, options?: BrowserDetectionOptions) => number | null;
30
41
  export declare const detectOS: (user_agent: string) => [string, string];
31
42
  export declare const detectDevice: (user_agent: string) => string;
32
43
  export declare const detectDeviceType: (user_agent: string, options?: {
@@ -1 +1 @@
1
- {"version":3,"file":"user-agent-utils.d.ts","sourceRoot":"","sources":["../../src/utils/user-agent-utils.ts"],"names":[],"mappings":"AA+DA;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IAGpC,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAuCD;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,GACxB,YAAY,MAAM,EAClB,QAAQ,MAAM,GAAG,SAAS,EAC1B,QAAQ,qBAAqB,KAC5B,MAmFF,CAAA;AAkCD;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAC/B,WAAW,MAAM,EACjB,QAAQ,MAAM,GAAG,SAAS,EAC1B,QAAQ,qBAAqB,KAC5B,MAAM,GAAG,IAmBX,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,GAC3B,YAAY,MAAM,EAClB,UAAU;IACR,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B,KACA,MA4BF,CAAA"}
1
+ {"version":3,"file":"user-agent-utils.d.ts","sourceRoot":"","sources":["../../src/utils/user-agent-utils.ts"],"names":[],"mappings":"AAgEA;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IAGpC,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AASD;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IAGtC,qBAAqB,CAAC,EAAE,OAAO,CAAA;CAChC;AAgCD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,GACxB,YAAY,MAAM,EAClB,QAAQ,MAAM,GAAG,SAAS,EAC1B,QAAQ,qBAAqB,EAC7B,UAAU,uBAAuB,KAChC,MA2FF,CAAA;AAmCD;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAC/B,WAAW,MAAM,EACjB,QAAQ,MAAM,GAAG,SAAS,EAC1B,QAAQ,qBAAqB,EAC7B,UAAU,uBAAuB,KAChC,MAAM,GAAG,IAmBX,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,GAC3B,YAAY,MAAM,EAClB,UAAU;IACR,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B,KACA,MA4BF,CAAA"}
@@ -78,6 +78,7 @@ const DUCKDUCKGO = 'DuckDuckGo';
78
78
  const PALE_MOON = 'Pale Moon';
79
79
  const WATERFOX = 'Waterfox';
80
80
  const BRAVE = 'Brave';
81
+ const GOOGLE_SEARCH_APP = 'Google Search App';
81
82
  const BROWSER_VERSION_REGEX_SUFFIX = '(\\d+(\\.\\d+)?)';
82
83
  const DEFAULT_BROWSER_VERSION_REGEX = new RegExp('Version/' + BROWSER_VERSION_REGEX_SUFFIX);
83
84
  function browserFromHints(hints) {
@@ -105,10 +106,11 @@ function isSafari(userAgent) {
105
106
  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);
106
107
  }
107
108
  const safariCheck = (ua, vendor)=>vendor && (0, external_string_utils_js_namespaceObject.includes)(vendor, APPLE) || isSafari(ua);
108
- const detectBrowser = function(user_agent, vendor, hints) {
109
+ const detectBrowser = function(user_agent, vendor, hints, options) {
109
110
  vendor = vendor || '';
110
111
  const fromHints = browserFromHints(hints);
111
112
  if (fromHints) return fromHints;
113
+ if (options?.detectGoogleSearchApp && (0, external_string_utils_js_namespaceObject.includes)(user_agent, 'GSA/')) return GOOGLE_SEARCH_APP;
112
114
  if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, ' OPR/') && (0, external_string_utils_js_namespaceObject.includes)(user_agent, 'Mini')) return OPERA_MINI;
113
115
  if ((0, external_string_utils_js_namespaceObject.includes)(user_agent, ' OPR/')) return OPERA;
114
116
  if (BLACKBERRY_REGEX.test(user_agent)) return BLACKBERRY;
@@ -205,6 +207,9 @@ const versionRegexes = {
205
207
  [WATERFOX]: [
206
208
  new RegExp(WATERFOX + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)
207
209
  ],
210
+ [GOOGLE_SEARCH_APP]: [
211
+ new RegExp('GSA\\/' + BROWSER_VERSION_REGEX_SUFFIX)
212
+ ],
208
213
  [INTERNET_EXPLORER]: [
209
214
  new RegExp('(rv:|MSIE )' + BROWSER_VERSION_REGEX_SUFFIX)
210
215
  ],
@@ -212,8 +217,8 @@ const versionRegexes = {
212
217
  new RegExp('rv:' + BROWSER_VERSION_REGEX_SUFFIX)
213
218
  ]
214
219
  };
215
- const detectBrowserVersion = function(userAgent, vendor, hints) {
216
- const browser = detectBrowser(userAgent, vendor, hints);
220
+ const detectBrowserVersion = function(userAgent, vendor, hints, options) {
221
+ const browser = detectBrowser(userAgent, vendor, hints, options);
217
222
  const regexes = versionRegexes[browser];
218
223
  if ((0, external_type_utils_js_namespaceObject.isUndefined)(regexes)) return null;
219
224
  for(let i = 0; i < regexes.length; i++){
@@ -46,6 +46,7 @@ const DUCKDUCKGO = 'DuckDuckGo';
46
46
  const PALE_MOON = 'Pale Moon';
47
47
  const WATERFOX = 'Waterfox';
48
48
  const BRAVE = 'Brave';
49
+ const GOOGLE_SEARCH_APP = 'Google Search App';
49
50
  const BROWSER_VERSION_REGEX_SUFFIX = '(\\d+(\\.\\d+)?)';
50
51
  const DEFAULT_BROWSER_VERSION_REGEX = new RegExp('Version/' + BROWSER_VERSION_REGEX_SUFFIX);
51
52
  function browserFromHints(hints) {
@@ -73,10 +74,11 @@ function isSafari(userAgent) {
73
74
  return includes(userAgent, SAFARI) && !includes(userAgent, CHROME) && !includes(userAgent, ANDROID);
74
75
  }
75
76
  const safariCheck = (ua, vendor)=>vendor && includes(vendor, APPLE) || isSafari(ua);
76
- const detectBrowser = function(user_agent, vendor, hints) {
77
+ const detectBrowser = function(user_agent, vendor, hints, options) {
77
78
  vendor = vendor || '';
78
79
  const fromHints = browserFromHints(hints);
79
80
  if (fromHints) return fromHints;
81
+ if (options?.detectGoogleSearchApp && includes(user_agent, 'GSA/')) return GOOGLE_SEARCH_APP;
80
82
  if (includes(user_agent, ' OPR/') && includes(user_agent, 'Mini')) return OPERA_MINI;
81
83
  if (includes(user_agent, ' OPR/')) return OPERA;
82
84
  if (BLACKBERRY_REGEX.test(user_agent)) return BLACKBERRY;
@@ -173,6 +175,9 @@ const versionRegexes = {
173
175
  [WATERFOX]: [
174
176
  new RegExp(WATERFOX + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)
175
177
  ],
178
+ [GOOGLE_SEARCH_APP]: [
179
+ new RegExp('GSA\\/' + BROWSER_VERSION_REGEX_SUFFIX)
180
+ ],
176
181
  [INTERNET_EXPLORER]: [
177
182
  new RegExp('(rv:|MSIE )' + BROWSER_VERSION_REGEX_SUFFIX)
178
183
  ],
@@ -180,8 +185,8 @@ const versionRegexes = {
180
185
  new RegExp('rv:' + BROWSER_VERSION_REGEX_SUFFIX)
181
186
  ]
182
187
  };
183
- const detectBrowserVersion = function(userAgent, vendor, hints) {
184
- const browser = detectBrowser(userAgent, vendor, hints);
188
+ const detectBrowserVersion = function(userAgent, vendor, hints, options) {
189
+ const browser = detectBrowser(userAgent, vendor, hints, options);
185
190
  const regexes = versionRegexes[browser];
186
191
  if (isUndefined(regexes)) return null;
187
192
  for(let i = 0; i < regexes.length; i++){
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posthog/core",
3
- "version": "1.30.14",
3
+ "version": "1.31.1",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -67,7 +67,7 @@
67
67
  }
68
68
  },
69
69
  "dependencies": {
70
- "@posthog/types": "1.383.3"
70
+ "@posthog/types": "1.384.1"
71
71
  },
72
72
  "devDependencies": {
73
73
  "@rslib/core": "0.10.6",
@@ -57,6 +57,7 @@ const DUCKDUCKGO = 'DuckDuckGo'
57
57
  const PALE_MOON = 'Pale Moon'
58
58
  const WATERFOX = 'Waterfox'
59
59
  const BRAVE = 'Brave'
60
+ const GOOGLE_SEARCH_APP = 'Google Search App'
60
61
 
61
62
  const BROWSER_VERSION_REGEX_SUFFIX = '(\\d+(\\.\\d+)?)'
62
63
  const DEFAULT_BROWSER_VERSION_REGEX = new RegExp('Version/' + BROWSER_VERSION_REGEX_SUFFIX)
@@ -80,6 +81,18 @@ function browserFromHints(hints: BrowserDetectionHints | undefined): string | nu
80
81
  return null
81
82
  }
82
83
 
84
+ /**
85
+ * Opt-in tweaks to UA-string detection. These change how existing traffic is
86
+ * attributed, so the host SDK gates them (behind its `2026-05-30` config
87
+ * defaults) rather than enabling them unconditionally — turning one on
88
+ * reattributes browsers that were previously reported as something else.
89
+ */
90
+ export interface BrowserDetectionOptions {
91
+ // Surface the Google Search App as its own browser via its `GSA/` UA marker
92
+ // instead of the underlying webview (Mobile Safari on iOS, Chrome on Android).
93
+ detectGoogleSearchApp?: boolean
94
+ }
95
+
83
96
  const XBOX_REGEX = new RegExp(XBOX, 'i')
84
97
  const PLAYSTATION_REGEX = new RegExp(PLAYSTATION + ' \\w+', 'i')
85
98
  const NINTENDO_REGEX = new RegExp(NINTENDO + ' \\w+', 'i')
@@ -119,11 +132,14 @@ const safariCheck = (ua: string, vendor?: string) => (vendor && includes(vendor,
119
132
  * detect browsers that intentionally do not identify themselves in the UA
120
133
  * string. When omitted, only UA-string detection runs — preserving the previous
121
134
  * behaviour.
135
+ *
136
+ * `options` toggles opt-in UA-detection tweaks (see `BrowserDetectionOptions`).
122
137
  */
123
138
  export const detectBrowser = function (
124
139
  user_agent: string,
125
140
  vendor: string | undefined,
126
- hints?: BrowserDetectionHints
141
+ hints?: BrowserDetectionHints,
142
+ options?: BrowserDetectionOptions
127
143
  ): string {
128
144
  vendor = vendor || '' // vendor is undefined for at least IE9
129
145
 
@@ -135,6 +151,14 @@ export const detectBrowser = function (
135
151
  return fromHints
136
152
  }
137
153
 
154
+ // The Google Search App embeds a platform webview, so its UA otherwise looks
155
+ // like Mobile Safari (iOS) or Chrome (Android). The `GSA/` marker is present
156
+ // on every platform, so checking it first lets us attribute GSA consistently
157
+ // — it must precede the Chrome and Safari branches that would match instead.
158
+ if (options?.detectGoogleSearchApp && includes(user_agent, 'GSA/')) {
159
+ return GOOGLE_SEARCH_APP
160
+ }
161
+
138
162
  if (includes(user_agent, ' OPR/') && includes(user_agent, 'Mini')) {
139
163
  return OPERA_MINI
140
164
  } else if (includes(user_agent, ' OPR/')) {
@@ -237,6 +261,7 @@ const versionRegexes: Record<string, RegExp[]> = {
237
261
  [DUCKDUCKGO]: [new RegExp('(DuckDuckGo|Ddg)\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
238
262
  [PALE_MOON]: [new RegExp('PaleMoon\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
239
263
  [WATERFOX]: [new RegExp(WATERFOX + '\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
264
+ [GOOGLE_SEARCH_APP]: [new RegExp('GSA\\/' + BROWSER_VERSION_REGEX_SUFFIX)],
240
265
  [INTERNET_EXPLORER]: [new RegExp('(rv:|MSIE )' + BROWSER_VERSION_REGEX_SUFFIX)],
241
266
  Mozilla: [new RegExp('rv:' + BROWSER_VERSION_REGEX_SUFFIX)],
242
267
  }
@@ -252,9 +277,10 @@ const versionRegexes: Record<string, RegExp[]> = {
252
277
  export const detectBrowserVersion = function (
253
278
  userAgent: string,
254
279
  vendor: string | undefined,
255
- hints?: BrowserDetectionHints
280
+ hints?: BrowserDetectionHints,
281
+ options?: BrowserDetectionOptions
256
282
  ): number | null {
257
- const browser = detectBrowser(userAgent, vendor, hints)
283
+ const browser = detectBrowser(userAgent, vendor, hints, options)
258
284
 
259
285
  // Desktop / Android Brave has no parseable UA version, so it returns null
260
286
  // below: its `versionRegexes` entry only matches the iOS `Brave/` marker