safe-memory-layer 1.0.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/timer.ts","../src/Scheduler.ts","../src/Entry.ts","../src/Stats.ts","../src/Events.ts","../src/utils/featureDetection.ts","../src/MemoryStore.ts","../src/WeakMemoryStore.ts"],"names":["entry"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Safe timer utilities for safe-memory-layer.\n *\n * Provides platform-agnostic timer creation and cleanup.\n * Uses only Web-compatible APIs (setTimeout/clearTimeout).\n *\n * @module utils/timer\n */\n\n/** Represents a timer handle that can be cancelled. */\nexport interface TimerHandle {\n /** Cancels the timer. Safe to call multiple times. */\n cancel: () => void;\n /** Whether the timer has been cancelled. */\n readonly cancelled: boolean;\n}\n\n/**\n * Creates a safe timer that can be cancelled.\n * Uses setTimeout internally for maximum compatibility.\n *\n * @param fn - The function to execute after the delay.\n * @param delay - The delay in milliseconds.\n * @returns A TimerHandle that can be used to cancel the timer.\n */\nexport function createTimer(fn: () => void, delay: number): TimerHandle {\n let cancelled = false;\n let timerId: ReturnType<typeof setTimeout> | undefined;\n\n const wrappedFn = (): void => {\n if (cancelled) return;\n timerId = undefined;\n try {\n fn();\n } catch {\n // Isolate errors from timer callbacks to prevent crashes\n }\n };\n\n timerId = setTimeout(wrappedFn, delay);\n\n return {\n cancel: (): void => {\n cancelled = true;\n if (timerId !== undefined) {\n clearTimeout(timerId);\n timerId = undefined;\n }\n },\n get cancelled(): boolean {\n return cancelled;\n },\n };\n}\n\n/**\n * Creates an interval timer that runs a function repeatedly.\n *\n * @param fn - The function to execute on each interval.\n * @param interval - The interval in milliseconds.\n * @returns A TimerHandle that can be used to cancel the interval.\n */\nexport function createInterval(\n fn: () => void,\n interval: number,\n): TimerHandle {\n let cancelled = false;\n let timerId: ReturnType<typeof setInterval> | undefined;\n\n const wrappedFn = (): void => {\n if (cancelled) return;\n try {\n fn();\n } catch {\n // Isolate errors from interval callbacks\n }\n };\n\n timerId = setInterval(wrappedFn, interval);\n\n return {\n cancel: (): void => {\n cancelled = true;\n if (timerId !== undefined) {\n clearInterval(timerId);\n timerId = undefined;\n }\n },\n get cancelled(): boolean {\n return cancelled;\n },\n };\n}","/**\n * Scheduler for automatic cleanup of expired entries.\n *\n * Design:\n * - Single interval timer for all cleanup operations\n * - Automatically stops when the store is empty to save CPU\n * - Restarts when new entries are added\n * - Very low CPU usage with configurable interval\n *\n * @module Scheduler\n */\n\nimport { createInterval } from \"./utils/timer.js\";\nimport type { TimerHandle } from \"./utils/timer.js\";\n\n/**\n * Function type for cleanup callbacks.\n * Returns the number of entries that were cleaned up.\n */\nexport type CleanupFn = () => number;\n\n/**\n * Manages a periodic cleanup scheduler.\n * Stops automatically when empty, restarts when needed.\n */\nexport class Scheduler {\n /** The cleanup interval timer handle. */\n #timer: TimerHandle | null = null;\n\n /** The interval in milliseconds between cleanup runs. */\n readonly #interval: number;\n\n /** The cleanup function to call on each interval. */\n readonly #cleanupFn: CleanupFn;\n\n /** The onEmpty callback, called when cleanup finds the store empty. */\n readonly #onEmpty: (() => void) | undefined;\n\n /** Whether the scheduler is currently running. */\n #running = false;\n\n /**\n * Creates a new Scheduler.\n *\n * @param cleanupFn - Function to call for cleanup. Should return count of removed entries.\n * @param interval - Interval in milliseconds between cleanup runs.\n * @param onEmpty - Optional callback when cleanup finds the store empty.\n */\n constructor(\n cleanupFn: CleanupFn,\n interval: number,\n onEmpty?: () => void,\n ) {\n this.#cleanupFn = cleanupFn;\n this.#interval = interval;\n this.#onEmpty = onEmpty;\n }\n\n /**\n * Starts the scheduler if it's not already running.\n * Does nothing if already started.\n */\n start(): void {\n if (this.#running || this.#timer !== null) return;\n\n this.#running = true;\n this.#timer = createInterval(() => {\n this.#tick();\n }, this.#interval);\n }\n\n /**\n * Stops the scheduler if running.\n * Safe to call multiple times.\n */\n stop(): void {\n this.#running = false;\n if (this.#timer !== null) {\n this.#timer.cancel();\n this.#timer = null;\n }\n }\n\n /**\n * Restarts the scheduler (stops then starts).\n */\n restart(): void {\n this.stop();\n this.start();\n }\n\n /**\n * Whether the scheduler is currently running.\n */\n get running(): boolean {\n return this.#running;\n }\n\n /**\n * Performs a single cleanup tick.\n * Stops the scheduler if the store is empty.\n */\n #tick(): void {\n const removed = this.#cleanupFn();\n\n // If nothing was removed and nothing remains, stop the scheduler\n // The store will restart the scheduler when new entries are added\n if (removed === 0) {\n this.stop();\n this.#onEmpty?.();\n }\n }\n\n /**\n * Disposes the scheduler, stopping it and releasing all references.\n */\n dispose(): void {\n this.stop();\n }\n}","/**\n * Entry type and factory for safe-memory-layer.\n *\n * Represents a single entry in the memory store with expiration tracking.\n *\n * @module Entry\n */\n\nimport type { InternalEntry, SetOptions } from \"./types.js\";\n\n/**\n * Creates a new internal entry with the given value and options.\n *\n * @param key - The key for this entry.\n * @param value - The value to store.\n * @param options - Optional settings including TTL.\n * @param now - Current timestamp (for testability).\n * @returns A new InternalEntry instance.\n */\nexport function createEntry<K, V>(\n key: K,\n value: V,\n options: SetOptions = {},\n now = Date.now(),\n): InternalEntry<K, V> {\n const ttl = options.ttl;\n const expiresAt = ttl !== undefined ? now + ttl : undefined;\n\n return {\n value,\n key,\n expiresAt,\n lastAccessed: now,\n createdAt: now,\n };\n}\n\n/**\n * Checks whether an entry has expired.\n *\n * @param entry - The entry to check.\n * @param now - Current timestamp (for testability).\n * @returns True if the entry has expired.\n */\nexport function isExpired<K, V>(\n entry: InternalEntry<K, V>,\n now = Date.now(),\n): boolean {\n return entry.expiresAt !== undefined && now >= entry.expiresAt;\n}\n\n/** Global counter for LRU to ensure unique ordering even within same millisecond. */\nlet lruCounter = 0;\n\n/**\n * Updates the last accessed timestamp on an entry.\n * Uses a monotonic counter to ensure unique LRU ordering.\n *\n * @param entry - The entry to update.\n * @param now - Current timestamp (for testability).\n */\nexport function touchEntry<K, V>(\n entry: InternalEntry<K, V>,\n now = Date.now(),\n): void {\n entry.lastAccessed = now * 10000 + (lruCounter++ % 10000);\n}\n\n/**\n * Calculates the remaining TTL in milliseconds.\n *\n * @param entry - The entry to check.\n * @param now - Current timestamp (for testability).\n * @returns Remaining milliseconds, or undefined if no expiration.\n */\nexport function getRemainingTTL<K, V>(\n entry: InternalEntry<K, V>,\n now = Date.now(),\n): number | undefined {\n if (entry.expiresAt === undefined) return undefined;\n const remaining = entry.expiresAt - now;\n return remaining > 0 ? remaining : 0;\n}","/**\n * Statistics tracking for safe-memory-layer.\n *\n * Tracks store metrics including entry counts, expiration counts,\n * deletion counts, cleanup cycles, uptime, and memory estimates.\n *\n * @module Stats\n */\n\nimport type { StoreStats, CleanupStats } from \"./types.js\";\n\n/**\n * Tracks statistics for a MemoryStore instance.\n *\n * All counters are monotonically increasing (except entries).\n */\nexport class Stats {\n /** Total number of entries that have expired. */\n #expired = 0;\n\n /** Total number of entries that have been deleted. */\n #deleted = 0;\n\n /** Total number of cleanup cycles completed. */\n #cleaned = 0;\n\n /** Timestamp when the store was created. */\n readonly #createdAt: number;\n\n /** Current number of entries in the store. */\n #entries = 0;\n\n /**\n * Creates a new Stats instance.\n *\n * @param createdAt - Timestamp when the store was created.\n */\n constructor(createdAt: number) {\n this.#createdAt = createdAt;\n }\n\n /**\n * Increments the expired counter.\n */\n incrementExpired(): void {\n this.#expired++;\n }\n\n /**\n * Increments the deleted counter.\n */\n incrementDeleted(): void {\n this.#deleted++;\n }\n\n /**\n * Increments the cleaned counter.\n */\n incrementCleaned(): void {\n this.#cleaned++;\n }\n\n /**\n * Sets the current number of entries.\n *\n * @param count - The current entry count.\n */\n setEntries(count: number): void {\n this.#entries = count;\n }\n\n /**\n * Returns the current statistics snapshot.\n *\n * @param currentEntries - Current number of entries (overrides internal count).\n * @returns A snapshot of the current statistics.\n */\n snapshot(currentEntries?: number): StoreStats {\n const uptime = Date.now() - this.#createdAt;\n const entries = currentEntries ?? this.#entries;\n\n return {\n entries,\n expired: this.#expired,\n deleted: this.#deleted,\n cleaned: this.#cleaned,\n uptime,\n memoryEstimate: this.estimateMemory(entries),\n };\n }\n\n /**\n * Creates a cleanup stats object.\n *\n * @param removed - Number of entries removed.\n * @param totalBefore - Total entries before cleanup.\n * @param totalAfter - Total entries after cleanup.\n * @param duration - Duration of cleanup in ms.\n * @returns A CleanupStats object.\n */\n createCleanupStats(\n removed: number,\n totalBefore: number,\n totalAfter: number,\n duration: number,\n ): CleanupStats {\n return {\n removed,\n totalBefore,\n totalAfter,\n duration,\n };\n }\n\n /**\n * Estimates memory usage based on entry count.\n * This is a rough approximation for monitoring purposes.\n *\n * @param entryCount - Number of entries.\n * @returns Estimated memory usage in bytes.\n */\n estimateMemory(entryCount: number): number {\n // Rough estimate: each entry is approximately 200 bytes\n // This includes the key, value reference, and metadata\n // Actual memory usage varies based on key/value sizes\n return entryCount * 200;\n }\n\n /**\n * Resets all statistics.\n */\n reset(): void {\n this.#expired = 0;\n this.#deleted = 0;\n this.#cleaned = 0;\n this.#entries = 0;\n }\n}","/**\n * Event handling utilities for safe-memory-layer.\n *\n * Provides type-safe event emission with error isolation.\n * All callbacks are wrapped to prevent crashes from propagating.\n *\n * @module Events\n */\n\nimport type { CleanupStats } from \"./types.js\";\n\n/**\n * Event types emitted by the MemoryStore.\n */\nexport interface StoreEvents<K, V> {\n /** Emitted when an entry expires. */\n expire: [key: K, value: V];\n /** Emitted when an entry is deleted. */\n delete: [key: K, value: V];\n /** Emitted after a cleanup cycle completes. */\n cleanup: [stats: CleanupStats];\n}\n\n/**\n * Type-safe event emitter for store events.\n * All callbacks are error-isolated.\n */\nexport class EventEmitter<K, V> {\n /** Registered event listeners. */\n #listeners: {\n expire?: Array<(key: K, value: V) => void>;\n delete?: Array<(key: K, value: V) => void>;\n cleanup?: Array<(stats: CleanupStats) => void>;\n } = {};\n\n /**\n * Registers a listener for the expire event.\n *\n * @param callback - Function to call when an entry expires.\n */\n onExpire(callback: (key: K, value: V) => void): void {\n this.#listeners.expire ??= [];\n this.#listeners.expire.push(callback);\n }\n\n /**\n * Registers a listener for the delete event.\n *\n * @param callback - Function to call when an entry is deleted.\n */\n onDelete(callback: (key: K, value: V) => void): void {\n this.#listeners.delete ??= [];\n this.#listeners.delete.push(callback);\n }\n\n /**\n * Registers a listener for the cleanup event.\n *\n * @param callback - Function to call after cleanup completes.\n */\n onCleanup(callback: (stats: CleanupStats) => void): void {\n this.#listeners.cleanup ??= [];\n this.#listeners.cleanup.push(callback);\n }\n\n /**\n * Emits an expire event to all registered listeners.\n * Errors in callbacks are isolated and logged.\n *\n * @param key - The key of the expired entry.\n * @param value - The value of the expired entry.\n */\n emitExpire(key: K, value: V): void {\n const listeners = this.#listeners.expire;\n if (listeners === undefined || listeners.length === 0) return;\n\n for (const callback of listeners) {\n try {\n callback(key, value);\n } catch {\n // Isolate errors - never let callback errors crash the library\n }\n }\n }\n\n /**\n * Emits a delete event to all registered listeners.\n * Errors in callbacks are isolated and logged.\n *\n * @param key - The key of the deleted entry.\n * @param value - The value of the deleted entry.\n */\n emitDelete(key: K, value: V): void {\n const listeners = this.#listeners.delete;\n if (listeners === undefined || listeners.length === 0) return;\n\n for (const callback of listeners) {\n try {\n callback(key, value);\n } catch {\n // Isolate errors - never let callback errors crash the library\n }\n }\n }\n\n /**\n * Emits a cleanup event to all registered listeners.\n * Errors in callbacks are isolated and logged.\n *\n * @param stats - The cleanup statistics.\n */\n emitCleanup(stats: CleanupStats): void {\n const listeners = this.#listeners.cleanup;\n if (listeners === undefined || listeners.length === 0) return;\n\n for (const callback of listeners) {\n try {\n callback(stats);\n } catch {\n // Isolate errors - never let callback errors crash the library\n }\n }\n }\n\n /**\n * Removes all event listeners.\n */\n removeAllListeners(): void {\n this.#listeners = {};\n }\n\n /**\n * Disposes the event emitter, removing all listeners.\n */\n dispose(): void {\n this.removeAllListeners();\n }\n}","/**\n * Feature detection utilities for safe-memory-layer.\n *\n * Detects available platform features and provides graceful fallbacks.\n *\n * @module utils/featureDetection\n */\n\nimport type { FeatureSupport } from \"../types.js\";\n\n/**\n * Detects which platform features are available.\n * This is called once at module load time and cached.\n */\nexport function detectFeatures(): FeatureSupport {\n return {\n finalizationRegistry:\n typeof globalThis.FinalizationRegistry !== \"undefined\",\n };\n}\n\n/** Cached feature support flags. */\nlet cachedFeatures: FeatureSupport | undefined;\n\n/**\n * Returns the cached feature support flags, detecting them on first call.\n */\nexport function getFeatures(): FeatureSupport {\n if (cachedFeatures === undefined) {\n cachedFeatures = detectFeatures();\n }\n return cachedFeatures;\n}\n\n/**\n * Creates a FinalizationRegistry if available, otherwise returns null.\n * Never throws if FinalizationRegistry is unavailable.\n */\nexport function createSafeFinalizationRegistry<T>(\n cleanup: (heldValue: T) => void,\n): FinalizationRegistry<T> | null {\n const features = getFeatures();\n if (features.finalizationRegistry) {\n try {\n return new FinalizationRegistry(cleanup);\n } catch {\n // Graceful fallback if construction fails\n return null;\n }\n }\n return null;\n}","/**\n * Main MemoryStore implementation for safe-memory-layer.\n *\n * Provides a secure, lightweight, dependency-free abstraction for storing\n * temporary in-memory data with TTL support, automatic cleanup, and\n * memory leak prevention.\n *\n * @module MemoryStore\n */\n\nimport { Scheduler } from \"./Scheduler.js\";\nimport { createEntry, isExpired, touchEntry } from \"./Entry.js\";\nimport { Stats } from \"./Stats.js\";\nimport { EventEmitter } from \"./Events.js\";\nimport { createTimer } from \"./utils/timer.js\";\nimport { createSafeFinalizationRegistry } from \"./utils/featureDetection.js\";\nimport type {\n InternalEntry,\n InternalStoreOptions,\n MemoryStoreOptions,\n SetOptions,\n StoreStats,\n} from \"./types.js\";\n\n/** Default cleanup interval in milliseconds (10 seconds). */\nconst DEFAULT_CLEANUP_INTERVAL = 10_000;\n\n/** Default auto-dispose delay in milliseconds (60 seconds). */\nconst DEFAULT_AUTO_DISPOSE_DELAY = 60_000;\n\n/**\n * A secure, high-performance in-memory store with TTL support,\n * automatic cleanup, and memory leak prevention.\n *\n * @typeParam K - The type of keys stored in the map.\n * @typeParam V - The type of values stored in the map.\n *\n * @example\n * ```ts\n * const store = new MemoryStore<string, User>();\n *\n * // Set with TTL\n * store.set(\"user:123\", user, { ttl: 60000 });\n *\n * // Get\n * const user = store.get(\"user:123\");\n * ```\n */\nexport class MemoryStore<K, V> implements Iterable<[K, V]> {\n /** Internal map of entries. */\n #map = new Map<K, InternalEntry<K, V>>();\n\n /** Store configuration options. */\n readonly #options: InternalStoreOptions<K, V>;\n\n /** Statistics tracker. */\n readonly #stats: Stats;\n\n /** Event emitter for store events. */\n readonly #events: EventEmitter<K, V>;\n\n /** Cleanup scheduler. */\n #scheduler: Scheduler;\n\n /** Auto-dispose timer handle. */\n #autoDisposeTimer: ReturnType<typeof createTimer> | null = null;\n\n /** Whether the store has been disposed. */\n #disposed = false;\n\n /** Timestamp when the store was created. */\n readonly #createdAt: number;\n\n /** Timestamp when the store became empty (for auto-dispose). */\n #emptySince: number | null = null; // Used for auto-dispose timing\n\n /**\n * Creates a new MemoryStore instance.\n *\n * @param options - Configuration options for the store.\n *\n * @example\n * ```ts\n * const store = new MemoryStore({\n * defaultTTL: 60000,\n * cleanupInterval: 5000,\n * autoCleanup: true,\n * maxEntries: 1000,\n * maxEntriesStrategy: \"LRU\"\n * });\n * ```\n */\n constructor(options: MemoryStoreOptions<K, V> = {}) {\n const opts = options;\n this.#options = {\n defaultTTL: opts.defaultTTL,\n cleanupInterval: opts.cleanupInterval ?? DEFAULT_CLEANUP_INTERVAL,\n autoCleanup: opts.autoCleanup ?? true,\n autoDispose: opts.autoDispose ?? false,\n autoDisposeDelay: opts.autoDisposeDelay ?? DEFAULT_AUTO_DISPOSE_DELAY,\n maxEntries: opts.maxEntries,\n maxEntriesStrategy: opts.maxEntriesStrategy ?? \"reject\",\n onExpire: opts.onExpire,\n onDelete: opts.onDelete,\n onCleanup: opts.onCleanup,\n };\n\n this.#createdAt = Date.now();\n this.#stats = new Stats(this.#createdAt);\n this.#events = new EventEmitter<K, V>();\n\n // Wire up event callbacks\n if (this.#options.onExpire !== undefined) {\n this.#events.onExpire(this.#options.onExpire);\n }\n if (this.#options.onDelete !== undefined) {\n this.#events.onDelete(this.#options.onDelete);\n }\n if (this.#options.onCleanup !== undefined) {\n this.#events.onCleanup(this.#options.onCleanup);\n }\n\n // Create scheduler\n this.#scheduler = new Scheduler(\n () => this.#runCleanup(),\n this.#options.cleanupInterval,\n () => this.#onSchedulerEmpty(),\n );\n\n // Start scheduler if auto-cleanup is enabled\n if (this.#options.autoCleanup) {\n this.#scheduler.start();\n }\n\n // Set up FinalizationRegistry if available (for leak detection)\n this.#setupFinalizationRegistry();\n }\n\n /**\n * Sets up FinalizationRegistry for weak reference tracking.\n * This is used only for monitoring, not for core functionality.\n */\n #setupFinalizationRegistry(): void {\n // We don't rely on FinalizationRegistry for correctness,\n // but we can use it to detect potential leaks\n const registry = createSafeFinalizationRegistry<K>(() => {\n // This is called when a key is garbage collected\n // We don't take action here, just log for debugging\n });\n\n if (registry !== null) {\n // Store registry reference to prevent it from being collected\n (this as unknown as { _registry: FinalizationRegistry<K> })._registry =\n registry;\n }\n }\n\n /**\n * Stores a value with the given key.\n *\n * @param key - The key to store the value under.\n * @param value - The value to store.\n * @param options - Optional settings (e.g., TTL).\n * @returns True if the value was stored, false if rejected (e.g., max entries reached).\n *\n * @example\n * ```ts\n * store.set(\"token\", token, { ttl: 60000 });\n * ```\n */\n set(key: K, value: V, options: SetOptions = {}): boolean {\n this.#checkDisposed();\n this.#clearAutoDisposeTimer();\n\n // Check max entries limit\n if (this.#options.maxEntries !== undefined && !this.#map.has(key)) {\n if (this.#map.size >= this.#options.maxEntries!) {\n const strategy = this.#options.maxEntriesStrategy;\n if (strategy === \"reject\") {\n return false;\n }\n\n if (strategy === \"FIFO\") {\n const firstKey = this.#map.keys().next().value;\n if (firstKey !== undefined) {\n this.delete(firstKey);\n }\n } else if (strategy === \"LRU\") {\n // Find and evict the least recently used entry\n let lruKey: K | undefined;\n let lruTime = Infinity;\n for (const [k, entry] of this.#map) {\n if (entry.lastAccessed < lruTime) {\n lruTime = entry.lastAccessed;\n lruKey = k;\n }\n }\n if (lruKey !== undefined) {\n this.delete(lruKey);\n }\n }\n }\n }\n\n const now = Date.now();\n const ttl = options.ttl ?? this.#options.defaultTTL;\n const entry = createEntry(\n key,\n value,\n ttl !== undefined ? { ttl } : {},\n now,\n );\n\n this.#map.set(key, entry);\n\n // Start scheduler if not running\n if (!this.#scheduler.running) {\n this.#scheduler.start();\n }\n\n return true;\n }\n\n /**\n * Retrieves a value by key.\n *\n * @param key - The key to look up.\n * @returns The value if found and not expired, undefined otherwise.\n *\n * @example\n * ```ts\n * const user = store.get(\"user:123\");\n * ```\n */\n get(key: K): V | undefined {\n this.#checkDisposed();\n\n const entry = this.#map.get(key);\n if (entry === undefined) return undefined;\n\n // Lazy expiration check\n if (isExpired(entry)) {\n // Remove expired entry\n this.#map.delete(key);\n this.#stats.incrementExpired();\n this.#events.emitExpire(entry.key, entry.value);\n return undefined;\n }\n\n // Update last accessed time for LRU\n touchEntry(entry);\n return entry.value;\n }\n\n /**\n * Checks if a key exists in the store and is not expired.\n *\n * @param key - The key to check.\n * @returns True if the key exists and is not expired.\n */\n has(key: K): boolean {\n this.#checkDisposed();\n\n const entry = this.#map.get(key);\n if (entry === undefined) return false;\n\n // Lazy expiration check\n if (isExpired(entry)) {\n // Remove expired entry\n this.#map.delete(key);\n this.#stats.incrementExpired();\n this.#events.emitExpire(entry.key, entry.value);\n return false;\n }\n\n return true;\n }\n\n /**\n * Deletes a value by key.\n *\n * @param key - The key to delete.\n * @returns True if the key was present, false otherwise.\n */\n delete(key: K): boolean {\n this.#checkDisposed();\n\n const entry = this.#map.get(key);\n if (entry === undefined) return false;\n\n this.#map.delete(key);\n this.#stats.incrementDeleted();\n this.#events.emitDelete(entry.key, entry.value);\n\n // Check if store is now empty\n if (this.#map.size === 0) {\n this.#onEmpty();\n }\n\n return true;\n }\n\n /**\n * Removes all entries from the store.\n */\n clear(): void {\n this.#checkDisposed();\n\n // Emit delete events for all entries\n for (const entry of this.#map.values()) {\n this.#events.emitDelete(entry.key, entry.value);\n this.#stats.incrementDeleted();\n }\n\n this.#map.clear();\n this.#onEmpty();\n }\n\n /**\n * Returns the number of entries in the store.\n * Excludes expired entries.\n */\n get size(): number {\n if (this.#disposed) return 0;\n\n // Count non-expired entries\n let count = 0;\n const now = Date.now();\n for (const entry of this.#map.values()) {\n if (!isExpired(entry, now)) {\n count++;\n }\n }\n return count;\n }\n\n /**\n * Returns an iterator of all keys.\n */\n keys(): IterableIterator<K> {\n this.#checkDisposed();\n return this.#map.keys();\n }\n\n /**\n * Returns an iterator of all values.\n */\n values(): IterableIterator<V> {\n this.#checkDisposed();\n return this.#getNonExpiredValues();\n }\n\n /**\n * Returns an iterator of all [key, value] pairs.\n */\n entries(): IterableIterator<[K, V]> {\n this.#checkDisposed();\n return this.#getNonExpiredEntries();\n }\n\n /**\n * Returns an iterator for the store (for...of support).\n */\n [Symbol.iterator](): IterableIterator<[K, V]> {\n return this.entries();\n }\n\n /**\n * Returns an iterator of non-expired values.\n */\n *#getNonExpiredValues(): IterableIterator<V> {\n const now = Date.now();\n for (const entry of this.#map.values()) {\n if (!isExpired(entry, now)) {\n yield entry.value;\n }\n }\n }\n\n /**\n * Returns an iterator of non-expired entries.\n */\n *#getNonExpiredEntries(): IterableIterator<[K, V]> {\n const now = Date.now();\n for (const entry of this.#map.values()) {\n if (!isExpired(entry, now)) {\n yield [entry.key, entry.value];\n }\n }\n }\n\n /**\n * Runs a cleanup cycle, removing all expired entries.\n *\n * @returns The number of entries removed.\n */\n cleanup(): number {\n this.#checkDisposed();\n return this.#runCleanup();\n }\n\n /**\n * Runs the cleanup cycle.\n * This is called by the scheduler and can be called manually.\n *\n * @returns The number of entries removed.\n */\n #runCleanup(): number {\n const startTime = Date.now();\n const totalBefore = this.#map.size;\n let removed = 0;\n const now = Date.now();\n\n // Iterate and remove expired entries\n for (const [entryKey, entry] of this.#map) {\n if (isExpired(entry, now)) {\n this.#map.delete(entryKey);\n this.#stats.incrementExpired();\n this.#events.emitExpire(entry.key, entry.value);\n removed++;\n }\n }\n\n // Use emptySince to track when store became empty\n if (this.#map.size === 0 && this.#emptySince !== null) {\n // Store is empty, auto-dispose timer may be running\n }\n\n const totalAfter = this.#map.size;\n const duration = Date.now() - startTime;\n\n this.#stats.incrementCleaned();\n\n // Emit cleanup event\n const cleanupStats = this.#stats.createCleanupStats(\n removed,\n totalBefore,\n totalAfter,\n duration,\n );\n this.#events.emitCleanup(cleanupStats);\n\n // Check if store is empty\n if (this.#map.size === 0) {\n this.#onEmpty();\n }\n\n return removed;\n }\n\n /**\n * Compacts the store by removing expired entries.\n * Alias for cleanup().\n */\n compact(): number {\n return this.cleanup();\n }\n\n /**\n * Returns statistics about the store.\n *\n * @returns A snapshot of the current statistics.\n */\n stats(): StoreStats {\n this.#checkDisposed();\n return this.#stats.snapshot(this.#map.size);\n }\n\n /**\n * Disposes the store, stopping all timers and releasing references.\n * After disposal, the store cannot be used.\n */\n dispose(): void {\n if (this.#disposed) return;\n\n this.#disposed = true;\n\n // Stop scheduler\n this.#scheduler.dispose();\n\n // Stop auto-dispose timer\n this.#clearAutoDisposeTimer();\n\n // Clear all entries\n this.#map.clear();\n\n // Dispose event emitter\n this.#events.dispose();\n\n // Remove FinalizationRegistry reference\n delete (this as unknown as { _registry?: FinalizationRegistry<K> })._registry;\n }\n\n /**\n * Checks if the store has been disposed.\n */\n get disposed(): boolean {\n return this.#disposed;\n }\n\n /**\n * Called when the store becomes empty.\n */\n #onEmpty(): void {\n this.#emptySince = Date.now();\n\n // Start auto-dispose timer if enabled\n if (this.#options.autoDispose) {\n this.#startAutoDisposeTimer();\n }\n }\n\n /**\n * Starts the auto-dispose timer.\n */\n #startAutoDisposeTimer(): void {\n this.#clearAutoDisposeTimer();\n\n this.#autoDisposeTimer = createTimer(() => {\n // Only dispose if still empty\n if (this.#map.size === 0 && !this.#disposed) {\n this.dispose();\n }\n }, this.#options.autoDisposeDelay);\n }\n\n /**\n * Clears the auto-dispose timer.\n */\n #clearAutoDisposeTimer(): void {\n if (this.#autoDisposeTimer !== null) {\n this.#autoDisposeTimer.cancel();\n this.#autoDisposeTimer = null;\n }\n this.#emptySince = null;\n }\n\n /**\n * Called when the scheduler finds the store empty.\n */\n #onSchedulerEmpty(): void {\n // Scheduler stopped itself\n // This is called when cleanup finds the store empty\n }\n\n /**\n * Checks if the store has been disposed and throws if so.\n */\n #checkDisposed(): void {\n if (this.#disposed) {\n throw new Error(\"MemoryStore has been disposed\");\n }\n }\n}","/**\n * WeakMemoryStore implementation for safe-memory-layer.\n *\n * Provides an object-only storage layer using WeakMap internally.\n * Objects disappear automatically after garbage collection.\n * No iteration, no manual cleanup required.\n *\n * @module WeakMemoryStore\n */\n\nimport type { WeakMemoryStoreOptions } from \"./types.js\";\n\n/**\n * A memory store that uses WeakMap for object-only storage.\n *\n * Keys must be objects. Values are held weakly and will be\n * automatically garbage collected when no longer referenced elsewhere.\n *\n * **Limitations:**\n * - Keys must be objects (not primitives)\n * - No iteration support (WeakMap is not iterable)\n * - No size property\n * - No TTL support\n * - No automatic cleanup needed\n *\n * @typeParam V - The type of values stored in the map.\n *\n * @example\n * ```ts\n * const weakStore = new WeakMemoryStore<User>();\n *\n * const user = { id: 1, name: \"Alice\" };\n * weakStore.set(user, userData);\n *\n * // When 'user' is no longer referenced, the entry is automatically removed\n * ```\n */\nexport class WeakMemoryStore<V> {\n /** Internal WeakMap for storage. */\n #map = new WeakMap<object, V>();\n\n /** Store configuration options. */\n readonly #options: WeakMemoryStoreOptions<V>;\n\n /** Statistics tracker. */\n #deleted = 0;\n\n /** Timestamp when the store was created. */\n readonly #createdAt: number;\n\n /** Whether the store has been disposed. */\n #disposed = false;\n\n /**\n * Creates a new WeakMemoryStore instance.\n *\n * @param options - Configuration options for the store.\n *\n * @example\n * ```ts\n * const store = new WeakMemoryStore({\n * onDelete: (value) => console.log(\"Deleted:\", value)\n * });\n * ```\n */\n constructor(options: WeakMemoryStoreOptions<V> = {}) {\n this.#options = options;\n this.#createdAt = Date.now();\n }\n\n /**\n * Stores a value with the given object key.\n *\n * @param key - The object key to store the value under. Must be an object.\n * @param value - The value to store.\n * @returns True if the value was stored.\n *\n * @example\n * ```ts\n * const obj = { id: 1 };\n * weakStore.set(obj, { name: \"Alice\" });\n * ```\n */\n set(key: object, value: V): boolean {\n this.#checkDisposed();\n\n // Type guard: ensure key is an object\n if (typeof key !== \"object\" || key === null) {\n throw new TypeError(\"WeakMemoryStore keys must be objects\");\n }\n\n this.#map.set(key, value);\n return true;\n }\n\n /**\n * Retrieves a value by object key.\n *\n * @param key - The object key to look up.\n * @returns The value if found, undefined otherwise.\n *\n * @example\n * ```ts\n * const user = weakStore.get(obj);\n * ```\n */\n get(key: object): V | undefined {\n this.#checkDisposed();\n\n if (typeof key !== \"object\" || key === null) {\n return undefined;\n }\n\n return this.#map.get(key);\n }\n\n /**\n * Checks if a key exists in the store.\n *\n * @param key - The object key to check.\n * @returns True if the key exists.\n */\n has(key: object): boolean {\n this.#checkDisposed();\n\n if (typeof key !== \"object\" || key === null) {\n return false;\n }\n\n return this.#map.has(key);\n }\n\n /**\n * Deletes a value by object key.\n *\n * @param key - The object key to delete.\n * @returns True if the key was present, false otherwise.\n */\n delete(key: object): boolean {\n this.#checkDisposed();\n\n if (typeof key !== \"object\" || key === null) {\n return false;\n }\n\n const value = this.#map.get(key);\n if (value === undefined) return false;\n\n this.#map.delete(key);\n this.#deleted++;\n\n // Emit delete event\n if (this.#options.onDelete !== undefined) {\n try {\n this.#options.onDelete(value);\n } catch {\n // Isolate errors from callbacks\n }\n }\n\n return true;\n }\n\n /**\n * Removes all entries from the store.\n * Note: This does not prevent garbage collection of the keys.\n */\n clear(): void {\n this.#checkDisposed();\n\n // We cannot iterate over WeakMap to emit events\n // Just clear the map\n this.#map = new WeakMap<object, V>();\n this.#deleted++;\n }\n\n /**\n * Returns the number of deleted entries.\n * Note: Cannot return current size (WeakMap limitation).\n */\n get deleted(): number {\n return this.#deleted;\n }\n\n /**\n * Returns statistics about the store.\n * Note: entries count is not available for WeakMap.\n *\n * @returns A snapshot of the current statistics.\n */\n stats(): {\n deleted: number;\n uptime: number;\n } {\n this.#checkDisposed();\n\n return {\n deleted: this.#deleted,\n uptime: Date.now() - this.#createdAt,\n };\n }\n\n /**\n * Disposes the store, releasing the WeakMap reference.\n * After disposal, the store cannot be used.\n */\n dispose(): void {\n if (this.#disposed) return;\n\n this.#disposed = true;\n this.#map = new WeakMap<object, V>();\n }\n\n /**\n * Checks if the store has been disposed.\n */\n get disposed(): boolean {\n return this.#disposed;\n }\n\n /**\n * Checks if the store has been disposed and throws if so.\n */\n #checkDisposed(): void {\n if (this.#disposed) {\n throw new Error(\"WeakMemoryStore has been disposed\");\n }\n }\n}"]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "safe-memory-layer",
3
+ "version": "1.0.0",
4
+ "description": "Secure, lightweight, dependency-free in-memory storage with TTL support, automatic cleanup, and memory leak prevention for Node.js and browsers",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "src",
24
+ "README.md",
25
+ "LICENSE"
26
+ ],
27
+ "scripts": {
28
+ "build": "tsup",
29
+ "dev": "tsup --watch",
30
+ "test": "vitest run",
31
+ "test:watch": "vitest",
32
+ "test:coverage": "vitest run --coverage",
33
+ "lint": "tsc --noEmit",
34
+ "prepublishOnly": "npm run build",
35
+ "docs": "typedoc --out docs src/index.ts"
36
+ },
37
+ "keywords": [
38
+ "memory-cache",
39
+ "cache",
40
+ "ttl",
41
+ "memory-store",
42
+ "weakmap",
43
+ "lru",
44
+ "typescript",
45
+ "memory-safe",
46
+ "zero-dependency"
47
+ ],
48
+ "license": "MIT",
49
+ "engines": {
50
+ "node": ">=20"
51
+ },
52
+ "devDependencies": {
53
+ "tsup": "^8.4.0",
54
+ "typescript": "^5.7.0",
55
+ "vitest": "^3.0.0"
56
+ }
57
+ }
package/src/Entry.ts ADDED
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Entry type and factory for safe-memory-layer.
3
+ *
4
+ * Represents a single entry in the memory store with expiration tracking.
5
+ *
6
+ * @module Entry
7
+ */
8
+
9
+ import type { InternalEntry, SetOptions } from "./types.js";
10
+
11
+ /**
12
+ * Creates a new internal entry with the given value and options.
13
+ *
14
+ * @param key - The key for this entry.
15
+ * @param value - The value to store.
16
+ * @param options - Optional settings including TTL.
17
+ * @param now - Current timestamp (for testability).
18
+ * @returns A new InternalEntry instance.
19
+ */
20
+ export function createEntry<K, V>(
21
+ key: K,
22
+ value: V,
23
+ options: SetOptions = {},
24
+ now = Date.now(),
25
+ ): InternalEntry<K, V> {
26
+ const ttl = options.ttl;
27
+ const expiresAt = ttl !== undefined ? now + ttl : undefined;
28
+
29
+ return {
30
+ value,
31
+ key,
32
+ expiresAt,
33
+ lastAccessed: now,
34
+ createdAt: now,
35
+ };
36
+ }
37
+
38
+ /**
39
+ * Checks whether an entry has expired.
40
+ *
41
+ * @param entry - The entry to check.
42
+ * @param now - Current timestamp (for testability).
43
+ * @returns True if the entry has expired.
44
+ */
45
+ export function isExpired<K, V>(
46
+ entry: InternalEntry<K, V>,
47
+ now = Date.now(),
48
+ ): boolean {
49
+ return entry.expiresAt !== undefined && now >= entry.expiresAt;
50
+ }
51
+
52
+ /** Global counter for LRU to ensure unique ordering even within same millisecond. */
53
+ let lruCounter = 0;
54
+
55
+ /**
56
+ * Updates the last accessed timestamp on an entry.
57
+ * Uses a monotonic counter to ensure unique LRU ordering.
58
+ *
59
+ * @param entry - The entry to update.
60
+ * @param now - Current timestamp (for testability).
61
+ */
62
+ export function touchEntry<K, V>(
63
+ entry: InternalEntry<K, V>,
64
+ now = Date.now(),
65
+ ): void {
66
+ entry.lastAccessed = now * 10000 + (lruCounter++ % 10000);
67
+ }
68
+
69
+ /**
70
+ * Calculates the remaining TTL in milliseconds.
71
+ *
72
+ * @param entry - The entry to check.
73
+ * @param now - Current timestamp (for testability).
74
+ * @returns Remaining milliseconds, or undefined if no expiration.
75
+ */
76
+ export function getRemainingTTL<K, V>(
77
+ entry: InternalEntry<K, V>,
78
+ now = Date.now(),
79
+ ): number | undefined {
80
+ if (entry.expiresAt === undefined) return undefined;
81
+ const remaining = entry.expiresAt - now;
82
+ return remaining > 0 ? remaining : 0;
83
+ }
package/src/Events.ts ADDED
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Event handling utilities for safe-memory-layer.
3
+ *
4
+ * Provides type-safe event emission with error isolation.
5
+ * All callbacks are wrapped to prevent crashes from propagating.
6
+ *
7
+ * @module Events
8
+ */
9
+
10
+ import type { CleanupStats } from "./types.js";
11
+
12
+ /**
13
+ * Event types emitted by the MemoryStore.
14
+ */
15
+ export interface StoreEvents<K, V> {
16
+ /** Emitted when an entry expires. */
17
+ expire: [key: K, value: V];
18
+ /** Emitted when an entry is deleted. */
19
+ delete: [key: K, value: V];
20
+ /** Emitted after a cleanup cycle completes. */
21
+ cleanup: [stats: CleanupStats];
22
+ }
23
+
24
+ /**
25
+ * Type-safe event emitter for store events.
26
+ * All callbacks are error-isolated.
27
+ */
28
+ export class EventEmitter<K, V> {
29
+ /** Registered event listeners. */
30
+ #listeners: {
31
+ expire?: Array<(key: K, value: V) => void>;
32
+ delete?: Array<(key: K, value: V) => void>;
33
+ cleanup?: Array<(stats: CleanupStats) => void>;
34
+ } = {};
35
+
36
+ /**
37
+ * Registers a listener for the expire event.
38
+ *
39
+ * @param callback - Function to call when an entry expires.
40
+ */
41
+ onExpire(callback: (key: K, value: V) => void): void {
42
+ this.#listeners.expire ??= [];
43
+ this.#listeners.expire.push(callback);
44
+ }
45
+
46
+ /**
47
+ * Registers a listener for the delete event.
48
+ *
49
+ * @param callback - Function to call when an entry is deleted.
50
+ */
51
+ onDelete(callback: (key: K, value: V) => void): void {
52
+ this.#listeners.delete ??= [];
53
+ this.#listeners.delete.push(callback);
54
+ }
55
+
56
+ /**
57
+ * Registers a listener for the cleanup event.
58
+ *
59
+ * @param callback - Function to call after cleanup completes.
60
+ */
61
+ onCleanup(callback: (stats: CleanupStats) => void): void {
62
+ this.#listeners.cleanup ??= [];
63
+ this.#listeners.cleanup.push(callback);
64
+ }
65
+
66
+ /**
67
+ * Emits an expire event to all registered listeners.
68
+ * Errors in callbacks are isolated and logged.
69
+ *
70
+ * @param key - The key of the expired entry.
71
+ * @param value - The value of the expired entry.
72
+ */
73
+ emitExpire(key: K, value: V): void {
74
+ const listeners = this.#listeners.expire;
75
+ if (listeners === undefined || listeners.length === 0) return;
76
+
77
+ for (const callback of listeners) {
78
+ try {
79
+ callback(key, value);
80
+ } catch {
81
+ // Isolate errors - never let callback errors crash the library
82
+ }
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Emits a delete event to all registered listeners.
88
+ * Errors in callbacks are isolated and logged.
89
+ *
90
+ * @param key - The key of the deleted entry.
91
+ * @param value - The value of the deleted entry.
92
+ */
93
+ emitDelete(key: K, value: V): void {
94
+ const listeners = this.#listeners.delete;
95
+ if (listeners === undefined || listeners.length === 0) return;
96
+
97
+ for (const callback of listeners) {
98
+ try {
99
+ callback(key, value);
100
+ } catch {
101
+ // Isolate errors - never let callback errors crash the library
102
+ }
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Emits a cleanup event to all registered listeners.
108
+ * Errors in callbacks are isolated and logged.
109
+ *
110
+ * @param stats - The cleanup statistics.
111
+ */
112
+ emitCleanup(stats: CleanupStats): void {
113
+ const listeners = this.#listeners.cleanup;
114
+ if (listeners === undefined || listeners.length === 0) return;
115
+
116
+ for (const callback of listeners) {
117
+ try {
118
+ callback(stats);
119
+ } catch {
120
+ // Isolate errors - never let callback errors crash the library
121
+ }
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Removes all event listeners.
127
+ */
128
+ removeAllListeners(): void {
129
+ this.#listeners = {};
130
+ }
131
+
132
+ /**
133
+ * Disposes the event emitter, removing all listeners.
134
+ */
135
+ dispose(): void {
136
+ this.removeAllListeners();
137
+ }
138
+ }