UNPKG

giantdb

Version:

Large object database in native JavaScript, with encryption support

130 lines (129 loc) 4.48 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.GiantDB = void 0; const node_crypto_1 = __importDefault(require("node:crypto")); const node_util_1 = __importDefault(require("node:util")); const fs_adapters_1 = require("fs-adapters"); const idset_js_1 = require("./idset.js"); const iomanager_js_1 = require("./iomanager.js"); const manager_js_1 = require("./middleware/manager.js"); const change_js_1 = require("./change.js"); const item_js_1 = require("./item.js"); const cryptoRandomBytes = node_util_1.default.promisify(node_crypto_1.default.randomBytes); /** * @returns Resolves to the generated id. */ async function generateId() { const buffer = await cryptoRandomBytes(16); return buffer.toString('hex'); } /** * @param source The raw source. * @returns An adapter for the source. */ function resolveSource(source) { if (typeof source === 'string') { return new fs_adapters_1.DirectoryAdapter(source); } return source ?? new fs_adapters_1.MemoryAdapter(); } /** * A GiantDB database. */ class GiantDB { _adapter; _middlewareManager; _ioManager; _idSet; /** * Construct a new database. The source can either be a file system adapter or * a directory path. If no source is given, a volatile in-memory store is used. * * @param source An adapter or directory path. */ constructor(source) { this._adapter = resolveSource(source); this._middlewareManager = new manager_js_1.MiddlewareManager(); this._ioManager = new iomanager_js_1.IOManager(this._adapter, this._middlewareManager); this._idSet = new idset_js_1.IdSet(async () => { const files = await this._adapter.listFiles(); return files.filter(fileName => { // ignore non-committed files return !fileName.includes('.tmp') && !fileName.includes('.json'); }); }); } async _commit(id) { // rename the file, add its id, resolve to an Item await this._ioManager.publish(id); await this._idSet.add(id); return await this.get(id); } async _destroy(id) { await this._ioManager.deleteTemporary(id); } /** * Register the given middleware. * * @param middleware The middleware object. */ use(middleware) { this._middlewareManager.register(middleware); } /** * Prepare a new item. This constructs a new Change object that can be written * to and then committed, making the item available. * * @param options Middleware options. * @returns A Promise that resolves to a new Change object. */ async create(options) { await this._adapter.init(); const id = await generateId(); const out = await this._ioManager.createTemporary(id, options); return new change_js_1.Change(id, out, this._commit.bind(this, id), this._destroy.bind(this, id)); } /** * Remove an item from this database. * * @param id The item's id. * @returns A Promise that is resolved when removal is complete. */ async remove(id) { await this._ioManager.delete(id); await this._idSet.remove(id); } /** * Retrieve an item in this database by id. * * @param id The item's id. * @returns A Promise that resolves to the item if found. */ async get(id) { // check for existence first if (!await this._idSet.includes(id)) { throw new Error('item does not exist: ' + id); } // obtain the metadata, then construct const metadata = await this._ioManager.readMetadata(id); return new item_js_1.Item(id, this._ioManager, metadata); } /** * Iterate over all items in this database. Iteration happens sequentially. If * the callback returns a Promise or thenable, it is awaited before continuing * with the next item. * * @param callbackFn The function to execute for each item. * @returns A Promise that is resolved when iteration is finished. */ async each(callbackFn) { await this._idSet.each(async (id) => { const item = await this.get(id); await callbackFn(item); }); } } exports.GiantDB = GiantDB;