Files
wehub-resource-sync 869b84f27c
Build / build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:29:30 +08:00

505 lines
18 KiB
Swift

//
// Conditions.swift
// PureMac
//
// Per-app matching rules and system-level skip conditions for the heuristic scan engine.
// These define edge cases where bundle ID and app name matching alone is insufficient.
//
import Foundation
// MARK: - AppCondition
/// Defines per-app overrides for the heuristic file matcher.
///
/// Some apps use naming conventions that collide with other apps (e.g. "Xcode" vs "Xcodes"),
/// or scatter files under unexpected names. This struct lets the scanner include additional
/// search terms, exclude false positives, and force-include/exclude specific filesystem paths.
struct AppCondition: Codable {
let bundleID: String
let includeTerms: [String]
let excludeTerms: [String]
let forceIncludePaths: [URL]?
let forceExcludePaths: [URL]?
init(
bundleID: String,
includeTerms: [String],
excludeTerms: [String],
forceIncludePaths: [String]? = nil,
forceExcludePaths: [String]? = nil
) {
self.bundleID = bundleID.normalizedForMatching()
self.includeTerms = includeTerms.map { $0.normalizedForMatching() }
self.excludeTerms = excludeTerms.map { $0.normalizedForMatching() }
self.forceIncludePaths = forceIncludePaths?.compactMap { path in
let url = URL(fileURLWithPath: path)
return FileManager.default.fileExists(atPath: url.path) ? url : nil
}
self.forceExcludePaths = forceExcludePaths?.compactMap { path in
let url = URL(fileURLWithPath: path)
return FileManager.default.fileExists(atPath: url.path) ? url : nil
}
}
}
// MARK: - SkipCondition
/// Defines prefixes and paths that should be skipped during scanning.
///
/// `skipPrefixes` - Normalized filename prefixes that are always skipped (e.g. system plists).
/// `allowPrefixes` - Exceptions to skipPrefixes for Apple apps we DO want to scan.
/// `skipPaths` - Absolute paths that should never appear in scan results.
struct SkipCondition {
let skipPrefixes: [String]
let allowPrefixes: [String]
let skipPaths: [String]
}
// MARK: - App Conditions Database
/// Per-app matching overrides.
/// Each entry handles a specific app whose files cannot be reliably found by bundle ID alone.
let appConditions: [AppCondition] = [
// ---------------------------------------------------------------
// Apple Developer Tools
// ---------------------------------------------------------------
// Xcode: includes simulator containers, DerivedData markers, and Apple DT prefixes.
// Must exclude Xcodes.app (third-party Xcode version manager) and Xcode cleaner utilities.
AppCondition(
bundleID: "com.apple.dt.xcode",
includeTerms: ["com.apple.dt", "xcode", "simulator"],
excludeTerms: [
"com.robotsandpencils.xcodesapp",
"com.xcodesorg.xcodesapp",
"com.oneminutegames.xcodecleaner",
"io.hyperapp.xcodecleaner",
"available-xcodes",
"xcodes",
"cleaner for xcode"
],
forceIncludePaths: [
"\(home)/Library/Containers/com.apple.iphonesimulator.ShareExtension"
]
),
// Xcodes.app (robotsandpencils variant)
AppCondition(
bundleID: "com.robotsandpencils.xcodesapp",
includeTerms: [],
excludeTerms: [
"com.apple.dt.xcode",
"com.oneminutegames.xcodecleaner",
"io.hyperapp.xcodecleaner"
]
),
// Xcodes.app (xcodesorg variant)
AppCondition(
bundleID: "com.xcodesorg.xcodesapp",
includeTerms: [],
excludeTerms: [
"com.apple.dt.xcode",
"com.oneminutegames.xcodecleaner",
"io.hyperapp.xcodecleaner"
]
),
// Xcode Cleaner (hyperapp)
AppCondition(
bundleID: "io.hyperapp.xcodecleaner",
includeTerms: [],
excludeTerms: [
"com.robotsandpencils.xcodesapp",
"com.oneminutegames.xcodecleaner",
"com.apple.dt.xcode",
"xcodes.json"
]
),
// ---------------------------------------------------------------
// Communication & Video Conferencing
// ---------------------------------------------------------------
// Zoom: uses "us.zoom.xos" bundle but files are often named just "zoom".
AppCondition(
bundleID: "us.zoom.xos",
includeTerms: ["zoom"],
excludeTerms: []
),
// Microsoft Teams: exclude general Office shared frameworks.
AppCondition(
bundleID: "com.microsoft.teams2",
includeTerms: [],
excludeTerms: ["office"]
),
// ---------------------------------------------------------------
// Web Browsers
// ---------------------------------------------------------------
// Brave Browser
AppCondition(
bundleID: "com.brave.browser",
includeTerms: ["brave"],
excludeTerms: []
),
// Google Chrome: include "google" and "chrome" but exclude iTerm's chromefeaturestate
// and unrelated "monochrome" matches.
AppCondition(
bundleID: "com.google.chrome",
includeTerms: ["google", "chrome"],
excludeTerms: ["iterm", "chromefeaturestate", "monochrome"]
),
// Microsoft Edge: exclude other Microsoft products that share the "com.microsoft" prefix.
AppCondition(
bundleID: "com.microsoft.edgemac",
includeTerms: [],
excludeTerms: ["vscode", "rdc", "appcenter", "office", "oneauth"]
),
// Mozilla Firefox (release)
AppCondition(
bundleID: "org.mozilla.firefox",
includeTerms: ["firefox"],
excludeTerms: ["thunderbird"]
),
// Mozilla Firefox Nightly
AppCondition(
bundleID: "org.mozilla.firefox.nightly",
includeTerms: ["mozilla", "firefox"],
excludeTerms: ["thunderbird"]
),
// Mozilla Thunderbird
AppCondition(
bundleID: "org.mozilla.thunderbird",
includeTerms: [],
excludeTerms: ["firefox"]
),
// Arc Browser: uses Firestore for sync; force-include its App Support and Caches folders.
AppCondition(
bundleID: "company.thebrowser.Browser",
includeTerms: ["firestore"],
excludeTerms: [],
forceIncludePaths: [
"\(home)/Library/Application Support/Arc/",
"\(home)/Library/Caches/Arc/"
]
),
// ---------------------------------------------------------------
// Developer Tools & IDEs
// ---------------------------------------------------------------
// VS Code: force-include the "Code" support directory.
// Must exclude VS Code Insiders to prevent cross-contamination.
AppCondition(
bundleID: "com.microsoft.VSCode",
includeTerms: ["vscode"],
excludeTerms: ["vscodeinsiders", "insiders"],
forceIncludePaths: [
"\(home)/Library/Application Support/Code/"
]
),
// VS Code Insiders: separate from stable VS Code.
AppCondition(
bundleID: "com.microsoft.VSCodeInsiders",
includeTerms: ["vscodeinsiders", "insiders"],
excludeTerms: [],
forceIncludePaths: [
"\(home)/Library/Application Support/Code - Insiders/"
]
),
// GitHub Desktop: uses "comgithubelectron" in some legacy cache paths.
AppCondition(
bundleID: "com.github.githubclient",
includeTerms: ["comgithubelectron"],
excludeTerms: []
),
// JetBrains IDEs (IntelliJ, PyCharm, WebStorm, etc.): share common directories.
// The bundle ID prefix "jetbrains" matches all JetBrains products.
AppCondition(
bundleID: "jetbrains",
includeTerms: ["jcef"],
excludeTerms: [],
forceIncludePaths: [
"\(home)/Library/Application Support/JetBrains/",
"\(home)/Library/Caches/JetBrains/",
"\(home)/Library/Logs/JetBrains/"
]
),
// Native Instruments (Native Access, Kontakt, etc.)
AppCondition(
bundleID: "com.native-instruments.nativeaccess",
includeTerms: ["comnative", "nativeinstruments"],
excludeTerms: []
),
// ---------------------------------------------------------------
// Productivity & Utilities
// ---------------------------------------------------------------
// Logi Options+: "logi" prefix collides with "login" and "logic".
AppCondition(
bundleID: "com.logi.optionsplus",
includeTerms: ["logi", "logipluginservice"],
excludeTerms: ["login", "logic"]
),
// 1Password: uses shared Chromium-based browser engine paths.
AppCondition(
bundleID: "com.1password.1password",
includeTerms: ["waveboxapp", "sidekick"],
excludeTerms: []
),
// Stats (system monitor): exclude "video" to avoid matching video-stats plists.
AppCondition(
bundleID: "eu.exelban.stats",
includeTerms: [],
excludeTerms: ["video"]
),
// BatteryToolkit: plist prefix uses concatenated form "memhaeuser".
AppCondition(
bundleID: "me.mhaeuser.BatteryToolkit",
includeTerms: ["memhaeuser"],
excludeTerms: []
),
// Okta Verify
AppCondition(
bundleID: "com.okta.mobile",
includeTerms: ["okta"],
excludeTerms: []
),
// ---------------------------------------------------------------
// Virtualization & Remote Access
// ---------------------------------------------------------------
// BlueStacks: uses interprocess communication files with non-obvious names.
AppCondition(
bundleID: "com.now.gg.BlueStacks",
includeTerms: ["bst_boost_interprocess"],
excludeTerms: []
),
// StrongDM: Electron app with "sdm" bundle but files named "strongdm".
AppCondition(
bundleID: "com.electron.sdm",
includeTerms: ["strongdm"],
excludeTerms: []
),
// ---------------------------------------------------------------
// Social & Messaging
// ---------------------------------------------------------------
// Facebook Archon (Workplace): includes login helper with different naming.
AppCondition(
bundleID: "com.facebook.archon.developerid",
includeTerms: ["archon.loginhelper"],
excludeTerms: []
),
]
// MARK: - Skip Conditions
/// System-level skip rules applied globally during scanning.
/// Prevents the scanner from matching macOS system files, the Trash, and known false positives.
let skipConditions: [SkipCondition] = [
SkipCondition(
skipPrefixes: [
"mobiledocuments",
"reminders",
"dsstore",
"comapplepasswordmanager"
],
allowPrefixes: [
"comappleconfigurator",
"comappledt",
"comappleiwork",
"comapplesfsymbols",
"comappletestflight",
"comapplesharedfilelist",
"comapplelssharedfilelist"
],
skipPaths: [
"\(home)/.Trash",
"/Library/SystemExtensions",
"/System/Volumes/Preboot/Cryptexes/App/System/Library/CoreServices/PasswordManagerBrowserExtensionHelper.app/Contents/MacOS/PasswordManagerBrowserExtensionHelper",
"\(home)/Library/Application Support/Chromium/NativeMessagingHosts/com.apple.passwordmanager.json",
"\(home)/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.apple.passwordmanager.json"
] + highRiskHomeDotPaths
)
]
/// Home-directory dotdirs that must never be matched as app artifacts no
/// matter how short or coincidental the app name is. Adding an entry here is
/// the correct fix for "removing webapp X also deleted my CLI tool X config"
/// (see issues #50 / #51).
let highRiskHomeDotPaths: [String] = [
"\(home)/.claude",
"\(home)/.ssh",
"\(home)/.aws",
"\(home)/.gnupg",
"\(home)/.gpg",
"\(home)/.kube",
"\(home)/.docker",
"\(home)/.config",
"\(home)/.git",
"\(home)/.gitconfig",
"\(home)/.git-credentials",
"\(home)/.netrc",
"\(home)/.npmrc",
"\(home)/.yarnrc",
"\(home)/.pnpmrc",
"\(home)/.pip",
"\(home)/.pypirc",
"\(home)/.rbenv",
"\(home)/.pyenv",
"\(home)/.nvm",
"\(home)/.cargo",
"\(home)/.rustup",
"\(home)/.gem",
"\(home)/.local",
"\(home)/.password-store",
"\(home)/.mozilla",
"\(home)/.wine",
"\(home)/.vscode",
"\(home)/.vim",
"\(home)/.viminfo",
"\(home)/.zshrc",
"\(home)/.zsh_history",
"\(home)/.bash_history",
"\(home)/.bashrc",
"\(home)/.bash_profile",
"\(home)/.profile",
]
// MARK: - Deep Search Exclusions
/// Library subdirectories excluded from depth=2 (deep) search.
/// These are macOS system directories that never contain third-party app files.
/// Searching them wastes time and produces false positives.
let skipDeepSearch: Set<String> = [
// Core System
"Apple", "Audio", "Bluetooth", "ColorSync", "Components", "CoreAnalytics",
"CoreMediaIO", "DirectoryServices", "Filesystems", "GPUBundles", "Graphics",
"KernelCollections", "OSAnalytics", "OpenDirectory", "Sandbox", "Security",
"SystemExtensions", "SystemMigration", "SystemProfiler", "StagedDriverExtensions",
"StagedExtensions", "StartupItems",
// User Data & System Services
"Accessibility", "Accounts", "AppleMediaServices", "Assistant", "Assistants",
"Autosave Information", "Biome", "Calendars", "CallServices", "CloudStorage",
"Contacts", "Cookies", "DataAccess", "DataDeliveryServices", "DoNotDisturb",
"DuetExpertCenter", "Finance", "FinanceBackup", "FrontBoard", "GameKit",
"GroupContainersAlias", "HomeKit", "IdentityServices", "IntelligencePlatform",
"Intents", "KeyboardServices", "LanguageModeling", "LockdownMode", "Mail",
"MediaAnalysis", "Messages", "Metadata", "Mobile Documents", "MobileDevice",
"News", "Passes", "PersonalizationPortrait", "Photos", "PrivateCloudCompute",
"Reminders", "ResponseKit", "Safari", "SafariSafeBrowsing", "SafariSandboxBroker",
"ScreenRecordings", "StatusKit", "Suggestions", "SyncedPreferences", "Translation",
"UnifiedAssetFramework", "Weather", "homeenergyd", "studentd",
// Development & System Tools
"Developer", "Perl", "Ruby", "Java", "Python", "Catacomb", "InstallerSandboxes",
"Trial", "Updates", "Staging", "ContainerManager", "Daemon Containers",
// Additional System Directories
"ColorPickers", "Colors", "Compositions", "Contextual Menu Items", "Documentation",
"DriverExtensions", "Favorites", "FontCollections", "Fonts", "Image Capture",
"Input Methods", "Jupyter", "Keyboard", "Keyboard Layouts", "Keychains",
"Managed Preferences", "PDF Services", "Printers", "QuickLook", "Receipts",
"Screen Savers", "ScriptingAdditions", "Scripts", "Sharing", "Shortcuts",
"Sounds", "Speech", "Spelling", "Spotlight", "User Pictures", "User Template",
"Video", "WebServer", "Workflows",
// Apple Service Bundles (com.apple.*)
"com.apple.AppleMediaServices", "com.apple.WatchListKit", "com.apple.aiml.instrumentation",
"com.apple.appleaccountd", "com.apple.bluetooth.services.cloud", "com.apple.bluetoothuser",
"com.apple.familycircled", "com.apple.iTunesCloud", "com.apple.internal.ck",
// iCloud & Sync Infrastructure
"com.apple.cloudpaird", "com.apple.iCloudHelper", "com.apple.nsurlsessiond",
"com.apple.sbd", "com.apple.touristd",
// System Agents & Daemons
"com.apple.AMPLibraryAgent", "com.apple.bird", "com.apple.coreduetd",
"com.apple.homed", "com.apple.photoanalysisd", "com.apple.routined",
"com.apple.siriactionsd", "com.apple.suggestd",
// Frameworks & Runtime (never user-facing)
"com.apple.AppStoreComponents", "com.apple.ScreenTimeUI",
"com.apple.TelephonyUtilities", "com.apple.WebInspector"
]
// MARK: - Reverse Search Exclusions
/// Normalized prefixes of files and folders to skip during orphan (reverse) search.
/// These are macOS system items, Apple daemons, and infrastructure that should never
/// be flagged as orphaned third-party app files.
let skipReverse: [String] = [
// Apple & System
"apple", "temporary", "btserver", "proapps", "scripteditor", "ilife",
"livefsd", "siritoday", "addressbook", "animoji", "appstore",
"askpermission", "callhistory", "clouddocs", "diskimages", "dock",
"facetime", "fileprovider", "instruments", "knowledge", "mobilesync",
"syncservices", "homeenergyd", "icloud", "icdd", "networkserviceproxy",
"familycircle", "geoservices", "installation", "passkit",
"sharedimagecache", "desktop", "mbuseragent", "swiftpm", "baseband",
"coresimulator", "photoslegacyupgrade", "photosupgrade", "siritts",
"ipod", "globalpreferences",
// Analytics & Telemetry
"apmanalytics", "apmexperiment", "avatarcache", "byhost",
"contextstoreagent", "mobilemeaccounts", "mobiledocuments", "mobile",
"intentbuilderc", "loginwindow", "momc", "replayd", "sharedfilelistd",
// Build Tools & Compilers
"clang", "audiocomponent", "csexattrcryptoservice",
"livetranscriptionagent", "sandboxhelper", "statuskitagent",
// System Daemons
"betaenrollmentd", "contentlinkingd", "diagnosticextensionsd", "gamed",
"heard", "homed", "itunescloudd", "lldb", "mds", "mediaanalysisd",
"metrickitd", "mobiletimerd", "proactived", "ptpcamerad", "studentd",
"talagent", "watchlistd", "apptranslocation", "xcrun",
// Generic Infrastructure
"ds_store", "caches", "crashreporter", "trash",
// PureMac itself (never flag our own files)
"puremac",
// Common SDKs and Shared Components
"amsdatamigratortool", "arfilecache", "assistant", "chromium",
"cloudkit", "webkit", "databases", "diagnostic", "cache", "gamekit",
"homebrew", "logi", "microsoft", "mozilla", "sync", "google",
"sentinel", "hexnode", "sentry", "tvappservices", "reminders", "pbs",
"notarytool", "differentialprivacy", "storeassetd", "webpush",
"storedownloadd", "fsck", "crash", "python", "discrecording",
"photossearch", "pylint", "jamf", "scopedbookmarkagent", "anonymous",
"identifier", "isolated", "nobackup", "privacypreservingmeasurement",
"symbols", "stickersd", "privatecloudcomputed", "tipsd",
"controlcenter", "contactsd", "staticcheck", "index", "segment",
"sparkle", "summaryevents", "launchdarkly", "identityservicesd",
"embeddedbinaryvalidationutility", "aaprofilepicture", "minilauncher",
"jna", "automator", "locationaccessstored", "spotlight", "cef"
]