diff --git a/packages/opencode/specs/effect-migration.md b/packages/opencode/specs/effect-migration.md index b0ce4a3ee1..32e5b3174d 100644 --- a/packages/opencode/specs/effect-migration.md +++ b/packages/opencode/specs/effect-migration.md @@ -336,6 +336,7 @@ For each service, the migration is roughly: ### Migration log +- `Snapshot` — facade destroyed 2026-04-11. Removed the `makeRuntime(...)` wrapper and the seven async exports (`init`, `track`, `patch`, `restore`, `revert`, `diff`, `diffFull`); `project/bootstrap.ts` now runs `svc.init()` through `BootstrapRuntime`, the debug CLI commands use `AppRuntime.runPromise(Snapshot.Service.use(...))`, and the snapshot/revert-compact/project-init-git tests were rewritten to the same pattern. `Snapshot.defaultLayer` was added to `BootstrapLayer` so bootstrap can resolve the service without pulling in `AppRuntime`. - `SessionStatus` — migrated 2026-04-11. Replaced the last route and retry-policy callers with `AppRuntime.runPromise(SessionStatus.Service.use(...))` and removed the `makeRuntime(...)` facade. - `ShareNext` — migrated 2026-04-11. Swapped remaining async callers to `AppRuntime.runPromise(ShareNext.Service.use(...))`, removed the `makeRuntime(...)` facade, and kept instance bootstrap on the shared app runtime. - `SessionTodo` — migrated 2026-04-10. Already matched the target service shape in `session/todo.ts`: single namespace, traced Effect methods, and no `makeRuntime(...)` facade remained; checklist updated to reflect the completed migration. diff --git a/packages/opencode/src/cli/cmd/debug/snapshot.ts b/packages/opencode/src/cli/cmd/debug/snapshot.ts index 4a409dc54e..6663398a45 100644 --- a/packages/opencode/src/cli/cmd/debug/snapshot.ts +++ b/packages/opencode/src/cli/cmd/debug/snapshot.ts @@ -1,3 +1,4 @@ +import { AppRuntime } from "@/effect/app-runtime" import { Snapshot } from "../../../snapshot" import { bootstrap } from "../../bootstrap" import { cmd } from "../cmd" @@ -14,7 +15,7 @@ const TrackCommand = cmd({ describe: "track current snapshot state", async handler() { await bootstrap(process.cwd(), async () => { - console.log(await Snapshot.track()) + console.log(await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track()))) }) }, }) @@ -30,7 +31,7 @@ const PatchCommand = cmd({ }), async handler(args) { await bootstrap(process.cwd(), async () => { - console.log(await Snapshot.patch(args.hash)) + console.log(await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(args.hash)))) }) }, }) @@ -46,7 +47,7 @@ const DiffCommand = cmd({ }), async handler(args) { await bootstrap(process.cwd(), async () => { - console.log(await Snapshot.diff(args.hash)) + console.log(await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diff(args.hash)))) }) }, }) diff --git a/packages/opencode/src/effect/bootstrap-runtime.ts b/packages/opencode/src/effect/bootstrap-runtime.ts index 648f2484e6..b645fb4175 100644 --- a/packages/opencode/src/effect/bootstrap-runtime.ts +++ b/packages/opencode/src/effect/bootstrap-runtime.ts @@ -3,7 +3,8 @@ import { memoMap } from "./run-service" import { Format } from "@/format" import { ShareNext } from "@/share/share-next" +import { Snapshot } from "@/snapshot" -export const BootstrapLayer = Layer.mergeAll(Format.defaultLayer, ShareNext.defaultLayer) +export const BootstrapLayer = Layer.mergeAll(Format.defaultLayer, ShareNext.defaultLayer, Snapshot.defaultLayer) export const BootstrapRuntime = ManagedRuntime.make(BootstrapLayer, { memoMap }) diff --git a/packages/opencode/src/project/bootstrap.ts b/packages/opencode/src/project/bootstrap.ts index 9f6f7fa6fa..8deabae4bc 100644 --- a/packages/opencode/src/project/bootstrap.ts +++ b/packages/opencode/src/project/bootstrap.ts @@ -22,7 +22,7 @@ export async function InstanceBootstrap() { File.init() FileWatcher.init() Vcs.init() - Snapshot.init() + void BootstrapRuntime.runPromise(Snapshot.Service.use((svc) => svc.init())) Bus.subscribe(Command.Event.Executed, async (payload) => { if (payload.properties.name === Command.Default.INIT) { diff --git a/packages/opencode/src/snapshot/index.ts b/packages/opencode/src/snapshot/index.ts index 834cdde252..07ed12073c 100644 --- a/packages/opencode/src/snapshot/index.ts +++ b/packages/opencode/src/snapshot/index.ts @@ -5,7 +5,6 @@ import path from "path" import z from "zod" import * as CrossSpawnSpawner from "@/effect/cross-spawn-spawner" import { InstanceState } from "@/effect/instance-state" -import { makeRuntime } from "@/effect/run-service" import { AppFileSystem } from "@/filesystem" import { Hash } from "@/util/hash" import { Config } from "../config/config" @@ -690,34 +689,4 @@ export namespace Snapshot { Layer.provide(AppFileSystem.defaultLayer), Layer.provide(Config.defaultLayer), ) - - const { runPromise } = makeRuntime(Service, defaultLayer) - - export async function init() { - return runPromise((svc) => svc.init()) - } - - export async function track() { - return runPromise((svc) => svc.track()) - } - - export async function patch(hash: string) { - return runPromise((svc) => svc.patch(hash)) - } - - export async function restore(snapshot: string) { - return runPromise((svc) => svc.restore(snapshot)) - } - - export async function revert(patches: Patch[]) { - return runPromise((svc) => svc.revert(patches)) - } - - export async function diff(hash: string) { - return runPromise((svc) => svc.diff(hash)) - } - - export async function diffFull(from: string, to: string) { - return runPromise((svc) => svc.diffFull(from, to)) - } } diff --git a/packages/opencode/test/server/project-init-git.test.ts b/packages/opencode/test/server/project-init-git.test.ts index eca562a0f5..3b6d9dcc6a 100644 --- a/packages/opencode/test/server/project-init-git.test.ts +++ b/packages/opencode/test/server/project-init-git.test.ts @@ -2,6 +2,7 @@ import { afterEach, describe, expect, spyOn, test } from "bun:test" import path from "path" import { GlobalBus } from "../../src/bus/global" import { Snapshot } from "../../src/snapshot" +import { AppRuntime } from "../../src/effect/app-runtime" import { InstanceBootstrap } from "../../src/project/bootstrap" import { Instance } from "../../src/project/instance" import { Server } from "../../src/server/server" @@ -64,7 +65,7 @@ describe("project.initGit endpoint", () => { await Instance.provide({ directory: tmp.path, fn: async () => { - expect(await Snapshot.track()).toBeTruthy() + expect(await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track()))).toBeTruthy() }, }) } finally { diff --git a/packages/opencode/test/session/revert-compact.test.ts b/packages/opencode/test/session/revert-compact.test.ts index 95d90325ad..9050788b63 100644 --- a/packages/opencode/test/session/revert-compact.test.ts +++ b/packages/opencode/test/session/revert-compact.test.ts @@ -7,6 +7,7 @@ import { SessionRevert } from "../../src/session/revert" import { SessionCompaction } from "../../src/session/compaction" import { MessageV2 } from "../../src/session/message-v2" import { Snapshot } from "../../src/snapshot" +import { AppRuntime } from "../../src/effect/app-runtime" import { Log } from "../../src/util/log" import { Instance } from "../../src/project/instance" import { MessageID, PartID } from "../../src/session/schema" @@ -460,12 +461,12 @@ describe("revert + compact workflow", () => { const u = await user(sid) await text(sid, u.id, `${file}:${next}`) const a = await assistant(sid, u.id, tmp.path) - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) if (!before) throw new Error("expected snapshot") await fs.writeFile(path.join(tmp.path, file), next) - const after = await Snapshot.track() + const after = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) if (!after) throw new Error("expected snapshot") - const patch = await Snapshot.patch(before) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before))) await Session.updatePart({ id: PartID.ascending(), messageID: a.id, @@ -550,12 +551,12 @@ describe("revert + compact workflow", () => { const u = await user(sid) await text(sid, u.id, `a.txt:${next}`) const a = await assistant(sid, u.id, tmp.path) - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) if (!before) throw new Error("expected snapshot") await fs.writeFile(path.join(tmp.path, "a.txt"), next) - const after = await Snapshot.track() + const after = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) if (!after) throw new Error("expected snapshot") - const patch = await Snapshot.patch(before) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before))) await Session.updatePart({ id: PartID.ascending(), messageID: a.id, diff --git a/packages/opencode/test/snapshot/snapshot.test.ts b/packages/opencode/test/snapshot/snapshot.test.ts index 3cedfb941d..f04bafcf12 100644 --- a/packages/opencode/test/snapshot/snapshot.test.ts +++ b/packages/opencode/test/snapshot/snapshot.test.ts @@ -3,11 +3,12 @@ import { $ } from "bun" import fs from "fs/promises" import path from "path" import { Snapshot } from "../../src/snapshot" +import { AppRuntime } from "../../src/effect/app-runtime" import { Instance } from "../../src/project/instance" import { Filesystem } from "../../src/util/filesystem" import { tmpdir } from "../fixture/fixture" -// Git always outputs /-separated paths internally. Snapshot.patch() joins them +// Git always outputs /-separated paths internally. AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch())) joins them // with path.join (which produces \ on Windows) then normalizes back to /. // This helper does the same for expected values so assertions match cross-platform. const fwd = (...parts: string[]) => path.join(...parts).replaceAll("\\", "/") @@ -40,12 +41,12 @@ test("tracks deleted files correctly", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await $`rm ${tmp.path}/a.txt`.quiet() - expect((await Snapshot.patch(before!)).files).toContain(fwd(tmp.path, "a.txt")) + expect((await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!)))).files).toContain(fwd(tmp.path, "a.txt")) }, }) }) @@ -55,12 +56,13 @@ test("revert should remove new files", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(`${tmp.path}/new.txt`, "NEW") - await Snapshot.revert([await Snapshot.patch(before!)]) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) + await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([patch]))) expect( await fs @@ -77,13 +79,14 @@ test("revert in subdirectory", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await $`mkdir -p ${tmp.path}/sub`.quiet() await Filesystem.write(`${tmp.path}/sub/file.txt`, "SUB") - await Snapshot.revert([await Snapshot.patch(before!)]) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) + await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([patch]))) expect( await fs @@ -102,7 +105,7 @@ test("multiple file operations", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await $`rm ${tmp.path}/a.txt`.quiet() @@ -111,7 +114,8 @@ test("multiple file operations", async () => { await Filesystem.write(`${tmp.path}/dir/d.txt`, "D") await Filesystem.write(`${tmp.path}/b.txt`, "MODIFIED") - await Snapshot.revert([await Snapshot.patch(before!)]) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) + await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([patch]))) expect(await fs.readFile(`${tmp.path}/a.txt`, "utf-8")).toBe(tmp.extra.aContent) expect( @@ -132,12 +136,12 @@ test("empty directory handling", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await $`mkdir ${tmp.path}/empty`.quiet() - expect((await Snapshot.patch(before!)).files.length).toBe(0) + expect((await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!)))).files.length).toBe(0) }, }) }) @@ -147,15 +151,15 @@ test("binary file handling", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(`${tmp.path}/image.png`, new Uint8Array([0x89, 0x50, 0x4e, 0x47])) - const patch = await Snapshot.patch(before!) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) expect(patch.files).toContain(fwd(tmp.path, "image.png")) - await Snapshot.revert([patch]) + await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([patch]))) expect( await fs .access(`${tmp.path}/image.png`) @@ -171,12 +175,12 @@ test("symlink handling", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await fs.symlink(`${tmp.path}/a.txt`, `${tmp.path}/link.txt`, "file") - expect((await Snapshot.patch(before!)).files).toContain(fwd(tmp.path, "link.txt")) + expect((await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!)))).files).toContain(fwd(tmp.path, "link.txt")) }, }) }) @@ -186,12 +190,12 @@ test("file under size limit handling", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(`${tmp.path}/large.txt`, "x".repeat(1024 * 1024)) - expect((await Snapshot.patch(before!)).files).toContain(fwd(tmp.path, "large.txt")) + expect((await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!)))).files).toContain(fwd(tmp.path, "large.txt")) }, }) }) @@ -201,14 +205,14 @@ test("large added files are skipped", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(`${tmp.path}/huge.txt`, new Uint8Array(2 * 1024 * 1024 + 1)) - expect((await Snapshot.patch(before!)).files).toEqual([]) - expect(await Snapshot.diff(before!)).toBe("") - expect(await Snapshot.track()).toBe(before) + expect((await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!)))).files).toEqual([]) + expect(await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diff(before!)))).toBe("") + expect(await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track()))).toBe(before) }, }) }) @@ -218,13 +222,14 @@ test("nested directory revert", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await $`mkdir -p ${tmp.path}/level1/level2/level3`.quiet() await Filesystem.write(`${tmp.path}/level1/level2/level3/deep.txt`, "DEEP") - await Snapshot.revert([await Snapshot.patch(before!)]) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) + await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([patch]))) expect( await fs @@ -241,14 +246,14 @@ test("special characters in filenames", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(`${tmp.path}/file with spaces.txt`, "SPACES") await Filesystem.write(`${tmp.path}/file-with-dashes.txt`, "DASHES") await Filesystem.write(`${tmp.path}/file_with_underscores.txt`, "UNDERSCORES") - const files = (await Snapshot.patch(before!)).files + const files = (await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!)))).files expect(files).toContain(fwd(tmp.path, "file with spaces.txt")) expect(files).toContain(fwd(tmp.path, "file-with-dashes.txt")) expect(files).toContain(fwd(tmp.path, "file_with_underscores.txt")) @@ -262,10 +267,10 @@ test("revert with empty patches", async () => { directory: tmp.path, fn: async () => { // Should not crash with empty patches - expect(Snapshot.revert([])).resolves.toBeUndefined() + expect(AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([])))).resolves.toBeUndefined() // Should not crash with patches that have empty file lists - expect(Snapshot.revert([{ hash: "dummy", files: [] }])).resolves.toBeUndefined() + expect(AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([{ hash: "dummy", files: [] }])))).resolves.toBeUndefined() }, }) }) @@ -275,14 +280,14 @@ test("patch with invalid hash", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() // Create a change await Filesystem.write(`${tmp.path}/test.txt`, "TEST") // Try to patch with invalid hash - should handle gracefully - const patch = await Snapshot.patch("invalid-hash-12345") + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch("invalid-hash-12345"))) expect(patch.files).toEqual([]) expect(patch.hash).toBe("invalid-hash-12345") }, @@ -294,18 +299,18 @@ test("revert non-existent file", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() // Try to revert a file that doesn't exist in the snapshot // This should not crash expect( - Snapshot.revert([ + AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([ { hash: before!, files: [`${tmp.path}/nonexistent.txt`], }, - ]), + ]))), ).resolves.toBeUndefined() }, }) @@ -316,7 +321,7 @@ test("unicode filenames", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() const unicodeFiles = [ @@ -330,14 +335,14 @@ test("unicode filenames", async () => { await Filesystem.write(file.path, file.content) } - const patch = await Snapshot.patch(before!) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) expect(patch.files.length).toBe(4) for (const file of unicodeFiles) { expect(patch.files).toContain(file.path) } - await Snapshot.revert([patch]) + await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([patch]))) for (const file of unicodeFiles) { expect( @@ -362,17 +367,17 @@ test.skip("unicode filenames modification and restore", async () => { await Filesystem.write(chineseFile, "original chinese") await Filesystem.write(cyrillicFile, "original cyrillic") - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(chineseFile, "modified chinese") await Filesystem.write(cyrillicFile, "modified cyrillic") - const patch = await Snapshot.patch(before!) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) expect(patch.files).toContain(chineseFile) expect(patch.files).toContain(cyrillicFile) - await Snapshot.revert([patch]) + await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([patch]))) expect(await fs.readFile(chineseFile, "utf-8")).toBe("original chinese") expect(await fs.readFile(cyrillicFile, "utf-8")).toBe("original cyrillic") @@ -385,17 +390,17 @@ test("unicode filenames in subdirectories", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await $`mkdir -p "${tmp.path}/目录/подкаталог"`.quiet() const deepFile = fwd(tmp.path, "目录", "подкаталог", "文件.txt") await Filesystem.write(deepFile, "deep unicode content") - const patch = await Snapshot.patch(before!) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) expect(patch.files).toContain(deepFile) - await Snapshot.revert([patch]) + await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([patch]))) expect( await fs .access(deepFile) @@ -411,7 +416,7 @@ test("very long filenames", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() const longName = "a".repeat(200) + ".txt" @@ -419,10 +424,10 @@ test("very long filenames", async () => { await Filesystem.write(longFile, "long filename content") - const patch = await Snapshot.patch(before!) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) expect(patch.files).toContain(longFile) - await Snapshot.revert([patch]) + await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([patch]))) expect( await fs .access(longFile) @@ -438,14 +443,14 @@ test("hidden files", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(`${tmp.path}/.hidden`, "hidden content") await Filesystem.write(`${tmp.path}/.gitignore`, "*.log") await Filesystem.write(`${tmp.path}/.config`, "config content") - const patch = await Snapshot.patch(before!) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) expect(patch.files).toContain(fwd(tmp.path, ".hidden")) expect(patch.files).toContain(fwd(tmp.path, ".gitignore")) expect(patch.files).toContain(fwd(tmp.path, ".config")) @@ -458,7 +463,7 @@ test("nested symlinks", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await $`mkdir -p ${tmp.path}/sub/dir`.quiet() @@ -466,7 +471,7 @@ test("nested symlinks", async () => { await fs.symlink(`${tmp.path}/sub/dir/target.txt`, `${tmp.path}/sub/dir/link.txt`, "file") await fs.symlink(`${tmp.path}/sub`, `${tmp.path}/sub-link`, "dir") - const patch = await Snapshot.patch(before!) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) expect(patch.files).toContain(fwd(tmp.path, "sub", "dir", "link.txt")) expect(patch.files).toContain(fwd(tmp.path, "sub-link")) }, @@ -478,7 +483,7 @@ test("file permissions and ownership changes", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() // Change permissions multiple times @@ -486,7 +491,7 @@ test("file permissions and ownership changes", async () => { await $`chmod 755 ${tmp.path}/a.txt`.quiet() await $`chmod 644 ${tmp.path}/a.txt`.quiet() - const patch = await Snapshot.patch(before!) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) // Note: git doesn't track permission changes on existing files by default // Only tracks executable bit when files are first added expect(patch.files.length).toBe(0) @@ -499,13 +504,13 @@ test("circular symlinks", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() // Create circular symlink await fs.symlink(`${tmp.path}/circular`, `${tmp.path}/circular`, "dir").catch(() => {}) - const patch = await Snapshot.patch(before!) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) expect(patch.files.length).toBeGreaterThanOrEqual(0) // Should not crash }, }) @@ -516,14 +521,14 @@ test("gitignore changes", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(`${tmp.path}/.gitignore`, "*.ignored") await Filesystem.write(`${tmp.path}/test.ignored`, "ignored content") await Filesystem.write(`${tmp.path}/normal.txt`, "normal content") - const patch = await Snapshot.patch(before!) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) // Should track gitignore itself expect(patch.files).toContain(fwd(tmp.path, ".gitignore")) @@ -540,7 +545,7 @@ test("git info exclude changes", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() const file = `${tmp.path}/.git/info/exclude` @@ -549,12 +554,12 @@ test("git info exclude changes", async () => { await Bun.write(`${tmp.path}/ignored.txt`, "ignored content") await Bun.write(`${tmp.path}/normal.txt`, "normal content") - const patch = await Snapshot.patch(before!) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) expect(patch.files).toContain(fwd(tmp.path, "normal.txt")) expect(patch.files).not.toContain(fwd(tmp.path, "ignored.txt")) - const after = await Snapshot.track() - const diffs = await Snapshot.diffFull(before!, after!) + const after = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) + const diffs = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diffFull(before!, after!))) expect(diffs.some((x) => x.file === "normal.txt")).toBe(true) expect(diffs.some((x) => x.file === "ignored.txt")).toBe(false) }, @@ -574,7 +579,7 @@ test("git info exclude keeps global excludes", async () => { const prev = process.env.GIT_CONFIG_GLOBAL process.env.GIT_CONFIG_GLOBAL = config try { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() const file = `${tmp.path}/.git/info/exclude` @@ -585,7 +590,7 @@ test("git info exclude keeps global excludes", async () => { await Bun.write(`${tmp.path}/info.tmp`, "info content") await Bun.write(`${tmp.path}/normal.txt`, "normal content") - const patch = await Snapshot.patch(before!) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) expect(patch.files).toContain(fwd(tmp.path, "normal.txt")) expect(patch.files).not.toContain(fwd(tmp.path, "global.tmp")) expect(patch.files).not.toContain(fwd(tmp.path, "info.tmp")) @@ -602,7 +607,7 @@ test("concurrent file operations during patch", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() // Start creating files @@ -615,7 +620,7 @@ test("concurrent file operations during patch", async () => { })() // Get patch while files are being created - const patchPromise = Snapshot.patch(before!) + const patchPromise = AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) await createPromise const patch = await patchPromise @@ -634,9 +639,9 @@ test("snapshot state isolation between projects", async () => { await Instance.provide({ directory: tmp1.path, fn: async () => { - const before1 = await Snapshot.track() + const before1 = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) await Filesystem.write(`${tmp1.path}/project1.txt`, "project1 content") - const patch1 = await Snapshot.patch(before1!) + const patch1 = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before1!))) expect(patch1.files).toContain(fwd(tmp1.path, "project1.txt")) }, }) @@ -644,9 +649,9 @@ test("snapshot state isolation between projects", async () => { await Instance.provide({ directory: tmp2.path, fn: async () => { - const before2 = await Snapshot.track() + const before2 = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) await Filesystem.write(`${tmp2.path}/project2.txt`, "project2 content") - const patch2 = await Snapshot.patch(before2!) + const patch2 = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before2!))) expect(patch2.files).toContain(fwd(tmp2.path, "project2.txt")) // Ensure project1 files don't appear in project2 @@ -664,20 +669,20 @@ test("patch detects changes in secondary worktree", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - expect(await Snapshot.track()).toBeTruthy() + expect(await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track()))).toBeTruthy() }, }) await Instance.provide({ directory: worktreePath, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() const worktreeFile = fwd(worktreePath, "worktree.txt") await Filesystem.write(worktreeFile, "worktree content") - const patch = await Snapshot.patch(before!) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) expect(patch.files).toContain(worktreeFile) }, }) @@ -696,7 +701,7 @@ test("revert only removes files in invoking worktree", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - expect(await Snapshot.track()).toBeTruthy() + expect(await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track()))).toBeTruthy() }, }) const primaryFile = `${tmp.path}/worktree.txt` @@ -705,14 +710,14 @@ test("revert only removes files in invoking worktree", async () => { await Instance.provide({ directory: worktreePath, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() const worktreeFile = fwd(worktreePath, "worktree.txt") await Filesystem.write(worktreeFile, "worktree content") - const patch = await Snapshot.patch(before!) - await Snapshot.revert([patch]) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(before!))) + await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([patch]))) expect( await fs @@ -740,14 +745,14 @@ test("diff reports worktree-only/shared edits and ignores primary-only", async ( await Instance.provide({ directory: tmp.path, fn: async () => { - expect(await Snapshot.track()).toBeTruthy() + expect(await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track()))).toBeTruthy() }, }) await Instance.provide({ directory: worktreePath, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(`${worktreePath}/worktree-only.txt`, "worktree diff content") @@ -755,7 +760,7 @@ test("diff reports worktree-only/shared edits and ignores primary-only", async ( await Filesystem.write(`${tmp.path}/shared.txt`, "primary edit") await Filesystem.write(`${tmp.path}/primary-only.txt`, "primary change") - const diff = await Snapshot.diff(before!) + const diff = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diff(before!))) expect(diff).toContain("worktree-only.txt") expect(diff).toContain("shared.txt") expect(diff).not.toContain("primary-only.txt") @@ -774,15 +779,15 @@ test("track with no changes returns same hash", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const hash1 = await Snapshot.track() + const hash1 = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(hash1).toBeTruthy() // Track again with no changes - const hash2 = await Snapshot.track() + const hash2 = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(hash2).toBe(hash1!) // Track again - const hash3 = await Snapshot.track() + const hash3 = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(hash3).toBe(hash1!) }, }) @@ -793,7 +798,7 @@ test("diff function with various changes", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() // Make various changes @@ -801,7 +806,7 @@ test("diff function with various changes", async () => { await Filesystem.write(`${tmp.path}/new.txt`, "new content") await Filesystem.write(`${tmp.path}/b.txt`, "modified content") - const diff = await Snapshot.diff(before!) + const diff = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diff(before!))) expect(diff).toContain("a.txt") expect(diff).toContain("b.txt") expect(diff).toContain("new.txt") @@ -814,7 +819,7 @@ test("restore function", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() // Make changes @@ -823,7 +828,7 @@ test("restore function", async () => { await Filesystem.write(`${tmp.path}/b.txt`, "modified") // Restore to original state - await Snapshot.restore(before!) + await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.restore(before!))) expect( await fs @@ -848,20 +853,20 @@ test("revert should not delete files that existed but were deleted in snapshot", await Instance.provide({ directory: tmp.path, fn: async () => { - const snapshot1 = await Snapshot.track() + const snapshot1 = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(snapshot1).toBeTruthy() await $`rm ${tmp.path}/a.txt`.quiet() - const snapshot2 = await Snapshot.track() + const snapshot2 = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(snapshot2).toBeTruthy() await Filesystem.write(`${tmp.path}/a.txt`, "recreated content") - const patch = await Snapshot.patch(snapshot2!) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(snapshot2!))) expect(patch.files).toContain(fwd(tmp.path, "a.txt")) - await Snapshot.revert([patch]) + await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([patch]))) expect( await fs @@ -880,18 +885,18 @@ test("revert preserves file that existed in snapshot when deleted then recreated fn: async () => { await Filesystem.write(`${tmp.path}/existing.txt`, "original content") - const snapshot = await Snapshot.track() + const snapshot = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(snapshot).toBeTruthy() await $`rm ${tmp.path}/existing.txt`.quiet() await Filesystem.write(`${tmp.path}/existing.txt`, "recreated") await Filesystem.write(`${tmp.path}/newfile.txt`, "new") - const patch = await Snapshot.patch(snapshot!) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(snapshot!))) expect(patch.files).toContain(fwd(tmp.path, "existing.txt")) expect(patch.files).toContain(fwd(tmp.path, "newfile.txt")) - await Snapshot.revert([patch]) + await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([patch]))) expect( await fs @@ -919,7 +924,7 @@ test("diffFull sets status based on git change type", async () => { await Filesystem.write(`${tmp.path}/trim.txt`, "line1\nline2\n") await Filesystem.write(`${tmp.path}/delete.txt`, "gone") - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(`${tmp.path}/grow.txt`, "one\ntwo\n") @@ -927,10 +932,10 @@ test("diffFull sets status based on git change type", async () => { await $`rm ${tmp.path}/delete.txt`.quiet() await Filesystem.write(`${tmp.path}/added.txt`, "new") - const after = await Snapshot.track() + const after = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(after).toBeTruthy() - const diffs = await Snapshot.diffFull(before!, after!) + const diffs = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diffFull(before!, after!))) expect(diffs.length).toBe(4) const added = diffs.find((d) => d.file === "added.txt") @@ -961,15 +966,15 @@ test("diffFull with new file additions", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(`${tmp.path}/new.txt`, "new content") - const after = await Snapshot.track() + const after = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(after).toBeTruthy() - const diffs = await Snapshot.diffFull(before!, after!) + const diffs = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diffFull(before!, after!))) expect(diffs.length).toBe(1) const newFileDiff = diffs[0] @@ -999,7 +1004,7 @@ test("diffFull with a large interleaved mixed diff", async () => { ...bin.map((file, i) => Filesystem.write(file, new Uint8Array([0, i, 255, i % 251]))), ]) - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Promise.all([ @@ -1009,10 +1014,10 @@ test("diffFull with a large interleaved mixed diff", async () => { ...del.map((file) => fs.rm(file)), ]) - const after = await Snapshot.track() + const after = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(after).toBeTruthy() - const diffs = await Snapshot.diffFull(before!, after!) + const diffs = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diffFull(before!, after!))) expect(diffs).toHaveLength(ids.length * 4) const map = new Map(diffs.map((item) => [item.file, item])) @@ -1054,17 +1059,17 @@ test("diffFull preserves git diff order across batch boundaries", async () => { await $`mkdir -p ${tmp.path}/order`.quiet() await Promise.all(ids.map((id) => Filesystem.write(`${tmp.path}/order/${id}.txt`, `before-${id}`))) - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Promise.all(ids.map((id) => Filesystem.write(`${tmp.path}/order/${id}.txt`, `after-${id}`))) - const after = await Snapshot.track() + const after = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(after).toBeTruthy() const expected = ids.map((id) => `order/${id}.txt`) - const diffs = await Snapshot.diffFull(before!, after!) + const diffs = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diffFull(before!, after!))) expect(diffs.map((item) => item.file)).toEqual(expected) }, }) @@ -1075,15 +1080,15 @@ test("diffFull with file modifications", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(`${tmp.path}/b.txt`, "modified content") - const after = await Snapshot.track() + const after = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(after).toBeTruthy() - const diffs = await Snapshot.diffFull(before!, after!) + const diffs = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diffFull(before!, after!))) expect(diffs.length).toBe(1) const modifiedFileDiff = diffs[0] @@ -1101,15 +1106,15 @@ test("diffFull with file deletions", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await $`rm ${tmp.path}/a.txt`.quiet() - const after = await Snapshot.track() + const after = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(after).toBeTruthy() - const diffs = await Snapshot.diffFull(before!, after!) + const diffs = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diffFull(before!, after!))) expect(diffs.length).toBe(1) const removedFileDiff = diffs[0] @@ -1126,15 +1131,15 @@ test("diffFull with multiple line additions", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(`${tmp.path}/multi.txt`, "line1\nline2\nline3") - const after = await Snapshot.track() + const after = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(after).toBeTruthy() - const diffs = await Snapshot.diffFull(before!, after!) + const diffs = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diffFull(before!, after!))) expect(diffs.length).toBe(1) const multiDiff = diffs[0] @@ -1152,16 +1157,16 @@ test("diffFull with addition and deletion", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(`${tmp.path}/added.txt`, "added content") await $`rm ${tmp.path}/a.txt`.quiet() - const after = await Snapshot.track() + const after = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(after).toBeTruthy() - const diffs = await Snapshot.diffFull(before!, after!) + const diffs = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diffFull(before!, after!))) expect(diffs.length).toBe(2) const addedFileDiff = diffs.find((d) => d.file === "added.txt") @@ -1184,7 +1189,7 @@ test("diffFull with multiple additions and deletions", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(`${tmp.path}/multi1.txt`, "line1\nline2\nline3") @@ -1192,10 +1197,10 @@ test("diffFull with multiple additions and deletions", async () => { await $`rm ${tmp.path}/a.txt`.quiet() await $`rm ${tmp.path}/b.txt`.quiet() - const after = await Snapshot.track() + const after = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(after).toBeTruthy() - const diffs = await Snapshot.diffFull(before!, after!) + const diffs = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diffFull(before!, after!))) expect(diffs.length).toBe(4) const multi1Diff = diffs.find((d) => d.file === "multi1.txt") @@ -1226,13 +1231,13 @@ test("diffFull with no changes", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() - const after = await Snapshot.track() + const after = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(after).toBeTruthy() - const diffs = await Snapshot.diffFull(before!, after!) + const diffs = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diffFull(before!, after!))) expect(diffs.length).toBe(0) }, }) @@ -1243,15 +1248,15 @@ test("diffFull with binary file changes", async () => { await Instance.provide({ directory: tmp.path, fn: async () => { - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(`${tmp.path}/binary.bin`, new Uint8Array([0x00, 0x01, 0x02, 0x03])) - const after = await Snapshot.track() + const after = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(after).toBeTruthy() - const diffs = await Snapshot.diffFull(before!, after!) + const diffs = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diffFull(before!, after!))) expect(diffs.length).toBe(1) const binaryDiff = diffs[0] @@ -1267,15 +1272,15 @@ test("diffFull with whitespace changes", async () => { directory: tmp.path, fn: async () => { await Filesystem.write(`${tmp.path}/whitespace.txt`, "line1\nline2") - const before = await Snapshot.track() + const before = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(before).toBeTruthy() await Filesystem.write(`${tmp.path}/whitespace.txt`, "line1\n\nline2\n") - const after = await Snapshot.track() + const after = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(after).toBeTruthy() - const diffs = await Snapshot.diffFull(before!, after!) + const diffs = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.diffFull(before!, after!))) expect(diffs.length).toBe(1) const whitespaceDiff = diffs[0] @@ -1292,26 +1297,26 @@ test("revert with overlapping files across patches uses first patch hash", async fn: async () => { // Write initial content and snapshot await Filesystem.write(`${tmp.path}/shared.txt`, "v1") - const snap1 = await Snapshot.track() + const snap1 = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(snap1).toBeTruthy() // Modify and snapshot again await Filesystem.write(`${tmp.path}/shared.txt`, "v2") - const snap2 = await Snapshot.track() + const snap2 = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(snap2).toBeTruthy() // Modify once more so both patches include shared.txt await Filesystem.write(`${tmp.path}/shared.txt`, "v3") - const patch1 = await Snapshot.patch(snap1!) - const patch2 = await Snapshot.patch(snap2!) + const patch1 = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(snap1!))) + const patch2 = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(snap2!))) // Both patches should include shared.txt expect(patch1.files).toContain(fwd(tmp.path, "shared.txt")) expect(patch2.files).toContain(fwd(tmp.path, "shared.txt")) // Revert with patch1 first — should use snap1's hash (restoring "v1") - await Snapshot.revert([patch1, patch2]) + await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([patch1, patch2]))) const content = await fs.readFile(`${tmp.path}/shared.txt`, "utf-8") expect(content).toBe("v1") @@ -1328,24 +1333,24 @@ test("revert preserves patch order when the same hash appears again", async () = await Filesystem.write(`${tmp.path}/foo/bar`, "v1") await Filesystem.write(`${tmp.path}/a.txt`, "v1") - const snap1 = await Snapshot.track() + const snap1 = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(snap1).toBeTruthy() await $`rm -rf ${tmp.path}/foo`.quiet() await Filesystem.write(`${tmp.path}/foo`, "v2") await Filesystem.write(`${tmp.path}/a.txt`, "v2") - const snap2 = await Snapshot.track() + const snap2 = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(snap2).toBeTruthy() await $`rm -rf ${tmp.path}/foo`.quiet() await Filesystem.write(`${tmp.path}/a.txt`, "v3") - await Snapshot.revert([ + await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([ { hash: snap1!, files: [fwd(tmp.path, "a.txt")] }, { hash: snap2!, files: [fwd(tmp.path, "foo")] }, { hash: snap1!, files: [fwd(tmp.path, "foo", "bar")] }, - ]) + ]))) expect(await fs.readFile(`${tmp.path}/a.txt`, "utf-8")).toBe("v1") expect((await fs.stat(`${tmp.path}/foo`)).isDirectory()).toBe(true) @@ -1365,16 +1370,16 @@ test("revert handles large mixed batches across chunk boundaries", async () => { await $`mkdir -p ${tmp.path}/batch ${tmp.path}/fresh`.quiet() await Promise.all(base.map((file, i) => Filesystem.write(file, `base-${i}`))) - const snap = await Snapshot.track() + const snap = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.track())) expect(snap).toBeTruthy() await Promise.all(base.map((file, i) => Filesystem.write(file, `next-${i}`))) await Promise.all(fresh.map((file, i) => Filesystem.write(file, `fresh-${i}`))) - const patch = await Snapshot.patch(snap!) + const patch = await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.patch(snap!))) expect(patch.files.length).toBe(base.length + fresh.length) - await Snapshot.revert([patch]) + await AppRuntime.runPromise(Snapshot.Service.use((svc) => svc.revert([patch]))) await Promise.all( base.map(async (file, i) => {