import { type SqliteDatabase } from '../sqlite/sqlite-driver.js'; import { ZodError } from 'zod'; import { type LoopBeeRelation, type LoopBeeRelationInput, type LoopBeeRelationRole } from './loop-bee-relation-types.js'; /** * Domain error thrown by LoopBeeRelationService when an invariant is * violated. The error code is machine-readable; the message is * NL-friendly. The CLI layer (M5) maps these into user-facing prompts. */ export type LoopBeeRelationIntegrityErrorCode = 'LOOP_RETIRED' | 'DUP_RELATION' | 'TWO_MAIN_BEES' | 'FK_LOOP_NOT_FOUND' | 'FK_BEE_NOT_FOUND'; export declare class LoopBeeRelationIntegrityError extends Error { readonly code: LoopBeeRelationIntegrityErrorCode; readonly findings: ReadonlyArray<{ path: string; message: string; }>; constructor(code: LoopBeeRelationIntegrityErrorCode, message: string, findings?: ReadonlyArray<{ path: string; message: string; }>); } /** * LoopBeeRelationService — thin service over the loop_bee_relation * store. M2 keeps the surface narrow: create / listByLoop / listByBee / * updateRole / remove. Promotion, evaluation, and crystallization * flows land in later slices. * * Integrity rules enforced here (spec §4.6 + RL-3): * * 1. The referenced loop_release row must exist (FK to loop_release.id). * — enforced at DB level (FK constraint). * — surfaced as FK_LOOP_NOT_FOUND for friendlier errors. * * 2. The referenced bee_release row must exist (FK to bee_release.id). * — enforced at DB level (FK constraint). * — surfaced as FK_BEE_NOT_FOUND for friendlier errors. * * 3. Cannot relate a bee to a retired loop. * — enforced in service by checking loop_release.lifecycle_status. * — Throws LoopBeeRelationIntegrityError(LOOP_RETIRED). * * 4. A loop cannot have two `main` bees. * — enforced at DB level via partial unique index. * — surfaced as TWO_MAIN_BEES for friendlier errors. * * 5. (loop_release_id, bee_release_id) must be unique per row. * — enforced at DB level (UNIQUE). * — surfaced as DUP_RELATION for friendlier errors. * * The constructor takes an open SQLite database (the existing * `peaks state` boundary) so callers do not have to thread a path * through this layer; this matches the LoopReleaseService pattern. * * All write paths re-validate the input via the Zod schema, mirroring * the boundary-check discipline used elsewhere. */ export declare class LoopBeeRelationService { private readonly db; constructor(db: SqliteDatabase); /** * Persist a new LoopBeeRelation row. * * Throws: * - ZodError if the input fails schema validation. * - LoopBeeRelationIntegrityError(LOOP_RETIRED) if the loop's * lifecycle_status is "retired". * - LoopBeeRelationIntegrityError(FK_LOOP_NOT_FOUND) if the loop * row does not exist. * - LoopBeeRelationIntegrityError(FK_BEE_NOT_FOUND) if the bee * row does not exist. * - LoopBeeRelationIntegrityError(DUP_RELATION) if a relation * for the same (loop, bee) already exists. * - LoopBeeRelationIntegrityError(TWO_MAIN_BEES) if a `main` row * already exists for the loop. */ create(input: LoopBeeRelationInput): LoopBeeRelation; /** Read a LoopBeeRelation row by id; returns undefined if absent. */ read(id: number): LoopBeeRelation | undefined; /** * List relations for a given loop. Filter by `role` when supplied. */ listByLoop(opts: { loop_release_id: string; role?: LoopBeeRelationRole; }): LoopBeeRelation[]; /** * List relations for a given bee. Filter by `role` when supplied. */ listByBee(opts: { bee_release_id: number; role?: LoopBeeRelationRole; }): LoopBeeRelation[]; /** * Update the role of an existing relation. The storage-level partial * unique index on `main` re-asserts "at most one main per loop" if * the new role is `main`. Returns the updated row or undefined if * the id is absent. */ updateRole(id: number, newRole: LoopBeeRelationRole): LoopBeeRelation | undefined; /** Remove a relation by id. Returns true if a row was deleted. */ remove(id: number): boolean; } /** * Convenience: re-export the Zod error class guard so callers can do * `if (err instanceof ZodError)`. Avoids forcing every caller to import * zod directly. */ export declare function isZodError(err: unknown): err is ZodError;