lifecycleion 0.0.10 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +4 -2
  2. package/dist/lib/event-emitter.cjs.map +1 -1
  3. package/dist/lib/event-emitter.js.map +1 -1
  4. package/dist/lib/http-client/index.cjs.map +1 -1
  5. package/dist/lib/http-client/index.js.map +1 -1
  6. package/dist/lib/lifecycle-manager/index.cjs.map +1 -1
  7. package/dist/lib/lifecycle-manager/index.js.map +1 -1
  8. package/dist/lib/logger/index.cjs +6 -5
  9. package/dist/lib/logger/index.cjs.map +1 -1
  10. package/dist/lib/logger/index.d.cts +3 -3
  11. package/dist/lib/logger/index.d.ts +3 -3
  12. package/dist/lib/logger/index.js +6 -5
  13. package/dist/lib/logger/index.js.map +1 -1
  14. package/dist/lib/lru-cache/index.cjs +896 -29
  15. package/dist/lib/lru-cache/index.cjs.map +1 -1
  16. package/dist/lib/lru-cache/index.d.cts +33 -17
  17. package/dist/lib/lru-cache/index.d.ts +33 -17
  18. package/dist/lib/lru-cache/index.js +884 -29
  19. package/dist/lib/lru-cache/index.js.map +1 -1
  20. package/dist/lib/process-signal-manager.cjs.map +1 -1
  21. package/dist/lib/process-signal-manager.js.map +1 -1
  22. package/dist/lib/promise-protected-resolver.cjs.map +1 -1
  23. package/dist/lib/promise-protected-resolver.js.map +1 -1
  24. package/dist/lib/retry-utils/index.cjs.map +1 -1
  25. package/dist/lib/retry-utils/index.js.map +1 -1
  26. package/dist/lib/safe-handle-callback.cjs.map +1 -1
  27. package/dist/lib/safe-handle-callback.d.cts +2 -2
  28. package/dist/lib/safe-handle-callback.d.ts +2 -2
  29. package/dist/lib/safe-handle-callback.js.map +1 -1
  30. package/dist/lib/single-event-observer.cjs.map +1 -1
  31. package/dist/lib/single-event-observer.js.map +1 -1
  32. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/lib/lru-cache/index.ts"],"sourcesContent":["/**\n * TTL-aware LRU (Least Recently Used) Cache implementation\n *\n * Features:\n * - Configurable maximum entries to limit item count\n * - Optional maximum size in bytes to limit memory usage\n * - Optional TTL (Time To Live) for cache entries\n * - Lazy cleanup of expired entries during cache operations\n * - Efficient LRU eviction policy\n * - Size-aware eviction when memory limits are reached\n */\n\nexport class LRUCache<K, V> {\n private maxEntries: number;\n private maxSize?: number; // Optional maximum size in bytes\n private defaultTtl?: number; // Optional default TTL in milliseconds\n private lastCleanup = Date.now();\n private cleanupInterval = 60 * 1000; // Run cleanup once per minute at most\n private currentSize = 0; // Track current total size in bytes\n private expirableEntryCount = 0; // Track how many entries currently have expirations\n private sizeCalculator?: (value: V) => number; // Optional function to calculate item size\n\n // Store values with their expiration time and size\n private map = new Map<K, { value: V; expires?: number; size: number }>();\n\n /**\n * Create a new LRU cache\n * @param maxEntries Maximum number of entries to store\n * @param options Configuration options\n * @param options.defaultTtl Default time to live in milliseconds for all entries\n * @param options.maxSize Maximum total size in bytes\n * @param options.sizeCalculator Function to calculate the size of a value\n */\n\n constructor(\n maxEntries: number,\n options?: {\n defaultTtl?: number;\n maxSize?: number;\n sizeCalculator?: (value: V) => number;\n },\n ) {\n if (!Number.isInteger(maxEntries) || maxEntries <= 0) {\n throw new RangeError('maxEntries must be a positive integer');\n }\n\n if (options?.defaultTtl !== undefined) {\n this.assertNonNegativeFiniteNumber(\n options.defaultTtl,\n 'defaultTtl must be a non-negative finite number',\n );\n }\n\n if (options?.maxSize !== undefined) {\n if (!Number.isInteger(options.maxSize) || options.maxSize <= 0) {\n throw new RangeError('maxSize must be a positive integer byte count');\n }\n }\n\n if (\n options?.sizeCalculator !== undefined &&\n typeof options.sizeCalculator !== 'function'\n ) {\n throw new TypeError('sizeCalculator must be a function');\n }\n\n this.maxEntries = maxEntries;\n this.defaultTtl = options?.defaultTtl;\n this.maxSize = options?.maxSize;\n this.sizeCalculator = options?.sizeCalculator;\n }\n\n /**\n * Get the current number of entries in the cache\n */\n\n public get size(): number {\n return this.map.size;\n }\n\n /**\n * Get the current total size in bytes of all cached items\n */\n\n public get byteSize(): number {\n return this.currentSize;\n }\n\n /**\n * Check if a key exists in the cache (without affecting LRU order)\n * @param key The key to check\n * @returns True if the key exists and hasn't expired\n */\n public has(key: K): boolean {\n const entry = this.map.get(key);\n\n if (entry) {\n // Check if entry has expired\n if (entry.expires && Date.now() > entry.expires) {\n this.delete(key);\n return false;\n }\n\n return true;\n }\n\n return false;\n }\n\n public get(key: K): V | undefined {\n const entry = this.map.get(key);\n\n if (entry) {\n // Check if entry has expired\n if (entry.expires && Date.now() > entry.expires) {\n this.delete(key);\n return undefined;\n }\n\n // Move to end of LRU (most recently used)\n this.map.delete(key);\n this.map.set(key, entry);\n\n // Run periodic cleanup if needed\n this.maybeCleanup();\n\n return entry.value;\n }\n\n return undefined;\n }\n\n public set(key: K, value: V, customTtl?: number): void {\n if (customTtl !== undefined) {\n this.assertNonNegativeFiniteNumber(\n customTtl,\n 'customTtl must be a non-negative finite number',\n );\n }\n\n // Calculate the size of the new value\n const size = this.calculateSize(value);\n\n // Remove existing entry if present\n if (this.map.has(key)) {\n this.delete(key);\n }\n\n // Calculate expiration if TTL is set\n const ttl = customTtl ?? this.defaultTtl;\n const expires = ttl && ttl > 0 ? Date.now() + ttl : undefined;\n\n // Add new entry\n this.map.set(key, { value, expires, size });\n this.currentSize += size;\n\n if (expires !== undefined) {\n this.expirableEntryCount++;\n }\n\n // Reclaim expired entries before evicting live entries for capacity.\n this.cleanupExpired();\n\n // Evict entries if we exceed capacity (either by count or size)\n this.evictIfNeeded();\n }\n\n /**\n * Clear all entries from the cache\n */\n\n public clear(): void {\n this.map.clear();\n this.currentSize = 0;\n this.expirableEntryCount = 0;\n this.lastCleanup = Date.now();\n }\n\n /**\n * Remove all expired entries from the cache\n * @returns The number of entries removed\n */\n\n public cleanupExpired(): number {\n let removed = 0;\n const now = Date.now();\n\n for (const [key, entry] of this.map.entries()) {\n if (entry.expires && now > entry.expires) {\n this.delete(key);\n removed++;\n }\n }\n\n this.lastCleanup = now;\n\n return removed;\n }\n\n /**\n * Delete a specific entry from the cache\n * @param key The key to delete\n * @returns True if the entry was deleted, false if it didn't exist\n */\n\n public delete(key: K): boolean {\n const entry = this.map.get(key);\n\n if (entry) {\n this.currentSize -= entry.size;\n\n if (entry.expires !== undefined) {\n this.expirableEntryCount--;\n }\n\n this.map.delete(key);\n\n return true;\n }\n\n return false;\n }\n\n private isBufferValue(value: unknown): value is { length: number } {\n const globalBuffer = (\n globalThis as {\n Buffer?: unknown;\n }\n ).Buffer;\n\n if (globalBuffer === undefined) {\n return false;\n }\n\n const bufferConstructor = globalBuffer as {\n isBuffer?: (value: unknown) => boolean;\n };\n\n return (\n typeof globalBuffer === 'function' &&\n typeof bufferConstructor.isBuffer === 'function' &&\n bufferConstructor.isBuffer(value)\n );\n }\n\n /**\n * Calculate the size of a value in bytes\n * Uses the provided sizeCalculator if available, otherwise makes a best guess\n */\n\n private calculateSize(\n value: unknown,\n visitedArrays = new WeakSet<object>(),\n ): number {\n // Use custom size calculator if provided (cast to V for the callback)\n if (this.sizeCalculator) {\n const size = this.sizeCalculator(value as V);\n\n if (!Number.isInteger(size) || size < 0) {\n throw new RangeError(\n 'sizeCalculator must return a non-negative integer byte count',\n );\n }\n\n return size;\n }\n\n // Default size estimation logic\n if (value === null || value === undefined) {\n return 0;\n } else if (typeof value === 'boolean') {\n return 4; // Boolean is typically 4 bytes\n } else if (typeof value === 'number') {\n return 8; // Number is typically 8 bytes (double)\n } else if (typeof value === 'string') {\n return value.length * 2; // String is ~2 bytes per character in UTF-16\n } else if (this.isBufferValue(value)) {\n return value.length; // Buffer size in bytes\n } else if (ArrayBuffer.isView(value)) {\n return value.byteLength; // TypedArray size\n } else if (value instanceof ArrayBuffer) {\n return value.byteLength; // ArrayBuffer size\n } else if (Array.isArray(value)) {\n if (visitedArrays.has(value)) {\n return 1000; // Fallback size for circular arrays\n }\n\n visitedArrays.add(value);\n\n // Rough estimate for arrays\n const estimatedSize =\n 40 +\n value.reduce(\n (acc: number, item: unknown) =>\n acc + this.calculateSize(item, visitedArrays),\n 0,\n );\n\n visitedArrays.delete(value);\n\n return estimatedSize;\n } else if (typeof value === 'object') {\n try {\n // Rough estimate based on JSON size\n const jsonSize = JSON.stringify(value).length * 2;\n return Math.max(jsonSize, 40); // At least 40 bytes for object overhead\n } catch {\n // Ignore JSON serialization errors for non-serializable objects\n return 1000; // Fallback size for non-serializable objects\n }\n }\n\n return 100; // Default fallback size\n }\n\n /**\n * Evict entries if we exceed either max entries or max size\n */\n\n private evictIfNeeded(): void {\n // First check if we need to evict based on entry count\n if (this.map.size > this.maxEntries) {\n this.evictOldest();\n }\n\n // Then check if we need to evict based on total size\n if (this.maxSize !== undefined && this.currentSize > this.maxSize) {\n // Keep evicting until we're under the size limit or the cache is empty\n while (this.currentSize > this.maxSize && this.map.size > 0) {\n this.evictOldest();\n }\n }\n }\n\n /**\n * Evict the oldest (least recently used) entry\n */\n\n private evictOldest(): void {\n if (this.map.size > 0) {\n const oldest = this.map.keys().next().value as K; // Safe: map.size > 0 guarantees a key exists\n this.delete(oldest);\n }\n }\n\n private maybeCleanup(): void {\n const now = Date.now();\n\n // Only run cleanup occasionally to avoid performance impact\n if (\n now - this.lastCleanup > this.cleanupInterval &&\n this.expirableEntryCount > 0\n ) {\n this.cleanupExpired();\n }\n }\n\n private assertNonNegativeFiniteNumber(value: number, message: string): void {\n if (!Number.isFinite(value) || value < 0) {\n throw new RangeError(message);\n }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/lib/lru-cache/index.ts","../../../src/lib/strings.ts","../../../src/lib/constants.ts","../../../src/lib/padding-utils.ts","../../../src/lib/ascii-tables/ascii-table-utils.ts","../../../src/lib/ascii-tables/multi-column-ascii-table.ts","../../../src/lib/ascii-tables/key-value-ascii-table.ts","../../../src/lib/clamp.ts","../../../src/lib/error-to-string.ts","../../../src/lib/is-promise.ts","../../../src/lib/is-function.ts","../../../src/lib/safe-handle-callback.ts"],"sourcesContent":["import { safeHandleCallback } from '../safe-handle-callback';\n\n/**\n * TTL-aware LRU (Least Recently Used) Cache implementation\n *\n * Features:\n * - Configurable maximum entries to limit item count\n * - Optional maximum size in bytes to limit memory usage\n * - Optional TTL (Time To Live) for cache entries\n * - Lazy cleanup of expired entries during cache operations\n * - Efficient LRU eviction policy\n * - Size-aware eviction when memory limits are reached\n */\n\ntype CacheEntry<V> = {\n value: V;\n expires?: number;\n size: number;\n};\n\nexport type LRUCacheChangeEvent<K, V> =\n | {\n reason: 'evict' | 'expired' | 'delete' | 'clear';\n key: K;\n value: V;\n }\n | {\n reason: 'set';\n key: K;\n newValue: V;\n oldValue?: V;\n }\n | {\n reason: 'skip';\n key: K;\n newValue: V;\n currentValue?: V;\n cause: 'maxSize';\n };\n\n// Extract the reason string union from the event type so it stays in sync.\nexport type LRUCacheChangeReason = LRUCacheChangeEvent<\n unknown,\n unknown\n>['reason'];\n\nconst VALID_CHANGE_REASONS: ReadonlySet<LRUCacheChangeReason> = new Set([\n 'evict',\n 'expired',\n 'delete',\n 'clear',\n 'set',\n 'skip',\n]);\n\nexport class LRUCache<K, V> {\n private maxEntries: number;\n private maxSize?: number; // Optional maximum size in bytes\n private defaultTtl?: number; // Optional default TTL in milliseconds\n private lastCleanup = Date.now();\n private cleanupInterval = 60 * 1000; // Run cleanup once per minute at most\n private currentSize = 0; // Track current total size in bytes\n private expirableEntryCount = 0; // Track how many entries currently have expirations\n private sizeCalculator?: (value: V) => number; // Optional function to calculate item size\n private onChange?: (event: LRUCacheChangeEvent<K, V>) => void | Promise<void>;\n private onChangeReasons?: ReadonlySet<LRUCacheChangeReason>;\n\n // Store values with their expiration time and size\n private map = new Map<K, CacheEntry<V>>();\n\n /**\n * Create a new LRU cache\n * @param maxEntries Maximum number of entries to store\n * @param options Configuration options\n * @param options.defaultTtl Default time to live in milliseconds for all entries\n * @param options.maxSize Maximum total size in bytes\n * @param options.sizeCalculator Function to calculate the size of a value\n * @param options.onChange Callback invoked for cache changes\n * @param options.onChangeReasons Optional list of change reasons that should trigger onChange\n */\n\n constructor(\n maxEntries: number,\n options?: {\n defaultTtl?: number;\n maxSize?: number;\n sizeCalculator?: (value: V) => number;\n onChange?: (event: LRUCacheChangeEvent<K, V>) => void | Promise<void>;\n onChangeReasons?: LRUCacheChangeReason[];\n },\n ) {\n if (!Number.isInteger(maxEntries) || maxEntries <= 0) {\n throw new RangeError('maxEntries must be a positive integer');\n }\n\n if (options?.defaultTtl !== undefined) {\n this.assertNonNegativeFiniteNumber(\n options.defaultTtl,\n 'defaultTtl must be a non-negative finite number',\n );\n }\n\n if (options?.maxSize !== undefined) {\n if (!Number.isInteger(options.maxSize) || options.maxSize <= 0) {\n throw new RangeError('maxSize must be a positive integer byte count');\n }\n }\n\n if (\n options?.sizeCalculator !== undefined &&\n typeof options.sizeCalculator !== 'function'\n ) {\n throw new TypeError('sizeCalculator must be a function');\n }\n\n if (\n options?.onChange !== undefined &&\n typeof options.onChange !== 'function'\n ) {\n throw new TypeError('onChange must be a function');\n }\n\n if (options?.onChangeReasons !== undefined) {\n if (!Array.isArray(options.onChangeReasons)) {\n throw new TypeError('onChangeReasons must be an array');\n }\n\n for (const reason of options.onChangeReasons) {\n if (!VALID_CHANGE_REASONS.has(reason)) {\n throw new RangeError(`Invalid onChange reason: ${String(reason)}`);\n }\n }\n }\n\n this.maxEntries = maxEntries;\n this.defaultTtl = options?.defaultTtl;\n this.maxSize = options?.maxSize;\n this.sizeCalculator = options?.sizeCalculator;\n this.onChange = options?.onChange;\n this.onChangeReasons = options?.onChangeReasons\n ? new Set(options.onChangeReasons)\n : undefined;\n }\n\n /**\n * Get the current number of entries in the cache\n */\n\n public get size(): number {\n return this.map.size;\n }\n\n /**\n * Get the current total size in bytes of all cached items\n */\n\n public get byteSize(): number {\n return this.currentSize;\n }\n\n /**\n * Check if a key exists in the cache (without affecting LRU order)\n * @param key The key to check\n * @returns True if the key exists and hasn't expired\n */\n public has(key: K): boolean {\n const entry = this.map.get(key);\n\n if (entry) {\n // Check if entry has expired\n if (entry.expires && Date.now() > entry.expires) {\n const changeEvents = this.createLocalChangeQueue();\n this.removeEntry(key, 'expired', changeEvents);\n this.flushLocalChangeQueue(changeEvents);\n return false;\n }\n\n return true;\n }\n\n return false;\n }\n\n public get(key: K): V | undefined {\n const entry = this.map.get(key);\n\n if (entry) {\n // Check if entry has expired\n if (entry.expires && Date.now() > entry.expires) {\n const changeEvents = this.createLocalChangeQueue();\n this.removeEntry(key, 'expired', changeEvents);\n this.flushLocalChangeQueue(changeEvents);\n return undefined;\n }\n\n // Move to end of LRU (most recently used)\n this.map.delete(key);\n this.map.set(key, entry);\n\n // Run periodic cleanup if needed\n this.maybeCleanup();\n\n return entry.value;\n }\n\n return undefined;\n }\n\n public set(key: K, value: V, customTtl?: number): void {\n if (customTtl !== undefined) {\n this.assertNonNegativeFiniteNumber(\n customTtl,\n 'customTtl must be a non-negative finite number',\n );\n }\n\n // Calculate size first so failed sizeCalculator calls do not mutate cache state.\n const size = this.calculateSize(value);\n const changeEvents = this.createLocalChangeQueue();\n\n // Reclaim expired entries before deciding whether this write is a fresh insert,\n // an overwrite, or a skipped oversized write.\n this.cleanupExpiredInternal(changeEvents);\n const existingEntry = this.map.get(key);\n\n // Oversized writes are skipped before mutating live entries so they remain a true no-op.\n if (this.maxSize !== undefined && size > this.maxSize) {\n changeEvents.push(\n existingEntry\n ? {\n reason: 'skip',\n key,\n currentValue: existingEntry.value,\n newValue: value,\n cause: 'maxSize',\n }\n : {\n reason: 'skip',\n key,\n newValue: value,\n cause: 'maxSize',\n },\n );\n\n this.flushLocalChangeQueue(changeEvents);\n return;\n }\n\n // Remove the old entry silently so an overwrite emits one `set` event,\n // not a separate removal event followed by `set`.\n if (existingEntry) {\n this.removeEntry(key);\n }\n\n // Calculate expiration if TTL is set\n const ttl = customTtl ?? this.defaultTtl;\n const expires = ttl && ttl > 0 ? Date.now() + ttl : undefined;\n\n // Add new entry\n this.map.set(key, { value, expires, size });\n this.currentSize += size;\n\n if (expires !== undefined) {\n this.expirableEntryCount++;\n }\n\n // Record the write after state is updated so observers see the final value.\n changeEvents?.push(\n existingEntry\n ? {\n reason: 'set',\n key,\n oldValue: existingEntry.value,\n newValue: value,\n }\n : {\n reason: 'set',\n key,\n newValue: value,\n },\n );\n\n // Evict entries if we exceed capacity (either by count or size)\n this.evictIfNeeded(changeEvents);\n\n // Flush this operation's queued events after all related state changes finish.\n this.flushLocalChangeQueue(changeEvents);\n }\n\n /**\n * Clear all entries from the cache\n */\n\n public clear(): void {\n const changeEvents = this.createLocalChangeQueue();\n const shouldEmitClear =\n this.onChange !== undefined &&\n (this.onChangeReasons === undefined || this.onChangeReasons.has('clear'));\n\n // Capture per-entry clear events before clearing the internal map.\n if (shouldEmitClear) {\n for (const [key, entry] of this.map.entries()) {\n changeEvents.push({\n reason: 'clear',\n key,\n value: entry.value,\n });\n }\n }\n\n this.map.clear();\n this.currentSize = 0;\n this.expirableEntryCount = 0;\n this.lastCleanup = Date.now();\n\n // Flush this operation's queued events after the cache has been reset.\n this.flushLocalChangeQueue(changeEvents);\n }\n\n /**\n * Delete a specific entry from the cache\n * @param key The key to delete\n * @returns True if the entry was deleted, false if it didn't exist\n */\n\n public delete(key: K): boolean {\n const changeEvents = this.createLocalChangeQueue();\n const wasDeleted = this.removeEntry(key, 'delete', changeEvents);\n\n // Flush this operation's queued events after the delete has been applied.\n this.flushLocalChangeQueue(changeEvents);\n return wasDeleted;\n }\n\n /**\n * Remove all expired entries from the cache\n * @returns The number of entries removed\n */\n\n public cleanupExpired(): number {\n const changeEvents = this.createLocalChangeQueue();\n const removed = this.cleanupExpiredInternal(changeEvents);\n\n // Flush this operation's queued events after expired entries have been removed.\n this.flushLocalChangeQueue(changeEvents);\n return removed;\n }\n\n private cleanupExpiredInternal(\n changeEvents: LRUCacheChangeEvent<K, V>[],\n ): number {\n let removed = 0;\n const now = Date.now();\n\n for (const [key, entry] of this.map.entries()) {\n if (entry.expires && now > entry.expires) {\n this.removeEntry(key, 'expired', changeEvents);\n removed++;\n }\n }\n\n this.lastCleanup = now;\n\n return removed;\n }\n\n private isBufferValue(value: unknown): value is { length: number } {\n const globalBuffer = (\n globalThis as {\n Buffer?: unknown;\n }\n ).Buffer;\n\n if (globalBuffer === undefined) {\n return false;\n }\n\n const bufferConstructor = globalBuffer as {\n isBuffer?: (value: unknown) => boolean;\n };\n\n return (\n typeof globalBuffer === 'function' &&\n typeof bufferConstructor.isBuffer === 'function' &&\n bufferConstructor.isBuffer(value)\n );\n }\n\n /**\n * Calculate the size of a value in bytes\n * Uses the provided sizeCalculator if available, otherwise makes a best guess\n */\n\n private calculateSize(\n value: unknown,\n visitedArrays = new WeakSet<object>(),\n ): number {\n // Use custom size calculator if provided (cast to V for the callback)\n if (this.sizeCalculator) {\n const size = this.sizeCalculator(value as V);\n\n if (!Number.isInteger(size) || size < 0) {\n throw new RangeError(\n 'sizeCalculator must return a non-negative integer byte count',\n );\n }\n\n return size;\n }\n\n // Default size estimation logic\n if (value === null || value === undefined) {\n return 0;\n } else if (typeof value === 'boolean') {\n return 4; // Boolean is typically 4 bytes\n } else if (typeof value === 'number') {\n return 8; // Number is typically 8 bytes (double)\n } else if (typeof value === 'string') {\n return value.length * 2; // String is ~2 bytes per character in UTF-16\n } else if (this.isBufferValue(value)) {\n return value.length; // Buffer size in bytes\n } else if (ArrayBuffer.isView(value)) {\n return value.byteLength; // TypedArray size\n } else if (value instanceof ArrayBuffer) {\n return value.byteLength; // ArrayBuffer size\n } else if (Array.isArray(value)) {\n if (visitedArrays.has(value)) {\n return 1000; // Fallback size for circular arrays\n }\n\n visitedArrays.add(value);\n\n // Rough estimate for arrays\n const estimatedSize =\n 40 +\n value.reduce(\n (acc: number, item: unknown) =>\n acc + this.calculateSize(item, visitedArrays),\n 0,\n );\n\n visitedArrays.delete(value);\n\n return estimatedSize;\n } else if (typeof value === 'object') {\n try {\n // Rough estimate based on JSON size\n const jsonSize = JSON.stringify(value).length * 2;\n return Math.max(jsonSize, 40); // At least 40 bytes for object overhead\n } catch {\n // Ignore JSON serialization errors for non-serializable objects\n return 1000; // Fallback size for non-serializable objects\n }\n }\n\n return 100; // Default fallback size\n }\n\n /**\n * Evict entries if we exceed either max entries or max size\n */\n\n private evictIfNeeded(changeEvents: LRUCacheChangeEvent<K, V>[]): void {\n // First check if we need to evict based on entry count\n if (this.map.size > this.maxEntries) {\n this.evictOldest(changeEvents);\n }\n\n // Then check if we need to evict based on total size\n if (this.maxSize !== undefined && this.currentSize > this.maxSize) {\n // Keep evicting until we're under the size limit or the cache is empty\n while (this.currentSize > this.maxSize && this.map.size > 0) {\n this.evictOldest(changeEvents);\n }\n }\n }\n\n /**\n * Evict the oldest (least recently used) entry\n */\n\n private evictOldest(changeEvents: LRUCacheChangeEvent<K, V>[]): void {\n if (this.map.size > 0) {\n const oldest = this.map.keys().next().value as K; // Safe: map.size > 0 guarantees a key exists\n this.removeEntry(oldest, 'evict', changeEvents);\n }\n }\n\n private maybeCleanup(): void {\n const now = Date.now();\n\n // Only run cleanup occasionally to avoid performance impact\n if (\n now - this.lastCleanup > this.cleanupInterval &&\n this.expirableEntryCount > 0\n ) {\n this.cleanupExpired();\n }\n }\n\n // Collect changes locally so each cache operation flushes its own events in order.\n private createLocalChangeQueue(): LRUCacheChangeEvent<K, V>[] {\n return [];\n }\n\n private flushLocalChangeQueue(\n changeEvents: LRUCacheChangeEvent<K, V>[],\n ): void {\n if (!this.onChange || changeEvents.length === 0) {\n return;\n }\n\n for (const changeEvent of changeEvents) {\n if (\n this.onChangeReasons !== undefined &&\n !this.onChangeReasons.has(changeEvent.reason)\n ) {\n continue;\n }\n\n safeHandleCallback('LRUCache onChange', this.onChange, changeEvent);\n }\n }\n\n private removeEntry(\n key: K,\n reason?: 'evict' | 'expired' | 'delete',\n changeEvents?: LRUCacheChangeEvent<K, V>[],\n ): boolean {\n const entry = this.map.get(key);\n\n if (!entry) {\n return false;\n }\n\n this.currentSize -= entry.size;\n\n if (entry.expires !== undefined) {\n this.expirableEntryCount--;\n }\n\n this.map.delete(key);\n\n // Internal callers can omit `reason` to perform a state-only removal without\n // notifying observers, which is how overwrite handling avoids duplicate events.\n if (reason && changeEvents) {\n changeEvents.push({\n reason,\n key,\n value: entry.value,\n });\n }\n\n return true;\n }\n\n private assertNonNegativeFiniteNumber(value: number, message: string): void {\n if (!Number.isFinite(value) || value < 0) {\n throw new RangeError(message);\n }\n }\n}\n","import { removeEmptyStringsFromArray } from './arrays';\n\nexport function isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\n/**\n * Converts a string or an array of strings to Pascal Case.\n *\n * This function takes an input string or an array of strings, each potentially containing hyphens,\n * and converts them to Pascal Case. It removes any characters that are not letters or numbers,\n * capitalizes the first letter of each substring, and ensures the rest of the substring\n * is in lowercase. Finally, it concatenates all these substrings to produce a Pascal Case\n * output.\n *\n * @param {string | string[]} input - The input string or array of strings to be converted to Pascal Case.\n * @returns {string} The converted string in Pascal Case.\n *\n * Examples:\n * toPascalCase(\"hello-world-123!$\") will return \"HelloWorld123\"\n * toPascalCase([\"hello\", \"world-123!$\"]) will return \"HelloWorld123\"\n */\n\nexport function toPascalCase(input: string | string[]): string {\n // Ensure input is an array\n const inputArray = Array.isArray(input) ? input : [input];\n\n // Process each string in the array\n const parts: string[] = [];\n\n for (const item of inputArray) {\n // Clean the string, split by hyphen, and remove empty strings\n const cleanedItem = item.replace(/[^a-zA-Z0-9-]/g, '');\n parts.push(...removeEmptyStringsFromArray(cleanedItem.split('-')));\n }\n\n // Process and rejoin the input strings\n return parts\n .map(\n (subString) =>\n subString.charAt(0).toUpperCase() + subString.slice(1).toLowerCase(),\n )\n .join('');\n}\n\n/**\n * Converts a string or an array of strings to Camel Case.\n *\n * This function takes an input string or an array of strings, each potentially containing hyphens,\n * and converts them to Camel Case. It removes any characters that are not letters or numbers,\n * capitalizes the first letter of each substring after the first one, and ensures the rest of the substring\n * is in lowercase. For the first substring, it ensures the entire substring is in lowercase.\n * Finally, it concatenates all these substrings to produce a Camel Case output.\n *\n * @param {string | string[]} input - The input string or array of strings to be converted to Camel Case.\n * @returns {string} The converted string in Camel Case.\n *\n * Examples:\n * toCamelCase(\"hello-world-123!$\") will return \"helloWorld123\"\n * toCamelCase([\"hello\", \"world-123!$\"]) will return \"helloWorld123\"\n */\n\nexport function toCamelCase(input: string | string[]): string {\n // Ensure input is an array\n const inputArray = Array.isArray(input) ? input : [input];\n\n // Process each string in the array\n const parts: string[] = [];\n\n for (const item of inputArray) {\n // Clean the string, split by hyphen, and remove empty strings\n const cleanedItem = item.replace(/[^a-zA-Z0-9-]/g, '');\n parts.push(...removeEmptyStringsFromArray(cleanedItem.split('-')));\n }\n\n // Process and rejoin the input strings\n return parts\n .map((subString, index) =>\n index === 0\n ? subString.toLowerCase()\n : subString.charAt(0).toUpperCase() + subString.slice(1).toLowerCase(),\n )\n .join('');\n}\n\n/**\n * This method converts a string or an array of strings to camel case,\n * but if starting with a leading hyphen, it will convert to Pascal case.\n */\n\nexport function toCamelCaseWithPascalOverride(\n input: string | string[],\n): string {\n if (isString(input) && input.startsWith('-')) {\n return toPascalCase(input);\n } else if (\n Array.isArray(input) &&\n input.length > 0 &&\n input[0].startsWith('-')\n ) {\n return toPascalCase(input);\n } else {\n return toCamelCase(input);\n }\n}\n\n/**\n * Converts a string or an array of strings to constant case.\n *\n * The function takes a string or an array of strings, where each string can be separated by a '-',\n * and converts them into a constant case format (all uppercase with underscores between words).\n * It first cleans the input by removing non-alphanumeric characters (except for hyphens), splits the\n * string into parts on hyphens, and then joins these parts with underscores, converting the entire\n * result to uppercase.\n *\n * @param {string | string[]} input - The input string or array of strings to be converted.\n * @returns {string} The converted string in constant case.\n *\n * Example:\n * toConstantCase(\"hello-world\") will return \"HELLO_WORLD\"\n * toConstantCase([\"hello\", \"world\"]) will return \"HELLO_WORLD\"\n */\n\nexport function toConstantCase(input: string | string[]): string {\n // Ensure input is an array\n const inputArray = Array.isArray(input) ? input : [input];\n\n // Process each string in the array\n let parts: string[] = [];\n\n for (const item of inputArray) {\n // Clean the string and split by hyphen\n const cleanedItem = item.replace(/[^a-zA-Z0-9-]/g, '');\n parts.push(...cleanedItem.split('-'));\n }\n\n // Remove empty strings from the array\n parts = removeEmptyStringsFromArray(parts);\n\n // Join parts with underscore and convert to uppercase\n return parts.join('_').toUpperCase();\n}\n\nexport function splitGraphemes(text: string): string[] {\n const graphemes: string[] = [];\n let grapheme = '';\n let zwjSequence = '';\n\n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n const nextChar = text[i + 1] || '';\n const code = char.charCodeAt(0);\n\n // Handling combining marks and zero width joiner\n if (\n (code >= 0x0300 && code <= 0x036f) || // Combining Diacritical Marks\n (code >= 0x1ab0 && code <= 0x1aff) || // Combining Diacritical Marks Extended\n (code >= 0x1dc0 && code <= 0x1dff) || // Combining Diacritical Marks Supplement\n (code >= 0xfe20 && code <= 0xfe2f) || // Combining Half Marks\n (code >= 0x0e31 && code <= 0x0e3a) || // Thai combining marks\n (code >= 0x0e47 && code <= 0x0e4e)\n ) {\n // Thai combining marks\n grapheme += char;\n } else if (char === '\\u200d') {\n // Zero Width Joiner (ZWJ)\n zwjSequence += grapheme + char;\n grapheme = '';\n } else {\n if (grapheme) {\n if (zwjSequence) {\n graphemes.push(zwjSequence + grapheme);\n zwjSequence = '';\n } else {\n graphemes.push(grapheme);\n }\n }\n grapheme = char;\n\n // Handle surrogate pairs (needed for certain characters including emojis)\n if (\n char >= '\\ud800' &&\n char <= '\\udbff' &&\n nextChar >= '\\udc00' &&\n nextChar <= '\\udfff'\n ) {\n grapheme += nextChar;\n i++;\n }\n }\n }\n\n if (grapheme) {\n if (zwjSequence) {\n graphemes.push(zwjSequence + grapheme);\n } else {\n graphemes.push(grapheme);\n }\n }\n\n return graphemes;\n}\n\nexport function skipTrailingNewLines(str: string): string {\n return str.replace(/\\n+$/, '');\n}\n\n/**\n * Filters a string to include only specified characters, optionally replacing disallowed characters.\n *\n * @param str - The input string to be filtered.\n * @param list - An array of allowed characters.\n * @param caseInsensitive - Optional. If true, the filtering is case-insensitive. Default is false.\n * @param replacementChar - Optional. Character to replace disallowed characters. If empty, disallowed characters are removed. Default is ''.\n * @returns A new string containing only the allowed characters from the input string, with disallowed characters optionally replaced.\n *\n * @example\n * // Case-sensitive usage, removing disallowed characters\n * characterAllowedOnly(\"Hello123!\", [\"H\", \"e\", \"l\", \"o\"]);\n * // Returns: \"Hello\"\n *\n * @example\n * // Case-insensitive usage, removing disallowed characters\n * characterAllowedOnly(\"Hello123!\", [\"h\", \"E\", \"L\", \"O\"], true);\n * // Returns: \"Hello\"\n *\n * @example\n * // Using replacement character\n * characterAllowedOnly(\"Hello123!\", [\"H\", \"e\", \"l\", \"o\"], false, \"-\");\n * // Returns: \"Hello---\"\n */\n\nexport function characterAllowedOnly(\n str: string,\n list: string[],\n // eslint-disable-next-line @typescript-eslint/naming-convention\n caseInsensitive = false,\n replacementChar = '',\n): string {\n let newStr = '';\n\n // Convert the allowed list to lowercase if case-insensitive\n if (caseInsensitive) {\n list = Array.from(new Set(list.map((item) => item.toLowerCase())));\n }\n\n // Convert the entire input string to lowercase if case-insensitive\n const processedStr = caseInsensitive ? str.toLowerCase() : str;\n\n for (const c of processedStr) {\n if (list.includes(c)) {\n newStr += c;\n } else if (replacementChar !== '') {\n newStr += replacementChar;\n }\n }\n\n return newStr;\n}\n\n// functions to chop characters from a string.\n\n/**\n * Will remove the matching first character from a string\n * @param str\n * @param char\n * @returns\n */\n\nexport function chopBeginningCharacter(str: string, char: string): string {\n if (str.startsWith(char)) {\n return str.slice(1);\n } else {\n return str;\n }\n}\n\n/**\n * Will remove the matching last character from the string\n * @param str\n * @param char\n * @returns\n */\nexport function chopEndingCharacter(str: string, char: string): string {\n if (str.endsWith(char)) {\n return str.slice(0, -1);\n } else {\n return str;\n }\n}\n\n/**\n * Will remove the matching character, from the beginning and/or end of the string if matching\n * @param str\n * @param char\n * @returns\n */\n\nexport function chopBothBeginningAndEndingCharacters(\n str: string,\n char: string,\n): string {\n return chopBeginningCharacter(chopEndingCharacter(str, char), char);\n}\n","// prettier-ignore\nexport const BLANK_SPACE = ' ';\n\nexport const EOL = '\\n';\nexport const DOUBLE_EOL = EOL + EOL;\nexport const INDENT = ' '.repeat(4);\nexport const DOUBLE_INDENT = INDENT + INDENT;\n\n// prettier-ignore\nexport const SINGLE_QUOTE = \"'\";\n\n// similar to Python string library\nexport const ASCII_LOWERCASE = 'abcdefghijklmnopqrstuvwxyz';\nexport const ASCII_UPPERCASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';\nexport const ASCII_LETTERS = ASCII_LOWERCASE + ASCII_UPPERCASE;\nexport const DIGITS = '0123456789';\nexport const HEX_DIGITS = DIGITS + 'abcdefABCDEF';\nexport const OCT_DIGITS = '01234567';\nexport const PUNCTUATION = '!\"#$%&\\'()*+,-./:;<=>?@[\\\\]^_`{|}~';\nexport const WHITESPACE = ' \\t\\n\\r\\v\\f';\nexport const PRINTABLE = DIGITS + ASCII_LETTERS + PUNCTUATION + WHITESPACE;\n","/**\n * Utilities to pad strings with another string up to a defined length.\n * Each function takes a string, length to pad and the string to be used for padding (but defaults to a blank space if not provided) and returns the modified string\n *\n * This only pads on the left side, so text is added to the front until the total string equals the given length.\n *\n * ```typescript\n * padLeft('Hey', 6, '*'); // returns ***Hey\n * ```\n * @category String Padding\n */\n\nimport { BLANK_SPACE } from './constants';\n\nexport function padLeft(\n str: string,\n length: number,\n padStr = BLANK_SPACE,\n): string {\n return str.padStart(length, padStr);\n}\n\n/**\n * This only pads on the right side, so text is added to the end until the total string equals the given length.\n *\n * ```typescript\n * padRight('Hey', 6, '*'); // returns Hey***\n * ```\n *\n * @category String Padding\n */\n\nexport function padRight(\n str: string,\n length: number,\n padStr = BLANK_SPACE,\n): string {\n return str.padEnd(length, padStr);\n}\n\n/**\n * Same as `padCenterPreferLeft` and `padCenterPreferRight` but you can pass in a string with `left` or `right` before the padStr to use more direct.\n *\n * Defaults to `left`\n *\n * @category String Padding\n */\n\nexport function padCenter(\n str: string,\n length: number,\n prefer: 'left' | 'right' = 'left',\n padStr = BLANK_SPACE,\n): string {\n const midStrLength = length - str.length;\n\n if (midStrLength > 0) {\n const padLeftAmount =\n prefer === 'left'\n ? Math.ceil(midStrLength / 2)\n : Math.floor(midStrLength / 2);\n\n const padRightAmount =\n prefer === 'left'\n ? Math.floor(midStrLength / 2)\n : Math.ceil(midStrLength / 2);\n\n return (\n padLeft('', padLeftAmount, padStr) +\n str +\n padRight('', padRightAmount, padStr)\n );\n } else {\n return str;\n }\n}\n\n/**\n * It tries to pad equally on both sides in an attempt to center your text. However if it can't the extra character will be added to the left side\n *\n * @category String Padding\n */\n\nexport function padCenterPreferLeft(\n str: string,\n length: number,\n padStr = BLANK_SPACE,\n): string {\n return padCenter(str, length, 'left', padStr);\n}\n\n/**\n * It tries to pad equally, but if it can't the extra character will be added to the right side\n *\n * @category String Padding\n */\n\nexport function padCenterPreferRight(\n str: string,\n length: number,\n padStr = BLANK_SPACE,\n): string {\n return padCenter(str, length, 'right', padStr);\n}\n","import { padCenterPreferRight, padRight } from '../padding-utils';\nimport stringWidth from 'string-width';\nimport { splitGraphemes } from '../strings';\n\nexport class ASCIITableUtils {\n public static centerText(text: string, width: number): string {\n return padCenterPreferRight(text, width, ' ');\n }\n\n public static createSeparator(\n columnWidths: number[],\n character: string = '=',\n ): string {\n const totalWidth =\n columnWidths.reduce((sum, width) => sum + width + 3, 0) - 1;\n\n return `+${padRight('', totalWidth, character)}+`;\n }\n\n public static wrapText(text: string, maxLength: number): string[] {\n const words = text.split(' ');\n const lines: string[] = [];\n let currentLine = '';\n\n for (const word of words) {\n if (stringWidth(currentLine) + stringWidth(word) + 1 <= maxLength) {\n currentLine += (currentLine ? ' ' : '') + word;\n } else {\n if (currentLine) {\n lines.push(currentLine);\n }\n\n if (stringWidth(word) <= maxLength) {\n currentLine = word;\n } else {\n const subWords = ASCIITableUtils.splitWord(word, maxLength);\n lines.push(...subWords.slice(0, -1));\n currentLine = subWords[subWords.length - 1];\n }\n }\n }\n\n if (currentLine) {\n lines.push(currentLine);\n }\n\n return lines;\n }\n\n public static splitWord(word: string, maxLength: number): string[] {\n const graphemes = splitGraphemes(word);\n const subWords: string[] = [];\n let currentSubWord = '';\n\n for (const grapheme of graphemes) {\n if (stringWidth(currentSubWord + grapheme) <= maxLength) {\n currentSubWord += grapheme;\n } else {\n subWords.push(currentSubWord);\n currentSubWord = grapheme;\n }\n }\n\n if (currentSubWord) {\n subWords.push(currentSubWord);\n }\n\n return subWords;\n }\n}\n","import { ASCIITableUtils } from './ascii-table-utils';\nimport stringWidth from 'string-width';\n\ninterface MultiColumnASCIITableOptions {\n tableWidth?: number;\n emptyMessage?: string;\n widthMode?: 'flex' | 'fixed';\n}\n\nexport class MultiColumnASCIITable {\n private headers: string[];\n private rows: string[][];\n private tableWidth: number;\n private emptyMessage: string;\n private widthMode: 'flex' | 'fixed';\n\n constructor(headers: string[], options: MultiColumnASCIITableOptions = {}) {\n this.headers = headers;\n this.rows = [];\n this.tableWidth = options.tableWidth || 80;\n this.emptyMessage = options.emptyMessage || '';\n this.widthMode = options.widthMode || 'flex';\n\n const minTableWidth = this.getMinimumWidth();\n\n if (this.tableWidth < minTableWidth) {\n throw new Error(\n `Table width must be at least ${minTableWidth} to accommodate the headers.`,\n );\n }\n }\n\n public getMinimumWidth(): number {\n return this.headers.length * 4 + 1;\n }\n\n public addRow(row: string[]): void {\n if (row.length !== this.headers.length) {\n throw new Error(\n `Number of values in the row (${row.length}) must match the number of headers (${this.headers.length}).`,\n );\n }\n\n this.rows.push(row);\n }\n\n public toString(options: MultiColumnASCIITableOptions = {}): string {\n const tableWidth = options.tableWidth || this.tableWidth;\n const emptyMessage = options.emptyMessage || this.emptyMessage;\n\n if (this.rows.length === 0) {\n const emptyTableWidth = Math.min(tableWidth, 40);\n\n const separator = '+' + '-'.repeat(emptyTableWidth - 2) + '+';\n const emptyMessageLines = ASCIITableUtils.wrapText(\n emptyMessage,\n emptyTableWidth - 4,\n );\n\n const emptyRows = emptyMessageLines.map((line) => {\n const paddingLeft = ' '.repeat(\n Math.floor((emptyTableWidth - stringWidth(line) - 4) / 2),\n );\n\n const paddingRight = ' '.repeat(\n Math.ceil((emptyTableWidth - stringWidth(line) - 4) / 2),\n );\n\n return `| ${paddingLeft}${line}${paddingRight} |`;\n });\n\n if (emptyRows.length === 0) {\n emptyRows.push(`| ${' '.repeat(emptyTableWidth - 4)} |`);\n }\n\n return [separator, ...emptyRows, separator].join('\\n');\n }\n\n const columnWidths = this.calculateColumnWidths(options);\n\n const headerSeparator = ASCIITableUtils.createSeparator(columnWidths);\n const rowSeparator = ASCIITableUtils.createSeparator(columnWidths, '-');\n let tableString = headerSeparator + '\\n';\n\n const header = this.renderRow(this.headers, columnWidths);\n tableString += header + '\\n' + rowSeparator + '\\n';\n\n const rows = this.rows.map((row) => {\n return this.renderRow(row, columnWidths);\n });\n\n tableString += rows.join('\\n' + rowSeparator + '\\n');\n tableString += '\\n' + headerSeparator;\n\n return tableString;\n }\n\n public calculateColumnWidths(\n options: MultiColumnASCIITableOptions = {},\n ): number[] {\n const tableWidth = options.tableWidth || this.tableWidth;\n const widthMode = options.widthMode || this.widthMode;\n\n const numColumns = this.headers.length;\n\n if (widthMode === 'fixed') {\n const availableWidth = tableWidth - (numColumns + 1) * 3 + 1;\n const columnWidth = Math.floor(availableWidth / numColumns);\n const extraWidth = availableWidth % numColumns;\n const columnWidths = new Array(numColumns).fill(columnWidth);\n\n // if there is any remaining width (extraWidth), we distribute it evenly among the columns starting from the first column.\n for (let i = 0; i < extraWidth; i++) {\n columnWidths[i] += 1;\n }\n\n return columnWidths as number[];\n }\n\n const availableWidth = tableWidth - (numColumns + 1) * 3 + 1;\n const maxColumnWidth = Math.floor(availableWidth / numColumns);\n const totalContentWidth = this.headers.reduce(\n (sum, header) => sum + stringWidth(header),\n 0,\n );\n\n if (availableWidth >= totalContentWidth) {\n const remainingWidth = availableWidth - totalContentWidth;\n const extraCharWidth = Math.floor(remainingWidth / numColumns);\n const extraCharRemainder = remainingWidth % numColumns;\n\n const columnWidths = this.headers.map((header, index) => {\n const extraWidth = index < extraCharRemainder ? 1 : 0;\n return stringWidth(header) + extraCharWidth + extraWidth;\n });\n\n return columnWidths;\n } else {\n const columnWidths = this.headers.map(() => maxColumnWidth);\n\n return columnWidths;\n }\n }\n\n private renderRow(row: string[], columnWidths: number[]): string {\n const wrappedCells = row.map((value, index) => {\n const wrappedLines = ASCIITableUtils.wrapText(value, columnWidths[index]);\n\n return wrappedLines\n .map((line) => line.padEnd(columnWidths[index]))\n .join('\\n');\n });\n\n const maxLines = Math.max(\n ...wrappedCells.map((cell) => cell.split('\\n').length),\n );\n\n const paddedRows = [];\n\n for (let i = 0; i < maxLines; i++) {\n const rowLine = wrappedCells.map((cell, index) => {\n const cellLines = cell.split('\\n');\n const cellLine = cellLines[i] || '';\n const padding = ' '.repeat(columnWidths[index] - stringWidth(cellLine));\n\n return ' ' + cellLine + padding + ' ';\n });\n\n paddedRows.push('|' + rowLine.join('|') + '|');\n }\n\n return paddedRows.join('\\n');\n }\n}\n","import { isString } from '../strings';\nimport { padRight } from '../padding-utils';\nimport { MultiColumnASCIITable } from './multi-column-ascii-table';\nimport { ASCIITableUtils } from './ascii-table-utils';\nimport stringWidth from 'string-width';\nimport { clamp } from '../clamp';\n\nexport type TableRowValue =\n | string\n | number\n | boolean\n | null\n | undefined\n | KeyValueASCIITable\n | MultiColumnASCIITable\n | NestedKeyValueEntry[];\n\ntype TableRow = TableRowRegular | TableRowOwn;\n\ninterface TableRowRegular {\n kind: 'regular';\n key: string;\n value: TableRowValue;\n}\n\ninterface TableRowOwn {\n kind: 'own';\n key: string;\n value: string | NestedKeyValueEntry[];\n}\n\nexport interface NestedKeyValueEntry {\n key: string;\n value: string | KeyValueASCIITable | NestedKeyValueEntry[];\n}\n\ninterface KeyValueASCIITableOptions {\n tableWidth?: number;\n autoAdjustWidthWhenPossible?: boolean;\n emptyMessage?: string;\n}\n\nexport class KeyValueASCIITable {\n public readonly tableWidth: number;\n private emptyMessage: string;\n private autoAdjustWidthWhenPossible: boolean = true;\n\n private rows: TableRow[] = [];\n\n constructor(options: KeyValueASCIITableOptions = {}) {\n const minTableWidth = this.getMinimumWidth();\n\n if (options.tableWidth && options.tableWidth < minTableWidth) {\n throw new Error(\n `Table width must be at least ${minTableWidth} to accommodate the table structure.`,\n );\n }\n\n this.tableWidth = options.tableWidth || 80;\n this.autoAdjustWidthWhenPossible =\n options.autoAdjustWidthWhenPossible ?? true;\n this.emptyMessage = options.emptyMessage || '';\n }\n\n public getMinimumWidth(): number {\n // The minimum width for the KeyValueASCIITable is 9 characters:\n // - 2 character for the '| ' at the start\n // - 1 character for the minimum key column width\n // - 3 character for the ' | ' separating the key and value columns\n // - 1 character for the minimum value column width\n // - 2 character for the ' |' at the end\n\n return 9;\n }\n\n /**\n * Adds key and value to the table but placing the value on its own row.\n *\n * @param key\n * @param value\n */\n\n public addValueOnSeparateRow(key: string, value: string): void {\n const row: TableRow = { kind: 'own', key, value };\n\n this.rows.push(row);\n }\n\n /**\n * Adds key and value to the table.\n *\n * If provided value is an instance of ASCIITable or MultiColumnASCIITable, it will be rendered as a nested table on its own row for readability.\n *\n * @param key\n * @param value\n */\n\n public addRow(key: string, value: TableRowValue): void {\n const row: TableRow = { kind: 'regular', key, value };\n\n this.rows.push(row);\n }\n\n public toString(options: KeyValueASCIITableOptions = {}): string {\n const tableWidth = options.tableWidth || this.tableWidth;\n const canAutoAdjustWidthWhenPossible =\n options.autoAdjustWidthWhenPossible ?? this.autoAdjustWidthWhenPossible;\n\n const emptyMessage = options.emptyMessage || this.emptyMessage;\n\n if (this.rows.length === 0) {\n const emptyTableWidth = Math.min(tableWidth, 40);\n\n const separator = '+' + '-'.repeat(emptyTableWidth - 2) + '+';\n const emptyMessageLines = ASCIITableUtils.wrapText(\n emptyMessage,\n emptyTableWidth - 4,\n );\n\n const emptyRows = emptyMessageLines.map((line) => {\n const paddingLeft = padRight(\n '',\n Math.floor((emptyTableWidth - stringWidth(line) - 4) / 2),\n ' ',\n );\n\n const paddingRight = padRight(\n '',\n Math.ceil((emptyTableWidth - stringWidth(line) - 4) / 2),\n ' ',\n );\n\n return `| ${paddingLeft}${line}${paddingRight} |`;\n });\n\n if (emptyRows.length === 0) {\n emptyRows.push(`| ${' '.repeat(emptyTableWidth - 4)} |`);\n }\n\n return [separator, ...emptyRows, separator].join('\\n');\n }\n\n const columnWidths = this.calculateColumnWidths(options);\n\n const headerSeparator = ASCIITableUtils.createSeparator(columnWidths);\n const rowSeparator = ASCIITableUtils.createSeparator(columnWidths, '-');\n\n let tableString = headerSeparator + '\\n';\n\n for (const [rowIndex, row] of this.rows.entries()) {\n const { kind, key, value } = row;\n\n if (\n kind === 'own' ||\n value instanceof KeyValueASCIITable ||\n value instanceof MultiColumnASCIITable ||\n Array.isArray(value)\n ) {\n const keyString = ASCIITableUtils.centerText(\n key,\n columnWidths[0] + columnWidths[1] + 3,\n );\n\n tableString += `| ${keyString} |\\n`;\n tableString += rowSeparator + '\\n';\n\n let valueString = '';\n\n if (value instanceof KeyValueASCIITable) {\n valueString = this.formatValue(\n value,\n tableWidth - 4,\n canAutoAdjustWidthWhenPossible,\n '',\n );\n } else if (value instanceof MultiColumnASCIITable) {\n valueString = this.formatValue(\n value,\n tableWidth - 4,\n canAutoAdjustWidthWhenPossible,\n '',\n );\n } else if (Array.isArray(value)) {\n valueString = this.formatValue(\n value,\n tableWidth - 4,\n canAutoAdjustWidthWhenPossible,\n '',\n );\n } else if (row.kind === 'own') {\n valueString = this.formatTableRowOnOwnRow(\n value as string,\n tableWidth - 4,\n tableWidth,\n );\n }\n\n const valueLines = valueString.split('\\n');\n const paddedValueLines = valueLines.map((line) => {\n const padding = padRight('', tableWidth - stringWidth(line) - 4, ' ');\n\n return `| ${line}${padding} |`;\n });\n\n tableString += paddedValueLines.join('\\n') + '\\n';\n tableString += headerSeparator + '\\n';\n } else {\n const keyLines = ASCIITableUtils.wrapText(key, columnWidths[0]);\n\n const valueLines = ASCIITableUtils.wrapText(\n this.formatValue(\n value,\n columnWidths[1],\n canAutoAdjustWidthWhenPossible,\n '',\n ),\n columnWidths[1],\n );\n\n const maxLines = Math.max(keyLines.length, valueLines.length);\n\n for (let i = 0; i < maxLines; i++) {\n const keyLine = keyLines[i] || '';\n const valueLine = valueLines[i] || '';\n\n const keyPadding = ' '.repeat(columnWidths[0] - stringWidth(keyLine));\n\n const valuePadding = ' '.repeat(\n columnWidths[1] - stringWidth(valueLine),\n );\n\n tableString += `| ${keyLine}${keyPadding} | ${valueLine}${valuePadding} |\\n`;\n\n if (i === maxLines - 1) {\n if (rowIndex === this.rows.length - 1) {\n tableString += headerSeparator + '\\n';\n } else {\n tableString += rowSeparator + '\\n';\n }\n }\n }\n }\n }\n\n return tableString.trim();\n }\n\n private calculateColumnWidths(\n options: KeyValueASCIITableOptions = {},\n ): number[] {\n const tableWidth = options.tableWidth || this.tableWidth;\n const canAutoAdjustWidthWhenPossible =\n options.autoAdjustWidthWhenPossible ?? this.autoAdjustWidthWhenPossible;\n\n const columnWidths: number[] = [0, 0];\n\n for (const row of this.rows) {\n const { key, value } = row;\n\n const keyWidth = stringWidth(key);\n const maxKeyWidth = Math.floor((tableWidth - 7) / 2);\n\n if (keyWidth > columnWidths[0]) {\n columnWidths[0] = Math.min(keyWidth, maxKeyWidth);\n columnWidths[1] = Math.max(0, tableWidth - columnWidths[0] - 7);\n }\n\n if (typeof value === 'string') {\n const valueWidth = Math.max(\n ...value.split('\\n').map((line) => stringWidth(line)),\n );\n\n if (valueWidth > columnWidths[1]) {\n columnWidths[1] = Math.min(\n valueWidth,\n tableWidth - columnWidths[0] - 7,\n );\n\n columnWidths[0] = Math.max(0, tableWidth - columnWidths[1] - 7);\n }\n } else if (row.kind === 'own') {\n let valueWidth = 0;\n\n if (isString(value)) {\n valueWidth = Math.max(\n ...value.split('\\n').map((line) => stringWidth(line)),\n );\n }\n\n if (valueWidth > columnWidths[1]) {\n columnWidths[1] = Math.min(\n valueWidth,\n tableWidth - columnWidths[0] - 7,\n );\n\n columnWidths[0] = Math.max(0, tableWidth - columnWidths[1] - 7);\n }\n } else if (value instanceof KeyValueASCIITable) {\n // Update column widths based on the nested table\n let nestedTableColumnWidths: number[] = [];\n\n if (canAutoAdjustWidthWhenPossible) {\n const minWidth = value.getMinimumWidth();\n\n const availableWidth = tableWidth - columnWidths[0] - 7;\n const adjustedWidth = clamp(availableWidth, minWidth, availableWidth);\n\n nestedTableColumnWidths = value.calculateColumnWidths({\n tableWidth: adjustedWidth,\n });\n } else {\n nestedTableColumnWidths = value.calculateColumnWidths();\n }\n\n const nestedTableWidth =\n nestedTableColumnWidths.reduce((sum, width) => sum + width, 0) +\n nestedTableColumnWidths.length * 3 -\n 1;\n\n const availableWidth = tableWidth - columnWidths[0] - 7;\n\n if (nestedTableWidth > availableWidth) {\n columnWidths[1] = availableWidth;\n } else {\n columnWidths[1] = Math.max(columnWidths[1], nestedTableWidth);\n }\n } else if (value instanceof MultiColumnASCIITable) {\n // Update column widths based on the nested multi-column table\n let nestedTableColumnWidths: number[] = [];\n\n if (canAutoAdjustWidthWhenPossible) {\n const minWidth = value.getMinimumWidth();\n\n const availableWidth = tableWidth - columnWidths[0] - 7;\n const adjustedWidth = clamp(availableWidth, minWidth, availableWidth);\n\n nestedTableColumnWidths = value.calculateColumnWidths({\n tableWidth: adjustedWidth,\n });\n } else {\n nestedTableColumnWidths = value.calculateColumnWidths();\n }\n\n const nestedTableWidth =\n nestedTableColumnWidths.reduce((sum, width) => sum + width, 0) +\n nestedTableColumnWidths.length * 3 -\n 1;\n\n const availableWidth = tableWidth - columnWidths[0] - 7;\n\n if (nestedTableWidth > availableWidth) {\n columnWidths[1] = availableWidth;\n } else {\n columnWidths[1] = Math.max(columnWidths[1], nestedTableWidth);\n }\n } else if (Array.isArray(value)) {\n for (const nestedCell of value) {\n const nestedKeyWidth = stringWidth(nestedCell.key);\n const maxNestedKeyWidth = Math.floor((tableWidth - 7) / 2);\n\n if (nestedKeyWidth > columnWidths[0]) {\n columnWidths[0] = Math.min(nestedKeyWidth, maxNestedKeyWidth);\n columnWidths[1] = Math.max(0, tableWidth - columnWidths[0] - 7);\n }\n\n if (typeof nestedCell.value === 'string') {\n const nestedValueWidth = Math.max(\n ...nestedCell.value.split('\\n').map((line) => stringWidth(line)),\n );\n\n if (nestedValueWidth > columnWidths[1]) {\n columnWidths[1] = Math.min(\n nestedValueWidth,\n tableWidth - columnWidths[0] - 7,\n );\n columnWidths[0] = Math.max(0, tableWidth - columnWidths[1] - 7);\n }\n }\n }\n }\n }\n\n return columnWidths;\n }\n\n private formatValue(\n value:\n | string\n | number\n | boolean\n | null\n | undefined\n | KeyValueASCIITable\n | MultiColumnASCIITable\n | NestedKeyValueEntry[],\n cellWidth: number,\n canAutoAdjustWidthWhenPossible: boolean,\n indent = '',\n ): string {\n if (typeof value === 'string') {\n return value;\n } else if (typeof value === 'number') {\n return String(value);\n } else if (typeof value === 'boolean') {\n return String(value);\n } else if (value === null) {\n return 'null';\n } else if (value === undefined) {\n return 'undefined';\n } else if (value instanceof KeyValueASCIITable) {\n let nestedTableLines: string[];\n\n if (canAutoAdjustWidthWhenPossible) {\n const minWidth = value.getMinimumWidth();\n\n const adjustedWidth = clamp(cellWidth, minWidth, cellWidth);\n\n nestedTableLines = value\n .toString({ tableWidth: adjustedWidth })\n .split('\\n');\n } else {\n nestedTableLines = value.toString().split('\\n');\n }\n\n const indentedLines = nestedTableLines.map((line) => `${indent}${line}`);\n\n return indentedLines.join('\\n');\n } else if (value instanceof MultiColumnASCIITable) {\n let nestedTableLines: string[];\n\n if (canAutoAdjustWidthWhenPossible) {\n const minWidth = value.getMinimumWidth();\n\n const adjustedWidth = clamp(cellWidth, minWidth, cellWidth);\n nestedTableLines = value\n .toString({ tableWidth: adjustedWidth })\n .split('\\n');\n } else {\n nestedTableLines = value.toString().split('\\n');\n }\n\n const indentedLines = nestedTableLines.map((line) => `${indent}${line}`);\n\n return indentedLines.join('\\n');\n } else if (Array.isArray(value)) {\n const nestedValueLines: string[] = [];\n\n for (const { key, value: nestedValue } of value) {\n const formattedKey = `${indent}${key}:`;\n const formattedValue = this.formatValue(\n nestedValue,\n cellWidth - indent.length - stringWidth(key) - 2,\n canAutoAdjustWidthWhenPossible,\n `${indent}`,\n );\n\n const wrappedSpacer = padRight('', 4, ' ');\n\n const wrappedValue = formattedValue\n .split('\\n')\n .map((line) => `${indent}${wrappedSpacer}${line}`);\n\n nestedValueLines.push(formattedKey);\n nestedValueLines.push(...wrappedValue);\n nestedValueLines.push('');\n }\n\n return nestedValueLines.slice(0, -1).join('\\n');\n } else {\n throw new TypeError('Invalid value type provided');\n }\n }\n\n private formatTableRowOnOwnRow(\n value: string,\n width: number,\n maxRowLength: number,\n ): string {\n const lines = value.split('\\n');\n\n const paddedLines = lines.map((line) => {\n const wrappedLines = ASCIITableUtils.wrapText(line, maxRowLength - 4);\n\n return wrappedLines\n .map((wrappedLine) => {\n const padding = padRight(\n '',\n width - stringWidth(wrappedLine) - 2,\n ' ',\n );\n\n return `${wrappedLine}${padding}`;\n })\n .join('\\n');\n });\n\n return paddedLines.join('\\n');\n }\n}\n","export function clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(value, max));\n}\n\n/**\n * Clamps a value to a minimum, returning a default if the value is not finite or is undefined/null.\n *\n * Useful for config/settings validation where you want to:\n * - Enforce a minimum value\n * - Handle invalid inputs (Infinity, NaN, undefined, null) gracefully\n *\n * @param value - The value to clamp (can be undefined or null)\n * @param min - The minimum allowed value\n * @param defaultValue - The default to return if value is not finite or is undefined/null\n * @returns The clamped value, or defaultValue if value is not finite/undefined/null\n *\n * @example\n * ```typescript\n * finiteClampMin(5000, 1000, 3000) // 5000 (value > min)\n * finiteClampMin(500, 1000, 3000) // 1000 (enforces min)\n * finiteClampMin(Infinity, 1000, 3000) // 3000 (not finite, use default)\n * finiteClampMin(NaN, 1000, 3000) // 3000 (not finite, use default)\n * finiteClampMin(undefined, 1000, 3000) // 3000 (undefined, use default)\n * finiteClampMin(null, 1000, 3000) // 3000 (null, use default)\n * ```\n */\nexport function finiteClampMin(\n value: number | undefined | null,\n min: number,\n defaultValue: number,\n): number {\n if (value === null || value === undefined || !Number.isFinite(value)) {\n return defaultValue;\n }\n\n return Math.max(value, min);\n}\n","import type { NestedKeyValueEntry } from './ascii-tables/key-value-ascii-table';\nimport { KeyValueASCIITable } from './ascii-tables/key-value-ascii-table';\n\nfunction safeStringify(value: unknown): string {\n if (value === null || value === undefined) {\n return String(value);\n }\n\n switch (typeof value) {\n case 'string':\n return value;\n case 'number':\n case 'boolean':\n case 'bigint':\n return String(value);\n case 'object':\n return JSON.stringify(value);\n case 'function':\n return '[Function]';\n case 'symbol':\n return value.toString();\n default:\n // This should never happen, but satisfy the linter\n return String(value as string | number | boolean);\n }\n}\n\nexport function errorToString(error: unknown, maxRowLength = 80): string {\n const table = errorToASCIITable(error, maxRowLength);\n\n return table.toString();\n}\n\nfunction errorToASCIITable(\n error: unknown,\n maxRowLength: number,\n): KeyValueASCIITable {\n const table = new KeyValueASCIITable({\n tableWidth: maxRowLength,\n autoAdjustWidthWhenPossible: true,\n });\n\n if (error && typeof error === 'object') {\n const err = error as Record<string, unknown>;\n table.addRow('Key', 'Value');\n\n if (err['message']) {\n table.addRow('Message', safeStringify(err['message']));\n }\n\n if (err['name']) {\n table.addRow('Name', safeStringify(err['name']));\n }\n\n if (err['code']) {\n table.addRow('Code', safeStringify(err['code']));\n }\n\n if (err['errno']) {\n table.addRow('Errno', safeStringify(err['errno']));\n }\n\n // other conventional that might be used to enhance the error object\n if (err['errPrefix']) {\n table.addRow('Prefix', safeStringify(err['errPrefix']));\n }\n\n if (err['errType']) {\n table.addRow('errType', safeStringify(err['errType']));\n }\n\n if (err['errCode']) {\n table.addRow('errCode', safeStringify(err['errCode']));\n }\n\n if (err['additionalInfo']) {\n const additionalInfo = err['additionalInfo'] as Record<string, unknown>;\n const sensitiveFieldNames =\n (err['sensitiveFieldNames'] as string[]) || [];\n\n for (const key in additionalInfo) {\n if (sensitiveFieldNames.includes(key)) {\n table.addRow(`AdditionalInfo.${key}`, '***');\n } else {\n const value = additionalInfo[key];\n\n table.addRow(\n `AdditionalInfo.${key}`,\n stringifyValue(value, table, maxRowLength),\n );\n }\n }\n }\n\n if (err['stack']) {\n table.addValueOnSeparateRow('Stack', safeStringify(err['stack']));\n }\n }\n\n return table;\n}\n\nfunction stringifyValue(\n value: unknown,\n table: KeyValueASCIITable,\n maxRowLength: number,\n): string | KeyValueASCIITable | NestedKeyValueEntry[] {\n if (typeof value === 'string') {\n return value;\n } else if (Array.isArray(value)) {\n // Handle arrays differently\n return value\n .map((item) => {\n const result = stringifyValue(item, table, maxRowLength);\n // Convert complex types to strings for joining\n if (typeof result === 'string') {\n return result;\n } else if (result instanceof KeyValueASCIITable) {\n return result.toString();\n } else {\n return JSON.stringify(result);\n }\n })\n .join(', ');\n } else if (typeof value === 'object' && value !== null) {\n if (value instanceof Error) {\n return errorToASCIITable(value, maxRowLength - 4);\n } else {\n // Handle objects differently\n const entries: NestedKeyValueEntry[] = Object.entries(value).map(\n ([key, val]) => ({\n key,\n value: stringifyValue(val, table, maxRowLength - 4),\n }),\n );\n\n return entries;\n }\n } else {\n return String(value);\n }\n}\n","// Helper from https://github.com/then/is-promise/tree/master\n// For some reason the @types/is-promise package stopped being picked up on\n\nexport function isPromise(obj: unknown): obj is Promise<unknown> {\n return (\n !!obj &&\n (typeof obj === 'object' || typeof obj === 'function') &&\n // @ts-expect-error - obj is checked to be object/function, then property access works at runtime\n typeof obj['then'] === 'function'\n );\n}\n","export function isFunction(value: unknown): boolean {\n return typeof value === 'function' || value instanceof Function;\n}\n","import { errorToString } from './error-to-string';\nimport { isPromise } from './is-promise';\nimport { isFunction } from './is-function';\nimport { DOUBLE_EOL } from './constants';\n\n/**\n * Safely handles a callback function by catching any errors and reporting them\n * using the global `reportError` event (supported in Node.js 25+, Bun, Deno, and browsers).\n * This function can seamlessly handle both synchronous and asynchronous (Promise-based) callback functions.\n *\n * Errors are dispatched as ErrorEvent objects with type 'reportError' via `globalThis.dispatchEvent()`.\n * You can listen for these errors using `globalThis.addEventListener('reportError', handler)`.\n *\n * This function is a \"fire-and-forget\" type of function, meaning it doesn't wait\n * for the callback to complete and doesn't return any result or error. If you need\n * to handle the result or error of the callback, consider using the\n * `safeHandleCallbackAndWait` function instead.\n *\n * @param {string} callbackName - The name of the callback function, used for error reporting.\n * @param {unknown} callback - The callback function to be executed. It can be either a\n * synchronous function or a function that returns a Promise.\n * @param {...unknown[]} args - Additional arguments to pass to the callback function.\n */\n\nexport function safeHandleCallback(\n callbackName: string,\n callback: unknown,\n ...args: unknown[]\n): void {\n const handleError = (error: Error): void => {\n // Dispatch error using the standard reportError event API\n // Supported in Node.js 25+, Bun, Deno, and browsers\n if (\n typeof (globalThis as Record<string, unknown>).dispatchEvent ===\n 'function'\n ) {\n (\n globalThis as unknown as {\n dispatchEvent: (event: Event) => void;\n }\n ).dispatchEvent(\n new ErrorEvent('reportError', {\n error: new Error(\n `Error in a callback ${callbackName}: ${DOUBLE_EOL}${errorToString(error)}`,\n ),\n }),\n );\n }\n };\n\n if (isFunction(callback)) {\n try {\n // We need to cast callback to the appropriate function type now\n const result = (callback as (...args: unknown[]) => unknown)(...args);\n\n if (isPromise(result)) {\n // Fire-and-forget async callback\n result.catch((error: unknown) => {\n handleError(error as Error);\n });\n }\n } catch (error) {\n handleError(error as Error);\n }\n } else {\n handleError(\n new Error(`Callback provided for ${callbackName} is not a function`),\n );\n }\n}\n\ninterface CallbackResult<T> {\n success: boolean;\n value?: T;\n error?: Error;\n}\n\n/**\n * Safely handles a callback function by catching any errors and reporting them\n * using the global `reportError` event (supported in Node.js 25+, Bun, Deno, and browsers).\n * This function can seamlessly handle both synchronous and asynchronous (Promise-based) callback\n * functions, and it waits for the callback to complete before returning the result or an error.\n *\n * Errors are dispatched as ErrorEvent objects with type 'reportError' via `globalThis.dispatchEvent()`.\n * You can listen for these errors using `globalThis.addEventListener('reportError', handler)`.\n *\n * @param {string} callbackName - The name of the callback function, used for error reporting.\n * @param {unknown} callback - The callback function to be executed. It can be either a\n * synchronous function or a function that returns a Promise.\n * @param {...unknown[]} args - Additional arguments to pass to the callback function.\n * @returns {Promise<CallbackResult<unknown>>} - A promise that resolves with an object containing\n * the success status, value (if any), and error (if any).\n */\n\nexport async function safeHandleCallbackAndWait<T>(\n callbackName: string,\n callback: unknown,\n ...args: unknown[]\n): Promise<CallbackResult<T>> {\n const handleError = (error: Error): CallbackResult<T> => {\n // Dispatch error using the standard reportError event API\n // Supported in Node.js 25+, Bun, Deno, and browsers\n if (\n typeof (globalThis as Record<string, unknown>).dispatchEvent ===\n 'function'\n ) {\n (\n globalThis as unknown as {\n dispatchEvent: (event: Event) => void;\n }\n ).dispatchEvent(\n new ErrorEvent('reportError', {\n error: new Error(\n `Error in a callback ${callbackName}: ${DOUBLE_EOL}${errorToString(error)}`,\n ),\n }),\n );\n }\n\n return { success: false, error };\n };\n\n if (isFunction(callback)) {\n try {\n // We need to cast callback to the appropriate function type now\n const result = (callback as (...args: unknown[]) => unknown)(...args);\n\n if (isPromise(result)) {\n // Wait for the async callback to complete\n const value = await (result as Promise<T>);\n\n return { success: true, value };\n } else {\n return { success: true, value: result as T };\n }\n } catch (error) {\n return handleError(error as Error);\n }\n } else {\n return handleError(\n new Error(`Callback provided for ${callbackName} is not a function`),\n );\n }\n}\n"],"mappings":"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","names":["stringWidth","import_string_width","stringWidth","availableWidth","import_string_width","stringWidth","availableWidth"]}
@@ -1,14 +1,20 @@
1
- /**
2
- * TTL-aware LRU (Least Recently Used) Cache implementation
3
- *
4
- * Features:
5
- * - Configurable maximum entries to limit item count
6
- * - Optional maximum size in bytes to limit memory usage
7
- * - Optional TTL (Time To Live) for cache entries
8
- * - Lazy cleanup of expired entries during cache operations
9
- * - Efficient LRU eviction policy
10
- * - Size-aware eviction when memory limits are reached
11
- */
1
+ type LRUCacheChangeEvent<K, V> = {
2
+ reason: 'evict' | 'expired' | 'delete' | 'clear';
3
+ key: K;
4
+ value: V;
5
+ } | {
6
+ reason: 'set';
7
+ key: K;
8
+ newValue: V;
9
+ oldValue?: V;
10
+ } | {
11
+ reason: 'skip';
12
+ key: K;
13
+ newValue: V;
14
+ currentValue?: V;
15
+ cause: 'maxSize';
16
+ };
17
+ type LRUCacheChangeReason = LRUCacheChangeEvent<unknown, unknown>['reason'];
12
18
  declare class LRUCache<K, V> {
13
19
  private maxEntries;
14
20
  private maxSize?;
@@ -18,6 +24,8 @@ declare class LRUCache<K, V> {
18
24
  private currentSize;
19
25
  private expirableEntryCount;
20
26
  private sizeCalculator?;
27
+ private onChange?;
28
+ private onChangeReasons?;
21
29
  private map;
22
30
  /**
23
31
  * Create a new LRU cache
@@ -26,11 +34,15 @@ declare class LRUCache<K, V> {
26
34
  * @param options.defaultTtl Default time to live in milliseconds for all entries
27
35
  * @param options.maxSize Maximum total size in bytes
28
36
  * @param options.sizeCalculator Function to calculate the size of a value
37
+ * @param options.onChange Callback invoked for cache changes
38
+ * @param options.onChangeReasons Optional list of change reasons that should trigger onChange
29
39
  */
30
40
  constructor(maxEntries: number, options?: {
31
41
  defaultTtl?: number;
32
42
  maxSize?: number;
33
43
  sizeCalculator?: (value: V) => number;
44
+ onChange?: (event: LRUCacheChangeEvent<K, V>) => void | Promise<void>;
45
+ onChangeReasons?: LRUCacheChangeReason[];
34
46
  });
35
47
  /**
36
48
  * Get the current number of entries in the cache
@@ -52,17 +64,18 @@ declare class LRUCache<K, V> {
52
64
  * Clear all entries from the cache
53
65
  */
54
66
  clear(): void;
55
- /**
56
- * Remove all expired entries from the cache
57
- * @returns The number of entries removed
58
- */
59
- cleanupExpired(): number;
60
67
  /**
61
68
  * Delete a specific entry from the cache
62
69
  * @param key The key to delete
63
70
  * @returns True if the entry was deleted, false if it didn't exist
64
71
  */
65
72
  delete(key: K): boolean;
73
+ /**
74
+ * Remove all expired entries from the cache
75
+ * @returns The number of entries removed
76
+ */
77
+ cleanupExpired(): number;
78
+ private cleanupExpiredInternal;
66
79
  private isBufferValue;
67
80
  /**
68
81
  * Calculate the size of a value in bytes
@@ -78,7 +91,10 @@ declare class LRUCache<K, V> {
78
91
  */
79
92
  private evictOldest;
80
93
  private maybeCleanup;
94
+ private createLocalChangeQueue;
95
+ private flushLocalChangeQueue;
96
+ private removeEntry;
81
97
  private assertNonNegativeFiniteNumber;
82
98
  }
83
99
 
84
- export { LRUCache };
100
+ export { LRUCache, type LRUCacheChangeEvent, type LRUCacheChangeReason };
@@ -1,14 +1,20 @@
1
- /**
2
- * TTL-aware LRU (Least Recently Used) Cache implementation
3
- *
4
- * Features:
5
- * - Configurable maximum entries to limit item count
6
- * - Optional maximum size in bytes to limit memory usage
7
- * - Optional TTL (Time To Live) for cache entries
8
- * - Lazy cleanup of expired entries during cache operations
9
- * - Efficient LRU eviction policy
10
- * - Size-aware eviction when memory limits are reached
11
- */
1
+ type LRUCacheChangeEvent<K, V> = {
2
+ reason: 'evict' | 'expired' | 'delete' | 'clear';
3
+ key: K;
4
+ value: V;
5
+ } | {
6
+ reason: 'set';
7
+ key: K;
8
+ newValue: V;
9
+ oldValue?: V;
10
+ } | {
11
+ reason: 'skip';
12
+ key: K;
13
+ newValue: V;
14
+ currentValue?: V;
15
+ cause: 'maxSize';
16
+ };
17
+ type LRUCacheChangeReason = LRUCacheChangeEvent<unknown, unknown>['reason'];
12
18
  declare class LRUCache<K, V> {
13
19
  private maxEntries;
14
20
  private maxSize?;
@@ -18,6 +24,8 @@ declare class LRUCache<K, V> {
18
24
  private currentSize;
19
25
  private expirableEntryCount;
20
26
  private sizeCalculator?;
27
+ private onChange?;
28
+ private onChangeReasons?;
21
29
  private map;
22
30
  /**
23
31
  * Create a new LRU cache
@@ -26,11 +34,15 @@ declare class LRUCache<K, V> {
26
34
  * @param options.defaultTtl Default time to live in milliseconds for all entries
27
35
  * @param options.maxSize Maximum total size in bytes
28
36
  * @param options.sizeCalculator Function to calculate the size of a value
37
+ * @param options.onChange Callback invoked for cache changes
38
+ * @param options.onChangeReasons Optional list of change reasons that should trigger onChange
29
39
  */
30
40
  constructor(maxEntries: number, options?: {
31
41
  defaultTtl?: number;
32
42
  maxSize?: number;
33
43
  sizeCalculator?: (value: V) => number;
44
+ onChange?: (event: LRUCacheChangeEvent<K, V>) => void | Promise<void>;
45
+ onChangeReasons?: LRUCacheChangeReason[];
34
46
  });
35
47
  /**
36
48
  * Get the current number of entries in the cache
@@ -52,17 +64,18 @@ declare class LRUCache<K, V> {
52
64
  * Clear all entries from the cache
53
65
  */
54
66
  clear(): void;
55
- /**
56
- * Remove all expired entries from the cache
57
- * @returns The number of entries removed
58
- */
59
- cleanupExpired(): number;
60
67
  /**
61
68
  * Delete a specific entry from the cache
62
69
  * @param key The key to delete
63
70
  * @returns True if the entry was deleted, false if it didn't exist
64
71
  */
65
72
  delete(key: K): boolean;
73
+ /**
74
+ * Remove all expired entries from the cache
75
+ * @returns The number of entries removed
76
+ */
77
+ cleanupExpired(): number;
78
+ private cleanupExpiredInternal;
66
79
  private isBufferValue;
67
80
  /**
68
81
  * Calculate the size of a value in bytes
@@ -78,7 +91,10 @@ declare class LRUCache<K, V> {
78
91
  */
79
92
  private evictOldest;
80
93
  private maybeCleanup;
94
+ private createLocalChangeQueue;
95
+ private flushLocalChangeQueue;
96
+ private removeEntry;
81
97
  private assertNonNegativeFiniteNumber;
82
98
  }
83
99
 
84
- export { LRUCache };
100
+ export { LRUCache, type LRUCacheChangeEvent, type LRUCacheChangeReason };