AUDIT / src /lib /artifacts /ArtifactStore.ts
Arypulka98's picture
feat(audit): deploy full backend cluster node (part 3)
95eb75a verified
Raw
History Blame Contribute Delete
3.75 kB
/**
* ArtifactStore.ts — Dexie/IndexedDB persistence for workspace artifacts (FASE 7)
*
* Artifacts are first-class workspace objects: code files, HTML pages,
* Markdown docs, CSV tables, JSON data, SVG images generated by the agent.
*
* Uses the same Dexie instance pattern as the rest of the app.
* Zero external dependencies beyond Dexie (already in package).
*/
import Dexie, { type Table } from "dexie";
export type ArtifactKind =
| "code" // runnable code (python, typescript, javascript, rust, …)
| "html" // full HTML page → sandboxed iframe
| "markdown" // rendered markdown document
| "csv" // tabular data → rendered table
| "json" // structured data
| "svg" // vector image
| "text"; // plain text / unknown
export interface Artifact {
id: string; // nanoid / uuid
kind: ArtifactKind;
language?: string; // for "code" kind: python, typescript, …
title: string; // short descriptive name
content: string; // raw text content
sessionId?: string; // conversation session it belongs to
messageId?: string; // message that generated it
createdAt: number; // unix ms
updatedAt: number; // unix ms
version: number; // monotonic — bump on edit
pinned: boolean; // user pinned to sidebar
tags: string[];
}
class ArtifactDatabase extends Dexie {
artifacts!: Table<Artifact, string>;
constructor() {
super("agente_artifacts_v1");
this.version(1).stores({
artifacts: "id, kind, sessionId, createdAt, updatedAt, pinned",
});
}
}
let _db: ArtifactDatabase | null = null;
function db(): ArtifactDatabase {
if (!_db) _db = new ArtifactDatabase();
return _db;
}
// ── CRUD ─────────────────────────────────────────────────────────────────────
export async function saveArtifact(artifact: Artifact): Promise<void> {
await db().artifacts.put(artifact);
}
export async function getArtifact(id: string): Promise<Artifact | undefined> {
return db().artifacts.get(id);
}
export async function updateArtifact(id: string, patch: Partial<Omit<Artifact, "id" | "createdAt">>): Promise<void> {
const existing = await db().artifacts.get(id);
if (!existing) return;
await db().artifacts.put({
...existing,
...patch,
id,
updatedAt: Date.now(),
version: existing.version + 1,
});
}
export async function deleteArtifact(id: string): Promise<void> {
await db().artifacts.delete(id);
}
export async function listArtifacts(opts: {
sessionId?: string;
kind?: ArtifactKind;
pinned?: boolean;
limit?: number;
} = {}): Promise<Artifact[]> {
let query = db().artifacts.orderBy("updatedAt").reverse();
const { sessionId, kind, pinned, limit } = opts;
const results = await query.filter(a => {
if (sessionId && a.sessionId !== sessionId) return false;
if (kind && a.kind !== kind) return false;
if (pinned !== undefined && a.pinned !== pinned) return false;
return true;
}).toArray();
return limit ? results.slice(0, limit) : results;
}
export async function searchArtifacts(query: string): Promise<Artifact[]> {
const q = query.toLowerCase();
return db().artifacts
.filter(a => a.title.toLowerCase().includes(q) || a.content.toLowerCase().includes(q))
.toArray();
}
export async function clearSessionArtifacts(sessionId: string): Promise<number> {
return db().artifacts.where("sessionId").equals(sessionId).delete();
}