@funztic/db
Version:
TypeScript기반 파일 데이터베이스, zod를 지원합니다.
117 lines (116 loc) • 4.18 kB
JavaScript
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];
}
}