about summary refs log tree commit diff stats
path: root/js/scripting-lang/scripting-harness/core/history.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/scripting-lang/scripting-harness/core/history.js')
-rw-r--r--js/scripting-lang/scripting-harness/core/history.js169
1 files changed, 169 insertions, 0 deletions
diff --git a/js/scripting-lang/scripting-harness/core/history.js b/js/scripting-lang/scripting-harness/core/history.js
new file mode 100644
index 0000000..94ad1b9
--- /dev/null
+++ b/js/scripting-lang/scripting-harness/core/history.js
@@ -0,0 +1,169 @@
+/**
+ * StateHistory - Manages state versioning and metadata
+ * 
+ * @description Provides automatic versioning, rollback, replay, and diffing capabilities
+ * for state management in the scripting harness. Each state change creates a new version
+ * with metadata including timestamp and hash for change detection.
+ * 
+ * Features:
+ * - Automatic version tracking
+ * - Configurable version limits with cleanup
+ * - State diffing between versions
+ * - Rollback and replay capabilities
+ * - Memory-efficient storage with automatic cleanup
+ */
+
+class StateHistory {
+    constructor(maxVersions = 100) {
+        this.versions = new Map();
+        this.maxVersions = maxVersions;
+    }
+
+    /**
+     * Add a new version to the history
+     * 
+     * @param {number} version - Version number
+     * @param {Object} inputState - Input state with metadata wrapper
+     * @param {Object} outputModel - Output model (pure table data)
+     */
+    addVersion(version, inputState, outputModel) {
+        // Store version data
+        this.versions.set(version, {
+            version,
+            timestamp: Date.now(),
+            inputState,
+            outputModel,
+            hash: this.calculateHash(outputModel)
+        });
+
+        // Clean up old versions if needed
+        this.cleanupOldVersions();
+    }
+
+    /**
+     * Get state at specific version
+     * 
+     * @param {number} version - Version number to retrieve
+     * @returns {Object|null} State data or null if version not found
+     */
+    getVersion(version) {
+        const versionData = this.versions.get(version);
+        return versionData ? versionData.outputModel : null;
+    }
+
+    /**
+     * Get all versions with metadata
+     * 
+     * @returns {Array} Array of version metadata objects
+     */
+    getAllVersions() {
+        return Array.from(this.versions.values()).map(v => ({
+            version: v.version,
+            timestamp: v.timestamp,
+            hash: v.hash
+        }));
+    }
+
+    /**
+     * Get diff between two versions
+     * 
+     * @param {number} fromVersion - Starting version
+     * @param {number} toVersion - Ending version
+     * @returns {Object|null} Diff object or null if versions not found
+     */
+    getDiff(fromVersion, toVersion) {
+        const fromState = this.getVersion(fromVersion);
+        const toState = this.getVersion(toVersion);
+        
+        if (!fromState || !toState) {
+            return null;
+        }
+        
+        return {
+            added: this.findAddedProperties(fromState, toState),
+            removed: this.findRemovedProperties(fromState, toState),
+            changed: this.findChangedProperties(fromState, toState)
+        };
+    }
+
+    /**
+     * Clean up old versions to prevent memory leaks
+     */
+    cleanupOldVersions() {
+        if (this.versions.size > this.maxVersions) {
+            const sortedVersions = Array.from(this.versions.keys()).sort();
+            const toDelete = sortedVersions.slice(0, this.versions.size - this.maxVersions);
+            
+            for (const version of toDelete) {
+                this.versions.delete(version);
+            }
+        }
+    }
+
+    /**
+     * Calculate simple hash for change detection
+     * 
+     * @param {Object} state - State object to hash
+     * @returns {number} Hash value
+     */
+    calculateHash(state) {
+        // Simple hash for change detection
+        if (state === undefined || state === null) {
+            return 0;
+        }
+        return JSON.stringify(state).length;
+    }
+
+    /**
+     * Find properties added in the new state
+     * 
+     * @param {Object} fromState - Original state
+     * @param {Object} toState - New state
+     * @returns {Object} Object containing added properties
+     */
+    findAddedProperties(fromState, toState) {
+        const added = {};
+        for (const key in toState) {
+            if (!(key in fromState)) {
+                added[key] = toState[key];
+            }
+        }
+        return added;
+    }
+
+    /**
+     * Find properties removed in the new state
+     * 
+     * @param {Object} fromState - Original state
+     * @param {Object} toState - New state
+     * @returns {Object} Object containing removed properties
+     */
+    findRemovedProperties(fromState, toState) {
+        const removed = {};
+        for (const key in fromState) {
+            if (!(key in toState)) {
+                removed[key] = fromState[key];
+            }
+        }
+        return removed;
+    }
+
+    /**
+     * Find properties changed in the new state
+     * 
+     * @param {Object} fromState - Original state
+     * @param {Object} toState - New state
+     * @returns {Object} Object containing changed properties with from/to values
+     */
+    findChangedProperties(fromState, toState) {
+        const changed = {};
+        for (const key in toState) {
+            if (key in fromState && fromState[key] !== toState[key]) {
+                changed[key] = { from: fromState[key], to: toState[key] };
+            }
+        }
+        return changed;
+    }
+}
+
+export { StateHistory }; 
\ No newline at end of file