mastercache
Version:
Multi-tier cache module for Node.js. Redis, Upstash, CloudfareKV, File, in-memory and others drivers
335 lines (322 loc) • 9.18 kB
JavaScript
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/cache/factory-runner.ts
var factory_runner_exports = {};
__export(factory_runner_exports, {
FactoryRunner: () => FactoryRunner
});
module.exports = __toCommonJS(factory_runner_exports);
// ../../node_modules/.pnpm/p-timeout@6.1.3/node_modules/p-timeout/index.js
var TimeoutError = class extends Error {
constructor(message) {
super(message);
this.name = "TimeoutError";
}
};
var AbortError = class extends Error {
constructor(message) {
super();
this.name = "AbortError";
this.message = message;
}
};
var getDOMException = (errorMessage) => globalThis.DOMException === void 0 ? new AbortError(errorMessage) : new DOMException(errorMessage);
var getAbortedReason = (signal) => {
const reason = signal.reason === void 0 ? getDOMException("This operation was aborted.") : signal.reason;
return reason instanceof Error ? reason : getDOMException(reason);
};
function pTimeout(promise, options) {
const {
milliseconds,
fallback,
message,
customTimers = { setTimeout, clearTimeout }
} = options;
let timer;
const wrappedPromise = new Promise((resolve, reject) => {
if (typeof milliseconds !== "number" || Math.sign(milliseconds) !== 1) {
throw new TypeError(`Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\``);
}
if (options.signal) {
const { signal } = options;
if (signal.aborted) {
reject(getAbortedReason(signal));
}
const abortHandler = () => {
reject(getAbortedReason(signal));
};
signal.addEventListener("abort", abortHandler, { once: true });
promise.finally(() => {
signal.removeEventListener("abort", abortHandler);
});
}
if (milliseconds === Number.POSITIVE_INFINITY) {
promise.then(resolve, reject);
return;
}
const timeoutError = new TimeoutError();
timer = customTimers.setTimeout.call(void 0, () => {
if (fallback) {
try {
resolve(fallback());
} catch (error) {
reject(error);
}
return;
}
if (typeof promise.cancel === "function") {
promise.cancel();
}
if (message === false) {
resolve();
} else if (message instanceof Error) {
reject(message);
} else {
timeoutError.message = message ?? `Promise timed out after ${milliseconds} milliseconds`;
reject(timeoutError);
}
}, milliseconds);
(async () => {
try {
resolve(await promise);
} catch (error) {
reject(error);
}
})();
});
const cancelablePromise = wrappedPromise.finally(() => {
cancelablePromise.clear();
});
cancelablePromise.clear = () => {
customTimers.clearTimeout.call(void 0, timer);
timer = void 0;
};
return cancelablePromise;
}
// src/libs/exception.ts
var import_node_util = require("util");
var Exception = class extends Error {
/**
* Name of the class that raised the exception.
*/
name;
/**
* A status code for the error. Usually helpful when converting errors
* to HTTP responses.
*/
status;
constructor(message, options) {
super(message, options);
const ErrorConstructor = this.constructor;
this.name = ErrorConstructor.name;
this.message = message || ErrorConstructor.message || "";
this.status = options?.status || ErrorConstructor.status || 500;
const code = options?.code || ErrorConstructor.code;
if (code !== void 0) {
this.code = code;
}
const help = ErrorConstructor.help;
if (help !== void 0) {
this.help = help;
}
Error.captureStackTrace(this, ErrorConstructor);
}
get [Symbol.toStringTag]() {
return this.constructor.name;
}
toString() {
if (this.code) {
return `${this.name} [${this.code}]: ${this.message}`;
}
return `${this.name}: ${this.message}`;
}
};
function createError(message, code, status) {
return class extends Exception {
static message = message;
static code = code;
static status = status;
constructor(args, options) {
super((0, import_node_util.format)(message, ...args || []), options);
this.name = "Exception";
}
};
}
// src/errors.ts
var E_FACTORY_SOFT_TIMEOUT = createError(
"Factory has timed out after waiting for soft timeout",
"E_FACTORY_SOFT_TIMEOUT"
);
var E_FACTORY_HARD_TIMEOUT = createError(
"Factory has timed out after waiting for hard timeout",
"E_FACTORY_HARD_TIMEOUT"
);
// src/events/cache/cache-hit.ts
var CacheHit = class {
constructor(key, value, store, graced = false) {
this.key = key;
this.value = value;
this.store = store;
this.graced = graced;
}
name = "cache:hit";
toJSON() {
return {
key: this.key,
value: this.value,
store: this.store,
graced: this.graced
};
}
};
// src/events/cache/cache-miss.ts
var CacheMiss = class {
constructor(key, store) {
this.key = key;
this.store = store;
}
name = "cache:miss";
toJSON() {
return {
key: this.key,
store: this.store
};
}
};
// src/events/cache/cache-cleared.ts
var CacheCleared = class {
constructor(store) {
this.store = store;
}
name = "cache:cleared";
toJSON() {
return {
store: this.store
};
}
};
// src/events/cache/cache-deleted.ts
var CacheDeleted = class {
constructor(key, store) {
this.key = key;
this.store = store;
}
name = "cache:deleted";
toJSON() {
return {
key: this.key,
store: this.store
};
}
};
// src/events/cache/cache-written.ts
var CacheWritten = class {
constructor(key, value, store) {
this.key = key;
this.value = value;
this.store = store;
}
name = "cache:written";
toJSON() {
return {
key: this.key,
store: this.store,
value: this.value
};
}
};
// src/events/bus/bus-message-received.ts
var BusMessageReceived = class {
constructor(message) {
this.message = message;
}
name = "bus:message:received";
toJSON() {
return {
keys: this.message.keys,
type: this.message.type
};
}
};
// src/events/bus/bus-message-published.ts
var BusMessagePublished = class {
constructor(message) {
this.message = message;
}
name = "bus:message:published";
toJSON() {
return {
keys: this.message.keys,
type: this.message.type
};
}
};
// src/events/index.ts
var events = {
BusMessagePublished,
BusMessageReceived,
CacheHit,
CacheMiss,
CacheCleared,
CacheDeleted,
CacheWritten
};
// src/cache/factory-runner.ts
var FactoryRunner = class {
#stack;
#stackWriter;
#locks;
constructor(stack, stackWriter, locks) {
this.#stack = stack;
this.#stackWriter = stackWriter;
this.#locks = locks;
}
async saveBackgroundFactoryResult(key, factoryResult, options, lockReleaser) {
await this.#stackWriter.set(key, factoryResult, options);
this.#locks.release(key, lockReleaser);
}
async writeFactoryResult(key, item, options, lockReleaser) {
await this.#stackWriter.set(key, item, options);
this.#stack.emit(new events.CacheMiss(key, this.#stack.name));
this.#stack.logger.trace({ key, cache: this.#stack.name, opId: options.id }, "cache miss");
this.#locks.release(key, lockReleaser);
}
async run(key, factory, hasFallback, options, lockReleaser) {
const timeoutDuration = options.factoryTimeout(hasFallback);
const timeoutException = timeoutDuration === options.timeouts?.hard ? E_FACTORY_HARD_TIMEOUT : E_FACTORY_SOFT_TIMEOUT;
const promisifiedFactory = async () => {
return await factory({ setTtl: (ttl) => options.setLogicalTtl(ttl) });
};
const factoryPromise = promisifiedFactory();
const factoryResult = await pTimeout(factoryPromise, {
milliseconds: timeoutDuration ?? Number.POSITIVE_INFINITY,
fallback: async () => {
factoryPromise.then((result) => this.saveBackgroundFactoryResult(key, result, options, lockReleaser)).catch(() => {
}).finally(() => this.#locks.release(key, lockReleaser));
throw new timeoutException();
}
});
await this.writeFactoryResult(key, factoryResult, options, lockReleaser);
return factoryResult;
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
FactoryRunner
});
//# sourceMappingURL=factory-runner.cjs.map