11import { memoize } from "@smithy/property-provider" ;
22import type { DefaultsMode , ResolvedDefaultsMode } from "@smithy/smithy-client" ;
33import type { Provider } from "@smithy/types" ;
4- import bowser from "bowser" ;
54
65import { DEFAULTS_MODE_OPTIONS } from "./constants" ;
76
@@ -27,7 +26,7 @@ export const resolveDefaultsModeConfig = ({
2726 const mode = typeof defaultsMode === "function" ? await defaultsMode ( ) : defaultsMode ;
2827 switch ( mode ?. toLowerCase ( ) ) {
2928 case "auto" :
30- return Promise . resolve ( isMobileBrowser ( ) ? "mobile" : "standard" ) ;
29+ return Promise . resolve ( useMobileConfiguration ( ) ? "mobile" : "standard" ) ;
3130 case "mobile" :
3231 case "in-region" :
3332 case "cross-region" :
@@ -43,12 +42,49 @@ export const resolveDefaultsModeConfig = ({
4342 }
4443 } ) ;
4544
46- const isMobileBrowser = ( ) : boolean => {
47- const parsedUA =
48- typeof window !== "undefined" && window ?. navigator ?. userAgent
49- ? bowser . parse ( window . navigator . userAgent )
50- : undefined ;
51- const platform = parsedUA ?. platform ?. type ;
52- // Reference: https://github.com/lancedikson/bowser/blob/master/src/constants.js#L86
53- return platform === "tablet" || platform === "mobile" ;
45+ /**
46+ * @internal
47+ */
48+ type NavigatorAugment = {
49+ userAgentData ?: {
50+ mobile ?: boolean ;
51+ } ;
52+ connection ?: {
53+ effectiveType ?: "4g" | string ;
54+ rtt ?: number ;
55+ downlink ?: number ;
56+ } ;
57+ } ;
58+
59+ /**
60+ * The aim of the mobile detection function is not really to know whether the device is a mobile device.
61+ * This is emphasized in the modern guidance on browser detection that feature detection is correct
62+ * whereas UA "sniffing" is usually a mistake.
63+ *
64+ * So then, the underlying reason we are trying to detect a mobile device is not for any particular device feature,
65+ * but rather the implied network speed available to the program (we use it to set a default request timeout value).
66+ *
67+ * Therefore, it is better to use network speed related feature detection when available. This also saves
68+ * 20kb (minified) from the bowser dependency we were using.
69+ *
70+ * @internal
71+ * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Browser_detection_using_the_user_agent
72+ */
73+ const useMobileConfiguration = ( ) : boolean => {
74+ const navigator = window ?. navigator as ( typeof window . navigator & NavigatorAugment ) | undefined ;
75+ if ( navigator ?. connection ) {
76+ // https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType
77+ // The maximum will report as 4g, regardless of 5g or further developments.
78+ const { effectiveType, rtt, downlink } = navigator ?. connection ;
79+ const slow =
80+ ( typeof effectiveType === "string" && effectiveType !== "4g" ) || Number ( rtt ) > 100 || Number ( downlink ) < 10 ;
81+ if ( slow ) {
82+ return true ;
83+ }
84+ }
85+
86+ // without the networkInformation object, we use the userAgentData or touch feature detection as a proxy.
87+ return (
88+ navigator ?. userAgentData ?. mobile || ( typeof navigator ?. maxTouchPoints === "number" && navigator ?. maxTouchPoints > 1 )
89+ ) ;
5490} ;
0 commit comments