import Database from 'better-sqlite3'; import fs from 'node:fs'; import path from 'node:path'; import { DB_PATH } from '../config.js'; const SCHEMA = ` CREATE TABLE IF NOT EXISTS meta ( key TEXT PRIMARY KEY, value TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS items ( unique_name TEXT PRIMARY KEY, -- e.g. T4_BAG@1 base_name TEXT NOT NULL, -- e.g. T4_BAG enchantment INTEGER NOT NULL DEFAULT 0, tier INTEGER, weight REAL, item_value INTEGER, shop_category TEXT, shop_subcategory TEXT, crafting_category TEXT, name_en TEXT ); CREATE INDEX IF NOT EXISTS idx_items_base ON items(base_name); CREATE INDEX IF NOT EXISTS idx_items_name_en ON items(name_en); CREATE TABLE IF NOT EXISTS recipes ( id INTEGER PRIMARY KEY AUTOINCREMENT, output_item TEXT NOT NULL, variant INTEGER NOT NULL DEFAULT 0, -- alternative recipe index for the same output amount_crafted REAL NOT NULL DEFAULT 1, silver_cost REAL NOT NULL DEFAULT 0, focus_cost REAL NOT NULL DEFAULT 0, source TEXT NOT NULL DEFAULT 'crafting' -- 'crafting' | 'upgrade' (enchanting) ); CREATE INDEX IF NOT EXISTS idx_recipes_output ON recipes(output_item); CREATE TABLE IF NOT EXISTS recipe_inputs ( recipe_id INTEGER NOT NULL REFERENCES recipes(id) ON DELETE CASCADE, input_item TEXT NOT NULL, count REAL NOT NULL, max_return_amount REAL, -- 0 means the resource is never returned (e.g. artifacts) PRIMARY KEY (recipe_id, input_item) ); CREATE INDEX IF NOT EXISTS idx_recipe_inputs_item ON recipe_inputs(input_item); `; let db: Database.Database | null = null; export function getDb(): Database.Database { if (!db) { fs.mkdirSync(path.dirname(DB_PATH), { recursive: true }); db = new Database(DB_PATH); db.pragma('journal_mode = WAL'); db.pragma('foreign_keys = ON'); db.exec(SCHEMA); } return db; } export function getMeta(key: string): string | null { const row = getDb().prepare('SELECT value FROM meta WHERE key = ?').get(key) as | { value: string } | undefined; return row?.value ?? null; } export function setMeta(key: string, value: string): void { getDb() .prepare('INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value') .run(key, value); }