Skip to content
Source
Sign inSign up
Open History to see the last commit

DesktopUpdateMarker.ts

54 lines1.9 KB

1import { DESKTOP_UPDATE_IN_PROGRESS_TTL_MS } from "@t3tools/contracts";
2import * as Clock from "effect/Clock";
3import * as Context from "effect/Context";
4import * as Effect from "effect/Effect";
5import * as FileSystem from "effect/FileSystem";
6import * as Layer from "effect/Layer";
7import * as Option from "effect/Option";
8import * as Schema from "effect/Schema";
9​
10const UpdateMarker = Schema.fromJsonString(
11 Schema.Struct({ startedAt: Schema.Number, expiresAt: Schema.Number }),
12);
13​
14export class DesktopUpdateMarker extends Context.Service<
15 DesktopUpdateMarker,
16 {
17 /**
18 * Remaining SSH launch wait in ms, capped at this build's TTL.
19 * Read-only: a newer update may have just replaced the marker.
20 */
21 readonly remainingWait: (markerFile: string) => Effect.Effect<Option.Option<number>>;
22 }
23>()("t3/desktopUpdate/DesktopUpdateMarker") {}
24​
25const make = Effect.gen(function* () {
26 const fileSystem = yield* FileSystem.FileSystem;
27​
28 const remainingWait: DesktopUpdateMarker["Service"]["remainingWait"] = Effect.fn(
29 "DesktopUpdateMarker.remainingWait",
30 )(function* (markerFile) {
31 const marker = yield* fileSystem
32 .readFileString(markerFile)
33 .pipe(Effect.flatMap(Schema.decodeUnknownEffect(UpdateMarker)), Effect.option);
34 // Sampled after the read so a marker published mid-read is not seen as future-dated.
35 const now = yield* Clock.currentTimeMillis;
36 if (Option.isSome(marker)) {
37 const { startedAt, expiresAt } = marker.value;
38 if (
39 Number.isFinite(startedAt) &&
40 Number.isFinite(expiresAt) &&
41 startedAt <= now &&
42 expiresAt > now &&
43 expiresAt > startedAt
44 ) {
45 return Option.some(Math.min(expiresAt - now, DESKTOP_UPDATE_IN_PROGRESS_TTL_MS));
46 }
47 }
48 return Option.none();
49 });
50​
51 return DesktopUpdateMarker.of({ remainingWait });
52});
53​
54export const layer = Layer.effect(DesktopUpdateMarker, make);