import { afterEach, expect, it, vi } from "vitest"; import { defineToolcraft } from "../../schema/define-toolcraft"; import { mediaSourceModule } from "../../modules/built-ins/media-source/media-source-module"; import { createToolcraftState } from "../../state/create-template-state"; import { createToolcraftPersistenceController } from "../../composition/public-persistence"; import { createToolcraftExternalStore } from "../../composition/public-state"; import { acquireToolcraftSourceAssetOwner } from "./toolcraft-source-asset-owner"; afterEach(() => { vi.unstubAllGlobals(); window.localStorage.clear(); }); it("wires durable snapshot retention through the default browser source owner", async () => { vi.stubGlobal("indexedDB", undefined); const control = { applicability: { mode: "always" as const }, type: "fileDrop", assetKind: "file", target: "source.files", multiple: true, } as const; const schema = defineToolcraft({ base: { identity: { id: "owner-workspace", title: "Toolcraft fixture", }, canvas: { enabled: true }, panels: { controls: { title: "Controls", sections: [ { id: "fixture-section-1", title: "Source", controls: { files: control }, }, ], }, }, persistence: { storage: "localStorage", }, }, modules: [mediaSourceModule()], }); const store = createToolcraftExternalStore(createToolcraftState(schema)); const owner = acquireToolcraftSourceAssetOwner({ store }); const controller = createToolcraftPersistenceController({ schema, getCommittedState: store.getCommittedState, storage: window.localStorage, }); const upload = (name: string) => { const file = Object.assign(new File([name], name, { type: "text/plain" }), { arrayBuffer: async () => new TextEncoder().encode(name).buffer, }); return owner.coordinator.importBatch( { files: [file], origin: "panel", target: control.target }, control, ); }; try { expect((await upload("a.txt")).kind).toBe("committed"); const asset = store.getCommittedState().mediaAssets[0]!; if (asset.assetKind === "model") throw new Error("Expected binary file"); expect(controller.flush()).toEqual({ status: "success" }); store.dispatch({ type: "history.undo" }); expect((await upload("b.txt")).kind).toBe("committed"); expect(store.getCommittedState().history.redo).toHaveLength(0); // No replacement save: the actual saved A, not live B, still owns its bytes. const bytes = await owner.coordinator.resolveResource?.(asset.resourceRef, { signal: new AbortController().signal, }); expect(bytes).not.toBeNull(); expect(Array.from(bytes ?? [])).toEqual([97, 46, 116, 120, 116]); } finally { controller.dispose(); owner.release(); await owner.coordinator.dispose(); } });