From 3774a4265c19537dd67e8833acceb8d99c811b25 Mon Sep 17 00:00:00 2001
From: Rim <info@rimmyscorner.com>
Date: Thu, 17 Apr 2025 06:43:05 -0400
Subject: [PATCH] refactor(serverUtils.js): highly optimise deep clone and json
 processing functions

---
 src/js/serverUtils.js | 138 +++++++++++++++++++++++++-----------------
 1 file changed, 82 insertions(+), 56 deletions(-)

diff --git a/src/js/serverUtils.js b/src/js/serverUtils.js
index a1ebb76..74a9342 100644
--- a/src/js/serverUtils.js
+++ b/src/js/serverUtils.js
@@ -26,40 +26,41 @@ try {
 }
 
 // Optimized replaceJsonKeys function
-const replaceJsonKeys = (obj) => {
-    if (!obj || typeof obj !== 'object') return obj;
-  
-    // Fast-path for arrays
-    if (Array.isArray(obj)) {
-      // Only process array if it has items
-      return obj.length > 0 ? obj.map(replaceJsonKeys) : obj;
+const replaceJsonKeys = (obj, replacements) => {
+  // Handle non-objects early
+  if (!obj || typeof obj !== 'object') return obj;
+
+  // Fast path for arrays
+  if (Array.isArray(obj)) {
+    return obj.length === 0 ?
+        obj
+      : obj.map((item) => replaceJsonKeys(item, replacements));
+  }
+
+  // Fast path for empty objects
+  const keys = Object.keys(obj);
+  if (keys.length === 0) return obj;
+
+  // Process normal objects
+  const newObj = {};
+  for (let i = 0; i < keys.length; i++) {
+    const key = keys[i];
+    const newKey = replacements[key] || key;
+    let value = obj[key];
+
+    // Replace string values if they match a key in replacements
+    if (typeof value === 'string' && replacements[value]) {
+      value = replacements[value];
+    } else if (value && typeof value === 'object') {
+      // Only recurse for objects/arrays
+      value = replaceJsonKeys(value, replacements);
     }
-  
-    const newObj = {};
-    const objKeys = Object.keys(obj);
-    
-    // Fast-path for empty objects
-    if (objKeys.length === 0) return obj;
-    
-    // Cache key check
-    const hasKeyReplacements = Object.keys(keyReplacements).length > 0;
-    
-    for (const key of objKeys) {
-      // Replace key if replacements exist
-      const newKey = hasKeyReplacements && keyReplacements[key] ? keyReplacements[key] : key;
-      let value = obj[key];
-      
-      // Replace string values if needed
-      if (hasKeyReplacements && typeof value === 'string' && keyReplacements[value]) {
-        value = keyReplacements[value];
-      }
-      
-      // Process recursively only if object/array
-      newObj[newKey] = (value && typeof value === 'object') ? replaceJsonKeys(value) : value;
-    }
-  
-    return newObj;
-  };
+
+    newObj[newKey] = value;
+  }
+
+  return newObj;
+};
 
 // Utility regex function
 const sanitizeJsonOutput = (data) => {
@@ -87,27 +88,18 @@ const sanitizeJsonOutput = (data) => {
 // Replace the processJsonOutput function with this more efficient version
 const processJsonOutput = (
   data,
-  options = { sanitize: true, replaceKeys: true }
+  options = {
+    sanitize: true,
+    replaceKeys: true,
+    keyReplacements: {},
+  }
 ) => {
-  // Use a more efficient deep clone approach instead of JSON.parse(JSON.stringify())
-  function deepClone(obj) {
-    if (obj === null || typeof obj !== 'object') {
-      return obj;
-    }
-
-    if (Array.isArray(obj)) {
-      return obj.map((item) => deepClone(item));
-    }
-
-    const clone = {};
-    Object.keys(obj).forEach((key) => {
-      clone[key] = deepClone(obj[key]);
-    });
-
-    return clone;
+  // Early return for null or non-objects
+  if (data === null || typeof data !== 'object') {
+    return data;
   }
 
-  // Create a deep copy of the data to avoid reference issues
+  // Copy data first to avoid reference issues
   let processedData = deepClone(data);
 
   // Apply sanitization if needed
@@ -115,14 +107,48 @@ const processJsonOutput = (
     processedData = sanitizeJsonOutput(processedData);
   }
 
-  // Apply key replacement if needed
-  if (options.replaceKeys) {
-    processedData = replaceJsonKeys(processedData);
+  // Apply key replacement if needed - pass replacements directly
+  if (
+    options.replaceKeys &&
+    Object.keys(options.keyReplacements || {}).length > 0
+  ) {
+    processedData = replaceJsonKeys(processedData, options.keyReplacements);
   }
 
   return processedData;
 };
 
+/**
+ * Optimized deep clone function
+ * @param {any} obj - The object to clone
+ * @returns {any} - The cloned object
+ */
+const deepClone = (obj) => {
+  if (obj === null || typeof obj !== 'object') {
+    return obj;
+  }
+
+  // Fast path for arrays
+  if (Array.isArray(obj)) {
+    const length = obj.length;
+    const clone = new Array(length);
+    for (let i = 0; i < length; i++) {
+      clone[i] = deepClone(obj[i]);
+    }
+    return clone;
+  }
+
+  // Fast path for objects
+  const clone = {};
+  const keys = Object.keys(obj);
+  for (let i = 0; i < keys.length; i++) {
+    const key = keys[i];
+    clone[key] = deepClone(obj[key]);
+  }
+
+  return clone;
+};
+
 // Store active sessions to avoid repeated logins
 const activeSessions = new Map();
 
@@ -248,5 +274,5 @@ module.exports = {
   ensureLogin,
   handleApiError,
   sanitizeHeaders,
-  processJsonOutput
-};
\ No newline at end of file
+  processJsonOutput,
+};