1import { T3_PROJECT_FILE_NAME, type EnvironmentId, type T3ProjectFile } from "@t3tools/contracts";
2import { parseT3ProjectFile } from "@t3tools/shared/t3ProjectFile";
3import { executeAtomQuery } from "@t3tools/client-runtime/state/runtime";
4
5import {
6 getProjectFileQueryAtom,
7 resolveProjectFileQueryData,
8} from "~/components/files/projectFilesQueryState";
9import { appAtomRegistry } from "~/rpc/atomRegistry";
10
11/**
12 * Read and decode the project's checked-in `t3.json`.
13 *
14 * Imperative counterpart to `useT3ProjectFileState` for the new-thread path,
15 * which resolves defaults at call time rather than render time. The file
16 * query atom caches per (environment, cwd), so repeat calls don't re-fetch.
17 * Optimistic in-app writes overlay the query result, matching what
18 * `useProjectFileQuery` renders. Missing, truncated, or invalid files
19 * resolve to null.
20 */
21export async function readT3ProjectFile(
22 environmentId: EnvironmentId,
23 workspaceRoot: string,
24): Promise<T3ProjectFile | null> {
25 const result = await executeAtomQuery(
26 appAtomRegistry,
27 getProjectFileQueryAtom(environmentId, workspaceRoot, T3_PROJECT_FILE_NAME),
28 { reportDefect: false, reportFailure: false },
29 );
30 const data = resolveProjectFileQueryData(
31 environmentId,
32 workspaceRoot,
33 T3_PROJECT_FILE_NAME,
34 result._tag === "Success" ? result.value : null,
35 );
36 if (data === null || data.truncated) return null;
37 return parseT3ProjectFile(data.contents);
38}