UNPKG

key-value-file-system

Version:

Turn any key-value store into a file system for hierarchical object storage

144 lines (143 loc) 5.51 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); class KeyValueFileSystem { constructor(store, keyNamespacePrefix = "/kvfs") { if (!keyNamespacePrefix.length) { throw new Error("keyNamespacePrefix must not be empty. This protects you from\ accidentally deleting everything else in your store."); } this.store = store; this.prefix = keyNamespacePrefix; } ls(spec) { return __awaiter(this, void 0, void 0, function* () { const normalizedSpec = spec || "*"; const keys = yield this.store.getAllKeys(); const regex = this.regexFromSpec(normalizedSpec); return keys .filter(key => regex.test(key)) .map(key => key.slice(this.prefix.length)); }); } read(path) { return __awaiter(this, void 0, void 0, function* () { this.validatePath(path); const item = yield this.store.getItem(this.prefix + path); if (item !== null) { return JSON.parse(item); } return item; }); } readMulti(spec) { return __awaiter(this, void 0, void 0, function* () { const keys = yield this.ls(spec); const values = yield this.store.multiGet(keys.map(key => this.prefix + key)); return values.map(([path, str]) => { const value = str ? JSON.parse(str) : null; return { path: path.slice(this.prefix.length), value, }; }); }); } write(path, value) { return __awaiter(this, void 0, void 0, function* () { this.validatePath(path); return yield this.store.setItem(this.prefix + path, JSON.stringify(value)); }); } /** * @param basePath The base path to prepend to each subPath in values. * @param values Each subPath and object you want written */ writeMulti(basePath, values) { return __awaiter(this, void 0, void 0, function* () { if (values.length === 0) { return; } const keyValues = values.map(({ path: subPath, value }) => { const fullPath = basePath ? `${basePath}${subPath}` : subPath; this.validatePath(fullPath); return [this.prefix + fullPath, JSON.stringify(value)]; }); return yield this.store.multiSet(keyValues); }); } rm(spec) { return __awaiter(this, void 0, void 0, function* () { this.validatePath(spec); const keys = yield this.store.getAllKeys(); const regex = this.regexFromSpec(spec); return yield this.store.multiRemove(keys.filter(key => regex.test(key))); }); } rmMulti(paths) { return __awaiter(this, void 0, void 0, function* () { if (paths.length === 0) { return; } const keys = paths.map(path => { this.validatePath(path); return this.prefix + path; }); return yield this.store.multiRemove(keys); }); } rmAllForce() { return __awaiter(this, void 0, void 0, function* () { const keys = yield this.store.getAllKeys(); const ourKeys = keys.filter(key => key.startsWith(this.prefix)); return yield this.store.multiRemove(ourKeys); }); } validatePath(path) { if (path.length === 0) { throw new Error("spec or path must not be empty"); } } regexFromSpec(spec) { const segments = []; let i = 0; let curSegment = ""; while (i < spec.length) { const char = spec[i]; switch (char) { case "\\": i++; curSegment += spec[i]; break; case "*": if (curSegment.length) { segments.push(curSegment); curSegment = ""; } break; default: curSegment += char; break; } i++; } if (curSegment.length) { segments.push(curSegment); } // Escapes all regex special characters, from ES7 proposal // https://stackoverflow.com/a/63838890 const segRegex = segments .map(seg => seg.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&")) .join(".*"); return new RegExp(`^${this.prefix}${segRegex}${spec[spec.length - 1] === "*" ? ".*" : ""}$`); } } exports.default = KeyValueFileSystem;