import { eq, and, type Db, missionTasks } from '@mosaicstack/db'; export type MissionTask = typeof missionTasks.$inferSelect; export type NewMissionTask = typeof missionTasks.$inferInsert; // SHARED-CONTRACT §5.1 phase 1 / §5.4: mission_tasks.status is prohibited as a // write source through the N-1 window. This repo is the sole path that authors // status from caller input, so the field is stripped here — accepted and // ignored rather than rejected, because the legacy surface is frozen with // existing consumers kept working (tool-gateway-mapping.md §3.2). Two other // surfaces touch the column and are deliberately NOT stripped: // packages/storage/migrate-tier.ts copies whole rows between storage tiers and // must preserve the stored value verbatim, and the generic table-keyed storage // adapters register mission_tasks but have no caller that targets it (runtime // callers use fixed collection constants). Neither authors a new status. The // column keeps its DB default, stays declared and readable, and is retired // only after no readers remain. function stripStatus(data: T): Omit { const rest = { ...data }; delete rest.status; return rest; } export function createMissionTasksRepo(db: Db) { return { async findByMission(missionId: string): Promise { return db.select().from(missionTasks).where(eq(missionTasks.missionId, missionId)); }, async findByMissionAndUser(missionId: string, userId: string): Promise { return db .select() .from(missionTasks) .where(and(eq(missionTasks.missionId, missionId), eq(missionTasks.userId, userId))); }, async findById(id: string): Promise { const rows = await db.select().from(missionTasks).where(eq(missionTasks.id, id)); return rows[0]; }, async findByIdAndUser(id: string, userId: string): Promise { const rows = await db .select() .from(missionTasks) .where(and(eq(missionTasks.id, id), eq(missionTasks.userId, userId))); return rows[0]; }, async create(data: NewMissionTask): Promise { const rows = await db.insert(missionTasks).values(stripStatus(data)).returning(); return rows[0]!; }, async update(id: string, data: Partial): Promise { const rows = await db .update(missionTasks) .set({ ...stripStatus(data), updatedAt: new Date() }) .where(eq(missionTasks.id, id)) .returning(); return rows[0]; }, async remove(id: string): Promise { const rows = await db.delete(missionTasks).where(eq(missionTasks.id, id)).returning(); return rows.length > 0; }, async removeByMission(missionId: string): Promise { const rows = await db .delete(missionTasks) .where(eq(missionTasks.missionId, missionId)) .returning(); return rows.length; }, }; } export type MissionTasksRepo = ReturnType;