| 1 | import type { |
| 2 | AuthClientMetadataDeviceType, |
| 3 | AuthClientPresentationMetadata, |
| 4 | ClientOs, |
| 5 | DesktopBridge, |
| 6 | } from "@t3tools/contracts"; |
| 7 | |
| 8 | interface BrowserIdentity { |
| 9 | readonly userAgent: string; |
| 10 | readonly platform: string; |
| 11 | readonly maxTouchPoints: number; |
| 12 | } |
| 13 | |
| 14 | function clientOsFromElectronPlatform(platform: string | undefined): ClientOs { |
| 15 | switch (platform) { |
| 16 | case "darwin": |
| 17 | return "macOS"; |
| 18 | case "win32": |
| 19 | return "Windows"; |
| 20 | case "linux": |
| 21 | return "Linux"; |
| 22 | default: |
| 23 | return platform ? "other" : "unknown"; |
| 24 | } |
| 25 | } |
| 26 | |
| 27 | function isIpadosDesktopUserAgent(identity: BrowserIdentity): boolean { |
| 28 | return ( |
| 29 | /macintosh/i.test(identity.userAgent) && |
| 30 | /mac/i.test(identity.platform) && |
| 31 | identity.maxTouchPoints > 1 |
| 32 | ); |
| 33 | } |
| 34 | |
| 35 | export function browserClientOs(identity: BrowserIdentity): ClientOs { |
| 36 | const userAgent = identity.userAgent; |
| 37 | if (userAgent.trim() === "") return "unknown"; |
| 38 | if (/iphone|ipad|ipod/i.test(userAgent) || isIpadosDesktopUserAgent(identity)) return "iOS"; |
| 39 | if (/android/i.test(userAgent)) return "Android"; |
| 40 | if (/cros/i.test(userAgent)) return "ChromeOS"; |
| 41 | if (/windows/i.test(userAgent)) return "Windows"; |
| 42 | if (/macintosh|mac os x/i.test(userAgent)) return "macOS"; |
| 43 | if (/linux|x11/i.test(userAgent)) return "Linux"; |
| 44 | return "other"; |
| 45 | } |
| 46 | |
| 47 | export function browserDeviceType(identity: BrowserIdentity): AuthClientMetadataDeviceType { |
| 48 | const userAgent = identity.userAgent; |
| 49 | if (userAgent.trim() === "") return "unknown"; |
| 50 | if ( |
| 51 | /ipad|tablet|kindle|silk/i.test(userAgent) || |
| 52 | (/android/i.test(userAgent) && !/mobile/i.test(userAgent)) || |
| 53 | isIpadosDesktopUserAgent(identity) |
| 54 | ) { |
| 55 | return "tablet"; |
| 56 | } |
| 57 | if (/iphone|ipod|android.+mobile|mobile/i.test(userAgent)) return "mobile"; |
| 58 | return "desktop"; |
| 59 | } |
| 60 | |
| 61 | export function clientPresentationMetadata(input: { |
| 62 | readonly appVersion: string; |
| 63 | readonly identity: BrowserIdentity; |
| 64 | readonly desktopBridge: Pick<DesktopBridge, "getClientPlatform"> | undefined; |
| 65 | }): AuthClientPresentationMetadata { |
| 66 | if (input.desktopBridge !== undefined) { |
| 67 | return { |
| 68 | label: "The Knife Desktop", |
| 69 | deviceType: "desktop", |
| 70 | os: clientOsFromElectronPlatform(input.desktopBridge.getClientPlatform?.()), |
| 71 | surface: "desktop", |
| 72 | ...(input.appVersion === "0.0.0" ? {} : { appVersion: input.appVersion }), |
| 73 | }; |
| 74 | } |
| 75 | |
| 76 | return { |
| 77 | label: "The Knife Web", |
| 78 | deviceType: browserDeviceType(input.identity), |
| 79 | os: browserClientOs(input.identity), |
| 80 | surface: "web", |
| 81 | ...(input.appVersion === "0.0.0" ? {} : { appVersion: input.appVersion }), |
| 82 | }; |
| 83 | } |