/** * Drizzle Initiative Repository Adapter * * Implements InitiativeRepository interface using Drizzle ORM. */ import { eq, inArray } from 'drizzle-orm'; import { nanoid } from 'nanoid'; import type { DrizzleDatabase } from '../../index.js'; import { agents, initiatives, initiativeProjects, type Initiative } from '../../schema.js'; import type { InitiativeRepository, CreateInitiativeData, UpdateInitiativeData, } from '../initiative-repository.js'; /** * Drizzle adapter for InitiativeRepository. * * Uses dependency injection for database instance, * enabling isolated test databases. */ export class DrizzleInitiativeRepository implements InitiativeRepository { constructor(private db: DrizzleDatabase) {} async create(data: CreateInitiativeData): Promise { const id = nanoid(); const now = new Date(); const [created] = await this.db.insert(initiatives).values({ id, ...data, status: data.status ?? 'active', createdAt: now, updatedAt: now, }).returning(); return created; } async findById(id: string): Promise { const result = await this.db .select() .from(initiatives) .where(eq(initiatives.id, id)) .limit(1); return result[0] ?? null; } async findAll(): Promise { return this.db.select().from(initiatives); } async findByStatus(status: 'active' | 'completed' | 'archived'): Promise { return this.db .select() .from(initiatives) .where(eq(initiatives.status, status)); } async findByProjectId(projectId: string): Promise { const linkedIds = this.db .select({ id: initiativeProjects.initiativeId }) .from(initiativeProjects) .where(eq(initiativeProjects.projectId, projectId)); return this.db .select() .from(initiatives) .where(inArray(initiatives.id, linkedIds)); } async update(id: string, data: UpdateInitiativeData): Promise { const [updated] = await this.db .update(initiatives) .set({ ...data, updatedAt: new Date() }) .where(eq(initiatives.id, id)) .returning(); if (!updated) { throw new Error(`Initiative not found: ${id}`); } return updated; } async delete(id: string): Promise { // Detach agents before deleting — agents.initiative_id FK may lack ON DELETE SET NULL // in databases that haven't applied migration 0025 yet. await this.db.update(agents).set({ initiativeId: null }).where(eq(agents.initiativeId, id)); const [deleted] = await this.db.delete(initiatives).where(eq(initiatives.id, id)).returning(); if (!deleted) { throw new Error(`Initiative not found: ${id}`); } } }