Last active
October 8, 2018 14:55
-
-
Save atg/42cc5d933ba0e86d5421b29a5eeef0d3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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