Julius MarmingeT3 Code Mobile [WIP] (#2013)b3e8c035 months agoHistory
401 lines · 11.9 KB · TypeScript
RawDownload
import type { SourceControlDiscoveryResult } from "@t3tools/contracts";import * as Effect from "effect/Effect";import { Atom, type AtomRegistry } from "effect/unstable/reactivity"; /* --- Types ---------------------------------------------------------- */ export interface SourceControlDiscoveryState {  readonly data: SourceControlDiscoveryResult | null;  readonly error: string | null;  readonly isPending: boolean;} export interface SourceControlDiscoveryTarget<TKey extends string = string> {  readonly key: TKey | null;} export interface SourceControlDiscoveryClient {  readonly discoverSourceControl: () => Promise<SourceControlDiscoveryResult>;} interface WatchedEntry {  refCount: number;  teardown: () => void;} /* --- Constants ------------------------------------------------------ */ export const EMPTY_SOURCE_CONTROL_DISCOVERY_STATE = Object.freeze<SourceControlDiscoveryState>({  data: null,  error: null,  isPending: false,}); const INITIAL_SOURCE_CONTROL_DISCOVERY_STATE = Object.freeze<SourceControlDiscoveryState>({  data: null,  error: null,  isPending: true,}); /* --- Atoms ---------------------------------------------------------- */ const knownSourceControlDiscoveryKeys = new Set<string>(); export const sourceControlDiscoveryStateAtom = Atom.family((key: string) => {  knownSourceControlDiscoveryKeys.add(key);  return Atom.make(INITIAL_SOURCE_CONTROL_DISCOVERY_STATE).pipe(    Atom.keepAlive,    Atom.withLabel(`source-control-discovery:${key}`),  );}); export const EMPTY_SOURCE_CONTROL_DISCOVERY_ATOM = Atom.make(  EMPTY_SOURCE_CONTROL_DISCOVERY_STATE,).pipe(Atom.keepAlive, Atom.withLabel("source-control-discovery:null")); /* --- Helpers -------------------------------------------------------- */ export function getSourceControlDiscoveryTargetKey<TKey extends string>(  target: SourceControlDiscoveryTarget<TKey>,): TKey | null {  const key = target.key;  return key && key.length > 0 ? key : null;} /* --- Refresh manager ------------------------------------------------ */ export interface SourceControlDiscoveryManagerConfig<TKey extends string = string> {  /**   * Get the atom registry used to read/write source-control discovery snapshots.   */  readonly getRegistry: () => AtomRegistry.AtomRegistry;  /**   * Resolve the runtime client for a discovery target key.   *   * Web currently uses a single `"primary"` target, but keeping this keyed   * lets mobile or future multi-environment clients provide separate discovery   * clients without changing the state primitive.   */  readonly getClient: (key: TKey) => SourceControlDiscoveryClient | null;  /**   * Optional: subscribe to environment/client availability changes.   *   * When provided, `watch` refreshes as clients appear or are replaced   * instead of relying on React hooks to manually kick discovery.   */  readonly subscribeClientChanges?: (listener: () => void) => () => void;  readonly staleTimeMs?: number;  readonly idleTtlMs?: number;} const NOOP: () => void = () => undefined;const DEFAULT_STALE_TIME_MS = 30_000;const DEFAULT_IDLE_TTL_MS = 5 * 60_000; export function createSourceControlDiscoveryManager<TKey extends string = string>(  config: SourceControlDiscoveryManagerConfig<TKey>,) {  const refreshInFlight = new Map<    string,    {      readonly client: SourceControlDiscoveryClient;      readonly promise: Promise<SourceControlDiscoveryResult | null>;    }  >();  const refreshVersions = new Map<string, number>();  const watched = new Map<string, WatchedEntry>();  const refreshTargets = new Map<string, SourceControlDiscoveryTarget<TKey>>();  const staleTimeMs = config.staleTimeMs ?? DEFAULT_STALE_TIME_MS;  const idleTtlMs = config.idleTtlMs ?? DEFAULT_IDLE_TTL_MS;   const watchedRefreshAtom = Atom.family((targetKey: string) =>    Atom.make(() =>      Effect.promise(() => {        const target = refreshTargets.get(targetKey);        return target ? refresh(target) : Promise.resolve(null);      }),    ).pipe(      Atom.swr({        staleTime: staleTimeMs,        revalidateOnMount: true,      }),      Atom.setIdleTTL(idleTtlMs),      Atom.withLabel(`source-control-discovery:watched-refresh:${targetKey}`),    ),  );   function getRefreshVersion(targetKey: string): number {    return refreshVersions.get(targetKey) ?? 0;  }   function bumpRefreshVersion(targetKey: string): void {    refreshVersions.set(targetKey, getRefreshVersion(targetKey) + 1);  }   /* -- Atom helpers -------------------------------------------------- */   function setState(targetKey: string, nextState: SourceControlDiscoveryState): void {    config.getRegistry().set(sourceControlDiscoveryStateAtom(targetKey), nextState);  }   function markPending(targetKey: string): void {    const current = config.getRegistry().get(sourceControlDiscoveryStateAtom(targetKey));    const next: SourceControlDiscoveryState =      current.data === null        ? INITIAL_SOURCE_CONTROL_DISCOVERY_STATE        : {            data: current.data,            error: null,            isPending: true,          };     if (      current.data === next.data &&      current.error === next.error &&      current.isPending === next.isPending    ) {      return;    }     setState(targetKey, next);  }   function setData(targetKey: string, data: SourceControlDiscoveryResult): void {    setState(targetKey, {      data,      error: null,      isPending: false,    });  }   function setError(targetKey: string, error: unknown): void {    const current = config.getRegistry().get(sourceControlDiscoveryStateAtom(targetKey));    setState(targetKey, {      data: current.data,      error: error instanceof Error ? error.message : "Failed to discover source control tools.",      isPending: false,    });  }   /* -- Public API ---------------------------------------------------- */   /**   * Trigger a one-shot source-control discovery RPC for a target.   *   * Calls are deduplicated while a refresh for the same target key is in   * flight. On failure, the previous successful snapshot is kept in `data`   * and the error message is stored separately so UI can keep rendering stale   * discovery results while showing the failure.   *   * @param target The logical runtime target to refresh.   * @param client Optional pre-resolved client, useful in tests.   */  function refresh(    target: SourceControlDiscoveryTarget<TKey>,    client?: SourceControlDiscoveryClient,  ): Promise<SourceControlDiscoveryResult | null> {    const targetKey = getSourceControlDiscoveryTargetKey(target);    if (targetKey === null) {      return Promise.resolve(null);    }    refreshTargets.set(targetKey, target);     const resolvedClient = client ?? config.getClient(targetKey);    if (!resolvedClient) {      const error = new Error("Source control discovery client is unavailable.");      setError(targetKey, error);      return Promise.resolve(getSnapshot(target).data);    }     const existing = refreshInFlight.get(targetKey);    if (existing) {      if (!client || existing.client === resolvedClient) {        return existing.promise;      }       return existing.promise.then(() => refresh(target, resolvedClient));    }     markPending(targetKey);    const refreshVersion = getRefreshVersion(targetKey);    const promise = resolvedClient.discoverSourceControl().then(      (result) => {        if (getRefreshVersion(targetKey) === refreshVersion) {          setData(targetKey, result);        }        return result;      },      (error: unknown) => {        if (getRefreshVersion(targetKey) === refreshVersion) {          setError(targetKey, error);        }        return getSnapshot(target).data;      },    );    let tracked: Promise<SourceControlDiscoveryResult | null>;    tracked = promise.finally(() => {      if (refreshInFlight.get(targetKey)?.promise === tracked) {        refreshInFlight.delete(targetKey);      }    });    refreshInFlight.set(targetKey, {      client: resolvedClient,      promise: tracked,    });    return tracked;  }   /**   * Reset discovery state for one target and ignore any currently in-flight   * refresh for that target. If no target is provided, every known target is   * invalidated.   */  function invalidate(target?: SourceControlDiscoveryTarget<TKey>): void {    if (!target) {      reset();      return;    }     const targetKey = getSourceControlDiscoveryTargetKey(target);    if (targetKey === null) {      return;    }     bumpRefreshVersion(targetKey);    refreshInFlight.delete(targetKey);    setState(targetKey, INITIAL_SOURCE_CONTROL_DISCOVERY_STATE);  }   /**   * Read the current atom snapshot for `target`.   *   * Invalid targets return the inert empty state rather than creating a new   * family atom entry.   */  function getSnapshot(target: SourceControlDiscoveryTarget<TKey>): SourceControlDiscoveryState {    const targetKey = getSourceControlDiscoveryTargetKey(target);    if (targetKey === null) {      return EMPTY_SOURCE_CONTROL_DISCOVERY_STATE;    }     return config.getRegistry().get(sourceControlDiscoveryStateAtom(targetKey));  }   /**   * Keep discovery warm for `target`.   *   * Multiple callers sharing a target key are ref-counted. With   * `subscribeClientChanges`, the manager refreshes whenever a client first   * appears or is replaced after reconnect.   */  function watch(    target: SourceControlDiscoveryTarget<TKey>,    client?: SourceControlDiscoveryClient,  ): () => void {    const targetKey = getSourceControlDiscoveryTargetKey(target);    if (targetKey === null) {      return NOOP;    }    refreshTargets.set(targetKey, target);     const existing = watched.get(targetKey);    if (existing) {      existing.refCount += 1;      return () => unwatch(targetKey);    }     let teardown: () => void;     if (client) {      void refresh(target, client);      teardown = NOOP;    } else if (config.subscribeClientChanges) {      let currentClient: SourceControlDiscoveryClient | null = null;       const sync = () => {        const resolved = config.getClient(targetKey);        if (!resolved) {          currentClient = null;          markPending(targetKey);          return;        }         if (currentClient === resolved) {          return;        }         const isClientReplacement = currentClient !== null;        currentClient = resolved;        refreshWatchedTarget(targetKey, target, isClientReplacement ? resolved : undefined);      };       const unsubChanges = config.subscribeClientChanges(sync);      sync();      teardown = unsubChanges;    } else {      if (!config.getClient(targetKey)) {        return NOOP;      }      refreshWatchedTarget(targetKey, target);      teardown = NOOP;    }     watched.set(targetKey, { refCount: 1, teardown });    return () => unwatch(targetKey);  }   function unwatch(targetKey: string): void {    const entry = watched.get(targetKey);    if (!entry) {      return;    }     entry.refCount -= 1;    if (entry.refCount > 0) {      return;    }     entry.teardown();    watched.delete(targetKey);  }   function refreshWatchedTarget(    targetKey: string,    target: SourceControlDiscoveryTarget<TKey>,    client?: SourceControlDiscoveryClient,  ): void {    refreshTargets.set(targetKey, target);    if (client) {      void refresh(target, client);      return;    }     config.getRegistry().get(watchedRefreshAtom(targetKey));  }   /**   * Clear in-flight refresh tracking and reset every known discovery atom.   * Primarily used by tests and runtime teardown.   */  function reset(): void {    const keys = new Set([...knownSourceControlDiscoveryKeys, ...refreshInFlight.keys()]);    for (const entry of watched.values()) {      entry.teardown();    }    watched.clear();    refreshTargets.clear();    refreshInFlight.clear();    for (const key of keys) {      bumpRefreshVersion(key);      setState(key, INITIAL_SOURCE_CONTROL_DISCOVERY_STATE);    }  }   return {    watch,    refresh,    getSnapshot,    invalidate,    reset,  };}