UNPKG

memory-card

Version:

ES6 Map like Async API, with Swagger API Backend Support.

338 lines 10.7 kB
import { log, VERSION, } from './config.js'; import { getStorage, } from './storage/mod.js'; export const NAMESPACE_MULTIPLEX_SEPRATOR = '\r'; export const NAMESPACE_KEY_SEPRATOR = '\n'; const NAMESPACE_MULTIPLEX_SEPRATOR_REGEX = new RegExp(NAMESPACE_MULTIPLEX_SEPRATOR); const NAMESPACE_KEY_SEPRATOR_REGEX = new RegExp(NAMESPACE_KEY_SEPRATOR); export class MemoryCard { /** * * * Static * * */ static VERSION = VERSION; static fromJSON(textOrObj) { log.verbose('MemoryCard', 'static fromJSON(...)'); let jsonObj; if (typeof textOrObj === 'string') { jsonObj = JSON.parse(textOrObj); } else { jsonObj = textOrObj; } const card = new this(jsonObj.options); card.payload = jsonObj.payload; return card; } static multiplex(memory, name) { log.verbose('MemoryCard', 'static multiplex(%s, %s)', memory, name); // if (!memory.options) { // throw new Error('can not multiplex a un-named MemoryCard') // } const subMemory = new this({ ...memory.options, multiplex: { name, parent: memory, }, }); return subMemory; } /** * * * Instance * * */ name; parent; payload; storage; multiplexNameList; options; constructor(options) { log.verbose('MemoryCard', 'constructor(%s)', JSON.stringify(options)); if (typeof options === 'string') { options = { name: options }; } this.options = options; this.name = options && options.name; if (options && options.multiplex) { this.parent = options.multiplex.parent; this.payload = this.parent.payload; this.multiplexNameList = [ ...this.parent.multiplexNameList, options.multiplex.name, ]; } else { // payload should be undefined before load() this.payload = undefined; this.multiplexNameList = []; } } toString() { let mpString = ''; if (this.multiplexNameList.length > 0) { mpString = this.multiplexNameList .map(mpName => `.multiplex(${mpName})`) .join(''); } return `MemoryCard<${this.options?.name ?? ''}>${mpString}`; } version() { return VERSION; } async getStorage() { log.verbose('MemoryCard', 'getStorage() for storage type: %s', (this.options && this.options.storageOptions && this.options.storageOptions.type) || 'N/A'); if (!this.options) { return; } const storage = await getStorage(this.options.name, this.options.storageOptions); return storage; } async load() { log.verbose('MemoryCard', 'load() from storage: %s', this.storage || 'N/A'); if (this.isMultiplex()) { log.verbose('MemoryCard', 'load() should not be called on a multiplex MemoryCard. NOOP'); return; } if (this.payload) { throw new Error('memory had already loaded before.'); } if (!this.storage) { this.storage = await this.getStorage(); } if (this.storage) { this.payload = await this.storage.load(); } else { log.verbose('MemoryCard', 'load() no storage'); this.payload = {}; } } async save() { log.verbose('MemoryCard', 'save()'); if (this.isMultiplex()) { if (!this.parent) { throw new Error('multiplex memory no parent'); } return this.parent.save(); } log.verbose('MemoryCard', '<%s>%s save() to %s', this.name || '', this.multiplexPath(), this.storage || 'N/A'); if (!this.payload) { throw new Error('no payload, please call load() first.'); } if (!this.storage) { log.verbose('MemoryCard', 'save() no storage, NOOP'); return; } await this.storage.save(this.payload); } /** * * Multiplexing related functions START * */ isMultiplexKey(key) { if (NAMESPACE_MULTIPLEX_SEPRATOR_REGEX.test(key) && NAMESPACE_KEY_SEPRATOR_REGEX.test(key)) { const namespace = this.multiplexNamespace(); return key.startsWith(namespace); } return false; } multiplexNamespace() { if (!this.isMultiplex()) { throw new Error('not a multiplex memory'); } const namespace = NAMESPACE_MULTIPLEX_SEPRATOR + this.multiplexNameList.join(NAMESPACE_MULTIPLEX_SEPRATOR); return namespace; } resolveKey(name) { if (this.isMultiplex()) { const namespace = this.multiplexNamespace(); return [ namespace, name, ].join(NAMESPACE_KEY_SEPRATOR); } else { return name; } } isMultiplex() { return this.multiplexNameList.length > 0; } multiplexPath() { return this.multiplexNameList.join('/'); } multiplex(name) { log.verbose('MemoryCard', 'multiplex(%s)', name); // FIXME: as any ? return this.constructor.multiplex(this, name); } /** * * Multiplexing related functions END * */ async destroy() { log.verbose('MemoryCard', 'destroy() storage: %s', this.storage || 'N/A'); if (this.isMultiplex()) { throw new Error('can not destroy on a multiplexed memory'); } await this.clear(); if (this.storage) { await this.storage.destroy(); this.storage = undefined; } // to prevent to use a destroied card this.payload = undefined; } /** * * ES6 Map API (Async Version) * * BEGIN * */ /** * size */ get size() { log.verbose('MemoryCard', '<%s> size', this.multiplexPath()); if (!this.payload) { throw new Error('no payload, please call load() first.'); } let count; if (this.isMultiplex()) { count = Object.keys(this.payload) .filter(key => this.isMultiplexKey(key)) .length; } else { count = Object.keys(this.payload).length; } return Promise.resolve(count); } async get(name) { log.verbose('MemoryCard', '<%s> get(%s)', this.multiplexPath(), name); if (!this.payload) { throw new Error('no payload, please call load() first.'); } const key = this.resolveKey(name); return this.payload[key]; } async set(name, data) { log.verbose('MemoryCard', '<%s> set(%s, %s)', this.multiplexPath(), name, data); if (!this.payload) { throw new Error('no payload, please call load() first.'); } const key = this.resolveKey(name); this.payload[key] = data; return this; } async *[Symbol.asyncIterator]() { log.verbose('MemoryCard', '<%s> *[Symbol.asyncIterator]()', this.multiplexPath()); yield* this.entries(); } async *entries() { log.verbose('MemoryCard', '<%s> *entries()', this.multiplexPath()); if (!this.payload) { throw new Error('no payload, please call load() first.'); } for await (const relativeKey of this.keys()) { const absoluteKey = this.resolveKey(relativeKey); const data = this.payload[absoluteKey]; const pair = [relativeKey, data]; yield pair; } } async clear() { log.verbose('MemoryCard', '<%s> clear()', this.multiplexPath()); if (!this.payload) { throw new Error('no payload, please call load() first.'); } if (this.isMultiplex()) { for (const key in this.payload) { if (this.isMultiplexKey(key)) { delete this.payload[key]; } } } else { this.payload = {}; } } async delete(name) { log.verbose('MemoryCard', '<%s> delete(%s)', this.multiplexPath(), name); if (!this.payload) { throw new Error('no payload, please call load() first.'); } const key = this.resolveKey(name); if (key in this.payload) { delete this.payload[key]; return true; } else { return false; } } async has(key) { log.verbose('MemoryCard', '<%s> has(%s)', this.multiplexPath(), key); if (!this.payload) { throw new Error('no payload, please call load() first.'); } const absoluteKey = this.resolveKey(key); return absoluteKey in this.payload; } async *keys() { log.verbose('MemoryCard', '<%s> keys()', this.multiplexPath()); if (!this.payload) { throw new Error('no payload, please call load() first.'); } for (const key of Object.keys(this.payload)) { // console.log('key', key) if (this.isMultiplex()) { if (this.isMultiplexKey(key)) { const namespace = this.multiplexNamespace(); // `+1` means there's another NAMESPACE_KEY_SEPRATOR we need to trim const mpKey = key.substr(namespace.length + 1); yield mpKey; } continue; } yield key; } } async *values() { log.verbose('MemoryCard', '<%s> values()', this.multiplexPath()); if (!this.payload) { throw new Error('no payload, please call load() first.'); } for await (const relativeKey of this.keys()) { const absoluteKey = this.resolveKey(relativeKey); yield this.payload[absoluteKey]; } } async forEach(callbackfn, thisArg) { for await (const [key, value] of this.entries()) { callbackfn.call(thisArg, value, key, this); } } /** * Huan(202108): FIXME * I totally forget what are the following two methods are... */ [Symbol.iterator]() { return {}; } get [Symbol.toStringTag]() { return Promise.resolve('MemoryCardStringTag'); } } export default MemoryCard; //# sourceMappingURL=memory-card.js.map