| 1 | import type { AppBindings } from "./bridge"; |
| 2 | import type { MockSessionLifecycleBindings } from "./sessionLifecycleBindings"; |
| 3 | import type { SessionRef } from "./sessionRef"; |
| 4 | import type { WorkspaceSnapshot } from "../generated/desktopContract.generated"; |
| 5 | import type { ProjectNode } from "./types"; |
| 6 | import { trash, recover, inspect, remove } from "./topicRemovalMock"; |
| 7 | export function makeMockSessionLifecycleBindings(mockWorkspaceSnapshot: () => WorkspaceSnapshot, mockArchivedSessionIDs: Set<string>, mockPurgedSessionIDs: Set<string>, notifyMockProjectTreeChanged: () => void, tree: ProjectNode[] = []): MockSessionLifecycleBindings { |
| 8 | const mockLifecycleResults = new Map<string, { request: string; result: import("../generated/desktopContract.generated").SessionLifecycleResult }>(); |
| 9 | return { |
| 10 | async InspectTopicRemoval(target) { return inspect(tree, target); }, |
| 11 | async RemoveTopic(request) { return remove(tree, request); }, |
| 12 | async PurgeCanonicalSession(ref: SessionRef) { |
| 13 | if (!mockArchivedSessionIDs.has(ref.sessionId) && !mockPurgedSessionIDs.has(ref.sessionId)) throw new Error("Only archived sessions can be permanently deleted"); |
| 14 | mockPurgedSessionIDs.add(ref.sessionId); mockArchivedSessionIDs.delete(ref.sessionId); notifyMockProjectTreeChanged(); |
| 15 | }, |
| 16 | async ListTrashEntries(this: AppBindings, query, cursor, limit) { |
| 17 | const items: import("../generated/desktopContract.generated").TrashEntry[] = []; |
| 18 | for (const workspace of mockWorkspaceSnapshot().workspaces) { |
| 19 | const page = await this.ListWorkspaceSessions(workspace.id, query, "", 1000, true); |
| 20 | items.push(...page.sessions.filter(row => row.archived).map(row => ({ id: row.ref.sessionId, ref: row.ref, title: row.title, |
| 21 | workspaceId: workspace.id, workspaceTitle: workspace.title, archivedAt: 0, health: "ready", canPreview: true, canRestore: true, canPurge: true }))); |
| 22 | } |
| 23 | const metadata = trash(tree); |
| 24 | items.push(...metadata.items.filter(item => item.title.toLowerCase().includes(query.toLowerCase()))); |
| 25 | const offset = Number(cursor || 0), end = offset + limit; |
| 26 | return { items: items.slice(offset, end), generation: metadata.generation, nextCursor: end < items.length ? String(end) : "" }; |
| 27 | }, |
| 28 | async ApplySessionLifecycle(this: AppBindings, request) { |
| 29 | const previous = mockLifecycleResults.get(request.operationId), encoded = JSON.stringify(request); |
| 30 | if (previous) { if (previous.request !== encoded) throw new Error("Lifecycle request conflict"); return previous.result; } |
| 31 | const result: import("../generated/desktopContract.generated").SessionLifecycleResult = { operationId: request.operationId, generation: 1, committed: true, items: [] }; |
| 32 | for (const target of request.targets) { |
| 33 | if (target.recoveryEntryId?.startsWith("topic-removal:")) { |
| 34 | if (!target.workspaceId) throw new Error("Missing recovery workspace"); |
| 35 | recover(tree, target.recoveryEntryId, target.workspaceId, request.action); |
| 36 | result.items.push({ target, workspaceId: target.workspaceId, committed: true, retryable: false }); |
| 37 | result.generation = trash(tree).generation; |
| 38 | continue; |
| 39 | } |
| 40 | if (!target.ref) throw new Error("Unknown mock recovery entry"); |
| 41 | if (request.action === "archive") await this.ArchiveCanonicalSession(target.ref); |
| 42 | else if (request.action === "restore") await this.RestoreCanonicalSession(target.ref); |
| 43 | else await this.PurgeCanonicalSession(target.ref); |
| 44 | const workspace = mockWorkspaceSnapshot().workspaces.find(row => row.sessionIds.includes(target.ref!.sessionId)); |
| 45 | result.items.push({ target, ref: target.ref, workspaceId: workspace?.id || "global", committed: true, retryable: false }); |
| 46 | } |
| 47 | mockLifecycleResults.set(request.operationId, { request: encoded, result }); |
| 48 | return result; |
| 49 | }, |
| 50 | async ListRecoveryEntries() { return { items: [], generation: 1 }; }, |
| 51 | async PreviewRecoveryEntry() { return { messages: [], startTurn: 0, endTurn: 0, totalTurns: 0, hasOlder: false }; }, |
| 52 | async RestoreRecoveryEntry() { throw new Error("recovery entry is unavailable"); }, |
| 53 | async GetSessionUpgradeStatus() { return { sources: 0, sessions: 0, operations: 0, discovered: 0, migrated: 0, pending: 0, failed: 0, conflicts: 0, pendingOperations: 0 }; }, |
| 54 | }; |
| 55 | } |
| 56 |