diff --git a/packages/app/src/runtime/server/global-sync/bootstrap.test.ts b/packages/app/src/runtime/server/global-sync/bootstrap.test.ts index a5f29fd6691..034a4568388 100644 --- a/packages/app/src/runtime/server/global-sync/bootstrap.test.ts +++ b/packages/app/src/runtime/server/global-sync/bootstrap.test.ts @@ -1,12 +1,60 @@ import { describe, expect, test } from "bun:test" import { QueryClient } from "@tanstack/solid-query" -import { loadPathQuery, loadProjectsQuery } from "./bootstrap" +import { OpenCode } from "@opencode-ai/client/promise" +import { createStore } from "solid-js/store" +import { bootstrapGlobal, loadPathQuery, loadProjectsQuery } from "./bootstrap" import { ServerScope } from "@/runtime/server/scope" import type { ServerApi } from "@/runtime/server/api" +import type { ServerSync } from "@/runtime/server/sync" type ProjectApi = ServerApi["project"] type WorktreeApi = ServerApi["worktree"] +test("bootstraps projects through the native store setter and preserves subsequent updates", async () => { + const api = OpenCode.make({ + baseUrl: "http://opencode.local", + fetch: Object.assign( + async (input: RequestInfo | URL, init?: RequestInit) => { + const url = new URL(new Request(input, init).url) + if (url.pathname === "/api/location") + return Response.json({ + directory: "/repo", + project: { id: "project", directory: "/repo", canonical: "/repo" }, + }) + if (url.pathname === "/api/project") + return Response.json([{ id: "project", canonical: "/repo", time: { created: 1, updated: 1 }, sandboxes: [] }]) + if (url.pathname === "/api/worktree") return Response.json([{ directory: "/repo" }]) + throw new Error(`Unexpected request: ${url.pathname}`) + }, + { preconnect() {} }, + ), + }) + const [store, setStore] = createStore({ + path: { state: "", config: "", worktree: "", directory: "", home: "" }, + project: [], + provider_auth: {}, + config: {}, + reload: undefined, + }) + const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }) + + try { + await bootstrapGlobal({ serverAPI: api, scope: ServerScope.local, setGlobalStore: setStore, queryClient }) + expect(store.project.map((project) => [project.id, project.worktree])).toEqual([["project", "/repo"]]) + + setStore("project", (projects) => projects.map((project) => ({ ...project, name: "Renamed" }))) + expect(store.project[0]?.name).toBe("Renamed") + setStore("project", []) + expect(store.project).toEqual([]) + + await bootstrapGlobal({ serverAPI: api, scope: ServerScope.local, setGlobalStore: setStore, queryClient }) + expect(store.project.map((project) => [project.id, project.worktree])).toEqual([["project", "/repo"]]) + expect(store.config).toEqual({}) + } finally { + queryClient.clear() + } +}) + describe("query keys", () => { test("partitions identical directories by server scope", () => { const location = {} as ServerApi["location"] diff --git a/packages/app/src/runtime/server/sync.tsx b/packages/app/src/runtime/server/sync.tsx index 4499c79c6e0..375aab7ddeb 100644 --- a/packages/app/src/runtime/server/sync.tsx +++ b/packages/app/src/runtime/server/sync.tsx @@ -79,25 +79,13 @@ export function createServerSyncContextInner(serverSDK: ServerSDK, data: Data) { }) const queryClient = useQueryClient() - const setProjects = (next: Project[] | ((draft: Project[]) => Project[])) => { - setGlobalStore("project", next) - } - - const setBootStore = ((...input: unknown[]) => { - if (input[0] === "project" && Array.isArray(input[1])) { - setProjects(input[1] as Project[]) - return input[1] - } - return (setGlobalStore as (...args: unknown[]) => unknown)(...input) - }) as typeof setGlobalStore - const bootstrap = useQuery(() => ({ queryKey: [serverSDK.scope, "bootstrap"], queryFn: async () => { await bootstrapGlobal({ serverAPI: serverSDK.api, scope: serverSDK.scope, - setGlobalStore: setBootStore, + setGlobalStore, queryClient, }) return Date.now() @@ -105,14 +93,6 @@ export function createServerSyncContextInner(serverSDK: ServerSDK, data: Data) { enabled: connected(), })) - const set = ((...input: unknown[]) => { - if (input[0] === "project" && (Array.isArray(input[1]) || typeof input[1] === "function")) { - setProjects(input[1] as Project[] | ((draft: Project[]) => Project[])) - return input[1] - } - return (setGlobalStore as (...args: unknown[]) => unknown)(...input) - }) as typeof setGlobalStore - const paused = () => untrack(() => globalStore.reload) !== undefined const queue = createRefreshQueue({ @@ -216,7 +196,7 @@ export function createServerSyncContextInner(serverSDK: ServerSDK, data: Data) { } function applyProjectUpdate(update: Parameters[1]) { - setProjects((projects) => + setGlobalStore("project", (projects) => projects.map((project) => (project.id === update.id ? updateProjectInfo(project, update) : project)), ) } @@ -275,7 +255,7 @@ export function createServerSyncContextInner(serverSDK: ServerSDK, data: Data) { return { data: globalStore, - set, + set: setGlobalStore, child: children.child, disableMcp: children.disableMcp, // bootstrap,