| 1 | import { EnvironmentId, type OrchestrationV2ArchivedShellSnapshot } from "@t3tools/contracts"; |
| 2 | import * as Arr from "effect/Array"; |
| 3 | import { pipe } from "effect/Function"; |
| 4 | import * as Option from "effect/Option"; |
| 5 | import * as Order from "effect/Order"; |
| 6 | import { AsyncResult, Atom } from "effect/reactivity"; |
| 7 | |
| 8 | export interface ArchivedSnapshotEntry { |
| 9 | readonly environmentId: EnvironmentId; |
| 10 | readonly snapshot: OrchestrationV2ArchivedShellSnapshot; |
| 11 | } |
| 12 | |
| 13 | export interface ArchivedThreadSnapshotsState { |
| 14 | readonly snapshots: ReadonlyArray<ArchivedSnapshotEntry>; |
| 15 | readonly error: string | null; |
| 16 | readonly isLoading: boolean; |
| 17 | } |
| 18 | |
| 19 | const ARCHIVED_THREADS_ENVIRONMENT_KEY_SEPARATOR = "\u001f"; |
| 20 | const environmentIdOrder = Order.String as Order.Order<EnvironmentId>; |
| 21 | |
| 22 | export function makeArchivedThreadsEnvironmentKey( |
| 23 | environmentIds: ReadonlyArray<EnvironmentId>, |
| 24 | ): string { |
| 25 | return pipe(environmentIds, Arr.sort(environmentIdOrder), (sortedEnvironmentIds) => |
| 26 | sortedEnvironmentIds.join(ARCHIVED_THREADS_ENVIRONMENT_KEY_SEPARATOR), |
| 27 | ); |
| 28 | } |
| 29 | |
| 30 | export function parseArchivedThreadsEnvironmentKey(key: string): ReadonlyArray<EnvironmentId> { |
| 31 | if (key.length === 0) { |
| 32 | return []; |
| 33 | } |
| 34 | return pipe( |
| 35 | key.split(ARCHIVED_THREADS_ENVIRONMENT_KEY_SEPARATOR), |
| 36 | Arr.map((environmentId) => EnvironmentId.make(environmentId)), |
| 37 | ); |
| 38 | } |
| 39 | |
| 40 | export function createArchivedThreadSnapshotsAtomFamily<E>(options: { |
| 41 | readonly getSnapshotAtom: ( |
| 42 | environmentId: EnvironmentId, |
| 43 | ) => Atom.Atom<AsyncResult.AsyncResult<OrchestrationV2ArchivedShellSnapshot, E>>; |
| 44 | readonly labelPrefix: string; |
| 45 | }) { |
| 46 | return Atom.family((environmentKey: string) => |
| 47 | Atom.make((get): ArchivedThreadSnapshotsState => { |
| 48 | const snapshots: ArchivedSnapshotEntry[] = []; |
| 49 | let error: string | null = null; |
| 50 | let isLoading = false; |
| 51 | |
| 52 | for (const environmentId of parseArchivedThreadsEnvironmentKey(environmentKey)) { |
| 53 | const result = get(options.getSnapshotAtom(environmentId)); |
| 54 | isLoading ||= result.waiting; |
| 55 | |
| 56 | const snapshot = Option.getOrNull(AsyncResult.value(result)); |
| 57 | if (snapshot !== null) { |
| 58 | snapshots.push({ environmentId, snapshot }); |
| 59 | } |
| 60 | |
| 61 | if (error === null && result._tag === "Failure") { |
| 62 | error = "Failed to load archived threads."; |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | return { snapshots, error, isLoading }; |
| 67 | }).pipe(Atom.withLabel(`${options.labelPrefix}:${environmentKey}`)), |
| 68 | ); |
| 69 | } |