| 1 | import { |
| 2 | type BackgroundActivityProfile, |
| 3 | type BackgroundActivitySettings, |
| 4 | DEFAULT_BACKGROUND_ACTIVITY_PROFILE, |
| 5 | DEFAULT_AUTOMATIC_GIT_FETCH_INTERVAL, |
| 6 | DEFAULT_PROVIDER_HEALTH_REFRESH_INTERVAL, |
| 7 | type ServerSettings, |
| 8 | } from "@t3tools/contracts"; |
| 9 | import * as Duration from "effect/Duration"; |
| 10 | |
| 11 | export interface ResolvedBackgroundActivitySettings { |
| 12 | readonly profile: BackgroundActivityProfile; |
| 13 | readonly automaticGitFetchInterval: Duration.Duration; |
| 14 | readonly providerHealthRefreshInterval: Duration.Duration; |
| 15 | readonly hostPowerMonitorActiveInterval: Duration.Duration; |
| 16 | readonly hostPowerMonitorIdleInterval: Duration.Duration; |
| 17 | readonly idleClientTtl: Duration.Duration; |
| 18 | readonly pauseWhenHostLocked: boolean; |
| 19 | readonly pauseWhenHostLowPower: boolean; |
| 20 | readonly pauseWhenClientLowPower: boolean; |
| 21 | readonly pauseWhenOnBattery: boolean; |
| 22 | } |
| 23 | |
| 24 | const PRESET_SETTINGS: Record<BackgroundActivityProfile, ResolvedBackgroundActivitySettings> = { |
| 25 | performance: { |
| 26 | profile: "performance", |
| 27 | automaticGitFetchInterval: Duration.seconds(15), |
| 28 | providerHealthRefreshInterval: Duration.minutes(1), |
| 29 | hostPowerMonitorActiveInterval: Duration.seconds(30), |
| 30 | hostPowerMonitorIdleInterval: Duration.minutes(2), |
| 31 | idleClientTtl: Duration.seconds(45), |
| 32 | pauseWhenHostLocked: true, |
| 33 | pauseWhenHostLowPower: false, |
| 34 | pauseWhenClientLowPower: false, |
| 35 | pauseWhenOnBattery: false, |
| 36 | }, |
| 37 | balanced: { |
| 38 | profile: "balanced", |
| 39 | automaticGitFetchInterval: DEFAULT_AUTOMATIC_GIT_FETCH_INTERVAL, |
| 40 | providerHealthRefreshInterval: DEFAULT_PROVIDER_HEALTH_REFRESH_INTERVAL, |
| 41 | hostPowerMonitorActiveInterval: Duration.seconds(30), |
| 42 | hostPowerMonitorIdleInterval: Duration.minutes(5), |
| 43 | idleClientTtl: Duration.seconds(45), |
| 44 | pauseWhenHostLocked: true, |
| 45 | pauseWhenHostLowPower: true, |
| 46 | pauseWhenClientLowPower: true, |
| 47 | pauseWhenOnBattery: false, |
| 48 | }, |
| 49 | "battery-saver": { |
| 50 | profile: "battery-saver", |
| 51 | automaticGitFetchInterval: Duration.seconds(0), |
| 52 | providerHealthRefreshInterval: Duration.minutes(15), |
| 53 | hostPowerMonitorActiveInterval: Duration.minutes(1), |
| 54 | hostPowerMonitorIdleInterval: Duration.minutes(10), |
| 55 | idleClientTtl: Duration.seconds(45), |
| 56 | pauseWhenHostLocked: true, |
| 57 | pauseWhenHostLowPower: true, |
| 58 | pauseWhenClientLowPower: true, |
| 59 | pauseWhenOnBattery: true, |
| 60 | }, |
| 61 | }; |
| 62 | |
| 63 | export function getBackgroundActivityPresetSettings( |
| 64 | profile: BackgroundActivityProfile, |
| 65 | ): ResolvedBackgroundActivitySettings { |
| 66 | return PRESET_SETTINGS[profile]; |
| 67 | } |
| 68 | |
| 69 | export function getBackgroundActivityBaseProfile( |
| 70 | backgroundActivity: BackgroundActivitySettings, |
| 71 | ): BackgroundActivityProfile { |
| 72 | if (backgroundActivity.profile === "custom") { |
| 73 | return backgroundActivity.baseProfile ?? DEFAULT_BACKGROUND_ACTIVITY_PROFILE; |
| 74 | } |
| 75 | return backgroundActivity.profile; |
| 76 | } |
| 77 | |
| 78 | export function resolveBackgroundActivitySettings( |
| 79 | backgroundActivity: BackgroundActivitySettings, |
| 80 | ): ResolvedBackgroundActivitySettings { |
| 81 | const baseProfile = getBackgroundActivityBaseProfile(backgroundActivity); |
| 82 | const preset = PRESET_SETTINGS[baseProfile]; |
| 83 | const overrides = backgroundActivity.profile === "custom" ? backgroundActivity.overrides : {}; |
| 84 | return { |
| 85 | profile: baseProfile, |
| 86 | automaticGitFetchInterval: |
| 87 | overrides.automaticGitFetchInterval ?? preset.automaticGitFetchInterval, |
| 88 | providerHealthRefreshInterval: |
| 89 | overrides.providerHealthRefreshInterval ?? preset.providerHealthRefreshInterval, |
| 90 | hostPowerMonitorActiveInterval: |
| 91 | overrides.hostPowerMonitorActiveInterval ?? preset.hostPowerMonitorActiveInterval, |
| 92 | hostPowerMonitorIdleInterval: |
| 93 | overrides.hostPowerMonitorIdleInterval ?? preset.hostPowerMonitorIdleInterval, |
| 94 | idleClientTtl: overrides.idleClientTtl ?? preset.idleClientTtl, |
| 95 | pauseWhenHostLocked: overrides.pauseWhenHostLocked ?? preset.pauseWhenHostLocked, |
| 96 | pauseWhenHostLowPower: overrides.pauseWhenHostLowPower ?? preset.pauseWhenHostLowPower, |
| 97 | pauseWhenClientLowPower: overrides.pauseWhenClientLowPower ?? preset.pauseWhenClientLowPower, |
| 98 | pauseWhenOnBattery: overrides.pauseWhenOnBattery ?? preset.pauseWhenOnBattery, |
| 99 | }; |
| 100 | } |
| 101 | |
| 102 | function durationsEqual(a: Duration.Duration, b: Duration.Duration): boolean { |
| 103 | return Duration.toMillis(a) === Duration.toMillis(b); |
| 104 | } |
| 105 | |
| 106 | function resolvedSettingsEqual( |
| 107 | a: ResolvedBackgroundActivitySettings, |
| 108 | b: ResolvedBackgroundActivitySettings, |
| 109 | ): boolean { |
| 110 | return ( |
| 111 | durationsEqual(a.automaticGitFetchInterval, b.automaticGitFetchInterval) && |
| 112 | durationsEqual(a.providerHealthRefreshInterval, b.providerHealthRefreshInterval) && |
| 113 | durationsEqual(a.hostPowerMonitorActiveInterval, b.hostPowerMonitorActiveInterval) && |
| 114 | durationsEqual(a.hostPowerMonitorIdleInterval, b.hostPowerMonitorIdleInterval) && |
| 115 | durationsEqual(a.idleClientTtl, b.idleClientTtl) && |
| 116 | a.pauseWhenHostLocked === b.pauseWhenHostLocked && |
| 117 | a.pauseWhenHostLowPower === b.pauseWhenHostLowPower && |
| 118 | a.pauseWhenClientLowPower === b.pauseWhenClientLowPower && |
| 119 | a.pauseWhenOnBattery === b.pauseWhenOnBattery |
| 120 | ); |
| 121 | } |
| 122 | |
| 123 | export function normalizeBackgroundActivitySettings( |
| 124 | backgroundActivity: BackgroundActivitySettings, |
| 125 | ): BackgroundActivitySettings { |
| 126 | if (backgroundActivity.profile !== "custom") { |
| 127 | return { |
| 128 | schemaVersion: 1, |
| 129 | profile: backgroundActivity.profile, |
| 130 | overrides: {}, |
| 131 | }; |
| 132 | } |
| 133 | |
| 134 | const resolved = resolveBackgroundActivitySettings(backgroundActivity); |
| 135 | const profiles: ReadonlyArray<BackgroundActivityProfile> = [ |
| 136 | getBackgroundActivityBaseProfile(backgroundActivity), |
| 137 | "balanced", |
| 138 | "performance", |
| 139 | "battery-saver", |
| 140 | ]; |
| 141 | for (const profile of profiles) { |
| 142 | if (resolvedSettingsEqual(resolved, PRESET_SETTINGS[profile])) { |
| 143 | return { |
| 144 | schemaVersion: 1, |
| 145 | profile, |
| 146 | overrides: {}, |
| 147 | }; |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | const baseProfile = getBackgroundActivityBaseProfile(backgroundActivity); |
| 152 | const preset = PRESET_SETTINGS[baseProfile]; |
| 153 | const overrides: BackgroundActivitySettings["overrides"] = { |
| 154 | ...(!durationsEqual(resolved.automaticGitFetchInterval, preset.automaticGitFetchInterval) |
| 155 | ? { automaticGitFetchInterval: resolved.automaticGitFetchInterval } |
| 156 | : {}), |
| 157 | ...(!durationsEqual( |
| 158 | resolved.providerHealthRefreshInterval, |
| 159 | preset.providerHealthRefreshInterval, |
| 160 | ) |
| 161 | ? { providerHealthRefreshInterval: resolved.providerHealthRefreshInterval } |
| 162 | : {}), |
| 163 | ...(!durationsEqual( |
| 164 | resolved.hostPowerMonitorActiveInterval, |
| 165 | preset.hostPowerMonitorActiveInterval, |
| 166 | ) |
| 167 | ? { hostPowerMonitorActiveInterval: resolved.hostPowerMonitorActiveInterval } |
| 168 | : {}), |
| 169 | ...(!durationsEqual(resolved.hostPowerMonitorIdleInterval, preset.hostPowerMonitorIdleInterval) |
| 170 | ? { hostPowerMonitorIdleInterval: resolved.hostPowerMonitorIdleInterval } |
| 171 | : {}), |
| 172 | ...(!durationsEqual(resolved.idleClientTtl, preset.idleClientTtl) |
| 173 | ? { idleClientTtl: resolved.idleClientTtl } |
| 174 | : {}), |
| 175 | ...(resolved.pauseWhenHostLocked !== preset.pauseWhenHostLocked |
| 176 | ? { pauseWhenHostLocked: resolved.pauseWhenHostLocked } |
| 177 | : {}), |
| 178 | ...(resolved.pauseWhenHostLowPower !== preset.pauseWhenHostLowPower |
| 179 | ? { pauseWhenHostLowPower: resolved.pauseWhenHostLowPower } |
| 180 | : {}), |
| 181 | ...(resolved.pauseWhenClientLowPower !== preset.pauseWhenClientLowPower |
| 182 | ? { pauseWhenClientLowPower: resolved.pauseWhenClientLowPower } |
| 183 | : {}), |
| 184 | ...(resolved.pauseWhenOnBattery !== preset.pauseWhenOnBattery |
| 185 | ? { pauseWhenOnBattery: resolved.pauseWhenOnBattery } |
| 186 | : {}), |
| 187 | }; |
| 188 | |
| 189 | return { |
| 190 | schemaVersion: 1, |
| 191 | profile: "custom", |
| 192 | baseProfile, |
| 193 | overrides, |
| 194 | }; |
| 195 | } |
| 196 | |
| 197 | export function resolveServerBackgroundActivitySettings( |
| 198 | settings: ServerSettings, |
| 199 | ): ResolvedBackgroundActivitySettings { |
| 200 | const defaultBackgroundActivity: BackgroundActivitySettings = { |
| 201 | schemaVersion: 1, |
| 202 | profile: DEFAULT_BACKGROUND_ACTIVITY_PROFILE, |
| 203 | overrides: {}, |
| 204 | }; |
| 205 | const backgroundActivityIsDefault = |
| 206 | settings.backgroundActivity.profile === defaultBackgroundActivity.profile && |
| 207 | settings.backgroundActivity.baseProfile === undefined && |
| 208 | Object.keys(settings.backgroundActivity.overrides).length === 0; |
| 209 | const legacyProfile = settings.backgroundActivityProfile; |
| 210 | const hasLegacyOverrides = |
| 211 | legacyProfile !== DEFAULT_BACKGROUND_ACTIVITY_PROFILE || |
| 212 | Duration.toMillis(settings.automaticGitFetchInterval) !== |
| 213 | Duration.toMillis(DEFAULT_AUTOMATIC_GIT_FETCH_INTERVAL) || |
| 214 | Duration.toMillis(settings.providerHealthRefreshInterval) !== |
| 215 | Duration.toMillis(DEFAULT_PROVIDER_HEALTH_REFRESH_INTERVAL); |
| 216 | if (backgroundActivityIsDefault && hasLegacyOverrides) { |
| 217 | return resolveBackgroundActivitySettings({ |
| 218 | schemaVersion: 1, |
| 219 | profile: |
| 220 | Duration.toMillis(settings.automaticGitFetchInterval) === |
| 221 | Duration.toMillis( |
| 222 | getBackgroundActivityPresetSettings(legacyProfile).automaticGitFetchInterval, |
| 223 | ) && |
| 224 | Duration.toMillis(settings.providerHealthRefreshInterval) === |
| 225 | Duration.toMillis( |
| 226 | getBackgroundActivityPresetSettings(legacyProfile).providerHealthRefreshInterval, |
| 227 | ) |
| 228 | ? legacyProfile |
| 229 | : "custom", |
| 230 | baseProfile: legacyProfile, |
| 231 | overrides: { |
| 232 | ...(Duration.toMillis(settings.automaticGitFetchInterval) !== |
| 233 | Duration.toMillis( |
| 234 | getBackgroundActivityPresetSettings(legacyProfile).automaticGitFetchInterval, |
| 235 | ) |
| 236 | ? { automaticGitFetchInterval: settings.automaticGitFetchInterval } |
| 237 | : {}), |
| 238 | ...(Duration.toMillis(settings.providerHealthRefreshInterval) !== |
| 239 | Duration.toMillis( |
| 240 | getBackgroundActivityPresetSettings(legacyProfile).providerHealthRefreshInterval, |
| 241 | ) |
| 242 | ? { providerHealthRefreshInterval: settings.providerHealthRefreshInterval } |
| 243 | : {}), |
| 244 | }, |
| 245 | }); |
| 246 | } |
| 247 | return resolveBackgroundActivitySettings(settings.backgroundActivity); |
| 248 | } |
| 249 | |
| 250 | export function normalizeServerBackgroundActivitySettings( |
| 251 | settings: ServerSettings, |
| 252 | ): BackgroundActivitySettings { |
| 253 | const resolved = resolveServerBackgroundActivitySettings(settings); |
| 254 | return normalizeBackgroundActivitySettings({ |
| 255 | schemaVersion: 1, |
| 256 | profile: "custom", |
| 257 | baseProfile: resolved.profile, |
| 258 | overrides: { |
| 259 | automaticGitFetchInterval: resolved.automaticGitFetchInterval, |
| 260 | providerHealthRefreshInterval: resolved.providerHealthRefreshInterval, |
| 261 | hostPowerMonitorActiveInterval: resolved.hostPowerMonitorActiveInterval, |
| 262 | hostPowerMonitorIdleInterval: resolved.hostPowerMonitorIdleInterval, |
| 263 | idleClientTtl: resolved.idleClientTtl, |
| 264 | pauseWhenHostLocked: resolved.pauseWhenHostLocked, |
| 265 | pauseWhenHostLowPower: resolved.pauseWhenHostLowPower, |
| 266 | pauseWhenClientLowPower: resolved.pauseWhenClientLowPower, |
| 267 | pauseWhenOnBattery: resolved.pauseWhenOnBattery, |
| 268 | }, |
| 269 | }); |
| 270 | } |