/** * The StorageAdapter contract — the seam that makes web / desktop / local modes * possible. Defined once here; every feature codes against this, never a backend. * See PLAN.md §3.4. */ import type { Board, Book, Chapter, DocumentRef, Entity, NewProject, Project, ProjectMeta, PlotBeat, AISession, } from "./types.js"; /** Live-update events fanned out to subscribed clients. */ export type StoreEvent = | { type: "doc-changed"; projectId: string; docId: string; rev: number } | { type: "entity-changed"; projectId: string; entityId: string; kind: string } | { type: "board-changed"; projectId: string; boardId: string } | { type: "ai-session-changed"; projectId: string; sessionId: string } | { type: "presence"; projectId: string; users: unknown[] } | { type: "conflict"; docId: string; remoteRev: number }; export interface StorageAdapter { // projects listProjects(): Promise; createProject(p: NewProject): Promise; getProject(id: string): Promise; deleteProject?(id: string): Promise; // books & chapters createBook(b: { projectId: string; title: string }): Promise; listBooks(projectId: string): Promise; listChapters(bookId: string): Promise; deleteChapter?(id: string): Promise; // content (debounced autosave) saveDocument(doc: DocumentRef): Promise; getChapter(id: string): Promise; saveEntity(e: Entity): Promise; /** * List entities for a project, optionally filtered by kind ("character" | * "world" | "note"). Returns every entity when `kind` is omitted. */ listEntities(projectId: string, kind?: string): Promise; getEntity(projectId: string, kind: string, id: string): Promise; deleteEntity(projectId: string, kind: string, id: string): Promise; /** * Fuzzy search across an entity's name and field values. Returns matches * ordered best-first; empty `q` returns all entities for the project. */ searchEntities(projectId: string, q: string): Promise; saveBoard(b: Board): Promise; /** Load the story board for a project (by id `b.id`, scoped to `projectId`). */ getBoard(projectId: string): Promise; /** Plot beats — the atomic units of story structure (CHUNK #5). */ listPlotBeats(projectId: string): Promise; savePlotBeat(b: PlotBeat): Promise; deletePlotBeat(projectId: string, id: string): Promise; saveAISession(s: AISession): Promise; // live updates onEvent(cb: (e: StoreEvent) => void): () => void; }