UNPKG

@kansnpms/storage-pipe

Version:

Browser storage and cookies monitoring - Real-time tracking of localStorage, sessionStorage, cookies, and IndexedDB

824 lines (742 loc) 21.9 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function getDefaultExportFromCjs (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } function getAugmentedNamespace(n) { if (n.__esModule) return n; var f = n.default; if (typeof f == "function") { var a = function a () { if (this instanceof a) { return Reflect.construct(f, arguments, this.constructor); } return f.apply(this, arguments); }; a.prototype = f.prototype; } else a = {}; Object.defineProperty(a, '__esModule', {value: true}); Object.keys(n).forEach(function (k) { var d = Object.getOwnPropertyDescriptor(n, k); Object.defineProperty(a, k, d.get ? d : { enumerable: true, get: function () { return n[k]; } }); }); return a; } var src = {exports: {}}; /** * StorageMonitor - Real-time browser storage and cookies monitoring * * Monitors and streams changes to: * - Cookies * - localStorage * - sessionStorage * - IndexedDB operations */ let StorageMonitor$1 = class StorageMonitor { constructor(options = {}) { this.config = { serverHost: options.serverHost || 'localhost', serverPort: options.serverPort || 3002, sessionId: options.sessionId || this._generateSessionId(), enableCookies: options.enableCookies !== false, enableLocalStorage: options.enableLocalStorage !== false, enableSessionStorage: options.enableSessionStorage !== false, enableIndexedDB: options.enableIndexedDB !== false, pollInterval: options.pollInterval || 1000, // ms ...options }; this.ws = null; this.isConnected = false; this.isMonitoring = false; // Storage state tracking this.previousState = { cookies: new Map(), localStorage: new Map(), sessionStorage: new Map(), indexedDB: new Map() }; // Monitoring intervals this.intervals = new Map(); // Original storage methods (for restoration) this.originalMethods = {}; } /** * Initialize storage monitoring */ async init() { // Initialize Storage Monitor try { // Connect to WebSocket await this._connectWebSocket(); // Start monitoring this._startMonitoring(); // Storage Monitor connected and monitoring return this; } catch (error) { // Storage Monitor initialization failed if (this.config.debug) { // eslint-disable-next-line no-console console.error('❌ Storage Monitor initialization failed:', error); } throw error; } } /** * Stop monitoring and cleanup */ stop() { // Stopping Storage Monitor this.isMonitoring = false; // Clear intervals this.intervals.forEach(interval => clearInterval(interval)); this.intervals.clear(); // Restore original methods this._restoreOriginalMethods(); // Close WebSocket if (this.ws) { this.ws.close(); this.ws = null; } this.isConnected = false; // Storage Monitor stopped } /** * Get current storage state */ getCurrentState() { return { cookies: this._getCurrentCookies(), localStorage: this._getCurrentLocalStorage(), sessionStorage: this._getCurrentSessionStorage(), indexedDB: this._getCurrentIndexedDBInfo() }; } /** * Connect to WebSocket server */ _connectWebSocket() { return new Promise((resolve, reject) => { const wsUrl = `ws://${this.config.serverHost}:${this.config.serverPort}`; this.ws = new WebSocket(wsUrl); this.ws.onopen = () => { this.isConnected = true; // Send initial connection message this._sendMessage({ type: 'storage_connect', sessionId: this.config.sessionId, timestamp: new Date().toISOString(), config: { enableCookies: this.config.enableCookies, enableLocalStorage: this.config.enableLocalStorage, enableSessionStorage: this.config.enableSessionStorage, enableIndexedDB: this.config.enableIndexedDB } }); resolve(); }; this.ws.onerror = error => { if (this.config.debug) { // eslint-disable-next-line no-console console.error('❌ Storage Monitor WebSocket error:', error); } reject(error); }; this.ws.onclose = () => { this.isConnected = false; // Storage Monitor disconnected }; this.ws.onmessage = event => { try { const message = JSON.parse(event.data); this._handleServerMessage(message); } catch (error) { if (this.config.debug) { // eslint-disable-next-line no-console console.error('Error parsing server message:', error); } } }; }); } /** * Start monitoring all enabled storage types */ _startMonitoring() { this.isMonitoring = true; // Initialize previous state with current state to properly detect changes if (this.config.enableCookies) { this.previousState.cookies = this._getCurrentCookies(); } if (this.config.enableLocalStorage) { this.previousState.localStorage = this._getCurrentLocalStorage(); } if (this.config.enableSessionStorage) { this.previousState.sessionStorage = this._getCurrentSessionStorage(); } if (this.config.enableIndexedDB) { this.previousState.indexedDB = new Map(); // IndexedDB state is more complex } // Send initial state this._sendStorageUpdate('initial_state', this.getCurrentState()); if (this.config.enableCookies) { this._startCookieMonitoring(); } if (this.config.enableLocalStorage) { this._startLocalStorageMonitoring(); } if (this.config.enableSessionStorage) { this._startSessionStorageMonitoring(); } if (this.config.enableIndexedDB) { this._startIndexedDBMonitoring(); } } /** * Start cookie monitoring */ _startCookieMonitoring() { // Poll for cookie changes const interval = setInterval(() => { if (!this.isMonitoring) return; const currentCookies = this._getCurrentCookies(); const changes = this._detectCookieChanges(currentCookies); if (changes.hasChanges) { this._sendStorageUpdate('cookies', changes); } }, this.config.pollInterval); this.intervals.set('cookies', interval); } /** * Start localStorage monitoring */ _startLocalStorageMonitoring() { // Intercept localStorage methods this._interceptStorageMethod('localStorage', 'setItem'); this._interceptStorageMethod('localStorage', 'removeItem'); this._interceptStorageMethod('localStorage', 'clear'); // Also poll for external changes const interval = setInterval(() => { if (!this.isMonitoring) return; const currentStorage = this._getCurrentLocalStorage(); const changes = this._detectStorageChanges('localStorage', currentStorage); if (changes.hasChanges) { this._sendStorageUpdate('localStorage', changes); } }, this.config.pollInterval); this.intervals.set('localStorage', interval); } /** * Start sessionStorage monitoring */ _startSessionStorageMonitoring() { // Intercept sessionStorage methods this._interceptStorageMethod('sessionStorage', 'setItem'); this._interceptStorageMethod('sessionStorage', 'removeItem'); this._interceptStorageMethod('sessionStorage', 'clear'); // Also poll for external changes const interval = setInterval(() => { if (!this.isMonitoring) return; const currentStorage = this._getCurrentSessionStorage(); const changes = this._detectStorageChanges('sessionStorage', currentStorage); if (changes.hasChanges) { this._sendStorageUpdate('sessionStorage', changes); } }, this.config.pollInterval); this.intervals.set('sessionStorage', interval); } /** * Start IndexedDB monitoring */ _startIndexedDBMonitoring() { // This is more complex and will be implemented in the next iteration // For now, just detect database existence const interval = setInterval(() => { if (!this.isMonitoring) return; // const currentDB = this._getCurrentIndexedDBInfo(); // TODO: Implement IndexedDB change detection }, this.config.pollInterval * 2); // Less frequent polling for IndexedDB this.intervals.set('indexedDB', interval); } /** * Generate unique session ID */ _generateSessionId() { return `clp_storage_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } /** * Send message to server */ _sendMessage(message) { if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.ws.send(JSON.stringify(message)); } } /** * Send storage update to server */ _sendStorageUpdate(type, data) { this._sendMessage({ type: 'storage_update', subType: type, sessionId: this.config.sessionId, timestamp: new Date().toISOString(), data }); } /** * Handle messages from server */ _handleServerMessage(message) { switch (message.type) { case 'storage_info': // Storage Monitor server info received break; case 'storage_command': this._handleServerCommand(message.data); break; } } /** * Handle commands from server */ _handleServerCommand(command) { switch (command.action) { case 'get_current_state': this._sendStorageUpdate('current_state', this.getCurrentState()); break; case 'clear_storage': this._clearStorage(command.storageType); break; } } /** * Get current cookies */ _getCurrentCookies() { const cookies = new Map(); if (typeof document !== 'undefined' && document.cookie) { document.cookie.split(';').forEach(cookie => { const [name, ...valueParts] = cookie.trim().split('='); if (name) { cookies.set(name, { name, value: valueParts.join('=') || '', domain: window.location.hostname, path: '/', timestamp: new Date().toISOString() }); } }); } return cookies; } /** * Get current localStorage */ _getCurrentLocalStorage() { const storage = new Map(); if (typeof localStorage !== 'undefined') { for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key) { storage.set(key, { key, value: localStorage.getItem(key), timestamp: new Date().toISOString() }); } } } return storage; } /** * Get current sessionStorage */ _getCurrentSessionStorage() { const storage = new Map(); if (typeof sessionStorage !== 'undefined') { for (let i = 0; i < sessionStorage.length; i++) { const key = sessionStorage.key(i); if (key) { storage.set(key, { key, value: sessionStorage.getItem(key), timestamp: new Date().toISOString() }); } } } return storage; } /** * Get current IndexedDB info */ _getCurrentIndexedDBInfo() { // Basic IndexedDB detection - full implementation would be more complex const info = { available: typeof indexedDB !== 'undefined', databases: [], timestamp: new Date().toISOString() }; return info; } /** * Detect cookie changes */ _detectCookieChanges(currentCookies) { const changes = { hasChanges: false, added: [], modified: [], deleted: [], current: Array.from(currentCookies.values()) }; const previousCookies = this.previousState.cookies; // Check for added and modified cookies currentCookies.forEach((cookie, name) => { if (!previousCookies.has(name)) { changes.added.push(cookie); changes.hasChanges = true; } else if (previousCookies.get(name).value !== cookie.value) { changes.modified.push({ ...cookie, oldValue: previousCookies.get(name).value }); changes.hasChanges = true; } }); // Check for deleted cookies previousCookies.forEach((cookie, name) => { if (!currentCookies.has(name)) { changes.deleted.push(cookie); changes.hasChanges = true; } }); // Update previous state this.previousState.cookies = new Map(currentCookies); return changes; } /** * Detect storage changes (localStorage/sessionStorage) */ _detectStorageChanges(storageType, currentStorage) { const changes = { hasChanges: false, added: [], modified: [], deleted: [], current: Array.from(currentStorage.values()) }; const previousStorage = this.previousState[storageType]; // Check for added and modified items currentStorage.forEach((item, key) => { if (!previousStorage.has(key)) { changes.added.push(item); changes.hasChanges = true; } else if (previousStorage.get(key).value !== item.value) { changes.modified.push({ ...item, oldValue: previousStorage.get(key).value }); changes.hasChanges = true; } }); // Check for deleted items previousStorage.forEach((item, key) => { if (!currentStorage.has(key)) { changes.deleted.push(item); changes.hasChanges = true; } }); // Update previous state this.previousState[storageType] = new Map(currentStorage); return changes; } /** * Intercept storage methods for real-time monitoring */ _interceptStorageMethod(storageType, methodName) { // In test environment, we need to use the same localStorage/sessionStorage // that the test is using. Try multiple approaches to find the right one. let storage; // First try: direct global reference (what tests usually use) try { storage = eval(storageType); // localStorage or sessionStorage } catch (e) { // eval failed, try other approaches } // Second try: global object if (!storage && typeof global !== 'undefined' && global[storageType]) { storage = global[storageType]; } // Third try: window object (browser) if (!storage && typeof window !== 'undefined' && window[storageType]) { storage = window[storageType]; } if (!storage) { return; } const originalMethod = storage[methodName]; this.originalMethods[`${storageType}_${methodName}`] = originalMethod; // Check if the property is configurable const descriptor = Object.getOwnPropertyDescriptor(storage, methodName); if (descriptor && descriptor.configurable === false) { return; } storage[methodName] = (...args) => { // Call original method const result = originalMethod.apply(storage, args); // Send immediate update setTimeout(() => { const currentStorage = storageType === 'localStorage' ? this._getCurrentLocalStorage() : this._getCurrentSessionStorage(); const changes = this._detectStorageChanges(storageType, currentStorage); if (changes.hasChanges) { this._sendStorageUpdate(storageType, { ...changes, operation: methodName, args }); } }, 0); return result; }; } /** * Restore original storage methods */ _restoreOriginalMethods() { Object.keys(this.originalMethods).forEach(key => { const [storageType, methodName] = key.split('_'); // Support both browser and test environments let storage; if (typeof window !== 'undefined' && window[storageType]) { storage = window[storageType]; } else if (typeof global !== 'undefined' && global[storageType]) { storage = global[storageType]; } else { // Fallback: try to get from global scope directly storage = eval(storageType); // localStorage or sessionStorage } if (storage && this.originalMethods[key]) { storage[methodName] = this.originalMethods[key]; } }); this.originalMethods = {}; } /** * Clear storage (for server commands) */ _clearStorage(storageType) { switch (storageType) { case 'localStorage': if (typeof localStorage !== 'undefined') { localStorage.clear(); } break; case 'sessionStorage': if (typeof sessionStorage !== 'undefined') { sessionStorage.clear(); } break; case 'cookies': this._clearAllCookies(); break; } } /** * Clear all cookies */ _clearAllCookies() { if (typeof document !== 'undefined') { document.cookie.split(';').forEach(cookie => { const eqPos = cookie.indexOf('='); const name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie; document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/`; }); } } }; // Export for different module systems if (typeof module !== 'undefined' && module.exports) { module.exports = StorageMonitor$1; } else if (typeof window !== 'undefined') { window.StorageMonitor = StorageMonitor$1; } var StorageMonitor$2 = /*#__PURE__*/Object.freeze({ __proto__: null, default: StorageMonitor$1 }); var require$$0 = /*@__PURE__*/getAugmentedNamespace(StorageMonitor$2); /** * Console Log Pipe Storage Monitor * * Real-time browser storage and cookies monitoring for development and debugging. * * Features: * - 🍪 Cookie monitoring with real-time change detection * - 💾 localStorage monitoring with automatic change tracking * - 🔄 sessionStorage monitoring with live updates * - 🗃️ IndexedDB monitoring (basic support) * - 📡 WebSocket-based real-time communication * - 🎯 AI-friendly structured data format * - 🔧 Configurable polling intervals and feature toggles */ // Import main classes const StorageMonitor = require$$0; // Version information const version = '0.1.0'; /** * Main API for Console Log Pipe Storage Monitor */ class ConsoleLogPipeStorage { constructor(options = {}) { this.version = version; this.monitor = null; this.config = { serverHost: 'localhost', serverPort: 3002, enableCookies: true, enableLocalStorage: true, enableSessionStorage: true, enableIndexedDB: true, pollInterval: 1000, autoConnect: true, ...options }; } /** * Initialize storage monitoring */ async init(options = {}) { // Merge options with existing config this.config = { ...this.config, ...options }; // Create monitor instance this.monitor = new StorageMonitor(this.config); if (this.config.autoConnect) { await this.monitor.init(); } return this; } /** * Start monitoring (if not auto-started) */ async start() { if (!this.monitor) { throw new Error('Storage monitor not initialized. Call init() first.'); } if (!this.monitor.isConnected) { await this.monitor.init(); } return this; } /** * Stop monitoring */ stop() { if (this.monitor) { this.monitor.stop(); } return this; } /** * Get current storage state */ getCurrentState() { return this.monitor ? this.monitor.getCurrentState() : null; } /** * Check if monitoring is active */ isMonitoring() { return this.monitor ? this.monitor.isMonitoring : false; } /** * Check if connected to server */ isConnected() { return this.monitor ? this.monitor.isConnected : false; } /** * Get configuration */ getConfig() { return { ...this.config }; } /** * Update configuration */ updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; if (this.monitor) { // Update monitor config Object.assign(this.monitor.config, newConfig); } return this; } /** * Get session information */ getSession() { return { sessionId: this.config.sessionId, serverPort: this.config.serverPort, isConnected: this.isConnected(), isMonitoring: this.isMonitoring(), enabledFeatures: { cookies: this.config.enableCookies, localStorage: this.config.enableLocalStorage, sessionStorage: this.config.enableSessionStorage, indexedDB: this.config.enableIndexedDB } }; } /** * Force a storage state check */ checkNow() { if (this.monitor && this.monitor.isMonitoring) { const currentState = this.monitor.getCurrentState(); this.monitor._sendStorageUpdate('manual_check', currentState); } return this; } } /** * Static factory methods for easy initialization */ ConsoleLogPipeStorage.create = function (options = {}) { return new ConsoleLogPipeStorage(options); }; ConsoleLogPipeStorage.init = async function (options = {}) { const instance = new ConsoleLogPipeStorage(options); await instance.init(); return instance; }; // Version information ConsoleLogPipeStorage.version = version; // Export the main class and utilities src.exports = ConsoleLogPipeStorage; var StorageMonitor_1 = src.exports.StorageMonitor = StorageMonitor; var version_1 = src.exports.version = version; // Default export for ES modules src.exports.default = ConsoleLogPipeStorage; var srcExports = src.exports; var index = /*@__PURE__*/getDefaultExportFromCjs(srcExports); exports.StorageMonitor = StorageMonitor_1; exports.default = index; exports.version = version_1; //# sourceMappingURL=index.js.map