UNPKG

@alinex/datastore

Version:

Read, work and write data structures from and to differents locations and formats.

268 lines 9.71 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.compressions = exports.formats = exports.DataStore = void 0; const path_1 = require("path"); const url_1 = require("url"); const objectPath = require("object-path"); const async_1 = require("@alinex/async"); const data = require("@alinex/data"); const debug_1 = require("debug"); const util = require("util"); const protocol_1 = require("./protocol"); const compressor = require("./compression"); const formatter = require("./format"); const debug = debug_1.default("datastore"); const debugDetails = debug_1.default("datastore:details"); let debuglog = util.debuglog; if (debugDetails.enabled) { process.env.NODE_DEBUG = process.env.NODE_DEBUG ? `${process.env.NODE_DEBUG},request` : "request"; util.debuglog = (set) => { debugDetails(`See more using NODE_DEBUG=${set}`); return debuglog(set); }; } function parseUri(uri) { if (!uri.match(/^[a-z]+:/)) uri = `file:${path_1.resolve(uri)}`; let parsed = new url_1.URL(uri); return parsed; } class DataStore { constructor(...input) { this.data = {}; this.options = {}; this._source = []; this._map = {}; this._load = undefined; this.changed = false; if (!input) return; this._source = input; } static async load(...input) { const ds = new DataStore(); return ds.load(...input).then(() => Promise.resolve(ds)); } static async url(source, options) { const ds = new DataStore(); return ds.load({ source, options }).then(() => Promise.resolve(ds)); } static async data(data, source) { const ds = new DataStore(); ds.data = data; if (source && !source.startsWith('preset:')) source = `preset:${source}`; ds.source = source; return Promise.resolve(ds); } async load(...input) { if (!input.length && this._load) return Promise.resolve(this.data); if (input.length) this._source = input; debug("Loading: %o", this._source); if (this._source.filter(e => e.source && !e.source.startsWith('preset:')).length) await async_1.default.map(this._source, this.multiload); if (this._source.length === 1) this.data = data.clone(this._source[0].data); else if (this._source.length) { const res = data.clone(data.merge(...this._source)); this.data = res.data; this._map = res.map; } debug("Result: %o", this.data); this.changed = false; return Promise.resolve(this.data); } async multiload(entry, index, list) { if (!entry.source) return; const source = parseUri(entry.source); const options = Object.assign({}, entry.options); return (protocol_1.default .load(source, options) .then(([buffer, meta]) => { entry.meta = meta; return compressor.uncompress(source, buffer, options); }) .then(buffer => { entry.meta.raw = buffer; return formatter.parse(source, buffer, options); }) .then(data => { entry.data = data; }) .catch(e => { debug("ERROR: %s", entry.source, e.message || e); if (list && list.length == 1) throw e; })); } async reload(time = 0) { if (!this._source.length) throw new Error("No source defined to reload DataStore from."); if (!this._source.filter(e => e.source && !e.source.startsWith('preset:')).length) throw Error("The datastore is not associated with a path to load from."); if (!this.changed) return false; let cache = new Date(); cache.setSeconds(cache.getSeconds() + time); if (this._load && cache < this._load) return false; const list = await async_1.default.map(this._source, e => { if (!e.source) return Promise.resolve(undefined); return protocol_1.default.modified(parseUri(e.source)); }); const change = list.reduce((prev, cur) => { if (!cur) return prev; if (!prev) return prev; if (prev < cur) return cur; return prev; }); if (this._load && change && change < this._load) return false; await this.load(); return true; } async save(output) { if (output) this.source = [output]; if (this._source.length > 1) throw new Error("Storing in multiple source list not possible, yet."); if (!this._source.length || !this._source[0].source) throw new Error("No source defined to save DataStore to."); const source = parseUri(this._source[0].source); const options = Object.assign({}, this._source[0].options); return formatter .format(source, this.data, options) .then(buffer => compressor.compress(source, buffer, options)) .then(buffer => protocol_1.default.save(source, buffer, options)) .then(() => { this._load = new Date(); this.changed = false; return true; }); } async parse(uri, buffer, options) { if (uri) this.source = uri; if (options) this._source[0].options = Object.assign(this._source[0].options, options); if (typeof buffer == "string") buffer = Buffer.from(buffer); if (!this._source.length) this._source = [{ source: "file:/dev/null" }]; const source = parseUri(this._source[0].source || "file:/dev/null"); this.data = await formatter.parse(source, buffer, this.options); return this.data; } async format(uri, options) { let c_uri = parseUri((this._source.length && this._source[0].source) || "file:/dev/null"); if (uri) c_uri = parseUri(uri); let c_options = this.options; if (options) c_options = Object.assign({}, this.options, options); return await formatter.format(c_uri, this.data, c_options); } get source() { if (this._source.length === 1) return this._source[0].source; if (this._source.length > 1) return this._source; return undefined; } set source(data) { if (data) { delete this._load; if (typeof data === "string") this._source = [{ source: data }]; else if (Array.isArray(data) && data.length) this.source = data; else throw new Error("Need a DataSource list or source string for DataStore."); } else { this._source = []; } } get meta() { if (this._source.length === 1) return this._source[0].meta; if (this._source.length > 1) return this._source.map(e => e.meta); return undefined; } get map() { return this._map; } has(command) { const res = data.filter(this.data, command || ""); return res ? true : false; } get(command, fallback) { const res = data.filter(this.data, command || ""); return typeof res === undefined ? fallback : res; } filter(command, fallback) { const res = data.filter(this.data, command || ""); const ds = new DataStore(); ds.data = typeof res === undefined ? fallback : res; return ds; } set(path, value, doNotReplace) { this.changed = true; if (path === "" || (Array.isArray(path) && path.length == 0)) { if (this.data === undefined) return; const old = this.data; this.data = value; return old; } let p = typeof path === "number" ? path : path.split(/\[([^\]]*)\]|\./g).filter(e => e); return objectPath.set(this.data, p, value, doNotReplace); } insert(path, value, pos) { this.changed = true; let p = typeof path === "number" ? path : path.split(/\[([^\]]*)\]|\./g).filter(e => e); objectPath.insert(this.data, p, value, pos); } push(path, ...values) { this.changed = true; let p = typeof path === "number" ? path : path.split(/\[([^\]]*)\]|\./g).filter(e => e); values.unshift(p); values.unshift(this.data); objectPath.push.apply(objectPath, values); } empty(path) { this.changed = true; if (path === "" || (Array.isArray(path) && path.length == 0)) { const old = this.data; this.data = {}; return old; } let p = typeof path === "number" ? path : path.split(/\[([^\]]*)\]|\./g).filter(e => e); return objectPath.empty(this.data, p); } delete(path) { this.changed = true; if (path === "" || (Array.isArray(path) && path.length == 0)) { const old = this.data; delete this.data; return old; } let p = typeof path === "number" ? path : path.split(/\[([^\]]*)\]|\./g).filter(e => e); return objectPath.del(this.data, p); } } exports.DataStore = DataStore; const formats = formatter.formats; exports.formats = formats; const compressions = compressor.compressions; exports.compressions = compressions; exports.default = DataStore; //# sourceMappingURL=index.js.map