UNPKG

@funztic/db

Version:

TypeScript기반 파일 데이터베이스, zod를 지원합니다.

117 lines (116 loc) 4.18 kB
import fs from 'fs/promises'; import path from 'path'; import { z } from 'zod'; import { Collection as BaseCollection, Database as BaseDatabase } from '../core/file-db.js'; export class Collection extends BaseCollection { constructor(collectionName, schema, options) { super(collectionName, options); this._schema = schema; try { schema.parse({ id: 'schema-check-id' }); } catch (e) { if (schema._def instanceof z.ZodObject && !(schema.shape?.id)) { console.warn(`[DB] Collection Warning: Zod schema for "${collectionName}" might be missing an 'id: z.string()' definition.`); } } } get schema() { return this._schema; } async init() { try { const dir = path.dirname(this.filePath); await fs.mkdir(dir, { recursive: true }); let rawData = {}; let loadedCount = 0; let validatedCount = 0; try { const fileContent = await fs.readFile(this.filePath, 'utf-8'); rawData = JSON.parse(fileContent); loadedCount = Object.keys(rawData).length; } catch (err) { if (err.code === 'ENOENT') { this.data = {}; await this.save(true); this.emit('loaded', 0, 0); this.isInitialized = true; return; } else { this.emit('error', err instanceof Error ? err : new Error(String(err))); throw err; } } const validatedData = {}; for (const id in rawData) { if (Object.prototype.hasOwnProperty.call(rawData, id)) { const item = rawData[id]; const result = this._schema.safeParse(item); if (result.success) { if (result.data.id === id) { validatedData[id] = result.data; validatedCount++; } else { this.emit('validationError', id, new Error(`Loaded item ID '${result.data.id}' does not match key '${id}'`)); } } else { this.emit('validationError', id, result.error); } } } this.data = validatedData; this.emit('loaded', loadedCount, validatedCount); this.isInitialized = true; } catch (err) { this.isInitialized = false; const error = err instanceof Error ? err : new Error(String(err)); this.emit('error', error); throw error; } } async create(itemData) { await this.ensureInitialized(); if (!itemData.id) throw new Error('ID는 필수 항목입니다.'); const id = itemData.id; const result = this._schema.safeParse(itemData); if (!result.success) { throw result.error; } this.data[id] = result.data; this.triggerSave(); this.emit('created', this.data[id]); return this.data[id]; } async update(id, updates) { await this.ensureInitialized(); const item = this.data[id]; if (!item) return undefined; const updatedItem = { ...item, ...updates }; const result = this._schema.safeParse(updatedItem); if (!result.success) { throw result.error; } this.data[id] = result.data; this.triggerSave(); this.emit('updated', this.data[id]); return this.data[id]; } } export class Database extends BaseDatabase { collection(name, schema) { if (!this.collections[name]) { this.collections[name] = new Collection(name, schema, { dir: this.dbDir, ...this.options }); } return this.collections[name]; } }