| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
A fast, flexible immutable DataStore for collections of records with indexing, versioning, and advanced querying capabilities.
npm install haroyarn add haropnpm add haroimport { haro } from 'haro';
const store = haro([{ id: 1, name: 'Alice' }], { index: ['name'] });
console.log(store.find({ name: 'Alice' }));import { haro } from 'haro';
const store = haro(data, config);import { Haro } from 'haro';
const store = new Haro({
index: ['name', 'email', 'department'],
key: 'id',
versioning: true,
immutable: true
});
const users = new Haro([
{ name: 'Alice', email: 'alice@company.com', department: 'Engineering' },
{ name: 'Bob', email: 'bob@company.com', department: 'Sales' }
], {
index: ['name', 'department'],
versioning: true
});import { Haro } from 'haro';
class UserStore extends Haro {
constructor(config) {
super({
index: ['email', 'department', 'role'],
key: 'id',
versioning: true,
...config
});
}
isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
}
const store = new UserStore();
const user = store.set(null, {
name: 'John',
email: 'john@example.com'
});Boolean - Enable LRU caching for search() and where() methods (default: false)
const store = haro(null, { cache: true });Number - Maximum number of cached query results (default: 1000)
const store = haro(null, { cache: true, cacheSize: 500 });String - Delimiter for composite indexes (default: '|')
const store = haro(null, { delimiter: '::' });String - Unique identifier for this store instance. Auto-generated if not provided.
const store = haro(null, { id: 'user-cache' });Boolean - Return frozen/immutable objects for data safety (default: false)
const store = haro(null, { immutable: true });Array - Fields to index for faster searches. Supports composite indexes.
const store = haro(null, {
index: ['name', 'email', 'name|department', 'department|role']
});String - Primary key field name (default: 'id')
const store = haro(null, { key: 'userId' });Boolean - Enable version history tracking (default: false)
const store = haro(null, { versioning: true });TypeScript definitions are included - no separate installation needed.
import { Haro } from 'haro';
const store = new Haro<{ name: string; age: number }>({
index: ['name'],
key: 'id'
});import { haro } from 'haro';
// One line to create indexed store
const users = haro(null, { index: ['email', 'name'] });
// Add data
users.set(null, { name: 'Alice', email: 'alice@example.com' });
// Instant lookup - O(1) performance
const user = users.find({ email: 'alice@example.com' });Time saved: No manual index creation, no caching logic, no performance tuning.
import { haro } from 'haro';
const products = haro(null, {
index: ['category', 'brand', 'price', 'category|brand']
});
products.setMany([
{ sku: '1', name: 'Laptop', category: 'Electronics', brand: 'Apple', price: 2499 },
{ sku: '2', name: 'Phone', category: 'Electronics', brand: 'Apple', price: 999 },
{ sku: '3', name: 'Headphones', category: 'Electronics', brand: 'Sony', price: 299 }
]);
// Find by indexed field
const electronics = products.find({ category: 'Electronics' });
// Complex queries
const appleProducts = products.where({
category: 'Electronics',
brand: 'Apple'
}, '&&');
// Search with regex
const searchResults = products.search(/^Laptop$/, 'name');
// Filter with custom logic
const affordable = products.filter(p => p.price < 500);
// Sort and paginate
const sorted = products.sortBy('price');
const page1 = products.limit(0, 10);import { haro } from 'haro';
const users = haro(null, {
index: ['name', 'user.email', 'user.profile.department', 'user.email|user.profile.department']
});
users.setMany([
{
id: '1',
name: 'Alice',
user: {
email: 'alice@company.com',
profile: { department: 'Engineering' }
}
},
{
id: '2',
name: 'Bob',
user: {
email: 'bob@company.com',
profile: { department: 'Sales' }
}
}
]);
// Find by nested field
const alice = users.find({ 'user.email': 'alice@company.com' });
// Query by deeply nested field
const engineers = users.find({ 'user.profile.department': 'Engineering' });
// Composite index with nested fields
const aliceEng = users.find({
'user.email': 'alice@company.com',
'user.profile.department': 'Engineering'
});
// Works with where(), search(), and sortBy()
const results = await users.where({ 'user.profile.department': 'Engineering' });
const sorted = users.sortBy('user.profile.department');import { haro } from 'haro';
const config = haro(null, { versioning: true });
config.set('api.timeout', { value: 30000 });
config.set('api.timeout', { value: 45000 });
config.set('api.timeout', { value: 60000 });
// Access version history
const history = config.versions.get('api.timeout');
console.log(history); // [previous versions]import { haro } from 'haro';
const store = haro(null, { immutable: true });
const user = store.set(null, { name: 'Alice', age: 30 });
// Attempting to modify will throw
try {
user.age = 31; // TypeError: Cannot assign to read only property
} catch (error) {
console.error(error.message);
}import { haro } from 'haro';
const store = haro(null, {
index: ['name'],
cache: true,
cacheSize: 1000
});
store.set("user1", { id: "user1", name: "John" });
// First call - cache miss
const results1 = await store.where({ name: "John" });
// Second call - cache hit (much faster)
const results2 = await store.where({ name: "John" });
// Get cache statistics
console.log(store.getCacheStats()); // { hits: 1, misses: 1, sets: 1, ... }
// Clear cache manually
store.clearCache();| Feature | Haro | Map | Object | lowdb | LokiJS |
|---|---|---|---|---|---|
| Indexing | ✅ Multi-field | ❌ | ❌ | ⚠️ Limited | ✅ |
| Versioning | ✅ Built-in | ❌ | ❌ | ❌ | ⚠️ Plugins |
| Immutable Mode | ✅ | ❌ | ❌ | ❌ | ❌ |
| Advanced Queries | ✅ find/where/search | ❌ | ❌ | ⚠️ Basic | ✅ |
| Batch Operations | ✅ setMany/deleteMany | ❌ | ❌ | ⚠️ Manual | ✅ |
| Persistence | ❌ In-memory | ❌ | ❌ | ✅ JSON/Local | ✅ |
| Performance (1k records) | ⚡ Fast | ⚡ Fastest | ⚡ Fast | 🐌 Slower | ⚡ Fast |
| Memory Overhead | Medium | Low | Low | Medium | High |
| TypeScript Support | ✅ | ✅ | ✅ | ✅ | ⚠️ Community |
| Bundle Size | ~6KB gzipped | Native | Native | ~8KB | ~2.6MB |
| Learning Curve | Low | Low | Low | Low | Medium |
Legend: ✅ Yes | ❌ No | ⚠️ Limited/Optional
For complete API documentation with all methods and examples, see API.md.
Quick Overview:
Cause: Passing invalid data to find() or where().
Solution: Ensure query objects have valid field names that exist in your index.
// ❌ Wrong
store.find(undefined);
// ✅ Correct
store.find({ name: 'Alice' });Cause: Too many indexes or complex queries on large collections.
Solution:
// Optimize indexes
const store = haro(null, {
index: ['name', 'email'] // Only essential fields
});
// Use pagination
const results = store.limit(0, 100);Cause: Versioning enabled with frequent updates.
Solution: Clear version history periodically or disable versioning if not needed.
// Clear specific version history
store.versions.delete('key123');
// Clear all versions
store.versions.clear();
// Disable versioning if not needed
const store = haro(null, { versioning: false });Cause: Attempting to modify frozen objects.
Solution: Use set() to update records instead of direct mutation.
// ❌ Wrong
const user = store.get('user123');
user.age = 31;
// ✅ Correct
store.set('user123', { age: 31 });Cause: Querying non-indexed fields.
Solution: Add the field to the index configuration.
const store = haro(null, {
index: ['name', 'email', 'department']
});| Error | Cause | Solution |
|---|---|---|
| "Key field validation error" | Missing key field in data | Ensure key field exists in records |
| "Index must be an array" | Invalid index configuration | Pass array to index option |
| "Function required" | Invalid function parameter | Pass a function to filter() or map() |
| "Invalid index name" | Sorting by non-indexed field | Add field to index or use sort() |
# Run unit tests
npm test
# Run with coverage
npm run coverage
# Run performance benchmarks
npm run benchmarkSee CONTRIBUTING.md for detailed testing guidelines.
Haro includes comprehensive benchmark suites for performance analysis.
# Run all benchmarks
node benchmarks/index.js
# Run specific categories
node benchmarks/index.js --basic-only # CRUD operations
node benchmarks/index.js --search-only # Query operations
node benchmarks/index.js --index-only # Index operations
node benchmarks/index.js --utilities-only # Utility operations
node benchmarks/index.js --pagination-only # Pagination benchmarks
node benchmarks/index.js --persistence-only # Persistence benchmarks
node benchmarks/index.js --core-only # Core benchmarks (basic, search, index)
node benchmarks/index.js --quiet # Minimal outputHaro provides excellent performance for in-memory data operations:
| Operation | Latency (avg) | Throughput (ops/s) |
|---|---|---|
| Basic Operations | ||
| set() 10000 records | 1507 ms | 747 ops/s |
| get() 10000 records | 35 ms | 29015 ops/s |
| has() 10000 keys | 2 ms | 495325 ops/s |
| delete() 10000 records | 2353 ms | 432 ops/s |
| Search & Filter | ||
| find() by indexed field 10000 records | 98 ms | 10299 ops/s |
| where() by indexed field 10000 records | 222 ms | 4516 ops/s |
| search() in index 10000 records | 116 ms | 8716 ops/s |
| filter() all records 10000 records | 115 ms | 8822 ops/s |
| Index Operations | ||
| haro() with indexes 10000 records | 10842 ms | 94 ops/s |
| find() with index 10000 records | 3996 ms | 267 ops/s |
| reindex() single field 10000 records | 5739 ms | 182 ops/s |
| Utility Operations | ||
| toArray() 1000 iterations | 21 ms | 47750 ops/s |
| entries() 1000 iterations | 92 ms | 10986 ops/s |
| keys() 1000 iterations | 17 ms | 59043 ops/s |
| values() 1000 iterations | 17 ms | 60553 ops/s |
| Pagination | ||
| limit() 10 10000 records | 15 ms | 69050 ops/s |
| limit() 50 10000 records | 16 ms | 65516 ops/s |
| limit() 100 10000 records | 17 ms | 61172 ops/s |
| limit() with offset 10000 records | 16 ms | 65522 ops/s |
| Persistence | ||
| dump() records 5000 records | 75 ms | 13583 ops/s |
See benchmarks/README.md for complete benchmark documentation and detailed results.
Copyright (c) 2026 Jason Mulligan
Licensed under the BSD-3-Clause license.
| Back | FazBrowse Home | New Git URL |