@catbee/utils
Version:
A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications (including Express-based services). All utilities are tree-shakable and can be imported independently.
267 lines (262 loc) • 8.73 kB
JavaScript
/*
* The MIT License
*
* Copyright (c) 2026 Catbee Technologies. https://catbee.in/license
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
;
var async_hooks = require('async_hooks');
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var StoreKeys = {
LOGGER: Symbol("LOGGER"),
REQUEST_ID: Symbol("REQUEST_ID"),
USER: Symbol("USER"),
SESSION: Symbol("SESSION"),
TRANSACTION_ID: Symbol("TRANSACTION_ID"),
USER_ID: Symbol("USER_ID"),
TENANT_ID: Symbol("TENANT_ID"),
TRACE_ID: Symbol("TRACE_ID"),
CORRELATION_ID: Symbol("CORRELATION_ID")
};
function getRequestId() {
return ContextStore.get(StoreKeys.REQUEST_ID);
}
__name(getRequestId, "getRequestId");
function getFromContext(key) {
return ContextStore.get(key);
}
__name(getFromContext, "getFromContext");
var ContextStore = class _ContextStore {
static {
__name(this, "ContextStore");
}
/**
* The underlying AsyncLocalStorage instance for context.
* @private
*/
static storage = new async_hooks.AsyncLocalStorage();
/**
* Returns the raw AsyncLocalStorage instance for advanced access.
*
* @returns {AsyncLocalStorage<Store>} The AsyncLocalStorage instance.
*/
static getInstance() {
return this.storage;
}
/**
* Retrieves a value from the async context store by symbol key.
*
* @typeParam T - The expected return type of the value.
* @param {symbol} key - Unique symbol used as the store key.
* @returns {T | undefined} The value found (typed) or undefined if not present.
*/
static get(key) {
const store = this.storage.getStore();
return store?.[key];
}
/**
* Sets a value in the current context store by symbol key.
*
* @typeParam T - The value type to set.
* @param {symbol} key - Unique symbol key.
* @param {T} value - Value to set in context.
* @throws {Error} If called outside an active context (not within a .run call or in the wrong async boundaries).
*/
static set(key, value) {
const store = this.storage.getStore();
if (!store) {
throw new Error(`Failed to set ${String(key)}: AsyncLocalStorage store is not initialized.`);
}
store[key] = value;
}
/**
* Retrieves the entire context store object for the current async context.
*
* @returns {Store | undefined} The current store object or undefined if called outside a context.
*/
static getAll() {
return this.storage.getStore();
}
/**
* Initializes a new async context and executes a callback within it.
* This must be called at the beginning of a request or logical async flow.
*
* @typeParam T - The callback's return type.
* @param {Store} store - The initial key-value store object.
* @param {() => T} callback - The function to run within the new context.
* @returns {T} The result of the callback function.
*/
static run(store, callback) {
return this.storage.run(store, callback);
}
/**
* Checks if a key exists in the current context store.
*
* @param {symbol} key - The symbol key to check.
* @returns {boolean} True if the key exists, false otherwise.
*/
static has(key) {
const store = this.storage.getStore();
return store !== void 0 && key in store;
}
/**
* Removes a value from the current context store by symbol key.
*
* @param {symbol} key - The symbol key to delete.
* @returns {boolean} True if the key was deleted, false if the key wasn't found or no active context.
* @throws {Error} If called outside an active context.
*/
static delete(key) {
const store = this.storage.getStore();
if (!store) {
throw new Error(`Failed to delete ${String(key)}: AsyncLocalStorage store is not initialized.`);
}
return delete store[key];
}
/**
* Updates multiple values in the current context store at once.
*
* @param {Partial<Record<symbol, unknown>>} values - Object containing symbol keys and values to update.
* @throws {Error} If called outside an active context.
*/
static patch(values) {
const store = this.storage.getStore();
if (!store) {
throw new Error("Failed to patch: AsyncLocalStorage store is not initialized.");
}
Object.getOwnPropertySymbols(values).forEach((key) => {
store[key] = values[key];
});
}
/**
* Executes a callback with a temporary store value that only exists during execution.
* Original store is restored after callback completes.
*
* @typeParam T - The callback's return type.
* @param {symbol} key - The symbol key to temporarily set.
* @param {unknown} value - The temporary value.
* @param {() => T} callback - The function to execute with the temporary value.
* @returns {T} The result of the callback function.
* @throws {Error} If called outside an active context.
*/
static withValue(key, value, callback) {
const store = this.storage.getStore();
if (!store) {
throw new Error(`Failed to set temporary value: AsyncLocalStorage store is not initialized.`);
}
const hasOriginal = key in store;
const originalValue = store[key];
store[key] = value;
try {
return callback();
} finally {
if (hasOriginal) {
store[key] = originalValue;
} else {
delete store[key];
}
}
}
/**
* Creates a new context that inherits values from the current context.
*
* @typeParam T - The callback's return type.
* @param {Partial<Record<symbol, unknown>>} newValues - New values to add to or override in the context.
* @param {() => T} callback - The function to execute in the new context.
* @returns {T} The result of the callback function.
*/
static extend(newValues, callback) {
const currentStore = this.storage.getStore() || {};
const newStore = {
...currentStore
};
Object.getOwnPropertySymbols(newValues).forEach((key) => {
newStore[key] = newValues[key];
});
return this.storage.run(newStore, callback);
}
/**
* Creates Express middleware that initializes a context for each request.
*
* @param {(req: any) => Partial<Record<symbol, unknown>>} initialValuesFactory - Function that returns initial context values.
* @returns Express middleware function.
*/
static createExpressMiddleware(initialValuesFactory = () => ({})) {
return (req, _res, next) => {
const initialValues = initialValuesFactory(req);
_ContextStore.run(initialValues, next);
};
}
};
var TypedContextKey = class {
static {
__name(this, "TypedContextKey");
}
symbol;
defaultValue;
/**
* Creates a new typed context key.
*
* @param symbol - The unique symbol for this key
* @param defaultValue - Optional default value if key is not found
*/
constructor(symbol, defaultValue) {
this.symbol = symbol;
this.defaultValue = defaultValue;
}
/**
* Gets the current value for this key.
*
* @returns The value or defaultValue if not found
*/
get() {
return ContextStore.get(this.symbol) ?? this.defaultValue;
}
/**
* Sets the value for this key.
*
* @param value The value to set
*/
set(value) {
ContextStore.set(this.symbol, value);
}
/**
* Checks if this key exists in the context.
*
* @returns True if the key exists
*/
exists() {
return ContextStore.has(this.symbol);
}
/**
* Deletes this key from the context.
*
* @returns True if the key was deleted
*/
delete() {
return ContextStore.delete(this.symbol);
}
};
exports.ContextStore = ContextStore;
exports.StoreKeys = StoreKeys;
exports.TypedContextKey = TypedContextKey;
exports.getFromContext = getFromContext;
exports.getRequestId = getRequestId;