Skip to content
Source
Sign inSign up

threadState.ts

43 lines1.6 KB

1import type { OrchestrationV2ThreadProjection } from "@t3tools/contracts";
2import * as Option from "effect/Option";
3​
4import { EMPTY_THREAD_HISTORY_META, type ThreadHistoryMeta } from "./threadHistoryMerge.ts";
5​
6export type EnvironmentThreadStatus = "empty" | "cached" | "synchronizing" | "live" | "deleted";
7​
8export interface EnvironmentThreadState {
9 readonly data: Option.Option<OrchestrationV2ThreadProjection>;
10 readonly status: EnvironmentThreadStatus;
11 readonly error: Option.Option<string>;
12 /**
13 * Errors here are thread/history-local and must not be promoted into the environment
14 * disconnect path.
15 */
16 readonly history: ThreadHistoryMeta;
17 /** The last server sequence `data` reflects, so readers can tell a write has landed without searching paged-out history. */
18 readonly sequence: number;
20​
21export const EMPTY_ENVIRONMENT_THREAD_STATE: EnvironmentThreadState = {
22 data: Option.none(),
23 status: "empty",
24 error: Option.none(),
25 history: EMPTY_THREAD_HISTORY_META,
26 sequence: 0,
27};
28​
29/**
30 * A thread holds a write once it has applied the write's commit sequence. A bounded
31 * projection can page the message out later, so finding it is only a fallback for
32 * servers that do not report the sequence.
33 */
34export function threadHasCaughtUpTo(
35 write: { readonly messageId: string; readonly sequence?: number | undefined },
36 thread: {
37 readonly sequence: number;
38 readonly messages: ReadonlyArray<{ readonly id: string }> | undefined;
39 },
40): boolean {
41 if (write.sequence !== undefined && thread.sequence >= write.sequence) return true;
42 return thread.messages?.some((entry) => entry.id === write.messageId) ?? false;