69 lines
1.9 KiB
TypeScript
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 [];
|
|
}
|
|
}
|
|
}
|