import assert from "node:assert/strict"; import fs from "node:fs"; import path from "node:path"; import test from "node:test"; import ts from "typescript"; const repoRoot = path.resolve(import.meta.dirname, "../../.."); const sourcePath = path.join(repoRoot, "web/protocol/dirty-state.ts"); const transpiled = ts.transpileModule(fs.readFileSync(sourcePath, "utf8"), { compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 }, fileName: sourcePath, reportDiagnostics: true, }); assert.deepEqual(transpiled.diagnostics, []); const moduleUrl = `data:text/javascript;base64,${Buffer.from(transpiled.outputText).toString("base64")}`; const { acceptHistoryTransaction, acceptMainSave, acceptMainTransaction, createDirtyState, recoverDirtyState } = await import(moduleUrl); test("M7-06 only marks dirty after an accepted Main transaction", () => { const clean = createDirtyState(7); const stale = acceptMainTransaction(clean, 7); assert.deepEqual(stale, { ok: false, state: clean, errorCode: "DIRTY_REVISION_STALE" }); const edited = acceptMainTransaction(clean, 8); assert.equal(edited.ok, true); assert.deepEqual(edited.state, { currentMainRevision: 8, committedMainRevision: 7, dirty: true }); }); test("M7-06 failed, preview and UI-only work preserve the same dirty object", () => { const clean = createDirtyState(3); const failedCommandState = clean; const previewState = failedCommandState; const uiOnlyState = previewState; assert.equal(failedCommandState, clean); assert.equal(previewState, clean); assert.equal(uiOnlyState, clean); }); test("M7-06 clears dirty only for a save matching the accepted Main revision", () => { const edited = recoverDirtyState(9, 7); assert.deepEqual(acceptMainSave(edited, 8), { ok: false, state: edited, errorCode: "DIRTY_SAVE_REVISION_MISMATCH" }); const saved = acceptMainSave(edited, 9); assert.equal(saved.ok, true); assert.deepEqual(saved.state, { currentMainRevision: 9, committedMainRevision: 9, dirty: false }); }); test("M7-07 keeps transaction revisions monotonic while undo content toggles dirty", () => { const saved = createDirtyState(10); const edited = acceptMainTransaction(saved, 11); assert.equal(edited.ok, true); const undone = acceptHistoryTransaction(edited.state, 12, true); assert.equal(undone.ok, true); assert.deepEqual(undone.state, { currentMainRevision: 12, committedMainRevision: 12, dirty: false }); const redone = acceptHistoryTransaction(undone.state, 13, false); assert.equal(redone.ok, true); assert.deepEqual(redone.state, { currentMainRevision: 13, committedMainRevision: 12, dirty: true }); });