@flighthq/useragent 0.3.0-next.906.07cea63 → 0.3.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.
@@ -1 +1 @@
1
- {"version":3,"file":"userAgent.d.ts","sourceRoot":"","sources":["../src/userAgent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAKlC,wBAAgB,gBAAgB,IAAI,kBAAkB,CAWrD;AAWD,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAsB3E;AAKD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,CAK/D;AAMD,wBAAgB,2BAA2B,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CA0BtF;AAKD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY,CAGnE;AAID,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,CAO3D;AAID,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAIrE;AAKD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,eAAe,CAUtG;AAKD,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CA4B5E"}
1
+ {"version":3,"file":"userAgent.d.ts","sourceRoot":"","sources":["../src/userAgent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAOlC,wBAAgB,gBAAgB,IAAI,kBAAkB,CAWrD;AAWD,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAsB3E;AAKD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,CAmB/D;AAMD,wBAAgB,2BAA2B,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CA0BtF;AAKD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,YAAY,CAGnE;AAID,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,CAO3D;AAID,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAIrE;AAKD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,eAAe,CAUtG;AAgBD,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CAK5E"}
package/dist/userAgent.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { parseUserAgentOsVersion } from './userAgentParse';
1
2
  // Probe host CPU byte order via a DataView write-then-read.
2
3
  // Overwhelmingly 'little' on all modern hardware (x86/x64/arm/arm64/wasm).
3
4
  // Returns 'unknown' if ArrayBuffer is unavailable.
@@ -58,8 +59,24 @@ export function parseUserAgentArch(ua, uadPlatform) {
58
59
  // Order matters: Edg/Chrome UAs contain 'Safari'; Firefox UAs do not contain 'Chrome'.
59
60
  // Returns 'unknown' for unrecognized or native-host UAs.
60
61
  export function parseUserAgentEngine(ua) {
62
+ // iOS and iPadOS decide the engine before any product token does: every browser there is required
63
+ // to run on the system WebKit, whatever it calls itself. Testing the platform first is also what
64
+ // keeps this from playing whack-a-mole with product tokens — `EdgiOS` contains `edg` and was being
65
+ // reported as blink (with an empty version, since no `Edg/` token follows), and any future
66
+ // `<Product>iOS` token would have failed the same way.
67
+ if (/iphone|ipad|ipod/i.test(ua))
68
+ return 'webkit';
61
69
  if (/firefox/i.test(ua))
62
70
  return 'gecko';
71
+ // Legacy EdgeHTML Edge (`Edge/18`) is not Blink — Edge only became Chromium at `Edg/79`. It has to be
72
+ // tested before the blink branch, whose `edg` alternative matches it, and it reports 'unknown' rather
73
+ // than a fourth engine value because PlatformEngine has no EdgeHTML member and adding one is a public
74
+ // type change every exhaustive consumer would have to handle. 'unknown' is the documented answer for
75
+ // an engine this SDK does not model, and it is the honest one: saying blink would have a caller apply
76
+ // Chromium workarounds to a browser that never ran Chromium. Distinguished from modern Edge by the
77
+ // trailing `e` — `Edg/` and `EdgiOS/` do not match.
78
+ if (/edge\/\d/i.test(ua))
79
+ return 'unknown';
63
80
  if (/chrome|chromium|edg|opr|samsung/i.test(ua))
64
81
  return 'blink';
65
82
  if (/safari|webkit/i.test(ua))
@@ -151,35 +168,25 @@ export function parseUserAgentRuntime(win) {
151
168
  return 'web';
152
169
  }
153
170
  // Extract the OS version string from a browser user-agent string for the given platform name.
154
- // Returns the raw dotted version string. Returns '' when the version is not present in the UA.
171
+ // Returns the raw dotted version string. Returns '' when the version is not present in the UA, and
172
+ // when `name` is not the platform the UA actually describes — asking a Windows UA for its iOS version
173
+ // has no answer, and a wrong one would be worse than none.
155
174
  // Never parsed for semantics — the caller may use comparePlatformVersions for numeric comparison.
175
+ //
176
+ // The extraction itself lives in parseUserAgentOsVersion; this function is the PlatformName-vocabulary
177
+ // view of it. It used to carry its own copy of the same four patterns, and the copies had drifted:
178
+ // these required exactly one space (`/android ([\d.]+)/`) where the originals accept any whitespace
179
+ // (`/android\s+([\d.]+)/`), so this function returned '' for UAs the other parsed correctly. One
180
+ // implementation, two vocabularies — the drift cannot recur.
181
+ //
182
+ // FROZEN UA VALUES: see parseUserAgentOsVersion. Windows 11 is indistinguishable from Windows 10
183
+ // here, and macOS reads 10.15.7 on every version since Big Sur.
156
184
  export function parseUserAgentVersion(ua, name) {
157
- switch (name) {
158
- case 'windows': {
159
- // 'Windows NT 10.0' → '10.0'
160
- const m = /windows nt ([\d.]+)/i.exec(ua);
161
- return m ? m[1] : '';
162
- }
163
- case 'macos': {
164
- // 'Mac OS X 10_15_7' or 'Mac OS X 10.15.7' → '10.15.7'
165
- const m = /mac os x ([\d_.]+)/i.exec(ua);
166
- return m ? m[1].replace(/_/g, '.') : '';
167
- }
168
- case 'ios': {
169
- // 'CPU OS 17_4_1' or 'CPU iPhone OS 17_4_1' → '17.4.1'
170
- const m = /cpu(?: iphone)? os ([\d_]+)/i.exec(ua);
171
- return m ? m[1].replace(/_/g, '.') : '';
172
- }
173
- case 'android': {
174
- // 'Android 14' → '14'
175
- const m = /android ([\d.]+)/i.exec(ua);
176
- return m ? m[1] : '';
177
- }
178
- case 'linux':
179
- // Linux does not embed a kernel version in the browser UA string.
180
- return '';
181
- default:
182
- return '';
183
- }
185
+ // Linux embeds no kernel version in a browser UA, and 'web' is the no-OS-detected fallback.
186
+ if (name === 'linux' || name === 'web')
187
+ return '';
188
+ if (parseUserAgentName(ua) !== name)
189
+ return '';
190
+ return parseUserAgentOsVersion(ua);
184
191
  }
185
192
  //# sourceMappingURL=userAgent.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"userAgent.js","sourceRoot":"","sources":["../src/userAgent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"userAgent.js","sourceRoot":"","sources":["../src/userAgent.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAE3D,4DAA4D;AAC5D,2EAA2E;AAC3E,mDAAmD;AACnD,MAAM,UAAU,gBAAgB;IAC9B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;QACjC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QACpC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;YAAE,OAAO,QAAQ,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,8CAA8C;IAChD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,+EAA+E;AAC/E,6GAA6G;AAC7G,yFAAyF;AACzF,wFAAwF;AACxF,6FAA6F;AAC7F,EAAE;AACF,gGAAgG;AAChG,mGAAmG;AACnG,8FAA8F;AAC9F,MAAM,UAAU,kBAAkB,CAAC,EAAU,EAAE,WAAoB;IACjE,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,CAAC,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC;QACpC,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,OAAO,OAAO,CAAC;QACtC,IACE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;YACjB,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC;YACrB,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;YACnB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;YACjB,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EACpB,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,OAAO,CAAC;IAC9C,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IAClC,IAAI,yBAAyB,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IACrD,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3C,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,QAAQ,CAAC;IACxC,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,MAAM,CAAC;IACpC,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,6EAA6E;AAC7E,uFAAuF;AACvF,yDAAyD;AACzD,MAAM,UAAU,oBAAoB,CAAC,EAAU;IAC7C,kGAAkG;IAClG,iGAAiG;IACjG,mGAAmG;IACnG,2FAA2F;IAC3F,uDAAuD;IACvD,IAAI,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,QAAQ,CAAC;IAClD,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,OAAO,CAAC;IACxC,sGAAsG;IACtG,sGAAsG;IACtG,sGAAsG;IACtG,qGAAqG;IACrG,sGAAsG;IACtG,mGAAmG;IACnG,oDAAoD;IACpD,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3C,IAAI,kCAAkC,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,OAAO,CAAC;IAChE,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC/C,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,sEAAsE;AACtE,iEAAiE;AACjE,yEAAyE;AACzE,6EAA6E;AAC7E,MAAM,UAAU,2BAA2B,CAAC,EAAU,EAAE,MAAsB;IAC5E,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,4BAA4B;YAC5B,MAAM,CAAC,GAAG,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACxC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvB,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,gFAAgF;YAChF,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACtC,IAAI,GAAG;gBAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;YACvB,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACtC,IAAI,GAAG;gBAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;YACvB,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC5C,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACjC,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,0EAA0E;YAC1E,MAAM,GAAG,GAAG,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC1C,IAAI,GAAG;gBAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;YACvB,MAAM,EAAE,GAAG,wBAAwB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC7C,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACzB,CAAC;QACD;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,6FAA6F;AAC7F,iFAAiF;AACjF,MAAM,UAAU,kBAAkB,CAAC,IAAkB;IACnD,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IAC1D,OAAO,KAAK,CAAC;AACf,CAAC;AAED,2EAA2E;AAC3E,iEAAiE;AACjE,MAAM,UAAU,kBAAkB,CAAC,EAAU;IAC3C,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/C,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,SAAS,CAAC;IACtC,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,OAAO,CAAC;IACpC,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,OAAO,CAAC;IACtC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,sEAAsE;AACtE,yFAAyF;AACzF,MAAM,UAAU,0BAA0B,CAAC,IAAY;IACrD,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,EAAE,CAAC;IAClD,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,EAAE,CAAC;IAChD,OAAO,CAAC,CAAC,CAAC;AACZ,CAAC;AAED,yEAAyE;AACzE,8FAA8F;AAC9F,0FAA0F;AAC1F,MAAM,UAAU,qBAAqB,CAAC,GAA+C;IACnF,IAAI,GAAG,IAAI,IAAI;QAAE,OAAO,SAAS,CAAC;IAClC,+EAA+E;IAC/E,MAAM,IAAI,GAAG,GAAG,CAAC,OAA8C,CAAC;IAChE,IAAI,IAAI,EAAE,QAAQ,IAAK,IAAI,CAAC,QAAoC,CAAC,QAAQ;QAAE,OAAO,UAAU,CAAC;IAC7F,4DAA4D;IAC5D,IAAI,GAAG,CAAC,SAAS;QAAE,OAAO,OAAO,CAAC;IAClC,8DAA8D;IAC9D,IAAI,GAAG,CAAC,SAAS;QAAE,OAAO,WAAW,CAAC;IACtC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8FAA8F;AAC9F,mGAAmG;AACnG,sGAAsG;AACtG,2DAA2D;AAC3D,kGAAkG;AAClG,EAAE;AACF,uGAAuG;AACvG,mGAAmG;AACnG,oGAAoG;AACpG,iGAAiG;AACjG,6DAA6D;AAC7D,EAAE;AACF,iGAAiG;AACjG,gEAAgE;AAChE,MAAM,UAAU,qBAAqB,CAAC,EAAU,EAAE,IAAkB;IAClE,4FAA4F;IAC5F,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,EAAE,CAAC;IAClD,IAAI,kBAAkB,CAAC,EAAE,CAAC,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IAC/C,OAAO,uBAAuB,CAAC,EAAE,CAAC,CAAC;AACrC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"userAgentParse.d.ts","sourceRoot":"","sources":["../src/userAgentParse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAqBjE,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,gBAAgB,CAsB7F;AAMD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAYvD;AAID,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAiB1D"}
1
+ {"version":3,"file":"userAgentParse.d.ts","sourceRoot":"","sources":["../src/userAgentParse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAqBjE,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,gBAAgB,CA6B7F;AAMD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAYvD;AAiBD,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAiB1D"}
@@ -30,6 +30,14 @@ export function parseUserAgentFormFactor(ua, maxTouchPoints) {
30
30
  if (/iphone|ipod|android.*mobile|windows phone|blackberry|bb\d+|mobile safari/i.test(ua)) {
31
31
  return DeviceFormFactorPhone;
32
32
  }
33
+ // iPadOS in desktop mode reports a Macintosh UA with no iPad token, so the UA alone cannot tell it
34
+ // from a real Mac — touch is the only signal that can. Apple ships no touchscreen Mac (the Touch
35
+ // Bar reports zero touch points), so more than one touch point on a Macintosh UA means iPad. This
36
+ // has to run before the desktop branch, which would otherwise claim the UA on `macintosh` alone and
37
+ // silently call every desktop-mode iPad a desktop. The hint was already threaded into this
38
+ // signature for exactly this purpose and was simply never consulted.
39
+ if (maxTouchPoints > 1 && /macintosh|mac os x/i.test(ua))
40
+ return DeviceFormFactorTablet;
33
41
  // Desktop: known desktop OS tokens
34
42
  if (/win(?:dows)?nt|macintosh|mac os x|linux(?!.*android)|x11/i.test(ua))
35
43
  return DeviceFormFactorDesktop;
@@ -67,6 +75,19 @@ export function parseUserAgentOsName(ua) {
67
75
  }
68
76
  // Parses an OS version string from a user-agent string.
69
77
  // Returns a dotted version string, e.g. '14.0', '10.0', '13.5.1'. Returns '' when not found.
78
+ //
79
+ // This is the single OS-version extractor in the package: parseUserAgentVersion (the PlatformName
80
+ // vocabulary) delegates here rather than carrying its own copy of these patterns, which had already
81
+ // drifted — its variants required exactly one space where these accept any whitespace, so it returned
82
+ // '' for UAs this handles.
83
+ //
84
+ // FROZEN UA VALUES — these are ceilings, not readings, and no UA-string parser can do better:
85
+ // - Windows 11 reports `Windows NT 10.0`, identical to Windows 10. Only the UA-CH
86
+ // `platformVersion` hint (>= 13) distinguishes them, and this function takes no hints.
87
+ // - macOS has been frozen at `10_15_7` since Big Sur, so every later macOS reads as 10.15.7.
88
+ // - iPadOS in desktop mode reports the macOS version, not the iPadOS version, because it reports a
89
+ // Macintosh UA (see parseUserAgentFormFactor for how the device is still identified).
90
+ // Treat a value from this function as "at least this version", never as an exact one.
70
91
  export function parseUserAgentOsVersion(ua) {
71
92
  // Android: "Android 14.0" or "Android 9"
72
93
  const android = ua.match(/android\s+([\d.]+)/i);
@@ -1 +1 @@
1
- {"version":3,"file":"userAgentParse.js","sourceRoot":"","sources":["../src/userAgentParse.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"userAgentParse.js","sourceRoot":"","sources":["../src/userAgentParse.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "name": "@flighthq/useragent",
3
- "version": "0.3.0-next.906.07cea63",
3
+ "version": "0.3.0",
4
+ "author": "Joshua Granick and other contributors",
5
+ "license": "MIT",
4
6
  "repository": {
5
7
  "type": "git",
6
8
  "url": "https://github.com/flighthq/flight.git",
@@ -36,7 +38,7 @@
36
38
  "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
37
39
  },
38
40
  "dependencies": {
39
- "@flighthq/types": "0.3.0-next.906.07cea63"
41
+ "@flighthq/types": "0.3.0"
40
42
  },
41
43
  "devDependencies": {
42
44
  "typescript": "^5.3.0"
@@ -1,3 +1,5 @@
1
+ import type { PlatformName } from '@flighthq/types/contract';
2
+
1
3
  import {
2
4
  detectEndianness,
3
5
  parseUserAgentArch,
@@ -9,6 +11,7 @@ import {
9
11
  parseUserAgentRuntime,
10
12
  parseUserAgentVersion,
11
13
  } from './userAgent';
14
+ import { parseUserAgentOsVersion } from './userAgentParse';
12
15
 
13
16
  describe('detectEndianness', () => {
14
17
  it('returns a known canonical value', () => {
@@ -21,6 +24,41 @@ describe('detectEndianness', () => {
21
24
  });
22
25
  });
23
26
 
27
+ describe('legacy EdgeHTML', () => {
28
+ // Edge only became Chromium at Edg/79. The blink branch's `edg` alternative matches `Edge/18` too, so
29
+ // EdgeHTML was reported as blink — and a caller gating a Chromium workaround on that would apply it to
30
+ // a browser that never ran Chromium.
31
+ const LEGACY_EDGE =
32
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763';
33
+
34
+ it('is not reported as blink', () => {
35
+ expect(parseUserAgentEngine(LEGACY_EDGE)).toBe('unknown');
36
+ });
37
+
38
+ // The realistic UA carries a Chrome token, so before the fix this did not merely come back empty —
39
+ // it came back with CHROME's version, which is a wrong answer rather than a missing one.
40
+ it('does not report the embedded Chrome version as its engine version', () => {
41
+ const engine = parseUserAgentEngine(LEGACY_EDGE);
42
+ expect(parseUserAgentEngineVersion(LEGACY_EDGE, engine)).toBe('');
43
+ });
44
+
45
+ it('is still not blink without a Chrome token', () => {
46
+ expect(parseUserAgentEngine('Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 Edge/18.17763')).toBe('unknown');
47
+ });
48
+
49
+ // The discriminator is the trailing `e`, so the fix must not catch modern Edge or Edge on iOS.
50
+ it('leaves modern Chromium Edge reported as blink', () => {
51
+ const modern =
52
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0';
53
+ expect(parseUserAgentEngine(modern)).toBe('blink');
54
+ expect(parseUserAgentEngineVersion(modern, 'blink')).toBe('120.0.0.0');
55
+ });
56
+
57
+ it('leaves Edge on iOS reported as webkit', () => {
58
+ expect(parseUserAgentEngine('Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) EdgiOS/120.0')).toBe('webkit');
59
+ });
60
+ });
61
+
24
62
  describe('parseUserAgentArch', () => {
25
63
  it('detects arm64 from aarch64 token', () => {
26
64
  expect(parseUserAgentArch('Mozilla/5.0 (Linux; aarch64) AppleWebKit/537.36')).toBe('arm64');
@@ -112,6 +150,58 @@ describe('parseUserAgentEngine', () => {
112
150
  });
113
151
  });
114
152
 
153
+ describe('parseUserAgentEngine for Opera on iOS', () => {
154
+ const IOS_PREFIX = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 ';
155
+
156
+ it.each([
157
+ ['OPiOS', `${IOS_PREFIX}(KHTML, like Gecko) OPiOS/9.2.0.11256 Mobile/15E148 Safari/9537.53`],
158
+ ['OPT', `${IOS_PREFIX}(KHTML, like Gecko) Version/17.0 OPT/3.6.1 Mobile/15E148 Safari/604.1`],
159
+ ])('keeps the %s product token on WebKit', (_token, ua) => {
160
+ const engine = parseUserAgentEngine(ua);
161
+ expect(engine).toBe('webkit');
162
+ expect(parseUserAgentEngineVersion(ua, engine)).not.toBe('');
163
+ });
164
+ });
165
+
166
+ describe('parseUserAgentEngine on iOS', () => {
167
+ // Every browser on iOS/iPadOS runs on the system WebKit, whatever product token it advertises.
168
+ // Deciding the engine from the product token got EdgiOS wrong — it contains `edg`, so it was
169
+ // reported as blink, and then no `Edg/` token followed so the version came back empty.
170
+ const IOS_PREFIX = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) ';
171
+
172
+ it('reports webkit for Edge on iOS, which contains the blink-family token "edg"', () => {
173
+ const ua = `${IOS_PREFIX}EdgiOS/120.0.0.0 Mobile/15E148 Safari/605.1.15`;
174
+ expect(parseUserAgentEngine(ua)).toBe('webkit');
175
+ expect(parseUserAgentEngineVersion(ua, 'webkit')).not.toBe('');
176
+ });
177
+
178
+ it('reports webkit for Chrome and Firefox on iOS', () => {
179
+ expect(parseUserAgentEngine(`${IOS_PREFIX}CriOS/120.0.6099.119 Mobile/15E148 Safari/604.1`)).toBe('webkit');
180
+ expect(parseUserAgentEngine(`${IOS_PREFIX}FxiOS/121.0 Mobile/15E148 Safari/605.1.15`)).toBe('webkit');
181
+ });
182
+
183
+ it('reports webkit for an iPad UA', () => {
184
+ const ua =
185
+ 'Mozilla/5.0 (iPad; CPU OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/120.0.0.0 Mobile/15E148 Safari/604.1';
186
+ expect(parseUserAgentEngine(ua)).toBe('webkit');
187
+ });
188
+
189
+ it('still reports blink for desktop Chrome and Edge, which are not on iOS', () => {
190
+ const mac =
191
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
192
+ const win =
193
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0';
194
+ expect(parseUserAgentEngine(mac)).toBe('blink');
195
+ expect(parseUserAgentEngine(win)).toBe('blink');
196
+ expect(parseUserAgentEngineVersion(win, 'blink')).toBe('120.0.0.0');
197
+ });
198
+
199
+ it('still reports gecko for desktop Firefox', () => {
200
+ const ua = 'Mozilla/5.0 (X11; Linux x86_64; rv:120.0) Gecko/20100101 Firefox/120.0';
201
+ expect(parseUserAgentEngine(ua)).toBe('gecko');
202
+ });
203
+ });
204
+
115
205
  describe('parseUserAgentEngineVersion', () => {
116
206
  it('extracts Firefox version', () => {
117
207
  expect(
@@ -302,3 +392,45 @@ describe('parseUserAgentVersion', () => {
302
392
  expect(parseUserAgentVersion('', 'windows')).toBe('');
303
393
  });
304
394
  });
395
+
396
+ describe('parseUserAgentVersion shares one extractor with parseUserAgentOsVersion', () => {
397
+ // The two families each carried their own copy of the same four patterns, and the copies had
398
+ // drifted: this one required exactly one space where the other accepts any whitespace, so it
399
+ // returned '' for UAs the other parsed correctly. These pin the agreement, not just the fix.
400
+ const CASES: readonly (readonly [string, PlatformName, string])[] = [
401
+ ['Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36', 'android', '14'],
402
+ ['Mozilla/5.0 (Windows NT 10.0; Win64; x64)', 'windows', '10.0'],
403
+ ['Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)', 'macos', '10.15.7'],
404
+ ];
405
+
406
+ it('parses the whitespace variants that used to return empty', () => {
407
+ for (const [ua, name, expected] of CASES) {
408
+ expect(parseUserAgentVersion(ua, name)).toBe(expected);
409
+ }
410
+ });
411
+
412
+ it('agrees with parseUserAgentOsVersion on every case', () => {
413
+ for (const [ua, name] of CASES) {
414
+ expect(parseUserAgentVersion(ua, name)).toBe(parseUserAgentOsVersion(ua));
415
+ }
416
+ });
417
+
418
+ it('returns empty when the requested platform is not the one the UA describes', () => {
419
+ const win = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36';
420
+ expect(parseUserAgentVersion(win, 'ios')).toBe('');
421
+ expect(parseUserAgentVersion(win, 'macos')).toBe('');
422
+ expect(parseUserAgentVersion(win, 'windows')).toBe('10.0');
423
+ });
424
+
425
+ it('does not read a macOS version off an iPad UA that says "like Mac OS X"', () => {
426
+ const ipad = 'Mozilla/5.0 (iPad; CPU OS 17_4 like Mac OS X) AppleWebKit/605.1.15';
427
+ expect(parseUserAgentVersion(ipad, 'macos')).toBe('');
428
+ expect(parseUserAgentVersion(ipad, 'ios')).toBe('17.4');
429
+ });
430
+
431
+ it('still returns empty for linux and the web fallback', () => {
432
+ const linux = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36';
433
+ expect(parseUserAgentVersion(linux, 'linux')).toBe('');
434
+ expect(parseUserAgentVersion('some-native-host/1.0', 'web')).toBe('');
435
+ });
436
+ });
@@ -65,6 +65,46 @@ describe('parseUserAgentFormFactor', () => {
65
65
  });
66
66
  });
67
67
 
68
+ describe('parseUserAgentFormFactor desktop-mode iPad', () => {
69
+ // iPadOS "Request Desktop Website" (the default for iPad Safari since iPadOS 13) sends a Macintosh
70
+ // UA with no iPad token. The UA alone cannot distinguish it from a real Mac; touch can, because
71
+ // Apple ships no touchscreen Mac. The hint was already in the signature and was never consulted, so
72
+ // every desktop-mode iPad answered "Desktop".
73
+ const IPAD_DESKTOP_MODE =
74
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15';
75
+ const REAL_MAC =
76
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
77
+
78
+ it('classifies a touch-capable Macintosh UA as a tablet', () => {
79
+ expect(parseUserAgentFormFactor(IPAD_DESKTOP_MODE, 5)).toBe(DeviceFormFactorTablet);
80
+ });
81
+
82
+ it('still classifies a real Mac as a desktop', () => {
83
+ expect(parseUserAgentFormFactor(REAL_MAC, 0)).toBe(DeviceFormFactorDesktop);
84
+ });
85
+
86
+ it('does not guess from an unavailable touch hint', () => {
87
+ // -1 means "unavailable", not "zero touch points" — it must not tip the answer either way.
88
+ expect(parseUserAgentFormFactor(IPAD_DESKTOP_MODE, -1)).toBe(DeviceFormFactorDesktop);
89
+ });
90
+
91
+ it('needs more than one touch point, so a single-touch desktop digitiser stays a desktop', () => {
92
+ expect(parseUserAgentFormFactor(REAL_MAC, 1)).toBe(DeviceFormFactorDesktop);
93
+ });
94
+
95
+ it('leaves an iPhone a phone even though its UA also says "like Mac OS X"', () => {
96
+ const iphone =
97
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1';
98
+ expect(parseUserAgentFormFactor(iphone, 5)).toBe(DeviceFormFactorPhone);
99
+ });
100
+
101
+ it('leaves a classic iPad UA a tablet', () => {
102
+ const ipad =
103
+ 'Mozilla/5.0 (iPad; CPU OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1';
104
+ expect(parseUserAgentFormFactor(ipad, 5)).toBe(DeviceFormFactorTablet);
105
+ });
106
+ });
107
+
68
108
  describe('parseUserAgentOsName', () => {
69
109
  it('returns Android for Android UA', () => {
70
110
  expect(parseUserAgentOsName('Mozilla/5.0 (Linux; Android 14; Pixel 8)')).toBe('Android');