1import {
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";
9import * as Duration from "effect/Duration";
10
11export 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
24const 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
63export function getBackgroundActivityPresetSettings(
64 profile: BackgroundActivityProfile,
65): ResolvedBackgroundActivitySettings {
66 return PRESET_SETTINGS[profile];
67}
68
69export 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
78export 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
102function durationsEqual(a: Duration.Duration, b: Duration.Duration): boolean {
103 return Duration.toMillis(a) === Duration.toMillis(b);
104}
105
106function 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
123export 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
197export 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
250export 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}