Files
Neon-Desk/frontend/lib/server/store.ts

101 lines
2.4 KiB
TypeScript

import { mkdir, readFile, writeFile } from 'node:fs/promises';
import path from 'node:path';
import type { Filing, Holding, PortfolioInsight, Task, WatchlistItem } from '@/lib/types';
export type DataStore = {
counters: {
watchlist: number;
holdings: number;
filings: number;
insights: number;
};
watchlist: WatchlistItem[];
holdings: Holding[];
filings: Filing[];
tasks: Task[];
insights: PortfolioInsight[];
};
const DATA_DIR = path.join(process.cwd(), 'data');
const STORE_PATH = path.join(DATA_DIR, 'store.json');
let writeQueue = Promise.resolve();
function nowIso() {
return new Date().toISOString();
}
function createDefaultStore(): DataStore {
const now = nowIso();
return {
counters: {
watchlist: 0,
holdings: 0,
filings: 0,
insights: 0
},
watchlist: [],
holdings: [],
filings: [],
tasks: [],
insights: [
{
id: 1,
user_id: 1,
provider: 'local-bootstrap',
model: 'zeroclaw',
content: [
'System initialized in local-first mode.',
'Add holdings and sync filings to produce a live AI brief via OpenClaw.'
].join('\n'),
created_at: now
}
]
};
}
async function ensureStoreFile() {
await mkdir(DATA_DIR, { recursive: true });
try {
await readFile(STORE_PATH, 'utf8');
} catch {
const defaultStore = createDefaultStore();
defaultStore.counters.insights = defaultStore.insights.length;
await writeFile(STORE_PATH, JSON.stringify(defaultStore, null, 2), 'utf8');
}
}
async function readStore(): Promise<DataStore> {
await ensureStoreFile();
const raw = await readFile(STORE_PATH, 'utf8');
return JSON.parse(raw) as DataStore;
}
async function writeStore(store: DataStore) {
await writeFile(STORE_PATH, JSON.stringify(store, null, 2), 'utf8');
}
function cloneStore(store: DataStore): DataStore {
return JSON.parse(JSON.stringify(store)) as DataStore;
}
export async function getStoreSnapshot() {
const store = await readStore();
return cloneStore(store);
}
export async function withStore<T>(mutator: (store: DataStore) => T | Promise<T>): Promise<T> {
const run = async () => {
const store = await readStore();
const result = await mutator(store);
await writeStore(store);
return result;
};
const nextRun = writeQueue.then(run, run);
writeQueue = nextRun.then(() => undefined, () => undefined);
return await nextRun;
}