import type { ColineWorkspaceCapabilities, ColineTask, ColineCalendarEvent, ColineMessage, } from "../capabilities/workspace"; import type { TestWorkspaceState } from "./fake-capabilities"; export function createFakeWorkspaceCapabilities( state: TestWorkspaceState, userId: string | null, ): ColineWorkspaceCapabilities { const missing = (): never => { throw Object.assign(new Error("Workspace object not found."), { code: "not_found" }); }; return { tasks: { list: async (input) => { const all = [...state.tasks.values()].filter( (t) => t.taskboardId === input.taskboardId && (!input.query || t.title.toLowerCase().includes(input.query.toLowerCase())), ); const start = Number(input.cursor ?? 0), limit = input.limit ?? 50; return { tasks: structuredClone(all.slice(start, start + limit)), nextCursor: all.length > start + limit ? String(start + limit) : null, }; }, board: async (id) => ({ id, statuses: [{ id: "status_open", name: "Open" }] }), get: async (board, id) => { const task = state.tasks.get(id); if (!task || task.taskboardId !== board) return missing(); return structuredClone(task); }, create: async (board, input) => { const task: ColineTask = { id: `task_${state.tasks.size + 1}`, taskboardId: board, title: input.title, description: input.description ?? "", statusId: input.statusId ?? "status_open", priority: input.priority ?? "none", dueDate: input.dueDate ?? null, assigneeUserIds: input.assigneeUserIds ?? [], }; state.tasks.set(task.id, structuredClone(task)); return task; }, update: async (board, id, input) => { const old = state.tasks.get(id); if (!old || old.taskboardId !== board) return missing(); const task = { ...old, ...input }; state.tasks.set(id, structuredClone(task)); return task; }, delete: async (board, id) => { if (state.tasks.get(id)?.taskboardId !== board) return missing(); state.tasks.delete(id); }, }, calendar: { list: async (input) => ({ events: structuredClone( [...state.calendar.values()].filter( (e) => e.startsAt < input.end && e.endsAt > input.start, ), ), }), get: async (id) => structuredClone(state.calendar.get(id) ?? missing()), create: async (input) => { if (Date.parse(input.endsAt) <= Date.parse(input.startsAt)) throw new Error("Event end must follow start."); const event: ColineCalendarEvent = { ...input, id: `event_${state.calendar.size + 1}`, description: input.description ?? null, location: input.location ?? null, isAllDay: input.isAllDay ?? false, }; state.calendar.set(event.id, structuredClone(event)); return event; }, update: async (id, input) => { const old = state.calendar.get(id) ?? missing(); const event = { ...old, ...input }; state.calendar.set(id, structuredClone(event)); return event; }, delete: async (id) => { if (!state.calendar.delete(id)) missing(); }, }, messages: { list: async (input) => ({ messages: structuredClone( (state.messages.get(`${input.conversation.type}:${input.conversation.id}`) ?? []).slice( 0, input.limit ?? 50, ), ), nextCursor: null, }), send: async (input) => { const key = `${input.conversation.type}:${input.conversation.id}`; const list = state.messages.get(key) ?? []; const message: ColineMessage = { id: `message_${list.length + 1}`, text: input.text, createdAt: new Date().toISOString(), authorUserId: userId, }; list.push(message); state.messages.set(key, list); return structuredClone(message); }, }, references: { resolve: async (reference) => { let title: string | undefined; if (reference.kind === "file") title = state.files.get(reference.id)?.name; if (reference.kind === "task") title = state.tasks.get(reference.id)?.title; if (reference.kind === "event") title = state.calendar.get(reference.id)?.title; if (reference.kind === "member") title = reference.id; if (reference.kind === "app_record") { for (const records of state.records.values()) { const data = records.get(reference.id)?.data; if (data) title = String(data.title ?? data.name ?? "Record"); } } if (!title) return missing(); return { reference, title, href: null }; }, }, }; }