140 lines · 4.1 KB · TypeScript
RawDownload
import { useAtomValue } from "@effect/atom-react";import type {  ServerProcessDiagnosticsResult,  ServerProcessResourceHistoryResult,} from "@t3tools/contracts";import * as Cause from "effect/Cause";import * as Effect from "effect/Effect";import * as Option from "effect/Option";import { AsyncResult, Atom } from "effect/unstable/reactivity";import { useCallback } from "react"; import { ensureLocalApi } from "../localApi";import { appAtomRegistry } from "../rpc/atomRegistry"; const PROCESS_DIAGNOSTICS_STALE_TIME_MS = 2_000;const PROCESS_DIAGNOSTICS_IDLE_TTL_MS = 5 * 60_000;const PROCESS_RESOURCE_HISTORY_STALE_TIME_MS = 5_000;const PROCESS_RESOURCE_HISTORY_INPUT_SEPARATOR = ":"; const processDiagnosticsAtom = Atom.make(  Effect.promise(() => ensureLocalApi().server.getProcessDiagnostics()),).pipe(  Atom.swr({    staleTime: PROCESS_DIAGNOSTICS_STALE_TIME_MS,    revalidateOnMount: true,  }),  Atom.setIdleTTL(PROCESS_DIAGNOSTICS_IDLE_TTL_MS),  Atom.withLabel("process-diagnostics"),); function formatProcessResourceHistoryKey(input: {  readonly windowMs: number;  readonly bucketMs: number;}): string {  return `${input.windowMs}${PROCESS_RESOURCE_HISTORY_INPUT_SEPARATOR}${input.bucketMs}`;} function parseProcessResourceHistoryKey(key: string): {  readonly windowMs: number;  readonly bucketMs: number;} {  const [windowMs = "0", bucketMs = "0"] = key.split(PROCESS_RESOURCE_HISTORY_INPUT_SEPARATOR);  return {    windowMs: Number(windowMs),    bucketMs: Number(bucketMs),  };} const processResourceHistoryAtom = Atom.family((key: string) => {  const input = parseProcessResourceHistoryKey(key);  return Atom.make(    Effect.promise(() => ensureLocalApi().server.getProcessResourceHistory(input)),  ).pipe(    Atom.swr({      staleTime: PROCESS_RESOURCE_HISTORY_STALE_TIME_MS,      revalidateOnMount: true,    }),    Atom.setIdleTTL(PROCESS_DIAGNOSTICS_IDLE_TTL_MS),    Atom.withLabel(`process-resource-history:${key}`),  );}); export interface ProcessDiagnosticsState {  readonly data: ServerProcessDiagnosticsResult | null;  readonly error: string | null;  readonly isPending: boolean;  readonly refresh: () => void;} export interface ProcessResourceHistoryState {  readonly data: ServerProcessResourceHistoryResult | null;  readonly error: string | null;  readonly isPending: boolean;  readonly refresh: () => void;} function formatProcessDiagnosticsError(error: unknown): string {  return error instanceof Error ? error.message : "Failed to load process diagnostics.";} function readProcessDiagnosticsError(  result: AsyncResult.AsyncResult<ServerProcessDiagnosticsResult, unknown>,): string | null {  if (result._tag !== "Failure") {    return null;  }   const squashed = Cause.squash(result.cause);  return formatProcessDiagnosticsError(squashed);} function readProcessResourceHistoryError(  result: AsyncResult.AsyncResult<ServerProcessResourceHistoryResult, unknown>,): string | null {  if (result._tag !== "Failure") {    return null;  }   const squashed = Cause.squash(result.cause);  return formatProcessDiagnosticsError(squashed);} export function refreshProcessDiagnostics(): void {  appAtomRegistry.refresh(processDiagnosticsAtom);} export function useProcessDiagnostics(): ProcessDiagnosticsState {  const result = useAtomValue(processDiagnosticsAtom);  const data = Option.getOrNull(AsyncResult.value(result));  const refresh = useCallback(() => {    refreshProcessDiagnostics();  }, []);   return {    data,    error: readProcessDiagnosticsError(result),    isPending: result.waiting,    refresh,  };} export function useProcessResourceHistory(input: {  readonly windowMs: number;  readonly bucketMs: number;}): ProcessResourceHistoryState {  const atom = processResourceHistoryAtom(formatProcessResourceHistoryKey(input));  const result = useAtomValue(atom);  const data = Option.getOrNull(AsyncResult.value(result));   const refresh = useCallback(() => {    appAtomRegistry.refresh(atom);  }, [atom]);   return {    data,    error: readProcessResourceHistoryError(result),    isPending: result.waiting,    refresh,  };}