@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
JavaScript
'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