From 100832195783e88e02c46c7eda0263a9c3286430 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Mon, 30 Jun 2025 12:17:46 -0500 Subject: [PATCH 01/11] Add brew snippets to local save history solves #3113 --- client/homebrew/utils/versionHistory.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/homebrew/utils/versionHistory.js b/client/homebrew/utils/versionHistory.js index ec3bd74e1..f3d6aa97b 100644 --- a/client/homebrew/utils/versionHistory.js +++ b/client/homebrew/utils/versionHistory.js @@ -42,6 +42,7 @@ function parseBrewForStorage(brew, slot = 0) { title : brew.title, text : brew.text, style : brew.style, + snippets : brew.snippets, version : brew.version, shareId : brew.shareId, savedAt : brew?.savedAt || new Date(), From 99b90e0998af579b686080571519368da2bd31af Mon Sep 17 00:00:00 2001 From: David Bolack Date: Mon, 7 Jul 2025 13:54:29 -0500 Subject: [PATCH 02/11] Include snippets in the restoration. --- client/homebrew/pages/editPage/editPage.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index f2b1e809f..d28d8ef61 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -188,8 +188,9 @@ const EditPage = createClass({ this.setState((prevState)=>({ brew : { ...prevState.brew, - style : newData.style, - text : newData.text + style : newData.style, + text : newData.text, + snippets : newData.snippets } })); }, From bc82afa5b2f483cb4c5ad1ec725736f615eb394d Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 10 Jul 2025 21:42:51 +1200 Subject: [PATCH 03/11] Split version from hash checks --- server/homebrew.api.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 84f639a4d..009ab408a 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -339,17 +339,22 @@ const api = { // Initialize brew from request and body, destructure query params, and set the initial value for the after-save method const brewFromClient = api.excludePropsFromUpdate(req.body); const brewFromServer = req.brew; + + if(brewFromServer?.version !== brewFromClient?.version){ + console.log(`Version mismatch on brew ${brewFromClient.editId}`); + + res.setHeader('Content-Type', 'application/json'); + return res.status(409).send(JSON.stringify({ message: `The server version is out of sync with the saved brew. Please save your changes elsewhere, refresh, and try again.` })); + } + splitTextStyleAndMetadata(brewFromServer); - + brewFromServer.text = brewFromServer.text.normalize(); brewFromServer.hash = await md5(brewFromServer.text); - if((brewFromServer?.version !== brewFromClient?.version) || (brewFromServer?.hash !== brewFromClient?.hash)) { - if(brewFromClient?.version !== brewFromClient?.version) - console.log(`Version mismatch on brew ${brewFromClient.editId}`); - if(brewFromServer?.hash !== brewFromClient?.hash) { - console.log(`Hash mismatch on brew ${brewFromClient.editId}`); - } + if(brewFromServer?.hash !== brewFromClient?.hash) { + console.log(`Hash mismatch on brew ${brewFromClient.editId}`); + res.setHeader('Content-Type', 'application/json'); return res.status(409).send(JSON.stringify({ message: `The server copy is out of sync with the saved brew. Please save your changes elsewhere, refresh, and try again.` })); } From 7f3a8185580bc460a1de0f36767ae95971caa39b Mon Sep 17 00:00:00 2001 From: "G.Ambatte" Date: Thu, 10 Jul 2025 23:25:57 +1200 Subject: [PATCH 04/11] Add Homebrew API coverage tests --- server/homebrew.api.spec.js | 78 +++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index 8bb3a0c0b..e771bd8df 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -1052,4 +1052,82 @@ brew`); expect(testBrew.tags).toEqual(['tag a']); }); }); + + describe('updateBrew', ()=>{ + it('should return error on version mismatch', async ()=>{ + const brewFromClient = { version: 1 }; + const brewFromServer = { version: 1000 }; + + const req = { + brew : brewFromServer, + body : brewFromClient + }; + + await api.updateBrew(req, res); + + expect(res.status).toHaveBeenCalledWith(409); + expect(res.send).toHaveBeenCalledWith('{\"message\":\"The server version is out of sync with the saved brew. Please save your changes elsewhere, refresh, and try again.\"}'); + }); + + it('should return error on hash mismatch', async ()=>{ + const brewFromClient = { version: 1, hash: '1234' }; + const brewFromServer = { version: 1, text: 'test' }; + + const req = { + brew : brewFromServer, + body : brewFromClient + }; + + await api.updateBrew(req, res); + + expect(req.brew.hash).toBe('098f6bcd4621d373cade4e832627b4f6'); + expect(res.status).toHaveBeenCalledWith(409); + expect(res.send).toHaveBeenCalledWith('{\"message\":\"The server copy is out of sync with the saved brew. Please save your changes elsewhere, refresh, and try again.\"}'); + }); + + it('should return error on applying patches', async ()=>{ + const brewFromClient = { version: 1, hash: '098f6bcd4621d373cade4e832627b4f6', patches: 'not a valid patch string' }; + const brewFromServer = { version: 1, text: 'test', title: 'Test Title', description: 'Test Description' }; + + const req = { + brew : brewFromServer, + body : brewFromClient + }; + + let err; + try { + await api.updateBrew(req, res); + } catch (e) { + err = e; + } + + expect(err).toEqual(Error('Invalid patch string: not a valid patch string')); + }); + + it('should save brew, no ID', async ()=>{ + const brewFromClient = { version: 1, hash: '098f6bcd4621d373cade4e832627b4f6', patches: '' }; + const brewFromServer = { version: 1, text: 'test', title: 'Test Title', description: 'Test Description' }; + + model.save = jest.fn((brew)=>{return brew;}); + + const req = { + brew : brewFromServer, + body : brewFromClient, + query : { saveToGoogle: false, removeFromGoogle: false } + }; + + await api.updateBrew(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.send).toHaveBeenCalledWith( + expect.objectContaining({ + _id : '1', + description : 'Test Description', + hash : '098f6bcd4621d373cade4e832627b4f6', + title : 'Test Title', + version : 2 + }) + ); + }); + }); }); From c7610cf0f8cec428147a5545f9cf56a82c8495c3 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Thu, 10 Jul 2025 07:10:13 -0500 Subject: [PATCH 05/11] Run patch processing in parallel to prior system to attempt to narrow down not-quite-so-edge cases that did not come up prior to user testing. --- client/homebrew/pages/editPage/editPage.jsx | 2 +- server/homebrew.api.js | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 7e6c03473..8eb011cab 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -265,7 +265,7 @@ const EditPage = createClass({ brew.pageCount = ((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; brew.patches = stringifyPatches(makePatches(this.savedBrew.text, brew.text)); brew.hash = await md5(this.savedBrew.text); - brew.text = undefined; + //brew.text = undefined; - Temporary parallel path brew.textBin = undefined; const transfer = this.state.saveGoogle == _.isNil(this.state.brew.googleId); diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 84f639a4d..e34195ffa 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -340,7 +340,7 @@ const api = { const brewFromClient = api.excludePropsFromUpdate(req.body); const brewFromServer = req.brew; splitTextStyleAndMetadata(brewFromServer); - + brewFromServer.text = brewFromServer.text.normalize(); brewFromServer.hash = await md5(brewFromServer.text); @@ -357,15 +357,20 @@ const api = { let brew = _.assign(brewFromServer, brewFromClient); brew.title = brew.title.trim(); brew.description = brew.description.trim() || ''; + try { const patches = parsePatch(brewFromClient.patches); - brew.text = applyPatches(patches, brewFromServer.text)[0]; + // Patch to a throwaway variable while parallelizing - we're more concerned with error/no error. + const patchedResult = applyPatches(patches, brewFromServer.text)[0]; + // brew.text = applyPatches(patches, brewFromServer.text)[0]; } catch (err) { console.error('Failed to apply patches:', { - patches: brewFromClient.patches, - brewId: brew.editId || 'unknown' + patches : brewFromClient.patches, + brewId : brew.editId || 'unknown', + error : err }); - throw err; // rethrow to preserve the 500 behavior + // While running in parallel, don't throw the error upstream. + // throw err; // rethrow to preserve the 500 behavior } brew.text = api.mergeBrewText(brew); From 440c7beff673e7d7e5e5379476adc535a024de2d Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Thu, 10 Jul 2025 09:47:21 -0400 Subject: [PATCH 06/11] Up to 3.19.3 so users can get the update --- changelog.md | 8 ++++++++ package-lock.json | 4 ++-- package.json | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index 3f85850fc..0a108b9f2 100644 --- a/changelog.md +++ b/changelog.md @@ -88,6 +88,14 @@ pre { ## changelog For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery). +### Wednesday 7/09/2025 - v3.19.3 + +{{taskList +##### calculuschild +* [x] Restoring original saving behavior; will continue investigating why save was failing for some users in background +}} + + ### Wednesday 7/09/2025 - v3.19.2 {{taskList diff --git a/package-lock.json b/package-lock.json index 8ed54390e..c7f21c942 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "homebrewery", - "version": "3.19.2", + "version": "3.19.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "homebrewery", - "version": "3.19.2", + "version": "3.19.3", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index bc23a434e..527bfe256 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "homebrewery", "description": "Create authentic looking D&D homebrews using only markdown", - "version": "3.19.2", + "version": "3.19.3", "type": "module", "engines": { "npm": "^10.8.x", @@ -72,7 +72,7 @@ "lines": 50 }, "server/homebrew.api.js": { - "statements": 69, + "statements": 60, "branches": 50, "functions": 65, "lines": 70 From c5805af9358b59181f9dce686ee832164bc86748 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Thu, 10 Jul 2025 11:12:42 -0400 Subject: [PATCH 07/11] 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 }; From 45689d119e62ab9d11a81a84146e0f236dc6d728 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Thu, 10 Jul 2025 11:18:39 -0400 Subject: [PATCH 08/11] Comment out one failing test Patch failure test is no longer being thrown while we monitor in the background --- server/homebrew.api.spec.js | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index e771bd8df..cb953f7e5 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -1085,24 +1085,25 @@ brew`); expect(res.send).toHaveBeenCalledWith('{\"message\":\"The server copy is out of sync with the saved brew. Please save your changes elsewhere, refresh, and try again.\"}'); }); - it('should return error on applying patches', async ()=>{ - const brewFromClient = { version: 1, hash: '098f6bcd4621d373cade4e832627b4f6', patches: 'not a valid patch string' }; - const brewFromServer = { version: 1, text: 'test', title: 'Test Title', description: 'Test Description' }; + // Commenting this one out for now, since we are no longer throwing this error while we monitor + // it('should return error on applying patches', async ()=>{ + // const brewFromClient = { version: 1, hash: '098f6bcd4621d373cade4e832627b4f6', patches: 'not a valid patch string' }; + // const brewFromServer = { version: 1, text: 'test', title: 'Test Title', description: 'Test Description' }; - const req = { - brew : brewFromServer, - body : brewFromClient - }; + // const req = { + // brew : brewFromServer, + // body : brewFromClient, + // }; - let err; - try { - await api.updateBrew(req, res); - } catch (e) { - err = e; - } + // let err; + // try { + // await api.updateBrew(req, res); + // } catch (e) { + // err = e; + // } - expect(err).toEqual(Error('Invalid patch string: not a valid patch string')); - }); + // expect(err).toEqual(Error('Invalid patch string: not a valid patch string')); + // }); it('should save brew, no ID', async ()=>{ const brewFromClient = { version: 1, hash: '098f6bcd4621d373cade4e832627b4f6', patches: '' }; From 489b4b269492c3d11a5e9960b371b105c5b3f8ef Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Thu, 10 Jul 2025 12:04:09 -0400 Subject: [PATCH 09/11] Also log differences on MD5 mismatch --- server/homebrew.api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index e69a7284a..2c5244a60 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -354,7 +354,7 @@ const api = { if(brewFromServer?.hash !== brewFromClient?.hash) { console.log(`Hash mismatch on brew ${brewFromClient.editId}`); - + debugTextMismatch(brewFromClient.text, brewFromServer.text, `edit/${brewFromClient.editId}`); res.setHeader('Content-Type', 'application/json'); return res.status(409).send(JSON.stringify({ message: `The server copy is out of sync with the saved brew. Please save your changes elsewhere, refresh, and try again.` })); } From 7cadbfbd7bbd214e214d6b763b7ec753dfe34a91 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Thu, 10 Jul 2025 17:11:31 -0400 Subject: [PATCH 10/11] allowExceedingIndices for our patch applier Test if it allows patches to go through, and log error if it doesn't match the expected output. --- client/homebrew/pages/editPage/editPage.jsx | 15 ++++++++------- server/homebrew.api.js | 18 +++++++++--------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 8eb011cab..48d4a0b13 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -247,6 +247,9 @@ const EditPage = createClass({ save : async function(){ if(this.debounceSave && this.debounceSave.cancel) this.debounceSave.cancel(); + const brewState = this.state.brew; // freeze the current state + const preSaveSnapshot = { ...brewState }; + this.setState((prevState)=>({ isSaving : true, error : null, @@ -256,12 +259,10 @@ const EditPage = createClass({ await updateHistory(this.state.brew).catch(console.error); await versionHistoryGarbageCollection().catch(console.error); - const preSaveSnapshot = { ...this.state.brew }; - //Prepare content to send to server - const brew = { ...this.state.brew }; - brew.text = brew.text.normalize(); - this.savedBrew.text = this.savedBrew.text.normalize(); + const brew = { ...brewState }; + brew.text = brew.text.normalize('NFC'); + this.savedBrew.text = this.savedBrew.text.normalize('NFC'); brew.pageCount = ((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; brew.patches = stringifyPatches(makePatches(this.savedBrew.text, brew.text)); brew.hash = await md5(this.savedBrew.text); @@ -295,8 +296,8 @@ const EditPage = createClass({ shareId : res.body.shareId, version : res.body.version }, - isSaving : false, - unsavedTime : new Date() + isSaving : false, + unsavedTime : new Date() }), ()=>{ this.setState({ unsavedChanges : this.hasChanges() }); }); diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 2c5244a60..e5d622fe1 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -339,6 +339,7 @@ const api = { // Initialize brew from request and body, destructure query params, and set the initial value for the after-save method const brewFromClient = api.excludePropsFromUpdate(req.body); const brewFromServer = req.brew; + splitTextStyleAndMetadata(brewFromServer); if(brewFromServer?.version !== brewFromClient?.version){ console.log(`Version mismatch on brew ${brewFromClient.editId}`); @@ -347,9 +348,7 @@ const api = { return res.status(409).send(JSON.stringify({ message: `The server version is out of sync with the saved brew. Please save your changes elsewhere, refresh, and try again.` })); } - splitTextStyleAndMetadata(brewFromServer); - - brewFromServer.text = brewFromServer.text.normalize(); + brewFromServer.text = brewFromServer.text.normalize('NFC'); brewFromServer.hash = await md5(brewFromServer.text); if(brewFromServer?.hash !== brewFromClient?.hash) { @@ -359,26 +358,27 @@ const api = { return res.status(409).send(JSON.stringify({ message: `The server copy is out of sync with the saved brew. Please save your changes elsewhere, refresh, and try again.` })); } - let brew = _.assign(brewFromServer, brewFromClient); - brew.title = brew.title.trim(); - brew.description = brew.description.trim() || ''; - try { const patches = parsePatch(brewFromClient.patches); // Patch to a throwaway variable while parallelizing - we're more concerned with error/no error. - const patchedResult = applyPatches(patches, brewFromServer.text)[0]; + const patchedResult = applyPatches(patches, brewFromServer.text, { allowExceedingIndices: true })[0]; + if(patchedResult != brewFromClient.text) + throw("Patches did not apply cleanly, text mismatch detected"); // 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', + brewId : brewFromClient.editId || 'unknown', error : err }); // While running in parallel, don't throw the error upstream. // throw err; // rethrow to preserve the 500 behavior } + let brew = _.assign(brewFromServer, brewFromClient); + brew.title = brew.title.trim(); + brew.description = brew.description.trim() || ''; brew.text = api.mergeBrewText(brew); const googleId = brew.googleId; From 9da8a1705362742ac441dd1a1e01287c61712e43 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Thu, 10 Jul 2025 17:17:25 -0400 Subject: [PATCH 11/11] Remove text mismatch logs --- package-lock.json | 7 +++++++ package.json | 1 + server/homebrew.api.js | 4 ++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index c7f21c942..8fa722ac7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@babel/preset-env": "^7.28.0", "@babel/preset-react": "^7.27.1", "@babel/runtime": "^7.27.6", + "@dmsnell/diff-match-patch": "^1.1.0", "@googleapis/drive": "^13.0.1", "@sanity/diff-match-patch": "^3.2.0", "body-parser": "^2.2.0", @@ -1888,6 +1889,12 @@ "@csstools/css-tokenizer": "^3.0.1" } }, + "node_modules/@dmsnell/diff-match-patch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@dmsnell/diff-match-patch/-/diff-match-patch-1.1.0.tgz", + "integrity": "sha512-yejLPmM5pjsGvxS9gXablUSbInW7H976c/FJ4iQxWIm7/38xBySRemTPDe34lhg1gVLbJntX0+sH0jYfU+PN9A==", + "license": "Apache-2.0" + }, "node_modules/@dual-bundle/import-meta-resolve": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", diff --git a/package.json b/package.json index 527bfe256..71e892058 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "@babel/preset-env": "^7.28.0", "@babel/preset-react": "^7.27.1", "@babel/runtime": "^7.27.6", + "@dmsnell/diff-match-patch": "^1.1.0", "@googleapis/drive": "^13.0.1", "@sanity/diff-match-patch": "^3.2.0", "body-parser": "^2.2.0", diff --git a/server/homebrew.api.js b/server/homebrew.api.js index e5d622fe1..b39f3575f 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -353,7 +353,7 @@ const api = { if(brewFromServer?.hash !== brewFromClient?.hash) { console.log(`Hash mismatch on brew ${brewFromClient.editId}`); - debugTextMismatch(brewFromClient.text, brewFromServer.text, `edit/${brewFromClient.editId}`); + //debugTextMismatch(brewFromClient.text, brewFromServer.text, `edit/${brewFromClient.editId}`); res.setHeader('Content-Type', 'application/json'); return res.status(409).send(JSON.stringify({ message: `The server copy is out of sync with the saved brew. Please save your changes elsewhere, refresh, and try again.` })); } @@ -366,7 +366,7 @@ const api = { throw("Patches did not apply cleanly, text mismatch detected"); // brew.text = applyPatches(patches, brewFromServer.text)[0]; } catch (err) { - debugTextMismatch(brewFromClient.text, brewFromServer.text, `edit/${brewFromClient.editId}`); + //debugTextMismatch(brewFromClient.text, brewFromServer.text, `edit/${brewFromClient.editId}`); console.error('Failed to apply patches:', { patches : brewFromClient.patches, brewId : brewFromClient.editId || 'unknown',