UNPKG

@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
/* * 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. */ 'use strict'; 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;