UNPKG

@citrineos/util

Version:

The OCPP util module which supplies helpful utilities like cache and queue connectors, etc.

165 lines 7.51 kB
"use strict"; // Copyright (c) 2023 S44, LLC // Copyright Contributors to the CitrineOS Project // // SPDX-License-Identifier: Apache 2.0 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 }); exports.MemoryCache = void 0; const class_transformer_1 = require("class-transformer"); /** * Implementation of cache interface with memory storage */ class MemoryCache { constructor() { const keySubscriptionMap = new Map(); const subscriptionHandler = { // Returns value on keySubscriptions when Map.set(key, value) is called set(target, property, value) { const setOutcome = Reflect.set(target, property, value); if (typeof property === 'string' && keySubscriptionMap.has(property) && setOutcome) { (keySubscriptionMap === null || keySubscriptionMap === void 0 ? void 0 : keySubscriptionMap.get(property))(value); } return setOutcome; }, // Returns null on keySubscriptions when Map.delete(key) is called deleteProperty(target, property) { const deleteOutcome = Reflect.deleteProperty(target, property); if (typeof property === 'string' && keySubscriptionMap.has(property) && deleteOutcome) { (keySubscriptionMap === null || keySubscriptionMap === void 0 ? void 0 : keySubscriptionMap.get(property))(null); } return deleteOutcome; }, // Here to support Map.get and Map.has, does not alter behavior get(target, property) { const value = Reflect.get(target, property); if (typeof value === 'function') { // Return a bound version of the method to the original target return value.bind(target); } return value; }, }; this._cache = new Proxy(new Map(), subscriptionHandler); this._keySubscriptionMap = keySubscriptionMap; this._keySubscriptionPromiseMap = new Map(); this._timeoutMap = new Map(); } exists(key, namespace) { namespace = namespace || 'default'; const namespaceKey = `${namespace}:${key}`; return Promise.resolve(this._cache.has(namespaceKey)); } remove(key, namespace) { return __awaiter(this, void 0, void 0, function* () { namespace = namespace || 'default'; const namespaceKey = `${namespace}:${key}`; return this._cache.delete(namespaceKey); }); } onChange(key, waitSeconds, namespace, classConstructor) { namespace = namespace || 'default'; const namespaceKey = `${namespace}:${key}`; // Either get existing promise awaiting change on this key or create a new one and store it. // This way, any number of threads can wait for the same key at the same time. // Type must include 'undefined' due to Map.get(key)'s return type, however in no case can it actually be undefined. const onChangeValuePromise = this._keySubscriptionPromiseMap.has(namespaceKey) ? this._keySubscriptionPromiseMap.get(namespaceKey) : this._keySubscriptionPromiseMap .set(namespaceKey, new Promise((resolve) => { this._keySubscriptionMap.set(namespaceKey, (value) => { resolve(value); this._keySubscriptionMap.delete(namespaceKey); this._keySubscriptionPromiseMap.delete(namespaceKey); }); })) .get(namespaceKey); return Promise.race([ onChangeValuePromise === null || onChangeValuePromise === void 0 ? void 0 : onChangeValuePromise.then((value) => { if (typeof value === 'string') { if (classConstructor) { return (0, class_transformer_1.plainToInstance)(classConstructor(), JSON.parse(value)); } else { return value; } } else { return value; } }), new Promise((resolve) => { setTimeout(() => { resolve(this.get(key, namespace, classConstructor)); }, waitSeconds * 1000); }), ]); } get(key, namespace, classConstructor) { return __awaiter(this, void 0, void 0, function* () { namespace = namespace || 'default'; const namespaceKey = `${namespace}:${key}`; const result = this._cache.get(namespaceKey); if (result) { if (classConstructor) { return (0, class_transformer_1.plainToInstance)(classConstructor(), JSON.parse(result)); } return result; } return null; }); } set(key, value, namespace, expireSeconds) { return __awaiter(this, void 0, void 0, function* () { namespace = namespace || 'default'; const namespaceKey = `${namespace}:${key}`; this._cache.set(namespaceKey, value); if (this._timeoutMap.has(namespaceKey)) { clearTimeout(this._timeoutMap.get(namespaceKey)); } if (expireSeconds) { this._timeoutMap.set(namespaceKey, setTimeout(() => { this._cache.delete(namespaceKey); }, expireSeconds * 1000)); } this.resolveOnChange(namespaceKey, value); return true; }); } setIfNotExist(key, value, namespace, expireSeconds) { return __awaiter(this, void 0, void 0, function* () { namespace = namespace || 'default'; const namespaceKey = `${namespace}:${key}`; if (this._cache.has(namespaceKey)) { return false; } this._cache.set(namespaceKey, value); if (this._timeoutMap.has(namespaceKey)) { clearTimeout(this._timeoutMap.get(namespaceKey)); } if (expireSeconds) { this._timeoutMap.set(namespaceKey, setTimeout(() => { this._cache.delete(namespaceKey); }, expireSeconds * 1000)); } this.resolveOnChange(namespaceKey, value); return true; }); } resolveOnChange(namespaceKey, value) { const resolveOnChangeCallback = this._keySubscriptionMap.get(namespaceKey); if (resolveOnChangeCallback) { resolveOnChangeCallback(value); } } } exports.MemoryCache = MemoryCache; //# sourceMappingURL=memory.js.map