@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
JavaScript
"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
});