Skip to content

Instantly share code, notes, and snippets.

@atg
Last active October 8, 2018 14:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atg/42cc5d933ba0e86d5421b29a5eeef0d3 to your computer and use it in GitHub Desktop.
Save atg/42cc5d933ba0e86d5421b29a5eeef0d3 to your computer and use it in GitHub Desktop.
const sqlite = require('sqlite');
const msgpack = require('msgpack-lite');
const fs = require('fs');
const pathlib = require('path');
const zoneToFilename = (zone) => zone.replace(/[^\p{L}\p{N}_\-]+/gu, '') || '_';
const separator = '\x1f';
const mangle = (key) => {
if (typeof key === 'string') return key + separator;
else return key.join(separator) + separator;
};
const unmangle = (mangled) => {
let keys = mangled.slice(0, mangled.length - 1).split('\x1f'); // remove the last dot
if (keys.length === 0) return '';
else if (keys.length === 1) return keys[0];
else return keys;
};
class DB {
constructor(dir) {
try { fs.mkdirSync(dir); } catch (err) { }
this.dir = dir;
this.zones = new Map;
}
zone(name) {
let zone = this.zones.get(name);
if (zone) return zone;
zone = new Zone(pathlib.join(this.dir, zoneToFilename(name)));
this.zones.set(name, zone);
return zone;
}
}
class Zone {
constructor(path) {
this.path = path;
this._dbPromise = sqlite.open(this.path);
this._dbPromise.then(db => {
this._db = db;
db.exec(`CREATE TABLE IF NOT EXISTS keys (id INTEGER PRIMARY KEY, key TEXT UNIQUE NOT NULL, value BLOB NOT NULL)`);
});
}
async get(key) {
if (!this._db) this._db = await this._dbPromise;
let result = await this._db.get(`SELECT value FROM keys WHERE key = ?`, mangle(key));
return result ? result.value : undefined;
}
async set(key, val) {
if (!this._db) this._db = await this._dbPromise;
await this._db.all(`INSERT OR REPLACE INTO keys (id, key, value) VALUES ((SELECT id FROM keys WHERE key = ?), ?, ?)`,
mangle(key), mangle(key), msgpack.encode(val));
}
async delete(key, val) {
if (!this._db) this._db = await this._dbPromise;
await this._db.all(`DELETE FROM keys WHERE key = ?`, mangle(key));
}
async has(key) {
if (!this._db) this._db = await this._dbPromise;
let result = await this._db.get(`SELECT COUNT(*) FROM keys WHERE key = ?`, mangle(key));
return 1 === result['COUNT(*)'];
}
async all() {
if (!this._db) this._db = await this._dbPromise;
let result = await this._db.all(`SELECT key, value FROM keys ORDER BY id`);
return result.map(kv => [unmangle(kv.key), msgpack.decode(kv.value)]);
}
}
module.exports = { DB, Zone };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment