105 lines · 3.9 KB · TypeScript
RawDownload
import type {  EnvironmentId,  OrchestrationProjectShell,  OrchestrationShellSnapshot,  ProjectId,  ScopedProjectRef,} from "@t3tools/contracts";import { Atom } from "effect/unstable/reactivity"; import type { EnvironmentProject } from "./models.ts";import { scopeProject } from "./models.ts";import type { EnvironmentCatalogState } from "./connections.ts";import { arrayElementsEqual, parseProjectKey, projectKey, projectRefsEqual } from "./entities.ts"; const EMPTY_PROJECTS: ReadonlyArray<OrchestrationProjectShell> = Object.freeze([]);const EMPTY_PROJECT_INDEX: ReadonlyMap<ProjectId, OrchestrationProjectShell> = new Map(); export function createEnvironmentProjectAtoms(input: {  readonly catalogValueAtom: Atom.Atom<EnvironmentCatalogState>;  readonly snapshotAtom: (    environmentId: EnvironmentId,  ) => Atom.Atom<OrchestrationShellSnapshot | null>;}) {  const environmentProjectsAtom = Atom.family((environmentId: EnvironmentId) =>    Atom.make(      (get): ReadonlyArray<OrchestrationProjectShell> =>        get(input.snapshotAtom(environmentId))?.projects ?? EMPTY_PROJECTS,    ).pipe(Atom.withLabel(`environment-projects:${environmentId}`)),  );   const environmentProjectIndexAtom = Atom.family((environmentId: EnvironmentId) =>    Atom.make((get): ReadonlyMap<ProjectId, OrchestrationProjectShell> => {      const projects = get(environmentProjectsAtom(environmentId));      if (projects.length === 0) {        return EMPTY_PROJECT_INDEX;      }      return new Map(projects.map((project) => [project.id, project] as const));    }).pipe(Atom.withLabel(`environment-project-index:${environmentId}`)),  );   const environmentProjectRefsAtom = Atom.family((environmentId: EnvironmentId) => {    let previous: ReadonlyArray<ScopedProjectRef> = [];    return Atom.make((get) => {      const next = get(environmentProjectsAtom(environmentId)).map((project) => ({        environmentId,        projectId: project.id,      }));      if (projectRefsEqual(previous, next)) {        return previous;      }      previous = next;      return next;    }).pipe(Atom.withLabel(`environment-project-refs:${environmentId}`));  });   const projectAtomFamily = Atom.family((key: string) => {    const ref = parseProjectKey(key);    let previousSource: OrchestrationProjectShell | null = null;    let previousValue: EnvironmentProject | null = null;    return Atom.make((get) => {      const source = get(environmentProjectIndexAtom(ref.environmentId)).get(ref.projectId) ?? null;      if (source === previousSource) {        return previousValue;      }      previousSource = source;      previousValue = source === null ? null : scopeProject(ref.environmentId, source);      return previousValue;    }).pipe(Atom.withLabel(`environment-project:${key}`));  });   let previousProjectRefs: ReadonlyArray<ScopedProjectRef> = [];  const projectRefsAtom = Atom.make((get) => {    const refs: ScopedProjectRef[] = [];    for (const environmentId of get(input.catalogValueAtom).entries.keys()) {      refs.push(...get(environmentProjectRefsAtom(environmentId)));    }    if (projectRefsEqual(previousProjectRefs, refs)) {      return previousProjectRefs;    }    previousProjectRefs = refs;    return refs;  }).pipe(Atom.withLabel("environment-project-refs"));   let previousProjects: ReadonlyArray<EnvironmentProject> = [];  const projectsAtom = Atom.make((get) => {    const next = get(projectRefsAtom).flatMap((ref) => {      const project = get(projectAtomFamily(projectKey(ref)));      return project === null ? [] : [project];    });    if (arrayElementsEqual(previousProjects, next)) {      return previousProjects;    }    previousProjects = next;    return previousProjects;  }).pipe(Atom.withLabel("environment-project-list"));   return {    environmentProjectsAtom,    environmentProjectIndexAtom,    environmentProjectRefsAtom,    projectRefsAtom,    projectsAtom,    projectAtom: (ref: ScopedProjectRef) => projectAtomFamily(projectKey(ref)),  };}