import chalk from 'chalk' import path from 'path'; import fs from 'fs/promises'; import { pathToFileURL } from 'url'; function Node(node) { let traits = [ "labels" ] for(let i = 0; i < traits.length; i++) { if(!node[traits[i]]) { throw new Error(`Node is missing field "${traits[i]}": ${JSON.stringify(node)}`) } } } export default class QuillDB { #nodes; get nodes() {return this.#nodes}; #edges; get edges() {return this.#edges}; #labels = {}; get labels() {return this.#labels} constructor() { this.loadData() } #checkLabelSchemas(id, entry, labelModels) { entry.labels.forEach(label => { const model = labelModels[label]; if (!model) { throw new Error("Data has unknown label or missing model: " + label) } model(entry); this.#labels[label].push(id); }); } async getLabelModels() { const labelHandlers = {}; const labelDir = path.join(process.cwd(), 'server/db/model'); const files = await fs.readdir(labelDir); for (const file of files) { if (!file.endsWith('.js')) continue; const label = path.basename(file, '.js'); const modulePath = path.join(labelDir, file); const module = await import(pathToFileURL(modulePath).href); labelHandlers[label] = module.default; if (!this.#labels[label]) { this.#labels[label] = []; } } return labelHandlers } async loadData() { const dbData = await fs.readFile(path.join(process.cwd(), 'db/db.json'), 'utf8'); let dbJson; try { dbJson = JSON.parse(dbData); } catch { dbJson = [] } this.#nodes = dbJson["nodes"]; this.#edges = dbJson["edges"]; let labelModels = await this.getLabelModels(); // Index by label for (const [id, entry] of Object.entries(this.#nodes)) { Node(entry) this.#checkLabelSchemas(id, entry, labelModels) } for (const [id, entry] of Object.entries(this.#edges)) { Edge(entry) this.#checkLabelSchemas(id, entry, labelModels) } console.log(chalk.yellow("DB established.")) Object.preventExtensions(this); } // superKey = "nodes" || "edges" async writeData(superKey, key, value) { const dbData = await fs.readFile(path.join(process.cwd(), 'db/db.json'), 'utf8'); let dbJson; try { dbJson = JSON.parse(dbData); } catch { dbJson = [] } dbJson[superKey][key] = value; await fs.writeFile(path.join(process.cwd(), 'db/db.json'), JSON.stringify(dbJson, null, 2), 'utf8') } generateUserID() { let id = this.#labels["User"].length + 1; while (this.get.user(`user-${id}`)) { id++; } return `user-${id}`; // O(1) most of the time } async getAll() { return { nodes: this.#nodes } } }