Mid-rewrite
This commit is contained in:
106
src/services/storage.ts
Normal file
106
src/services/storage.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import Database from 'better-sqlite3';
|
||||
import { decodeExtendedJsonObject, encodeExtendedJsonObject } from '../utils/ext-json.js';
|
||||
|
||||
export class Storage {
|
||||
static async create(dbPath: string): Promise<Storage> {
|
||||
// Create the database
|
||||
const database = new Database(dbPath);
|
||||
|
||||
// Create the storage table if it doesn't exist
|
||||
database.prepare('CREATE TABLE IF NOT EXISTS storage (key TEXT PRIMARY KEY, value TEXT)').run();
|
||||
|
||||
return new Storage(database, '');
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly database: Database.Database,
|
||||
private readonly basePath: string,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the full key with basePath prefix
|
||||
*/
|
||||
private getFullKey(key: string): string {
|
||||
return this.basePath ? `${this.basePath}.${key}` : key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip the basePath prefix from a key
|
||||
*/
|
||||
private stripBasePath(fullKey: string): string {
|
||||
if (!this.basePath) return fullKey;
|
||||
const prefix = `${this.basePath}.`;
|
||||
return fullKey.startsWith(prefix) ? fullKey.slice(prefix.length) : fullKey;
|
||||
}
|
||||
|
||||
async set(key: string, value: any): Promise<void> {
|
||||
// Encode the extended json object
|
||||
const encodedValue = encodeExtendedJsonObject(value);
|
||||
|
||||
// Insert or replace the value into the database with full key (including basePath)
|
||||
const fullKey = this.getFullKey(key);
|
||||
this.database.prepare('INSERT OR REPLACE INTO storage (key, value) VALUES (?, ?)').run(fullKey, encodedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all key-value pairs from this storage namespace (shallow only - no nested children)
|
||||
*/
|
||||
async all(): Promise<{ key: string; value: any }[]> {
|
||||
let query = 'SELECT key, value FROM storage';
|
||||
const params: any[] = [];
|
||||
|
||||
if (this.basePath) {
|
||||
// Filter by basePath prefix
|
||||
query += ' WHERE key LIKE ?';
|
||||
params.push(`${this.basePath}.%`);
|
||||
}
|
||||
|
||||
// Get all the rows from the database
|
||||
const rows = await this.database.prepare(query).all(...params) as { key: string; value: any }[];
|
||||
|
||||
// Filter for shallow results (only direct children)
|
||||
const filteredRows = rows.filter(row => {
|
||||
const strippedKey = this.stripBasePath(row.key);
|
||||
// Only include keys that don't have additional dots (no deeper nesting)
|
||||
return !strippedKey.includes('.');
|
||||
});
|
||||
|
||||
// Decode the extended json objects and strip basePath from keys
|
||||
return filteredRows.map(row => ({
|
||||
key: this.stripBasePath(row.key),
|
||||
value: decodeExtendedJsonObject(row.value)
|
||||
}));
|
||||
}
|
||||
|
||||
async get(key: string): Promise<any> {
|
||||
// Get the row from the database using full key
|
||||
const fullKey = this.getFullKey(key);
|
||||
const row = await this.database.prepare('SELECT value FROM storage WHERE key = ?').get(fullKey) as { value: any };
|
||||
|
||||
// Return null if not found
|
||||
if (!row) return null;
|
||||
|
||||
// Decode the extended json object
|
||||
return decodeExtendedJsonObject(row.value);
|
||||
}
|
||||
|
||||
async remove(key: string): Promise<void> {
|
||||
// Delete using full key
|
||||
const fullKey = this.getFullKey(key);
|
||||
this.database.prepare('DELETE FROM storage WHERE key = ?').run(fullKey);
|
||||
}
|
||||
|
||||
async clear(): Promise<void> {
|
||||
if (this.basePath) {
|
||||
// Clear only items under this namespace
|
||||
this.database.prepare('DELETE FROM storage WHERE key LIKE ?').run(`${this.basePath}.%`);
|
||||
} else {
|
||||
// Clear everything
|
||||
this.database.prepare('DELETE FROM storage').run();
|
||||
}
|
||||
}
|
||||
|
||||
child(key: string): Storage {
|
||||
return new Storage(this.database, this.getFullKey(key));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user