evelodb-prime
v1.0.0
Published
A high-performance native B-tree database for Node.js. Made by Evelocore. With B-tree Operations.
Downloads
231
Maintainers
Readme
📚 Introduction
EveloDB Prime is the streamlined, high-performance evolution of EveloDB. It removes the overhead of multi-format support and encryption to focus strictly on BSON-backed B-Tree storage. It's designed for large-scale applications that need fast indexing and reliable local storage without the complexity of configuration.
Requirements
- Node.js
Table of Contents
- 📥 Installation
- 📘 TypeScript / ES Modules
- 🔢 Comparison Operators
- 💈 Atomic Update Operators
- 🛠️ Configuration
- ⚙️ Operations
- 💉 Inject Data
- 🔍 Get Query Result
- 💾 Backup Data
- 📦 Object Store
- 🔒 Transactions (atomic)
- 📁 Store Files
- 🖼️ Image Utilities
- 💡 Features
- 📈 Changelog
📥 Installation
Npm Install
npm i evelodb-primeImport
CommonJS
const eveloDB = require('evelodb-prime');
const db = new eveloDB();TypeScript / ES Modules
import eveloDB from 'evelodb-prime';
const db = new eveloDB();Configuration
[!CAUTION] CRITICAL: Use a Single Instance Do NOT initialize
new eveloDB()multiple times in different files (e.g., in different routes or middleware). Doing so will create separate, desynchronized memory caches and file handles, leading to missing data andEPERMlock errors. Instead, create a singledb.jsordb.tsfile, initialize EveloDB there, and export the instance to use throughout your application.
const db = new eveloDB({
directory: './evelodbprime', // Storage directory
noRepeat: false, // Reject duplicate data
schema: {
users: {
fields: {
username: { type: String, required: true, min: 5, max: 30 },
email: { type: String, required: true },
age: { type: Number, required: true, max: 90 },
vehicle: {
type: {
color: { type: String, required: true },
model: { type: String, required: true }
},
required: false
}
},
indexes: ["email", "username"],
uniqueKeys: ["email", "username"],
objectIdKey: "userId"
},
products: {
fields: {
name: { type: String, required: true },
price: { type: Number, required: true, min: 0 },
inStock: { type: Boolean, required: true }
},
indexes: ["name"],
uniqueKeys: ["name"],
objectIdKey: "productId"
}
}
});Configuration Parameters
| Parameter | Type | Required | Description | Default |
|--------------------|----------|----------|----------------------------------------------|-----------------------------|
| directory | string | No | Where database files are stored | './evelodbprime' |
| maxHandles | number | No | Max open collection handles (LRU) | 64 |
| compactThreshold | number | No | Auto-compact ratio (0.1 - 0.9) | 0.3 |
| schema | Object | No | Schema, Indexes, and Unique Keys for collections | {} |
Schema Definition
When defining a schema, each collection can have fields, indexes, and uniqueKeys.
Field Validation
| Property | Type | Description | Required |
|------------|--------------------|-----------------------------------------------------------------------------|----------|
| type | Constructor / Obj | Data type (e.g., String, Number, Boolean, or a nested object schema). | Yes |
| required | boolean | If true, the field must be present during creation/update. | No |
| min | number | Minimum value for Number or minimum length for String. | No |
| max | number | Maximum value for Number or maximum length for String. | No |
Collection Options
| Option | Type | Description | Required |
|--------------|----------|-----------------------------------------------------------------------------|----------|
| fields | Object | Field validation rules (as defined in the table above). | No |
| indexes | string[] | Fields to create B-Tree indexes for (enables O(log n) searches). | No |
| uniqueKeys | string[] | Fields that must contain unique values across the entire collection. | No |
| objectIdKey| string | Virtual name for the internal _id field (e.g., "userId"). | No |
| noRepeat | boolean | If true (default), rejects insertions of exact duplicate records. | No |
[!IMPORTANT] System Managed Fields: Fields like
_id(or your customobjectIdKey),_createdAt, and_modifiedAtare automatically managed by EveloDB. Any attempt to manually set or update these fields increate()oredit()will result in an error.
Note: All parameters are optional. If no directory is specified, EveloDB will default to
./evelodbprime.
Note: EveloDB Prime uses
.dbextension and_idas the primary key. Secondary indexes use.field.bidxfiles.
Easy Schema Explanation
indexes: ["email", "username"]
- These indexes will be created as B-Trees on the disk for faster searching
- If not specified, it will default to [objectIdKey] or ["_id"]
uniqueKeys: ["email", "username"]
- These fields will be checked for uniqueness before insertion
- If not specified, it will default to []
objectIdKey: "userId"
- This field will be the auto generated id
- If not specified, it will default to '_id'
name: { type: String, required: true }
- 'name' is String value and required
username: { type: String, required: true, min: 5, max: 30 }
- 'username' is String value and required
- minimum length is 5
- maximum length is 30
age: { type: Number, required: true, max: 90 }
- 'age' is Number value and required
- maximum value is 90
vehicle: { type: { color: { type: String, required: true }, model: { type: String, required: true } } }
- 'vehicle' is Object value and not required
- inside 'vehicle' there is 'color' and 'model' which are String value and required
📘 TypeScript / ES Modules
EveloDB Prime is fully written in TypeScript and supports both CommonJS and ES Module environments natively.
Importing Types
import eveloDB, { type EveloDBConfig } from 'evelodb-prime';
const config: EveloDBConfig = {
directory: './database',
noRepeat: true
};
const db = new eveloDB(config);🔢 Comparison Operators
Used to filter with conditions like greater than, less than, equal, etc.
| Operator | Description | Example |
|----------|-------------------------|-----------------------------------------|
| $eq | Equal | { age: { $eq: 25 } } |
| $ne | Not equal | { age: { $ne: 25 } } |
| $gt | Greater than | { age: { $gt: 25 } } |
| $gte | Greater than or equal | { age: { $gte: 25 } } |
| $lt | Less than | { age: { $lt: 25 } } |
| $lte | Less than or equal | { age: { $lte: 25 } } |
| $in | Matches any in an array | { status: { $in: ["active", "pending"] } } |
| $nin | Not in array | { status: { $nin: ["inactive"] } } |
| $regex | Regular expression | { name: { $regex: "^Jo", $options: "i" } } |
Example: Using Operators
db.find('users', { age: { $gte: 25 } }).all()💈 Atomic Update Operators
EveloDB supports atomic operators to modify fields without manual read-modify-write cycles. This is essential for counters (like stock) in high-traffic APIs.
| Operator | Description | Example |
| :--- | :--- | :--- |
| $inc | Increments/decrements a numeric field | { $inc: { stock: -1 } } |
| $set | Sets a field to a specific value | { $set: { status: 'active' } } |
| $unset | Removes a field from the document | { $unset: { temporaryFlag: true } } |
| $push | Appends a value to an array | { $push: { tags: 'new-tag' } } |
| $pull | Removes a value or matching items from an array | { $pull: { tags: 'old-tag' } } |
Example: Atomic Stock Update
db.edit('products', { productId: '123' }, { $inc: { stock: -1 } });⚙️ Operations
[!CAUTION] Bulk Operations: Passing an empty object
{}as the conditions parameter inedit(),delete(), orfind()will target every record in the collection. Example:db.edit("users", {}, { status: "active" })will update all users in the collection.
Create
Adds a new record to the collection.
db.create('users', {
username: 'john',
email: '[email protected]'
})Output
{
success: true,
userId: '662e5a4e3d5a4e3d5a4e3d5a', // Renamed via objectIdKey
_createdAt: '2026-04-28T10:00:00Z',
_modifiedAt: '2026-04-28T10:00:00Z'
}Note: If
objectIdKeyis not defined in the schema, this field defaults to_id.
Update
Modifies existing records that match the conditions.
Note:
db.update()is an alias fordb.edit().
db.edit('users',
{ username: 'john' },
{ email: '[email protected]' }
)Output
{
success: true,
modifiedCount: 1,
skippedDuplicates: 0 // If noRepeat is enabled
}Delete
Removes records that match the conditions.
db.delete('users', { username: 'john' })Output
{
success: true,
deletedCount: 1
}Inject
Perform high-performance bulk data injection. Useful for migrations or importing backups.
const data = [
{
username: 'alice',
email: '[email protected]',
_createdAt: '2026-04-29T08:50:16Z',
_modifiedAt: '2026-04-29T10:00:00Z',
_id_: '69f1c6486266d6824c7680e4'
},
// ... more records
];
// Method: 'overwrite' (default) - Clears collection before injection
db.inject("users", data);
// Method: 'merge' - Appends data to existing collection
db.inject("users", data, { method: 'merge' });[!IMPORTANT] Data Integrity: Injected data must include system fields:
_createdAt,_modifiedAt, and the ID field (e.g.,userIdor_id). If a schema ornoRepeatis defined, validation will be enforced during injection.
Output
{ success: true, count: 2 }Find
Search for records. Returns a QueryResult object.
// Find one using virtual ID key
const user = db.findOne('users', { userId: '662e5a4e3d5a4e3d5a4e3d5a' });
// Find many (returns QueryResult)
const result = db.find('users', { age: { $gt: 18 } });Search
Performs a case-insensitive "contains" search on fields. Useful for autocomplete or simple text matching.
// Matches "John", "johnny", "Elton John", etc.
const results = db.search('users', { username: 'john' });Get
Retrieves all records from a collection. Returns a QueryResult.
const allData = db.get('users').all();Count
Returns the total number of records in a collection.
const { count } = db.count('users');Check
Checks if at least one record exists that matches the conditions. Returns boolean.
const exists = db.check('users', { email: '[email protected]' });Drop / Reset
Permanently deletes a collection and all its associated index files.
db.drop('users');
// or
db.reset('users');Compact
Manually triggers the compaction process to reclaim storage space used by deleted or updated records.
db.compact('users');Rebuild Indexes
Rebuilds all B-Tree indexes (primary and secondary) for a collection from the raw data. Useful for recovery if index files are corrupted or missing.
db.rebuildIndexes('users');Close All
Closes all open collection handles and ensures all data/indexes are flushed to the disk.
db.closeAll();🔍 Get Query Result
This is a wrapper that provides chainable methods for working with query results in eveloDB. It enables pagination, sorting, and other data manipulation operations on query results.
Overview
The Query Result returned by the following eveloDB methods:
- db.find(collection, conditions)
- db.search(collection, conditions)
- db.get(collection)
when data is an array
Examples
getList
- Implements pagination by returning a subset of results.
// Get first 10 users
const firstPage = db.find('users', { status: 'active' }).getList(0, 10);
// Get next 10 users (pagination)
const secondPage = db.find('users', { status: 'active' }).getList(10, 10);
// Get 5 users starting from index 20
const customPage = db.find('users', { status: 'active' }).getList(20, 5);count
- Returns the total number of items in the result set.
// Get total count of active users
const totalActiveUsers = db.find('users', { status: 'active' }).count();
// Get count of search results
const searchCount = db.search('products', { name: 'phone' }).count();
// Use for pagination info
const results = db.find('orders', { status: 'pending' });
const total = results.count();
const currentPage = results.getList(0, 20);
console.log(`Showing ${currentPage.length} of ${total} results`);sort
- Sorts the results using a comparison function.
// Sort by name (ascending)
const sortedByName = db.find('users', { status: 'active' })
.sort((a, b) => a.name.localeCompare(b.name))
// Sort by age (descending)
const sortedByAge = db.find('users', { status: 'active' })
.sort((a, b) => b.age - a.age)
.getList(0, 20);
// Sort by date (newest first)
const sortedByDate = db.find('posts', { published: true })
.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))
.getList(0, 10);Method Chaining
One of the key features of QueryResult is method chaining, allowing you to combine operations:
const db = new eveloDB();
// Chain multiple operations
const result = db.find('products', { category: 'electronics' })
.sort((a, b) => b.price - a.price) // Sort by price (high to low)
.getList(10, 5); // Get items 11-15
// Complex chaining example
const topExpensiveProducts = db.search('products', { name: 'laptop' })
.sort((a, b) => b.price - a.price) // Sort by price descending
.getList(0, 3); // Get top 3 most expensive
// Get count after sorting (count remains the same)
const sortedResults = db.find('users', { role: 'admin' })
.sort((a, b) => a.name.localeCompare(b.name));
const totalCount = sortedResults.count(); // Total admins
const firstPage = sortedResults.getList(0, 10); // First 10 sorted admins💾 Backup Data
Export your collection data for safekeeping or migration.
[!NOTE] Backups always preserve the original
_idfield, even if you have configured anobjectIdKey. This ensures your backups remain compatible even if you change your schema configuration later.
// 1. Backup as Secure Binary (Full-file XOR Encoding)
db.createBackup('users', {
type: 'binary',
path: './backups',
password: 'my_secret_password',
title: 'User Records April 2026'
});
// 2. Backup as JSON
db.createBackup('users', { type: 'json', path: './backups' });
Output (
createBackup)
{
success: true,
backupPath: './backups/users_backup_2026-04-28.backup'
}🔍 Read Backup Info
Inspect a backup file (Metadata & Data) without performing a restore.
Note: Backup type defaults to
'binary'if not specified.
const info = db.readBackupFile('./backups/users_backup.backup', 'my_secret_password');Output (
readBackupFile)
{
success: true,
title: 'User Records April 2026',
protected: true,
schema: { ... },
length: 150,
data: [ { username: 'john', ... }, ... ],
created: 2026-04-28T10:00:00Z
}🔄 Restore Backup
Restore a collection from a previous backup.
[!WARNING] Restoring a backup will overwrite current data in the collection.
db.restoreBackup('users', {
type: 'binary',
file: './backups/users_backup_2026-04-28.backup',
password: 'my_secret_password'
});Output (
restoreBackup)
{ success: true }📦 Object Store
Store simple application configuration or small state objects as standalone BSON files (.objdb). Perfect for keeping app data that doesn't require complex collections or indexing.
Write/Update Object
db.object("appConfig").write({ theme: 'dark', version: '1.0.4' })
db.object("appConfig").update({ theme: 'light' }) // Merges with existing dataRead Object
const config = db.object("appConfig").read() // { theme: 'light', version: '1.0.4' } or nullRename/Delete
db.object("appConfig").rename("userSettings")
db.object("userSettings").delete()List all objects
const objects = db.object().list() // ["userSettings", "themeCache"] or []🔒 Transactions (db.atomic)
EveloDB provides an asynchronous transaction system to prevent race conditions during complex operations that involve await gaps.
By using db.atomic(), you can ensure that a block of code runs in isolation. Any other atomic operation targeting the same collection will wait in a queue until the current one finishes.
Usage
1. Collection-Level Lock (Recommended)
Only blocks the specific collection, allowing other collections to remain fast.
await db.atomic('products', async (tx) => {
const item = tx.findOne('products', { id: 'p1' });
await someAsyncLogic();
tx.edit('products', { id: 'p1' }, { stock: item.stock - 1 });
});2. Global Lock
Blocks all collections. Useful for migrations or multi-collection updates.
await db.atomic(async (tx) => {
const user = tx.findOne('users', { id: 'u1' });
tx.edit('logs', {}, { message: `User ${user.name} logged in` });
});Why use this?
While Atomic Operators ($inc) are great for simple math, you need Transactions when:
- You have multiple steps (Read -> Logic -> Write).
- You have
awaitcalls between your database operations. - You want to ensure "All or Nothing" behavior.
[!TIP] Alternative:
db.transaction()If you only need to lock a single collection and don't need thetxobject, you can use the simplerdb.transaction('collection', async () => { ... })method.
📁 File Store
EveloDB is a lightweight file storage system for handling any type of file directly in your local storage.
- File Management – Read, write, and delete files easily.
- Image Utilities – Special functions to process images (resize, compress, transform, ...).
- Lightweight & Fast – No external database required, works directly with the file system.
Store image buffer as image.jpg
db.writeFile('image.jpg', imageBuffer){ success: true }Read image.jpg
db.readFile('image.jpg'){
success: true,
data: <Buffer ff d8 ff e0 00 10 ...>
}Delete profile.pdf
db.deleteFile('profile.pdf'){ success: true }List all files
db.allFiles() // ['image.jpg', 'profile.pdf']🖼️ Image Utilities
EveloDB includes built-in utilities to read and process images with ease, backed by an highly optimized performance architecture.
⚡ Performance Architecture
- Zero-Decode LRU Cache: Sub-millisecond reads. A 200MB hash-based cache skips
sharpprocessing entirely on cache hits. - Metadata Fast Path: Dimensions are only extracted when strictly required (pixel-budget resizing), maximizing throughput for direct format/filter conversions.
- AVIF Concurrency Limiter: AVIF encodes are strictly bounded by a concurrency limiter (Tune via
AVIF_CONCURRENCYenv var, defaults to 2) to eliminate CPU saturation under burst traffic. - Hardware-Friendly Defaults: Disables
mozjpegin favor of standardlibjpeg(3-5x faster) and reduces AVIF effort to level 2 (2-4x faster).
✨ Features
- Resize by maximum total pixels or exact width/height constraints
- Adjust brightness and contrast
- Apply filters (grayscale, invert, mirror, flip vertically)
- Control quality and dynamic output format based on extension (
.jpg,.webp,.avif,.png, etc.) - Output formats available directly as
BufferorBase64data URLs
⚙️ Parameters
| Parameter | Type | Default | Description |
|-----------------|---------|---------|-------------|
| returnBase64 | Boolean | true | If true, returns a Base64 Data URL. Otherwise returns a Buffer. |
| quality | Number | 1 | Output quality (0.1 – 1). Lower values reduce size. |
| pixels | Number | 0 | Maximum total pixels. 0 = keep original size. Useful for scaling down large images. |
| maxWidth | Number | null | Maximum width in pixels. |
| maxHeight | Number | null | Maximum height in pixels. |
| blackAndWhite | Boolean | false | Converts the image to grayscale. |
| mirror | Boolean | false | Flips the image horizontally. |
| upToDown | Boolean | false | Flips the image vertically. |
| invert | Boolean | false | Inverts image colors. |
| brightness | Number | 1 | Brightness multiplier (0.1 – 5). 1 = original. |
| contrast | Number | 1 | Contrast multiplier (0.1 – 5). 1 = original. |
Read image.jpg with preset config
(async () => {
const result = await db.readImage("image.jpg", {
returnBase64: true,
quality: 0.8,
pixels: 500000,
blackAndWhite: false,
mirror: false,
upToDown: false,
invert: false,
brightness: 1,
contrast: 1
});
console.log(result)
})(){
success: true,
data: "data:image/jpeg;base64,/9j/4AAQSk...",
metadata: {
filename: "image.jpg",
extension: ".jpg",
originalSize: 254399,
processingApplied: {
resized: true,
qualityReduced: true,
blackAndWhite: true,
mirrored: true,
flippedVertical: false,
inverted: false,
brightnessAdjusted: true,
contrastAdjusted: true
}
}
}💡 Features
- BSON Native: Optimized for binary serialization. No JSON overhead.
- Secure Backups: Full-file XOR encoding for binary backups.
- B-Tree Indexing: O(log n) lookups for primary and secondary keys.
- Unique Constraints: Prevent data duplication at the database level.
- Atomic Renames: Crash-safe file writes using temporary staging.
- Auto-Compaction: Automatic reclamation of deleted record space.
- Atomic Operators: Support for
$inc,$set,$push,$pull, and$unsetfor safe concurrent updates. - System Timestamps: Automatic
_createdAtand_modifiedAtmanagement.
📈 Changelog
v1.0.0 (EveloDB Prime)
- Feature: Added
$pullatomic operator for removing items from arrays by value or condition.
v1.0.0-beta.9 (EveloDB Prime)
- Created agents.md for AI agents and automated tools.
v1.0.0-beta.7 (EveloDB Prime)
- Performance: Completely overhauled
imageProcesswith a new high-performance architecture. - Performance: Introduced a 200MB Zero-Decode LRU Cache for instant image processing hits.
- Performance: Reduced AVIF CPU usage with built-in concurrency limiting and effort tuning.
- Performance: Replaced
mozjpegwith standardlibjpegfor 3-5x faster JPEG encoding.
v1.0.0-beta.6 (EveloDB Prime)
- Feature: Added Atomic Update Operators (
$inc,$set,$unset,$push) for thread-safe field modifications. - Feature: Added
db.atomic()anddb.transaction()for asynchronous collection-level and global locking. - Bug Fix: Resolved a race condition where parallel async API calls could result in stale data reads/writes.
- Bug Fix: Fixed a mutation issue in
$pushthat caused double-updates during schema validation.
v1.0.0-beta.0 (EveloDB Prime)
- Breaking: Fully modularized backup system into
BackupManager. - Breaking: Updated
restoreBackupto use singlefilepath instead of directory/filename. - Feature: Added Secure Binary Backups with full-file XOR encoding.
- Feature: Added
readBackupFilefor safe inspection of encrypted backups. - Feature: Switched to 24-character Hex ObjectIDs for better indexing compatibility.
- Feature: Added Secondary B-Tree Indexes and Unique Key support.
- Feature: Added objectIdKey to schema for virtual renaming of the
_idfield in API calls. - Performance: Optimized B-Tree insertion and search speeds.
