learning_ai_common_plat/packages/event-store/src/file-store.ts

69 lines
1.9 KiB
TypeScript

/**
* File-based event store implementation.
* Appends events as JSON lines to a file on disk.
* Suitable for single-instance dev/staging deployments.
*/
import { readFile, appendFile, writeFile, mkdir } from 'node:fs/promises';
import { dirname } from 'node:path';
import type { EventStore, StoredEvent, EventStoreQuery } from './types.js';
export interface FileStoreOptions {
filePath: string;
}
export class FileEventStore implements EventStore {
private readonly filePath: string;
constructor(options: FileStoreOptions) {
this.filePath = options.filePath;
}
async append(event: StoredEvent): Promise<void> {
await mkdir(dirname(this.filePath), { recursive: true });
await appendFile(this.filePath, JSON.stringify(event) + '\n', 'utf-8');
}
async query(q: EventStoreQuery): Promise<StoredEvent[]> {
const all = await this.readAll();
let results = all;
if (q.userId) results = results.filter(e => e.userId === q.userId);
if (q.type) results = results.filter(e => e.type === q.type);
if (q.after) results = results.filter(e => e.timestamp > q.after!);
if (q.before) results = results.filter(e => e.timestamp < q.before!);
if (q.limit && q.limit > 0) {
results = results.slice(-q.limit);
}
return results;
}
async recent(limit = 50): Promise<StoredEvent[]> {
const all = await this.readAll();
return all.slice(-limit);
}
async count(): Promise<number> {
const all = await this.readAll();
return all.length;
}
async clear(): Promise<void> {
await writeFile(this.filePath, '', 'utf-8');
}
private async readAll(): Promise<StoredEvent[]> {
try {
const content = await readFile(this.filePath, 'utf-8');
return content
.split('\n')
.filter(line => line.trim())
.map(line => JSON.parse(line) as StoredEvent);
} catch {
return [];
}
}
}