Skip to content

Commit 7ad70dd

Browse files
fix(app): detach cached project state from Solid proxies
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <[email protected]>
1 parent 6b6f41e commit 7ad70dd

2 files changed

Lines changed: 69 additions & 8 deletions

File tree

packages/app/src/context/global-sync/utils.test.ts

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, expect, test } from "bun:test"
2-
import type { Agent } from "@opencode-ai/sdk/v2/client"
3-
import { normalizeAgentList } from "./utils"
2+
import type { Agent, Project } from "@opencode-ai/sdk/v2/client"
3+
import { createStore } from "solid-js/store"
4+
import { normalizeAgentList, sanitizeProject } from "./utils"
45

56
const agent = (name = "build") =>
67
({
@@ -33,3 +34,57 @@ describe("normalizeAgentList", () => {
3334
expect(normalizeAgentList([{ name: "build" }, agent("docs")])).toEqual([agent("docs")])
3435
})
3536
})
37+
38+
describe("sanitizeProject", () => {
39+
test("clones nested project data and strips cached icon urls", () => {
40+
const [store] = createStore({
41+
value: {
42+
id: "proj_1",
43+
worktree: "/tmp/project",
44+
name: "Project",
45+
icon: {
46+
url: "https://example.com/icon.png",
47+
override: "data:image/png;base64,abc",
48+
color: "pink",
49+
},
50+
commands: {
51+
start: "bun dev",
52+
},
53+
time: {
54+
created: 1,
55+
updated: 2,
56+
},
57+
sandboxes: ["/tmp/project-a"],
58+
} satisfies Project,
59+
})
60+
61+
const next = sanitizeProject(store.value)
62+
63+
expect(next).not.toBe(store.value)
64+
expect(next.time).not.toBe(store.value.time)
65+
expect(next.sandboxes).not.toBe(store.value.sandboxes)
66+
expect(next.commands).not.toBe(store.value.commands)
67+
expect(next.icon).not.toBe(store.value.icon)
68+
expect(next.icon?.url).toBeUndefined()
69+
expect(next.icon?.override).toBeUndefined()
70+
expect(next.icon?.color).toBe("pink")
71+
72+
next.sandboxes.push("/tmp/project-b")
73+
expect(store.value.sandboxes).toEqual(["/tmp/project-a"])
74+
})
75+
76+
test("returns a detached copy even without icon overrides", () => {
77+
const project = {
78+
id: "proj_2",
79+
worktree: "/tmp/project-2",
80+
time: { created: 1, updated: 1 },
81+
sandboxes: [],
82+
} satisfies Project
83+
84+
const next = sanitizeProject(project)
85+
86+
expect(next).not.toBe(project)
87+
expect(next.time).not.toBe(project.time)
88+
expect(next.sandboxes).not.toBe(project.sandboxes)
89+
})
90+
})

packages/app/src/context/global-sync/utils.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,19 @@ export function normalizeProviderList(input: ProviderListResponse): ProviderList
2727
}
2828

2929
export function sanitizeProject(project: Project) {
30-
if (!project.icon?.url && !project.icon?.override) return project
3130
return {
3231
...project,
33-
icon: {
34-
...project.icon,
35-
url: undefined,
36-
override: undefined,
37-
},
32+
time: { ...project.time },
33+
sandboxes: [...project.sandboxes],
34+
...(project.commands ? { commands: { ...project.commands } } : {}),
35+
...(project.icon
36+
? {
37+
icon: {
38+
...project.icon,
39+
url: undefined,
40+
override: undefined,
41+
},
42+
}
43+
: {}),
3844
}
3945
}

0 commit comments

Comments
 (0)