yq-store
Version:
A high-performance, persistent and in-memory key-value store with TTL support, built for Node.js applications.
1,317 lines (1,314 loc) • 34.8 kB
TypeScript
/**
* @fileoverview A high-performance, persistent, zero-dependency Key-Value store
* with advanced querying and type-safe events.
*/
/**
* Defines the configuration options for creating a new KvStore instance.
*/
export interface KvStoreOptions {
/**
* The interval in milliseconds for the store to automatically perform
* compaction. Compaction is the process of rewriting the database file to
* permanently remove data from expired, deleted, or updated keys.
* This saves disk space and speeds up initial load times.
*
* @remarks
* This is a global setting for database maintenance and is different from
* the 'ttlSeconds' parameter on the `set` method, which applies a
* Time-To-Live expiration to an individual key.
*
* Set to 0 to disable automatic compaction.
* @default 3600000 (1 hour)
*/
compactionInterval?: number;
/**
* The default Time-To-Live (TTL) for keys in seconds. If set, each key will
* automatically expire after this duration. If not set, keys will persist
* until deleted.
*
* @remarks
* This is a per-key setting and overrides the global `compactionInterval`.
* If a key-specific TTL is set, it takes precedence over the global setting.
*
* Set to 0 to disable expiration for a key.
* @default 0 (no expiration)
*/
ttl?: number;
/**
* Enable soft delete mode. When enabled, deleted entries are marked as deleted
* but not physically removed from the database until compaction or manual cleanup.
* When disabled, deleted entries are immediately removed from the database.
*
* @default true
*/
softDelete?: boolean;
/**
* Storage configuration.
*/
storage?: {
/**
* Storage type
* - 'memory': In-memory SQLite database (':memory:')
* - 'persistence': File-based SQLite database
* @default 'persistence'
*/
type?: "memory" | "persistence";
/**
* Maximum number of entries before LRU eviction kicks in
* @default 50000 for memory mode, 1000000 for persistence mode
*/
maxEntries?: number;
/**
* Maximum memory usage in bytes (only applies to memory mode)
* @default 104857600 (100MB)
*/
maxMemory?: number;
/**
* LRU eviction interval in milliseconds
* @default 60000 (1 minute)
*/
evictionInterval?: number;
/**
* Enable LRU eviction when maxItems is reached
* @default false
*/
eviction?: boolean;
/**
* Persistence storage configuration
*/
persistence?: {
/**
* Directory path for storing the persistent database files. If not specified,
* defaults to the system's temporary directory (os.tmpdir()). The database
* filename will be an MD5 hash of __dirname if dbFileName is not provided.
*
* @remarks
* For production use, it's recommended to specify a custom directory path
* to ensure data persistence across system reboots.
*
* @default os.tmpdir()
*/
dbDir?: string;
/**
* Custom database file name (without extension)
* @default auto-generated
*/
dbFileName?: string;
/**
* SQLite configuration
*/
sqlite?: {
/**
* Journal mode for SQLite
* @default 'WAL'
*/
journalMode?: "DELETE" | "TRUNCATE" | "PERSIST" | "MEMORY" | "WAL" | "OFF";
/**
* Synchronous mode for SQLite
* @default 'NORMAL'
*/
synchronous?: "OFF" | "NORMAL" | "FULL" | "EXTRA";
/**
* Cache size in pages (negative value = KB)
* @default -10000 (10MB)
*/
cacheSize?: number;
/**
* Temporary storage location
* @default 'memory'
*/
tempStore?: "default" | "file" | "memory";
/**
* Enable foreign key constraints
* @default false
*/
foreignKeys?: boolean;
/**
* Busy timeout in milliseconds
* @default 30000
*/
busyTimeout?: number;
};
/**
* Vacuum configuration
*/
vacuum?: {
/**
* Enable vacuum
* @default true
*/
enabled?: boolean;
/**
* Vacuum mode
* @default 'incremental'
*/
mode?: "none" | "incremental" | "full";
};
};
};
/**
* Logging configuration
*/
logging?: {
/**
* Enable logging
* @default false
*/
enabled?: boolean;
/**
* Log directory path
* @default './logs'
*/
logDir?: string;
};
/**
* Debug mode configuration
*/
debug?: {
/**
* Enable debug mode for detailed logging and performance metrics
* @default false
*/
enabled?: boolean;
/**
* Enable performance timing logs
* @default false
*/
timing?: boolean;
/**
* Enable SQL query logging
* @default false
*/
sqlLogging?: boolean;
};
/**
* Keep the Node.js process alive while the store is open.
*
* When `false` (default), internal timers (compaction, eviction, LRU updates)
* are unref'd so they don't prevent the process from exiting naturally.
* The store will still work correctly, but the process can exit when there's
* no other work to do.
*
* When `true`, the internal timers will keep the process alive until
* `store.close()` is called explicitly.
*
* @default false
*/
keepAlive?: boolean;
}
/**
* Defines the mapping of event names to their listener function signatures.
* This ensures full type safety when using `store.on()` or `store.emit()`.
*/
export interface KvStoreEvents<T = unknown> {
/** Emitted when the store has successfully initialized and is ready for use. */
ready: () => void;
/** Emitted when the database is ready for operations. */
"db:ready": () => void;
/** Emitted when a new key-value pair is successfully set. */
set: (key: string, value: T) => void;
/** Emitted when a key is successfully deleted (either directly or via expiration). */
delete: (key: string) => void;
/** Emitted when a key is found to be expired during a `get` or query operation. */
expire: (key: string) => void;
/** Emitted when entries are evicted due to LRU or memory limits. */
evict: (key: string | number) => void;
/** Emitted when a compaction process is about to begin. */
"compact:start": () => void;
/** Emitted when a compaction process has successfully completed. */
"compact:end": (stats: {
newSize: number;
keysProcessed: number;
}) => void;
/** Emitted when a recoverable error occurs, such as a corrupted line in the DB file. */
error: (error: Error) => void;
/** Emitted when the database file is closed and all resources are cleaned up. */
close: () => void;
}
/**
* Represents the structure of a single entry in the append-only log file.
*/
export type LogEntry<T> = {
key: string;
value: T;
expiresAt?: number;
tombstone?: never;
} | {
key: string;
value?: never;
expiresAt?: never;
tombstone: true;
};
/**
* In-memory metadata for a key, pointing to its location on disk.
*/
export interface IndexMeta {
offset: number;
size: number;
expiresAt?: number;
db?: string;
}
/**
* Default namespace used when no namespace is specified
*/
export declare const DEFAULT_NAMESPACE = "_default";
/**
* Maximum safe integer for expires_at (never expires)
* Using Number.MAX_SAFE_INTEGER (2^53 - 1) for JavaScript compatibility
* This represents ~285 million years from epoch, which is effectively "never"
*/
export declare const NEVER_EXPIRES = 9007199254740991;
/**
* Options for key-based operations (get, delete, has)
*/
export interface KeyOptions {
/**
* Namespace to scope the operation to
* @default '_default'
*/
namespace?: string;
}
/**
* Options for set operations
*/
export interface SetOptions extends KeyOptions {
/**
* Time-to-live in seconds. After this duration, the key will expire.
* Set to 0 or undefined for no expiration.
*/
ttl?: number;
}
/**
* Options for listing and querying operations
*/
export interface ListOptions extends KeyOptions {
/**
* Filter keys that start with this prefix
*/
prefix?: string;
/**
* Filter keys that end with this suffix
*/
suffix?: string;
/**
* Maximum number of results to return
* @default 100
*/
limit?: number;
/**
* Number of results to skip (for pagination)
* @default 0
*/
offset?: number;
}
/**
* Options for size/count operations
*/
export interface SizeOptions extends KeyOptions {
/**
* Filter keys that start with this prefix
*/
prefix?: string;
/**
* Filter keys that end with this suffix
*/
suffix?: string;
}
/**
* Entry type for bulk set operations
*/
export interface BulkSetEntry<T = unknown> {
key: string;
value: T;
ttl?: number;
}
/**
* Statistics for a single namespace
*/
export interface NamespaceStats {
/**
* Namespace name
*/
namespace: string;
/**
* Count of active (non-deleted, non-expired) entries
*/
activeCount: number;
/**
* Count of soft-deleted entries
*/
deletedCount: number;
/**
* Count of expired entries (not yet cleaned up)
*/
expiredCount: number;
/**
* Total size in bytes (approximate)
*/
sizeBytes: number;
}
/**
* Global store statistics
*/
export interface StoreStats {
/**
* Statistics per namespace
*/
namespaces: NamespaceStats[];
/**
* Total active entries across all namespaces
*/
totalActive: number;
/**
* Total deleted entries across all namespaces
*/
totalDeleted: number;
/**
* Total expired entries across all namespaces
*/
totalExpired: number;
/**
* Total size in bytes across all namespaces
*/
totalSizeBytes: number;
}
/**
* Size type for getSize operations
*/
export type SizeType = "active" | "deleted" | "expired" | "all";
/**
* Batch operation types
*/
export interface PutBatchOperation<T = unknown> {
type: "put";
key: string;
value: T;
ttl?: number;
namespace?: string;
}
export interface DelBatchOperation {
type: "del";
key: string;
namespace?: string;
}
export type BatchOperation<T = unknown> = PutBatchOperation<T> | DelBatchOperation;
/**
* Storage entry returned from storage manager
*/
export interface StorageEntry<T = unknown> {
key: string;
namespace: string;
value: T;
createdAt: number;
expiresAt?: number;
isDeleted: boolean;
lastAccessed: number;
}
/**
* Namespaced store interface - provides a namespace-scoped view of the store
*/
export interface INamespacedStore {
/**
* The namespace name
*/
readonly name: string;
/**
* Store a value
*/
set<T>(key: string, value: T, ttl?: number): Promise<void>;
/**
* Retrieve a value
*/
get<T>(key: string): Promise<T | null>;
/**
* Delete a key
*/
delete(key: string): Promise<boolean>;
/**
* Check if a key exists
*/
has(key: string): Promise<boolean>;
/**
* Get multiple values at once
*/
getMany<T>(keys: string[]): Promise<Map<string, T>>;
/**
* Set multiple values at once
*/
setMany<T>(entries: BulkSetEntry<T>[]): Promise<void>;
/**
* Delete multiple keys at once
*/
deleteMany(keys: string[]): Promise<number>;
/**
* List all keys in this namespace
*/
listKeys(options?: Omit<ListOptions, "namespace">): Promise<string[]>;
/**
* List all key-value pairs in this namespace
*/
list<T>(options?: Omit<ListOptions, "namespace">): Promise<Array<{
key: string;
value: T;
}>>;
/**
* Get the count of entries
*/
getSize(type?: SizeType): Promise<number>;
/**
* Clear all entries in this namespace
*/
clear(): Promise<number>;
/**
* Get statistics for this namespace
*/
getStats(): Promise<NamespaceStats>;
}
/**
* Performance metrics for monitoring
*/
export interface StorageMetrics {
operations: {
get: {
count: number;
totalMs: number;
};
set: {
count: number;
totalMs: number;
};
delete: {
count: number;
totalMs: number;
};
has: {
count: number;
totalMs: number;
};
};
cache: {
hits: number;
misses: number;
};
errors: {
count: number;
lastError?: Error;
};
}
/**
* Current schema version - increment when schema changes
*/
export declare const SCHEMA_VERSION = 1;
/**
* Schema validation result
*/
export interface SchemaValidationResult {
isValid: boolean;
currentVersion?: number;
expectedVersion: number;
missingColumns: string[];
extraColumns: string[];
message?: string;
}
/**
* Migration options
*/
export interface MigrationOptions {
/**
* Path to the database file
*/
dbPath: string;
/**
* Create a backup before migration
* @default true
*/
backup?: boolean;
/**
* Dry run - only check what would be migrated without making changes
* @default false
*/
dryRun?: boolean;
/**
* Verbose logging
* @default false
*/
verbose?: boolean;
}
/**
* Migration result
*/
export interface MigrationResult {
success: boolean;
fromVersion: number | null;
toVersion: number;
changes: string[];
backupPath?: string;
error?: string;
}
/**
* Schema check result
*/
export interface SchemaCheckResult {
needsMigration: boolean;
validation: SchemaValidationResult;
dbPath: string;
exists: boolean;
}
/**
* Check database schema without making changes
*/
export declare function checkSchema(dbPath: string): Promise<SchemaCheckResult>;
/**
* Migrate database schema to the latest version
*/
export declare function migrate(options: MigrationOptions): Promise<MigrationResult>;
/**
* Find all yq-store database files in a directory
*/
export declare function findDatabases(dir: string, pattern?: string): string[];
/**
* A high-performance, persistent Key-Value store with SQLite backend.
* Supports TTL, namespaces, soft deletes, compaction, and type-safe event handling.
*
* @example
* ```typescript
* const store = await YqStore.create({
* storage: { type: 'persistence' },
* compactionInterval: 3600000,
* softDelete: true
* });
*
* // Simple usage
* await store.set('key', { data: 'value' }, 60); // TTL of 60 seconds
* const value = await store.get('key');
*
* // With namespace
* await store.set('john', { name: 'John' }, { namespace: 'users' });
* const user = await store.get('john', { namespace: 'users' });
*
* // Namespace proxy (recommended for repeated use)
* const users = store.ns('users');
* await users.set('jane', { name: 'Jane' });
* await users.get('jane');
*
* await store.close();
* ```
*/
export declare class YqStore {
private readonly options;
private readonly emitter;
private readonly engine;
private readonly namespaceCache;
private compactionTimer?;
private evictionTimer?;
private isCompacting;
private isClosing;
private dbName;
private constructor();
/**
* Creates and initializes a new YqStore instance.
*
* @param options - Configuration options for the store
* @returns Promise that resolves to an initialized YqStore instance
*/
static create(options?: KvStoreOptions): Promise<YqStore>;
private initialize;
on<E extends keyof KvStoreEvents>(event: E, listener: KvStoreEvents[E]): this;
off<E extends keyof KvStoreEvents>(event: E, listener: KvStoreEvents[E]): this;
addEventListener<E extends keyof KvStoreEvents>(event: E, listener: KvStoreEvents[E]): this;
removeEventListener<E extends keyof KvStoreEvents>(event: E, listener: KvStoreEvents[E]): this;
/**
* Checks if a key exists in the store and is not expired.
*
* @param key - The key to check for existence
* @param options - Optional namespace options
* @returns Promise that resolves to true if the key exists and is valid
*/
has(key: string, options?: KeyOptions): Promise<boolean>;
/**
* Retrieves a value from the store by its key.
*
* @template T - The expected type of the stored value
* @param key - The key to retrieve the value for
* @param options - Optional namespace options
* @returns Promise that resolves to the stored value or null if not found/expired
*
* @example
* ```typescript
* const user = await store.get<User>('user:123');
* const user = await store.get<User>('john', { namespace: 'users' });
* ```
*/
get<T>(key: string, options?: KeyOptions): Promise<T | null>;
/**
* Stores a value in the store with optional TTL and namespace.
*
* @template T - The type of the value being stored
* @param key - The key to store the value under
* @param value - The value to store
* @param ttlOrOptions - TTL in seconds (backward compatible) or SetOptions object
*
* @example
* ```typescript
* // Backward compatible
* await store.set('key', value);
* await store.set('key', value, 60); // TTL 60 seconds
*
* // New options API
* await store.set('key', value, { ttl: 60 });
* await store.set('key', value, { namespace: 'users' });
* await store.set('key', value, { ttl: 60, namespace: 'users' });
* ```
*/
set<T>(key: string, value: T, ttlOrOptions?: number | SetOptions): Promise<void>;
/**
* Deletes a key from the store.
*
* @param key - The key to delete
* @param options - Optional namespace options
* @returns Promise that resolves to true if the key was deleted
*/
delete(key: string, options?: KeyOptions): Promise<boolean>;
/**
* Get multiple values at once (10-100x faster than individual gets)
*
* @example
* ```typescript
* const values = await store.getMany(['key1', 'key2', 'key3']);
* const values = await store.getMany(['key1', 'key2'], { namespace: 'users' });
* ```
*/
getMany<T>(keys: string[], options?: KeyOptions): Promise<Map<string, T>>;
/**
* Set multiple values at once (uses transaction, much faster)
*
* @example
* ```typescript
* await store.setMany([
* { key: 'user1', value: { name: 'John' } },
* { key: 'user2', value: { name: 'Jane' }, ttl: 3600 }
* ]);
* ```
*/
setMany<T>(entries: BulkSetEntry<T>[], options?: KeyOptions): Promise<void>;
/**
* Delete multiple keys at once
*
* @example
* ```typescript
* const count = await store.deleteMany(['key1', 'key2', 'key3']);
* ```
*/
deleteMany(keys: string[], options?: KeyOptions): Promise<number>;
getSize(): Promise<number>;
getSize(type: SizeType): Promise<number>;
getSize(options: SizeOptions): Promise<number>;
getSize(type: SizeType, options: SizeOptions): Promise<number>;
listKeys(): Promise<string[]>;
listKeys(options: ListOptions): Promise<string[]>;
/**
* List key-value pairs matching criteria
*/
list<T>(options?: ListOptions): Promise<Array<{
key: string;
value: T;
}>>;
/**
* Iterate over entries (memory efficient for large datasets)
*/
forEach<T>(callback: (key: string, value: T) => void | Promise<void>, options?: ListOptions): Promise<void>;
/**
* Get a namespace-scoped store (recommended for repeated namespace operations)
*
* @example
* ```typescript
* const users = store.ns('users');
* await users.set('john', { name: 'John' });
* const john = await users.get('john');
* await users.delete('john');
* ```
*/
ns(namespace: string): INamespacedStore;
/**
* Alias for ns()
*/
namespace(namespace: string): INamespacedStore;
/**
* List all namespaces with active entries
*/
listNamespaces(): Promise<string[]>;
/**
* Clear all entries in a namespace
*/
clearNamespace(namespace: string): Promise<number>;
/**
* Get statistics for a namespace
*/
getNamespaceStats(namespace: string): Promise<NamespaceStats>;
/**
* Get global store statistics
*/
getStats(): Promise<StoreStats>;
/**
* Get performance metrics
*/
getMetrics(): StorageMetrics;
/**
* Reset performance metrics
*/
resetMetrics(): void;
clearSoftDeletedEntries(): Promise<number>;
clearExpiredEntries(): Promise<number>;
clear(): Promise<void>;
compact(): Promise<void>;
private runEviction;
/**
* Execute batch operations atomically
*/
batch(operations: BatchOperation[]): Promise<void>;
/**
* Create a transaction for batch operations
*/
createTransaction(): {
put<T>(key: string, value: T, ttlSeconds?: number, namespace?: string): void;
del(key: string, namespace?: string): void;
commit(): Promise<void>;
rollback(): void;
};
close(): Promise<void>;
/**
* Get the database file path
*/
getDbFilePath(): string;
/**
* Check if a database needs schema migration.
*
* @param dbPath - Path to the database file
* @returns Promise resolving to schema check result
*
* @example
* ```typescript
* const result = await YqStore.checkSchema('/path/to/store.yqs');
* if (result.needsMigration) {
* console.log('Migration needed:', result.validation.missingColumns);
* }
* ```
*/
static checkSchema(dbPath: string): Promise<SchemaCheckResult>;
/**
* Migrate a database to the latest schema version.
*
* @param options - Migration options
* @returns Promise resolving to migration result
*
* @example
* ```typescript
* // Basic migration with backup
* const result = await YqStore.migrate({
* dbPath: '/path/to/store.yqs',
* backup: true,
* verbose: true
* });
*
* if (result.success) {
* console.log('Migrated from v' + result.fromVersion + ' to v' + result.toVersion);
* }
*
* // Dry run to preview changes
* const preview = await YqStore.migrate({
* dbPath: '/path/to/store.yqs',
* dryRun: true
* });
* console.log('Would make changes:', preview.changes);
* ```
*/
static migrate(options: MigrationOptions): Promise<MigrationResult>;
/**
* Find all yq-store database files in a directory.
*
* @param dir - Directory to search
* @param pattern - Glob pattern (default: '*.yqs')
* @returns Array of database file paths
*
* @example
* ```typescript
* const databases = YqStore.findDatabases('/data/stores');
* for (const dbPath of databases) {
* const check = await YqStore.checkSchema(dbPath);
* if (check.needsMigration) {
* await YqStore.migrate({ dbPath, backup: true });
* }
* }
* ```
*/
static findDatabases(dir: string, pattern?: string): string[];
}
/**
* @fileoverview A generic, type-safe wrapper around Node.js's EventEmitter.
*/
/**
* Provides a fully type-safe event emitter implementation.
* @template TEvents A record mapping event names to their argument tuples.
*/
export declare class TypedEventEmitter<TEvents extends Record<string, any>> {
private emitter;
/**
* Registers an event listener. Alias for `on`.
* @param event The name of the event to listen for.
* @param listener The callback function.
*/
addEventListener<E extends keyof TEvents>(event: E, listener: TEvents[E]): this;
/**
* Registers an event listener.
* @param event The name of the event to listen for.
* @param listener The callback function.
*/
on<E extends keyof TEvents>(event: E, listener: TEvents[E]): this;
/**
* Unregisters an event listener. Alias for `off`.
* @param event The name of the event to stop listening to.
* @param listener The callback function to remove.
*/
removeEventListener<E extends keyof TEvents>(event: E, listener: TEvents[E]): this;
/**
* Unregisters an event listener.
* @param event The name of the event to stop listening to.
* @param listener The callback function to remove.
*/
off<E extends keyof TEvents>(event: E, listener: TEvents[E]): this;
/**
* Emits an event, calling all registered listeners with the provided arguments.
* @param event The name of the event to emit.
* @param args The arguments to pass to the listeners.
* @returns `true` if the event had listeners, `false` otherwise.
*/
emit<E extends keyof TEvents>(event: E, ...args: Parameters<TEvents[E]>): boolean;
}
/**
* Error thrown when database schema doesn't match expected schema
*/
export declare class SchemaMismatchError extends Error {
readonly validationResult: SchemaValidationResult;
constructor(result: SchemaValidationResult);
private static buildMessage;
}
/**
* Configuration for the storage engine
*/
export interface StorageEngineConfig {
/**
* Directory for database file
*/
dbDir: string;
/**
* Database name (used for filename)
*/
dbName: string;
/**
* Use in-memory database
* @default false
*/
inMemory?: boolean;
/**
* Custom database filename
*/
dbFileName?: string;
/**
* Enable soft delete mode
* @default true
*/
softDelete?: boolean;
/**
* Track LRU access times
* @default true
*/
trackLRU?: boolean;
/**
* Maximum entries before LRU eviction
*/
maxEntries?: number;
/**
* SQLite configuration
*/
sqlite?: {
journalMode?: "DELETE" | "TRUNCATE" | "PERSIST" | "MEMORY" | "WAL" | "OFF";
synchronous?: "OFF" | "NORMAL" | "FULL" | "EXTRA";
cacheSize?: number;
tempStore?: "default" | "file" | "memory";
busyTimeout?: number;
mmapSize?: number;
};
/**
* Enable debug mode
* @default false
*/
debug?: boolean;
/**
* Keep the Node.js process alive while the store is open.
* When false, internal timers are unref'd so they don't prevent exit.
* @default false
*/
keepAlive?: boolean;
}
/**
* Abstract base class for storage engines
* Provides common functionality and defines the interface
*/
export declare abstract class BaseStorageEngine {
protected readonly config: Required<StorageEngineConfig>;
protected readonly emitter: TypedEventEmitter<KvStoreEvents>;
protected isInitialized: boolean;
protected metrics: StorageMetrics;
constructor(config: StorageEngineConfig, emitter: TypedEventEmitter<KvStoreEvents>);
abstract initialize(): Promise<void>;
abstract close(): Promise<void>;
abstract set(key: string, value: unknown, ttl?: number, namespace?: string): Promise<void>;
abstract get(key: string, namespace?: string): Promise<StorageEntry | null>;
abstract delete(key: string, namespace?: string): Promise<boolean>;
abstract has(key: string, namespace?: string): Promise<boolean>;
abstract getMany(keys: string[], namespace?: string): Promise<Map<string, unknown>>;
abstract setMany(entries: BulkSetEntry[], namespace?: string): Promise<void>;
abstract deleteMany(keys: string[], namespace?: string): Promise<number>;
abstract batch(operations: BatchOperation[]): Promise<void>;
abstract listKeys(options?: ListOptions): Promise<string[]>;
abstract listEntries(options?: ListOptions): Promise<StorageEntry[]>;
abstract getSize(type?: SizeType, options?: SizeOptions): Promise<number>;
abstract clearExpired(): Promise<number>;
abstract clearDeleted(): Promise<number>;
abstract clearNamespace(namespace: string): Promise<number>;
abstract clearAll(): Promise<void>;
abstract evictLRU(targetCount?: number): Promise<number>;
abstract listNamespaces(): Promise<string[]>;
abstract getNamespaceStats(namespace: string): Promise<NamespaceStats>;
abstract getDbFilePath(): string;
/**
* Check if initialized
*/
protected ensureInitialized(): void;
/**
* Get current timestamp in milliseconds
*/
protected now(): number;
/**
* Calculate expiration timestamp
*/
protected calculateExpiresAt(ttlSeconds?: number): number;
/**
* Normalize namespace (use default if not provided)
*/
protected normalizeNamespace(namespace?: string): string;
/**
* Serialize value to binary
*/
protected serialize(value: unknown): Uint8Array;
/**
* Deserialize binary to value
*/
protected deserialize(data: Uint8Array): unknown;
/**
* Track operation metrics
*/
protected trackMetric(operation: "get" | "set" | "delete" | "has", durationMs: number): void;
/**
* Track cache hit/miss
*/
protected trackCacheHit(hit: boolean): void;
/**
* Track error
*/
protected trackError(error: Error): void;
/**
* Get current metrics
*/
getMetrics(): StorageMetrics;
/**
* Reset metrics
*/
resetMetrics(): void;
/**
* Log debug message
*/
protected debug(message: string, ...args: unknown[]): void;
/**
* Check if this is Bun runtime
*/
protected isBunRuntime(): boolean;
/**
* Validate existing database schema against expected schema.
* Returns validation result with details about any mismatches.
*
* @param existingColumns - Array of column names from PRAGMA table_info
* @param schemaVersion - Optional schema version from meta table
*/
protected validateSchema(existingColumns: string[], schemaVersion?: number): SchemaValidationResult;
/**
* SQL to check if kv_store table exists
*/
protected getTableExistsSQL(): string;
/**
* SQL to get table column info
*/
protected getTableInfoSQL(): string;
/**
* SQL to create/update schema_version meta table
*/
protected getSchemaVersionTableSQL(): string;
/**
* SQL to get current schema version
*/
protected getSchemaVersionSQL(): string;
/**
* SQL for creating the optimized table schema
*/
protected getCreateTableSQL(): string;
/**
* SQL for configuring SQLite pragmas
*/
protected getPragmaSQL(): string[];
}
/**
* High-performance storage engine for Bun runtime
*
* Optimizations:
* - Prepared statement caching (compile once, execute many)
* - Single query for get() with expiry check built-in
* - Optional async LRU tracking
* - Optimized schema with composite indexes
* - WITHOUT ROWID for TEXT primary keys
*/
export declare class BunStorageEngine extends BaseStorageEngine {
private db?;
private statements?;
private dbFilePath;
private pendingAccessUpdates;
private accessFlushTimer?;
constructor(config: StorageEngineConfig, emitter: TypedEventEmitter<KvStoreEvents>);
/**
* Initialize the database and prepare statements
*/
initialize(): Promise<void>;
/**
* Prepare and cache all SQL statements
*/
private prepareStatements;
/**
* Close the database connection
*/
close(): Promise<void>;
/**
* Set a key-value pair
*/
set(key: string, value: unknown, ttl?: number, namespace?: string): Promise<void>;
/**
* Get a value by key
* Single query with expiry check - no separate expired check needed
*/
get(key: string, namespace?: string): Promise<StorageEntry | null>;
/**
* Delete a key
*/
delete(key: string, namespace?: string): Promise<boolean>;
/**
* Check if a key exists
*/
has(key: string, namespace?: string): Promise<boolean>;
/**
* Get multiple values at once
*/
getMany(keys: string[], namespace?: string): Promise<Map<string, unknown>>;
/**
* Set multiple values at once
*/
setMany(entries: BulkSetEntry[], namespace?: string): Promise<void>;
/**
* Delete multiple keys at once
*/
deleteMany(keys: string[], namespace?: string): Promise<number>;
/**
* Execute batch operations atomically
*/
batch(operations: BatchOperation[]): Promise<void>;
/**
* List keys matching criteria
*/
listKeys(options?: ListOptions): Promise<string[]>;
/**
* List full entries matching criteria
*/
listEntries(options?: ListOptions): Promise<StorageEntry[]>;
/**
* Get count of entries
*/
getSize(type?: SizeType, options?: SizeOptions): Promise<number>;
/**
* Clear expired entries
*/
clearExpired(): Promise<number>;
/**
* Clear soft-deleted entries
*/
clearDeleted(): Promise<number>;
/**
* Clear all entries in a namespace
*/
clearNamespace(namespace: string): Promise<number>;
/**
* Clear all entries
*/
clearAll(): Promise<void>;
/**
* Queue an access update for batch processing
*/
private queueAccessUpdate;
/**
* Flush pending access updates in a single transaction
*/
private flushAccessUpdates;
/**
* Evict least recently used entries
*/
evictLRU(targetCount?: number): Promise<number>;
/**
* List all namespaces
*/
listNamespaces(): Promise<string[]>;
/**
* Get statistics for a namespace
*/
getNamespaceStats(namespace: string): Promise<NamespaceStats>;
/**
* Get database file path
*/
getDbFilePath(): string;
}
/**
* High-performance storage engine for Node.js runtime
*
* Optimizations:
* - Prepared statement caching (compile once, execute many)
* - Single query for get() with expiry check built-in
* - Optional async LRU tracking
* - Optimized schema with composite indexes
* - WITHOUT ROWID for TEXT primary keys
*/
export declare class NodeStorageEngine extends BaseStorageEngine {
private db?;
private statements?;
private dbFilePath;
private pendingAccessUpdates;
private accessFlushTimer?;
constructor(config: StorageEngineConfig, emitter: TypedEventEmitter<KvStoreEvents>);
/**
* Initialize the database and prepare statements
*/
initialize(): Promise<void>;
/**
* Prepare and cache all SQL statements
*/
private prepareStatements;
/**
* Close the database connection
*/
close(): Promise<void>;
/**
* Set a key-value pair
*/
set(key: string, value: unknown, ttl?: number, namespace?: string): Promise<void>;
/**
* Get a value by key
* Single query with expiry check - no separate expired check needed
*/
get(key: string, namespace?: string): Promise<StorageEntry | null>;
/**
* Delete a key
*/
delete(key: string, namespace?: string): Promise<boolean>;
/**
* Check if a key exists
*/
has(key: string, namespace?: string): Promise<boolean>;
/**
* Get multiple values at once
*/
getMany(keys: string[], namespace?: string): Promise<Map<string, unknown>>;
/**
* Set multiple values at once
*/
setMany(entries: BulkSetEntry[], namespace?: string): Promise<void>;
/**
* Delete multiple keys at once
*/
deleteMany(keys: string[], namespace?: string): Promise<number>;
/**
* Execute batch operations atomically
*/
batch(operations: BatchOperation[]): Promise<void>;
/**
* List keys matching criteria
*/
listKeys(options?: ListOptions): Promise<string[]>;
/**
* List full entries matching criteria
*/
listEntries(options?: ListOptions): Promise<StorageEntry[]>;
/**
* Get count of entries
*/
getSize(type?: SizeType, options?: SizeOptions): Promise<number>;
/**
* Clear expired entries
*/
clearExpired(): Promise<number>;
/**
* Clear soft-deleted entries
*/
clearDeleted(): Promise<number>;
/**
* Clear all entries in a namespace
*/
clearNamespace(namespace: string): Promise<number>;
/**
* Clear all entries
*/
clearAll(): Promise<void>;
/**
* Queue an access update for batch processing
*/
private queueAccessUpdate;
/**
* Flush pending access updates in a single transaction
*/
private flushAccessUpdates;
/**
* Evict least recently used entries
*/
evictLRU(targetCount?: number): Promise<number>;
/**
* List all namespaces
*/
listNamespaces(): Promise<string[]>;
/**
* Get statistics for a namespace
*/
getNamespaceStats(namespace: string): Promise<NamespaceStats>;
/**
* Get database file path
*/
getDbFilePath(): string;
}
/**
* Create the appropriate storage engine for the current runtime
*/
export declare function createStorageEngine(config: StorageEngineConfig, emitter: TypedEventEmitter<KvStoreEvents>): BaseStorageEngine;
/**
* Storage engine type for explicit selection
*/
export type StorageEngineType = "bun" | "node" | "auto";
/**
* Create a storage engine with explicit type selection
*/
export declare function createStorageEngineOfType(type: StorageEngineType, config: StorageEngineConfig, emitter: TypedEventEmitter<KvStoreEvents>): BaseStorageEngine;
export {
YqStore as default,
};
export {};