| 1 | import type { SessionDraftView, SessionDraftSubmissionView, SessionDraftSaveRequest, SessionDraftSubmissionRequest } from "../generated/desktopContract.generated"; |
| 2 | import type { PreviousDraftBindings } from "./previousDraftBindings"; |
| 3 | |
| 4 | export function makePreviousDraftMock(): PreviousDraftBindings { |
| 5 | const mockDrafts = new Map<string, SessionDraftView>(); |
| 6 | const mockDraftOperations = new Map<string, SessionDraftSubmissionView>(); |
| 7 | const mockDraftForTarget = (scope: string, workspaceRoot: string): SessionDraftView => { |
| 8 | const normalizedScope = scope === "project" && workspaceRoot ? "project" : "global"; |
| 9 | const normalizedRoot = normalizedScope === "project" ? workspaceRoot : ""; |
| 10 | const workspaceId = normalizedScope === "global" ? "global" : `project-${normalizedRoot}`; |
| 11 | const prior = [...mockDrafts.values()].find((draft) => draft.workspaceId === workspaceId && draft.status === "active"); |
| 12 | if (prior) return prior; |
| 13 | throw new Error("No previous draft exists; create a formal conversation"); |
| 14 | }; |
| 15 | return { |
| 16 | async OpenSessionDraft(workspaceId: string) { |
| 17 | const prior = [...mockDrafts.values()].find((draft) => draft.workspaceId === workspaceId && draft.status === "active"); |
| 18 | if (prior) return structuredClone(prior); |
| 19 | return structuredClone(mockDraftForTarget(workspaceId === "global" ? "global" : "project", workspaceId === "global" ? "" : workspaceId)); |
| 20 | }, |
| 21 | async OpenSessionDraftForTarget(scope: string, workspaceRoot: string) { |
| 22 | return structuredClone(mockDraftForTarget(scope, workspaceRoot)); |
| 23 | }, |
| 24 | async RestoreSessionDraft() { |
| 25 | return structuredClone([...mockDrafts.values()].find((draft) => draft.status === "active") ?? null); |
| 26 | }, |
| 27 | async SaveSessionDraft(request: SessionDraftSaveRequest) { |
| 28 | const current = mockDrafts.get(request.draftId); |
| 29 | if (!current || current.status !== "active") throw new Error("session draft not found"); |
| 30 | if (current.revision !== request.revision) return { draft: structuredClone(current), conflict: true, outcome: "conflict" }; |
| 31 | const next = { ...current, revision: current.revision + 1, contentJson: request.contentJson, settings: structuredClone(request.settings), updatedAt: Date.now() }; |
| 32 | mockDrafts.set(next.id, next); |
| 33 | return { draft: structuredClone(next), conflict: false, outcome: "saved" }; |
| 34 | }, |
| 35 | async ListSessionDraftSummaries() { |
| 36 | return [...mockDrafts.values()].filter((draft) => draft.status === "active").map((draft) => ({ |
| 37 | id: draft.id, workspaceId: draft.workspaceId, scope: draft.scope, workspaceRoot: draft.workspaceRoot, |
| 38 | revision: draft.revision, hasContent: draft.contentJson !== "{}", state: "saved", updatedAt: draft.updatedAt, |
| 39 | })); |
| 40 | }, |
| 41 | async DiscardSessionDraft(draftId: string, revision: number) { |
| 42 | const current = mockDrafts.get(draftId); |
| 43 | if (!current || current.revision !== revision) throw new Error("session draft revision conflict"); |
| 44 | mockDrafts.set(draftId, { ...current, status: "discarded", revision: current.revision + 1 }); |
| 45 | }, |
| 46 | async DismissSessionDraft(_draftId: string) {}, |
| 47 | async SetSessionDraftRestoreTarget(_draftId: string) {}, |
| 48 | async GetSessionDraft(draftId: string) { |
| 49 | const draft = mockDrafts.get(draftId); |
| 50 | if (!draft) throw new Error("session draft not found"); |
| 51 | return structuredClone(draft); |
| 52 | }, |
| 53 | async GetDraftContext(draftId: string) { |
| 54 | const draft = mockDrafts.get(draftId); |
| 55 | if (!draft) throw new Error("session draft not found"); |
| 56 | return { draft: structuredClone(draft), commands: [], servers: [] }; |
| 57 | }, |
| 58 | async GetSessionDraftState(draftId: string) { |
| 59 | const draft = mockDrafts.get(draftId); |
| 60 | if (!draft) throw new Error("session draft not found"); |
| 61 | const operation = [...mockDraftOperations.values()].reverse().find(item => item.draftId === draftId); |
| 62 | return structuredClone({ draft, operation }); |
| 63 | }, |
| 64 | async ResumeDraftSubmission(operationId: string, revision: number) { |
| 65 | const operation = mockDraftOperations.get(operationId); |
| 66 | if (!operation || operation.revision !== revision) throw new Error("operation revision conflict"); |
| 67 | return structuredClone(operation); |
| 68 | }, |
| 69 | async BeginDraftSubmission(request: SessionDraftSubmissionRequest) { |
| 70 | const draft = mockDrafts.get(request.draftId); |
| 71 | if (!draft || draft.revision !== request.revision) throw new Error("session draft revision conflict"); |
| 72 | const operationId = `draft-op-mock-${mockDraftOperations.size + 1}`; |
| 73 | const sessionId = `draft-session-mock-${mockDraftOperations.size + 1}`; |
| 74 | const view: SessionDraftSubmissionView = { operationId, draftId: draft.id, requestId: request.requestId, revision: 1, canResume: false, canEdit: false, canCancel: false, canDiscard: false, phase: "accepted", submissionId: `draft-submit-mock-${mockDraftOperations.size + 1}`, session: { hostId: "local", sessionId }, updatedAt: Date.now() }; |
| 75 | mockDraftOperations.set(operationId, view); |
| 76 | mockDrafts.set(draft.id, { ...draft, status: "converted", revision: draft.revision + 1 }); |
| 77 | return structuredClone(view); |
| 78 | }, |
| 79 | async GetDraftSubmission(operationId: string) { |
| 80 | const operation = mockDraftOperations.get(operationId); |
| 81 | if (!operation) throw new Error("session draft submission not found"); |
| 82 | return structuredClone(operation); |
| 83 | }, |
| 84 | async CancelDraftSubmission(operationId: string) { |
| 85 | const operation = mockDraftOperations.get(operationId); |
| 86 | if (!operation) throw new Error("session draft submission not found"); |
| 87 | const next = { ...operation, phase: operation.phase === "accepted" ? "accepted" : "cancelled", updatedAt: Date.now() }; |
| 88 | mockDraftOperations.set(operationId, next); |
| 89 | return structuredClone(next); |
| 90 | }, |
| 91 | }; |
| 92 | } |
| 93 |