|  | 
|  | 1 | +import { getDevicesAndBrowsers } from "../../lib/device-cache"; | 
|  | 2 | +import { resolveVersion } from "./version-resolver"; | 
|  | 3 | +import { customFuzzySearch } from "../../lib/fuzzy"; | 
|  | 4 | +import { DesktopSearchArgs, DesktopEntry } from "./types"; | 
|  | 5 | + | 
|  | 6 | +export async function filterDesktop( | 
|  | 7 | +  args: DesktopSearchArgs, | 
|  | 8 | +): Promise<DesktopEntry> { | 
|  | 9 | +  const data = await getDevicesAndBrowsers("live"); | 
|  | 10 | +  const allEntries = getAllDesktopEntries(data); | 
|  | 11 | + | 
|  | 12 | +  // Filter OS | 
|  | 13 | +  const osList = filterByOS(allEntries, args.os); | 
|  | 14 | + | 
|  | 15 | +  // Filter browser | 
|  | 16 | +  const browserList = filterByBrowser(osList, args.browser, args.os); | 
|  | 17 | + | 
|  | 18 | +  // Resolve OS version | 
|  | 19 | +  const uniqueOSVersions = getUniqueOSVersions(browserList); | 
|  | 20 | +  const chosenOS = resolveOSVersion(args.os, args.osVersion, uniqueOSVersions); | 
|  | 21 | + | 
|  | 22 | +  // Filter entries based on chosen OS version | 
|  | 23 | +  const entriesForOS = filterByOSVersion(browserList, chosenOS); | 
|  | 24 | + | 
|  | 25 | +  // Resolve browser version | 
|  | 26 | +  const browserVersions = entriesForOS.map((e) => e.browser_version); | 
|  | 27 | +  const chosenBrowserVersion = resolveVersion( | 
|  | 28 | +    args.browserVersion, | 
|  | 29 | +    browserVersions, | 
|  | 30 | +  ); | 
|  | 31 | + | 
|  | 32 | +  // Find final entry | 
|  | 33 | +  const finalEntry = entriesForOS.find( | 
|  | 34 | +    (e) => e.browser_version === chosenBrowserVersion, | 
|  | 35 | +  ); | 
|  | 36 | +  if (!finalEntry) { | 
|  | 37 | +    throw new Error(`No entry for browser version "${args.browserVersion}".`); | 
|  | 38 | +  } | 
|  | 39 | + | 
|  | 40 | +  // Add notes if versions were adjusted | 
|  | 41 | +  addNotes(finalEntry, args, chosenOS, chosenBrowserVersion); | 
|  | 42 | + | 
|  | 43 | +  return finalEntry; | 
|  | 44 | +} | 
|  | 45 | + | 
|  | 46 | +function getAllDesktopEntries(data: any): DesktopEntry[] { | 
|  | 47 | +  return data.desktop.flatMap((plat: any) => | 
|  | 48 | +    plat.browsers.map((b: any) => ({ | 
|  | 49 | +      os: plat.os, | 
|  | 50 | +      os_version: plat.os_version, | 
|  | 51 | +      browser: b.browser, | 
|  | 52 | +      browser_version: b.browser_version, | 
|  | 53 | +    })), | 
|  | 54 | +  ); | 
|  | 55 | +} | 
|  | 56 | + | 
|  | 57 | +function filterByOS(entries: DesktopEntry[], os: string): DesktopEntry[] { | 
|  | 58 | +  const filtered = entries.filter((e) => e.os === os); | 
|  | 59 | +  if (!filtered.length) throw new Error(`No OS entries for "${os}".`); | 
|  | 60 | +  return filtered; | 
|  | 61 | +} | 
|  | 62 | + | 
|  | 63 | +function filterByBrowser( | 
|  | 64 | +  entries: DesktopEntry[], | 
|  | 65 | +  browser: string, | 
|  | 66 | +  os: string, | 
|  | 67 | +): DesktopEntry[] { | 
|  | 68 | +  const filtered = entries.filter((e) => e.browser === browser); | 
|  | 69 | +  if (!filtered.length) throw new Error(`No browser "${browser}" on ${os}.`); | 
|  | 70 | +  return filtered; | 
|  | 71 | +} | 
|  | 72 | + | 
|  | 73 | +function getUniqueOSVersions(entries: DesktopEntry[]): string[] { | 
|  | 74 | +  return Array.from(new Set(entries.map((e) => e.os_version))); | 
|  | 75 | +} | 
|  | 76 | + | 
|  | 77 | +function resolveOSVersion( | 
|  | 78 | +  os: string, | 
|  | 79 | +  requestedVersion: string, | 
|  | 80 | +  availableVersions: string[], | 
|  | 81 | +): string { | 
|  | 82 | +  if (os === "OS X") { | 
|  | 83 | +    return resolveMacOSVersion(requestedVersion, availableVersions); | 
|  | 84 | +  } else { | 
|  | 85 | +    // For Windows, use semantic versioning | 
|  | 86 | +    return resolveVersion(requestedVersion, availableVersions); | 
|  | 87 | +  } | 
|  | 88 | +} | 
|  | 89 | + | 
|  | 90 | +function resolveMacOSVersion(requested: string, available: string[]): string { | 
|  | 91 | +  if (requested === "latest") { | 
|  | 92 | +    return available[available.length - 1]; | 
|  | 93 | +  } else if (requested === "oldest") { | 
|  | 94 | +    return available[0]; | 
|  | 95 | +  } else { | 
|  | 96 | +    // Try fuzzy matching | 
|  | 97 | +    const fuzzy = customFuzzySearch( | 
|  | 98 | +      available.map((v) => ({ os_version: v })), | 
|  | 99 | +      ["os_version"], | 
|  | 100 | +      requested, | 
|  | 101 | +      1, | 
|  | 102 | +    ); | 
|  | 103 | +    const matched = fuzzy.length ? fuzzy[0].os_version : requested; | 
|  | 104 | + | 
|  | 105 | +    // Fallback if not valid | 
|  | 106 | +    return available.includes(matched) ? matched : available[0]; | 
|  | 107 | +  } | 
|  | 108 | +} | 
|  | 109 | + | 
|  | 110 | +function filterByOSVersion( | 
|  | 111 | +  entries: DesktopEntry[], | 
|  | 112 | +  osVersion: string, | 
|  | 113 | +): DesktopEntry[] { | 
|  | 114 | +  return entries.filter((e) => e.os_version === osVersion); | 
|  | 115 | +} | 
|  | 116 | + | 
|  | 117 | +function addNotes( | 
|  | 118 | +  entry: DesktopEntry, | 
|  | 119 | +  args: DesktopSearchArgs, | 
|  | 120 | +  resolvedOS: string, | 
|  | 121 | +  resolvedBrowser: string, | 
|  | 122 | +): void { | 
|  | 123 | +  if ( | 
|  | 124 | +    args.osVersion !== resolvedOS && | 
|  | 125 | +    args.osVersion !== "latest" && | 
|  | 126 | +    args.osVersion !== "oldest" | 
|  | 127 | +  ) { | 
|  | 128 | +    entry.notes = `Note: OS version ${args.osVersion} was not found. Using "${resolvedOS}" instead.`; | 
|  | 129 | +  } | 
|  | 130 | + | 
|  | 131 | +  if ( | 
|  | 132 | +    args.browserVersion !== resolvedBrowser && | 
|  | 133 | +    args.browserVersion !== "latest" && | 
|  | 134 | +    args.browserVersion !== "oldest" | 
|  | 135 | +  ) { | 
|  | 136 | +    if (!entry.notes) { | 
|  | 137 | +      entry.notes = `Note: `; | 
|  | 138 | +    } else { | 
|  | 139 | +      entry.notes += ` `; | 
|  | 140 | +    } | 
|  | 141 | +    entry.notes += `Browser version ${args.browserVersion} was not found. Using "${resolvedBrowser}" instead.`; | 
|  | 142 | +  } | 
|  | 143 | +} | 
0 commit comments