From c5805af9358b59181f9dce686ee832164bc86748 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Thu, 10 Jul 2025 11:12:42 -0400 Subject: [PATCH] On patch failure, compare client and server text bytewise --- server/homebrew.api.js | 3 ++- shared/helpers.js | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index e34195ffa..2a7851c7f 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -11,7 +11,7 @@ import { nanoid } from 'nanoid'; import {makePatches, applyPatches, stringifyPatches, parsePatch} from '@sanity/diff-match-patch'; import { md5 } from 'hash-wasm'; import { splitTextStyleAndMetadata, - brewSnippetsToJSON } from '../shared/helpers.js'; + brewSnippetsToJSON, debugTextMismatch } from '../shared/helpers.js'; import checkClientVersion from './middleware/check-client-version.js'; @@ -364,6 +364,7 @@ const api = { const patchedResult = applyPatches(patches, brewFromServer.text)[0]; // brew.text = applyPatches(patches, brewFromServer.text)[0]; } catch (err) { + debugTextMismatch(brewFromClient.text, brewFromServer.text, `edit/${brewFromClient.editId}`); console.error('Failed to apply patches:', { patches : brewFromClient.patches, brewId : brew.editId || 'unknown', diff --git a/shared/helpers.js b/shared/helpers.js index 0ca681dfb..e09b0bdc4 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -139,9 +139,45 @@ const fetchThemeBundle = async (obj, renderer, theme)=>{ })); }; +const debugTextMismatch = (clientTextRaw, serverTextRaw, label) => { + const clientText = clientTextRaw?.normalize('NFC') || ''; + const serverText = serverTextRaw?.normalize('NFC') || ''; + + const clientBuffer = Buffer.from(clientText, 'utf8'); + const serverBuffer = Buffer.from(serverText, 'utf8'); + + if (clientBuffer.equals(serverBuffer)) { + console.log(`✅ ${label} text matches byte-for-byte.`); + return; + } + + console.warn(`❗${label} text mismatch detected.`); + console.log(`Client length: ${clientBuffer.length}`); + console.log(`Server length: ${serverBuffer.length}`); + + // Byte-level diff + for (let i = 0; i < Math.min(clientBuffer.length, serverBuffer.length); i++) { + if (clientBuffer[i] !== serverBuffer[i]) { + console.log(`Byte mismatch at offset ${i}: client=0x${clientBuffer[i].toString(16)} server=0x${serverBuffer[i].toString(16)}`); + break; + } + } + + // Char-level diff + for (let i = 0; i < Math.min(clientText.length, serverText.length); i++) { + if (clientText[i] !== serverText[i]) { + console.log(`Char mismatch at index ${i}:`); + console.log(` Client: '${clientText[i]}' (U+${clientText.charCodeAt(i).toString(16).toUpperCase()})`); + console.log(` Server: '${serverText[i]}' (U+${serverText.charCodeAt(i).toString(16).toUpperCase()})`); + break; + } + } +} + export { splitTextStyleAndMetadata, printCurrentBrew, fetchThemeBundle, - brewSnippetsToJSON + brewSnippetsToJSON, + debugTextMismatch };