src / provider.ts
import { Artifact, Chunk, Query, QueryResult, PluginConfig, ArtifactFSProvider } from "./types";
import { Chunker } from "./chunker";
import { SyncManager } from "./syncManager";
import { createHash } from "node:crypto";
export class Provider implements ArtifactFSProvider {
private store: Map<string, Artifact> = new Map();
private chunks: Map<string, Chunk[]> = new Map();
private chunker: Chunker;
private syncMgr: SyncManager;
constructor(config: PluginConfig, private client: any) {
this.chunker = new Chunker(config.maxChunkSize, Math.floor(config.maxChunkSize * 0.05));
this.syncMgr = new SyncManager();
}
async readFile(path: string): Promise<string> {
const artifact = [...this.store.values()].find((a) => a.path === path);
if (!artifact) throw new Error(`File not found: ${path}`);
const fileChunks = this.chunks.get(artifact.id);
return fileChunks?.map((c) => c.content).join("\n") ?? "";
}
async writeFile(path: string, content: string): Promise<Artifact> {
const checksum = createHash("sha256").update(content).digest("hex");
const existing = [...this.store.values()].find((a) => a.path === path);
const now = Date.now();
const artifact: Artifact = {
id: existing?.id ?? `file_${now}_${Math.random().toString(36).slice(2, 8)}`,
path,
checksum,
version: existing ? existing.version + 1 : 1,
metadata: {},
createdAt: existing?.createdAt ?? now,
updatedAt: now,
};
this.store.set(artifact.id, artifact);
this.chunks.set(artifact.id, this.chunker.chunk(artifact.id, content));
if (existing) await this.syncMgr.enqueue(artifact.id, "update");
else await this.syncMgr.enqueue(artifact.id, "create");
return artifact;
}
async deleteFile(id: string): Promise<void> {
this.store.delete(id);
this.chunks.delete(id);
await this.syncMgr.enqueue(id, "delete");
}
async listFiles(path?: string): Promise<Artifact[]> {
const all = [...this.store.values()];
return path ? all.filter((a) => a.path.startsWith(path)) : all;
}
async queryFiles(query: Query): Promise<QueryResult> {
const start = Date.now();
let results: Chunk[] = [];
for (const [, artifactChunks] of this.chunks) {
for (const chunk of artifactChunks) {
if (query.text && !chunk.content.toLowerCase().includes(query.text.toLowerCase())) continue;
if (query.artifactId && chunk.artifactId !== query.artifactId) continue;
results.push(chunk);
}
}
const totalCount = results.length;
const off = query.offset ?? 0;
const lim = query.limit ?? 10;
results = results.slice(off, off + lim);
return { chunks: results, totalCount, queryTimeMs: Date.now() - start };
}
async getChunks(artifactId: string): Promise<Chunk[]> {
return this.chunks.get(artifactId) ?? [];
}
async sync(): Promise<any[]> {
return this.syncMgr.getPending();
}
}