UNPKG

jdb-lite

Version:

A lightweight schema-based JSON database library with validation

182 lines 6.87 kB
import * as fs from 'fs'; import * as path from 'path'; import { ObjectId } from './objectid'; export class DatabaseManager { constructor(dbPath = '.db_json') { this.collections = new Map(); this.dbPath = path.resolve(dbPath); this.ensureDbDirectory(); } ensureDbDirectory() { if (!fs.existsSync(this.dbPath)) { fs.mkdirSync(this.dbPath, { recursive: true }); } } getCollectionPath(collectionName) { return path.join(this.dbPath, `${collectionName}.json`); } loadCollection(collectionName) { if (this.collections.has(collectionName)) { return this.collections.get(collectionName); } const filePath = this.getCollectionPath(collectionName); if (!fs.existsSync(filePath)) { // Create empty collection file immediately const emptyCollection = []; this.collections.set(collectionName, emptyCollection); this.saveCollection(collectionName); return emptyCollection; } try { const data = fs.readFileSync(filePath, 'utf-8'); const collection = JSON.parse(data); this.collections.set(collectionName, collection); return collection; } catch (error) { console.warn(`Failed to load collection ${collectionName}:`, error); const emptyCollection = []; this.collections.set(collectionName, emptyCollection); this.saveCollection(collectionName); return emptyCollection; } } saveCollection(collectionName) { const collection = this.collections.get(collectionName); if (!collection) return; const filePath = this.getCollectionPath(collectionName); try { fs.writeFileSync(filePath, JSON.stringify(collection, null, 2), 'utf-8'); } catch (error) { console.error(`Failed to save collection ${collectionName}:`, error); throw error; } } async insertOne(collectionName, doc) { const collection = this.loadCollection(collectionName); // Generate _id if not provided if (!doc._id) { doc._id = new ObjectId().toString(); } // Add timestamps if they don't exist const now = new Date(); if (!doc.createdAt) doc.createdAt = now; if (!doc.updatedAt) doc.updatedAt = now; collection.push({ ...doc }); this.saveCollection(collectionName); return doc; } async insertMany(collectionName, docs) { const collection = this.loadCollection(collectionName); const insertedDocs = []; for (const doc of docs) { if (!doc._id) { doc._id = new ObjectId().toString(); } const now = new Date(); if (!doc.createdAt) doc.createdAt = now; if (!doc.updatedAt) doc.updatedAt = now; const insertedDoc = { ...doc }; collection.push(insertedDoc); insertedDocs.push(insertedDoc); } this.saveCollection(collectionName); return insertedDocs; } async find(collectionName, filter = {}) { const collection = this.loadCollection(collectionName); if (Object.keys(filter).length === 0) { return [...collection]; } return collection.filter(doc => this.matchesFilter(doc, filter)); } async findOne(collectionName, filter = {}) { const collection = this.loadCollection(collectionName); if (Object.keys(filter).length === 0) { return collection[0] || null; } return collection.find(doc => this.matchesFilter(doc, filter)) || null; } async findById(collectionName, id) { return this.findOne(collectionName, { _id: id }); } async updateOne(collectionName, filter, update) { const collection = this.loadCollection(collectionName); const docIndex = collection.findIndex(doc => this.matchesFilter(doc, filter)); if (docIndex === -1) { return { modifiedCount: 0 }; } // Apply update const updatedDoc = { ...collection[docIndex], ...update }; updatedDoc.updatedAt = new Date(); collection[docIndex] = updatedDoc; this.saveCollection(collectionName); return { modifiedCount: 1 }; } async updateMany(collectionName, filter, update) { const collection = this.loadCollection(collectionName); let modifiedCount = 0; for (let i = 0; i < collection.length; i++) { if (this.matchesFilter(collection[i], filter)) { collection[i] = { ...collection[i], ...update }; collection[i].updatedAt = new Date(); modifiedCount++; } } if (modifiedCount > 0) { this.saveCollection(collectionName); } return { modifiedCount }; } async deleteOne(collectionName, filter) { const collection = this.loadCollection(collectionName); const docIndex = collection.findIndex(doc => this.matchesFilter(doc, filter)); if (docIndex === -1) { return { deletedCount: 0 }; } collection.splice(docIndex, 1); this.saveCollection(collectionName); return { deletedCount: 1 }; } async deleteMany(collectionName, filter) { const collection = this.loadCollection(collectionName); const initialLength = collection.length; const filteredCollection = collection.filter(doc => !this.matchesFilter(doc, filter)); this.collections.set(collectionName, filteredCollection); this.saveCollection(collectionName); return { deletedCount: initialLength - filteredCollection.length }; } async countDocuments(collectionName, filter = {}) { const docs = await this.find(collectionName, filter); return docs.length; } async dropCollection(collectionName) { const filePath = this.getCollectionPath(collectionName); if (fs.existsSync(filePath)) { fs.unlinkSync(filePath); } this.collections.delete(collectionName); } matchesFilter(doc, filter) { for (const [key, value] of Object.entries(filter)) { if (doc[key] !== value) { return false; } } return true; } ensureCollection(collectionName) { this.loadCollection(collectionName); } getCollectionNames() { const files = fs.readdirSync(this.dbPath); return files.filter(file => file.endsWith('.json')).map(file => file.replace('.json', '')); } } //# sourceMappingURL=database.js.map