(ua: string)
| 155 | } |
| 156 | |
| 157 | const parse = (ua: string): UAParser.IResult => { |
| 158 | // Check cache first |
| 159 | const cached = parseCache.get(ua); |
| 160 | if (cached) { |
| 161 | return cached; |
| 162 | } |
| 163 | |
| 164 | const parser = new UAParser(ua); |
| 165 | let res = parser.getResult(); |
| 166 | |
| 167 | // Some user agents are not detected correctly by ua-parser-js |
| 168 | // Doing some extra checks for ios |
| 169 | if (!res.device.model && !res.os.name) { |
| 170 | const iphone = isIphone(ua); |
| 171 | if (iphone) { |
| 172 | const result = { |
| 173 | ...res, |
| 174 | device: { |
| 175 | ...res.device, |
| 176 | model: iphone.model, |
| 177 | vendor: 'Apple', |
| 178 | }, |
| 179 | os: { |
| 180 | ...res.os, |
| 181 | name: iphone.os, |
| 182 | version: iphone.osVersion, |
| 183 | }, |
| 184 | }; |
| 185 | parseCache.set(ua, result); |
| 186 | return result; |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | // Mozilla/5.0 (iPad; iPadOS 18_0; like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/18.0 |
| 191 | if (res.device.model === 'iPad' && !res.os.version) { |
| 192 | const osVersion = ua.match(IPAD_OS_VERSION_REGEX); |
| 193 | if (osVersion) { |
| 194 | res = { |
| 195 | ...res, |
| 196 | os: { |
| 197 | ...res.os, |
| 198 | version: osVersion[1]!.replace(/_/g, '.'), |
| 199 | }, |
| 200 | }; |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | // Extract app-style UA info (e.g., "Model=Redmi Note 8 Pro; Manufacturer=Xiaomi") |
| 205 | // This handles UAs from mobile apps that include device info in a custom format |
| 206 | const appInfo = extractAppStyleInfo(ua); |
| 207 | if (appInfo.model || appInfo.manufacturer) { |
| 208 | const model = res.device.model || appInfo.model; |
| 209 | const vendor = |
| 210 | res.device.vendor || appInfo.manufacturer || detectBrand(ua, model); |
| 211 | |
| 212 | res = { |
| 213 | ...res, |
| 214 | device: { |
no test coverage detected