UNPKG

@openally/timestore

Version:

An abstract class designed to manage the Time To Live (TTL) of a given list of identifiers.

178 lines (174 loc) 5.53 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { TSV_SYMBOL: () => TSV_SYMBOL, TimeStore: () => TimeStore, tSv: () => tSv }); module.exports = __toCommonJS(index_exports); var import_node_events = require("events"); // src/TimeValue.ts var TSV_SYMBOL = Symbol.for("TimeStoreValue"); function tSv(options = {}) { const { ttl } = options; return (value) => { return { value, ttl, [TSV_SYMBOL]: true }; }; } // src/index.ts var kUniqueNullValue = Symbol("UniqueNullValue"); var TimeStore = class _TimeStore extends import_node_events.EventEmitter { static Expired = Symbol.for("ExpiredTimeStoreEntry"); static Renewed = Symbol.for("RenewedTimeStoreEntry"); #keepEventLoopAlive; #identifiers = /* @__PURE__ */ new Map(); #ttl; #current = { identifier: kUniqueNullValue, ttl: 0 }; #timer = null; #customEventEmitter = null; constructor(options = {}) { super(); const { ttl, expireIdentifiersOnProcessExit = false, keepEventLoopAlive = false, eventEmitter = null } = options; this.#ttl = ttl ?? 0; this.#customEventEmitter = eventEmitter; this.#keepEventLoopAlive = keepEventLoopAlive; process.on("exit", () => { if (expireIdentifiersOnProcessExit) { for (const identifier of this.#identifiers.keys()) { this.emit(_TimeStore.Expired, identifier); } } this.clear(); }); } get ttl() { return this.#ttl; } get size() { return this.#identifiers.size; } addTsv(data) { if (!data[TSV_SYMBOL]) { return this; } const { value, ttl } = data; return this.add(value, { ttl }); } add(identifier, options = {}) { const { keepIdentifierBirthTTL = false } = options; const originalIdentifier = this.#identifiers.get(identifier); const hasIdentifier = typeof originalIdentifier !== "undefined"; const keepIdentifierBirth = hasIdentifier && keepIdentifierBirthTTL; const ttl = keepIdentifierBirth ? originalIdentifier.ttl : options.ttl ?? this.#ttl; const timestamp = keepIdentifierBirth ? originalIdentifier.timestamp : Date.now(); if (!keepIdentifierBirthTTL) { this.#identifiers.set(identifier, { timestamp, ttl }); } if (hasIdentifier) { this.emit(_TimeStore.Renewed, identifier); this.#customEventEmitter?.emit(_TimeStore.Renewed, identifier); } if (ttl === 0) { return this; } if (this.#timer === null) { this.#setNewUpfrontIdentifier(identifier, ttl); } else if (this.#current.identifier === identifier || this.#hasTTLUnderCurrentIdentifier(timestamp, ttl)) { this.#updateTimeStoreInterval(); } return this; } delete(identifier) { this.#identifiers.delete(identifier); if (this.#current.identifier === identifier) { this.#updateTimeStoreInterval(); } return this; } clear() { this.#resetTimerAndCurrentIdentifier(); this.#identifiers.clear(); return this; } has(identifier) { return this.#identifiers.has(identifier); } get(identifier) { return this.#identifiers.get(identifier) ?? null; } #hasTTLUnderCurrentIdentifier(now, ttl) { const delta = now - this.#identifiers.get(this.#current.identifier).timestamp; return this.#current.ttl - delta >= ttl; } #updateTimeStoreInterval() { this.#resetTimerAndCurrentIdentifier(); if (this.#identifiers.size === 0) { return; } const sortedIdentifiers = [...this.#identifiers.entries()].sort((left, right) => right[1].timestamp + right[1].ttl - (left[1].timestamp + left[1].ttl)); while (sortedIdentifiers.length > 0) { const [identifier, value] = sortedIdentifiers.pop(); if (value.ttl === 0) { continue; } const delta = Date.now() - value.timestamp; if (delta >= value.ttl) { this.emit(_TimeStore.Expired, identifier); } else { this.#setNewUpfrontIdentifier(identifier, value.ttl - delta); break; } } } #resetTimerAndCurrentIdentifier() { if (this.#timer !== null) { clearTimeout(this.#timer); this.#timer = null; } this.#current = { identifier: kUniqueNullValue, ttl: 0 }; } #setNewUpfrontIdentifier(identifier, ttl = this.#ttl) { this.#current = { identifier, ttl }; this.#timer = setTimeout(() => { this.delete(identifier); this.emit(_TimeStore.Expired, identifier); this.#customEventEmitter?.emit(_TimeStore.Expired, identifier); }, ttl); if (!this.#keepEventLoopAlive) { this.#timer.unref(); } } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { TSV_SYMBOL, TimeStore, tSv });