From c5461f2ea2b6a703d4fb9717211ea3b7802bbbf8 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Tue, 13 Oct 2026 12:16:26 -0500 Subject: [PATCH 01/35] Rebuild BR Issue_5048 Moves calls to compress/decompress the text/textBin document members to the hombrewery model code. Compression is done in a save().pre setup. Decompression was already handled in the static for loading a brew but the textBin member was not emptied on decompression. --- package-lock.json | 147 +++++++++++++++++------------------- server/homebrew.api.js | 15 +--- server/homebrew.api.spec.js | 15 +++- server/homebrew.model.js | 8 ++ 4 files changed, 88 insertions(+), 97 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0e4277d3a..d17c0ace2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2360,6 +2360,7 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -2371,6 +2372,7 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -2381,6 +2383,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -3646,6 +3649,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.4.tgz", "integrity": "sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -3782,6 +3786,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3802,6 +3807,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3822,6 +3828,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3842,6 +3849,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3862,9 +3870,7 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3885,9 +3891,7 @@ "cpu": [ "arm" ], - "libc": [ - "musl" - ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3908,9 +3912,7 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3931,9 +3933,7 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3954,9 +3954,7 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3977,9 +3975,7 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4000,6 +3996,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4020,6 +4017,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4037,6 +4035,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, "license": "MIT", "optional": true, "engines": { @@ -4165,9 +4164,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4184,9 +4180,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4203,9 +4196,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4222,9 +4212,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4241,9 +4228,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4260,9 +4244,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4404,6 +4385,7 @@ "version": "0.10.4", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.4.tgz", "integrity": "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -4913,6 +4895,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4926,6 +4909,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4939,6 +4923,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4952,6 +4937,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4965,6 +4951,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4978,6 +4965,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4991,6 +4979,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5004,9 +4993,7 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5020,9 +5007,7 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5036,9 +5021,7 @@ "cpu": [ "loong64" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5052,9 +5035,7 @@ "cpu": [ "loong64" ], - "libc": [ - "musl" - ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5068,9 +5049,7 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5084,9 +5063,7 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5100,9 +5077,7 @@ "cpu": [ "riscv64" ], - "libc": [ - "musl" - ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5116,9 +5091,7 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5132,9 +5105,7 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5148,9 +5119,7 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5164,6 +5133,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5177,6 +5147,7 @@ "cpu": [ "wasm32" ], + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -5195,6 +5166,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5208,6 +5180,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5221,6 +5194,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -11278,9 +11252,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -11301,9 +11272,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -11324,9 +11292,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -11347,9 +11312,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -11907,6 +11869,33 @@ "url": "https://opencollective.com/mongoose" } }, + "node_modules/mongoose/node_modules/gcp-metadata": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-7.0.1.tgz", + "integrity": "sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/mongoose/node_modules/google-logging-utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.2.0.tgz", + "integrity": "sha512-WE9av4wKDZgRjBwgVUabocx8T6/7o3Ca1Fat46FXDhXVAFibzNadedcOXrdgd1Kzmk8tsk/9ZH89Wyf/SqeZ3A==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "engines": { + "node": ">=18" + } + }, "node_modules/mongoose/node_modules/mongodb": { "version": "7.7.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.7.0.tgz", @@ -14725,7 +14714,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "devOptional": true, + "dev": true, "license": "0BSD" }, "node_modules/type-check": { diff --git a/server/homebrew.api.js b/server/homebrew.api.js index aa1f1f84a..efea261b3 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -2,7 +2,6 @@ import _ from 'lodash'; import { model as HomebrewModel } from './homebrew.model.js'; import express from 'express'; -import zlib from 'zlib'; import GoogleActions from './googleActions.js'; import { hbfm } from 'marked-hbfm'; import * as yaml from 'js-yaml'; @@ -303,13 +302,8 @@ const api = { if(!googleId) return; api.excludeStubProps(newHomebrew); newHomebrew.googleId = googleId; - } else { - // Compress brew text to binary before saving - newHomebrew.textBin = zlib.deflateRawSync(newHomebrew.text); - // Delete the non-binary text field since it's not needed anymore - newHomebrew.text = undefined; } - + saved = await newHomebrew.save() .catch((err)=>{ console.error(err, err.toString(), err.stack); @@ -464,11 +458,6 @@ const api = { if(brew.googleId) { // If the google id exists after all those actions, exclude the props that are stored in google and aren't needed for rendering the brew items api.excludeStubProps(brew); - } else { - // Compress brew text to binary before saving - brew.textBin = zlib.deflateRawSync(brew.text); - // Delete the non-binary text field since it's not needed anymore - brew.text = undefined; } brew.updatedAt = new Date(); brew.version = (brew.version || 1) + 1; @@ -549,8 +538,6 @@ const api = { if(shouldDeleteGoogleBrew) { // When there are still authors remaining, we delete the google brew but store the full brew in the Homebrewery database brew.googleId = undefined; - brew.textBin = zlib.deflateRawSync(brew.text); - brew.text = undefined; } brew.markModified('authors'); //Mongo will not properly update arrays without markModified() await brew.save() diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index 8bda5407b..68aeec616 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -700,8 +700,16 @@ brew`); shareId : expect.any(String), style : undefined, tags : [], - text : undefined, - textBin : expect.objectContaining({}), + text : '```metadata\n' + + 'title: asdf\n' + + 'bleedSize: {}\n' + + 'safetySpace: {}\n' + + 'trimSize: {}\n' + + '\n' + + '```\n' + + '\n' + + 'asdf', + textBin : undefined, theme : '5ePHB', thumbnail : '', title : 'asdf', @@ -1233,8 +1241,7 @@ brew`); expect(saveFunc).toHaveBeenCalled(); expect(saved.authors).toEqual(['test2']); expect(saved.googleId).toEqual(undefined); - expect(saved.text).toEqual(undefined); - expect(saved.textBin).not.toEqual(undefined); + expect(saved.text).not.toEqual(undefined); }); it('should retain google brew and update stub when multiple authors and extra author requests deletion', async ()=>{ diff --git a/server/homebrew.model.js b/server/homebrew.model.js index e923ac928..b78ece631 100644 --- a/server/homebrew.model.js +++ b/server/homebrew.model.js @@ -32,6 +32,13 @@ const HomebrewSchema = mongoose.Schema({ lock : { type: Object, index: true } }, { versionKey: false }); +HomebrewSchema.pre('save', ()=>{ + if(!this.googleId) { + this.textBin = zlib.deflateRawSync(this.text); + this.text = undefined; + } +}); + HomebrewSchema.statics.increaseView = async function(query) { const brew = await Homebrew.findOne(query).exec(); brew.lastViewed = new Date(); @@ -51,6 +58,7 @@ HomebrewSchema.statics.get = async function(query, fields=null){ if(!_.isNil(brew.textBin)) { // Uncompress zipped text field const unzipped = zlib.inflateRawSync(brew.textBin); brew.text = unzipped.toString(); + brew.textBin = undefined; } return brew; }; From a322812cd82b6866d57b9fb511f0af932e9fceb9 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Mon, 19 Oct 2026 13:29:42 -0500 Subject: [PATCH 02/35] Revert package-lock.json changes --- package-lock.json | 147 +++++++++++++++++++++++++--------------------- 1 file changed, 79 insertions(+), 68 deletions(-) diff --git a/package-lock.json b/package-lock.json index d17c0ace2..0e4277d3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2360,7 +2360,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -2372,7 +2371,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -2383,7 +2381,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -3649,7 +3646,6 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.4.tgz", "integrity": "sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -3786,7 +3782,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3807,7 +3802,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3828,7 +3822,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3849,7 +3842,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3870,7 +3862,9 @@ "cpu": [ "arm" ], - "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3891,7 +3885,9 @@ "cpu": [ "arm" ], - "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3912,7 +3908,9 @@ "cpu": [ "arm64" ], - "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3933,7 +3931,9 @@ "cpu": [ "arm64" ], - "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3954,7 +3954,9 @@ "cpu": [ "x64" ], - "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3975,7 +3977,9 @@ "cpu": [ "x64" ], - "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3996,7 +4000,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4017,7 +4020,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4035,7 +4037,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "license": "MIT", "optional": true, "engines": { @@ -4164,6 +4165,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4180,6 +4184,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4196,6 +4203,9 @@ "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4212,6 +4222,9 @@ "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4228,6 +4241,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4244,6 +4260,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4385,7 +4404,6 @@ "version": "0.10.4", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.4.tgz", "integrity": "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -4895,7 +4913,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4909,7 +4926,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4923,7 +4939,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4937,7 +4952,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4951,7 +4965,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4965,7 +4978,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4979,7 +4991,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4993,7 +5004,9 @@ "cpu": [ "arm64" ], - "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5007,7 +5020,9 @@ "cpu": [ "arm64" ], - "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5021,7 +5036,9 @@ "cpu": [ "loong64" ], - "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5035,7 +5052,9 @@ "cpu": [ "loong64" ], - "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5049,7 +5068,9 @@ "cpu": [ "ppc64" ], - "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5063,7 +5084,9 @@ "cpu": [ "riscv64" ], - "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5077,7 +5100,9 @@ "cpu": [ "riscv64" ], - "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5091,7 +5116,9 @@ "cpu": [ "s390x" ], - "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5105,7 +5132,9 @@ "cpu": [ "x64" ], - "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5119,7 +5148,9 @@ "cpu": [ "x64" ], - "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5133,7 +5164,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5147,7 +5177,6 @@ "cpu": [ "wasm32" ], - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -5166,7 +5195,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5180,7 +5208,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5194,7 +5221,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -11252,6 +11278,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -11272,6 +11301,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -11292,6 +11324,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -11312,6 +11347,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -11869,33 +11907,6 @@ "url": "https://opencollective.com/mongoose" } }, - "node_modules/mongoose/node_modules/gcp-metadata": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-7.0.1.tgz", - "integrity": "sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==", - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "gaxios": "^7.0.0", - "google-logging-utils": "^1.0.0", - "json-bigint": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/mongoose/node_modules/google-logging-utils": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.2.0.tgz", - "integrity": "sha512-WE9av4wKDZgRjBwgVUabocx8T6/7o3Ca1Fat46FXDhXVAFibzNadedcOXrdgd1Kzmk8tsk/9ZH89Wyf/SqeZ3A==", - "license": "Apache-2.0", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, "node_modules/mongoose/node_modules/mongodb": { "version": "7.7.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.7.0.tgz", @@ -14714,7 +14725,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, + "devOptional": true, "license": "0BSD" }, "node_modules/type-check": { From 8dde0e37edc09ea37573da0a5a2c1ff5060e110c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Tue, 20 Oct 2026 12:41:11 +0200 Subject: [PATCH 03/35] simplify functions and reorder imports --- .../editor/metadataEditor/metadataEditor.jsx | 41 +++++++------------ 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.jsx b/client/homebrew/editor/metadataEditor/metadataEditor.jsx index d713c1cc9..b347f3f25 100644 --- a/client/homebrew/editor/metadataEditor/metadataEditor.jsx +++ b/client/homebrew/editor/metadataEditor/metadataEditor.jsx @@ -2,13 +2,14 @@ import '../uiEditor.less'; import React, { useState } from 'react'; import _ from 'lodash'; -import request from '../../utils/request-middleware.js'; -import Combobox from '@components/combobox.jsx'; -import TagInput from '../tagInput/tagInput.jsx'; +import request from '../../utils/request-middleware.js'; import Themes from '@themes/themes.json'; import validations from './validations.js'; +import Combobox from '@components/combobox.jsx'; +import TagInput from '../tagInput/tagInput.jsx'; + import homebreweryThumbnail from '../../thumbnail.png'; const callIfExists = (val, fn, ...args)=>{ @@ -31,20 +32,12 @@ const MetadataEditor = ({ theme : '5ePHB', lang : 'en', }, - onChange = ()=>{}, reportError = ()=>{}, ...props })=>{ const [showThumbnail, setShowThumbnail] = useState(true); - const toggleThumbnailDisplay = ()=>{ - setShowThumbnail(!showThumbnail); - }; - const renderThumbnail = ()=>{ - if(!showThumbnail) return; - return ; - }; const handleFieldChange = (name, e)=>{ // load validation rules, and check input value against them const inputRules = validations[name] ?? []; @@ -141,18 +134,6 @@ const MetadataEditor = ({ ; } }; - const renderDelete = ()=>{ - if(!metadata.editId) return; - - return
- -
- -
-
; - }; const renderAuthors = ()=>{ const authors = metadata.authors; const isOwner = global.account?.username && global.account?.username === authors[0]; @@ -296,7 +277,6 @@ const MetadataEditor = ({ }} /> - ; }; const renderRenderOptions = ()=>{ @@ -364,13 +344,13 @@ const MetadataEditor = ({ /> - {renderThumbnail()} + {showThumbnail && }
@@ -431,7 +411,14 @@ const MetadataEditor = ({
- {renderDelete()} + {metadata.editId &&
+ +
+ +
+
} ); }; From 12941d38a31575619448f41702fc908898f31381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Tue, 20 Oct 2026 12:44:36 +0200 Subject: [PATCH 04/35] specifically pass used props --- client/homebrew/editor/metadataEditor/metadataEditor.jsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.jsx b/client/homebrew/editor/metadataEditor/metadataEditor.jsx index b347f3f25..6dd88fc22 100644 --- a/client/homebrew/editor/metadataEditor/metadataEditor.jsx +++ b/client/homebrew/editor/metadataEditor/metadataEditor.jsx @@ -34,7 +34,8 @@ const MetadataEditor = ({ }, onChange = ()=>{}, reportError = ()=>{}, - ...props + userThemes = {}, + themeBundle = {} })=>{ const [showThumbnail, setShowThumbnail] = useState(true); @@ -185,7 +186,7 @@ const MetadataEditor = ({ ); }; const renderThemeDropdown = ()=>{ - const mergedThemes = _.merge(Themes, props.userThemes); + const mergedThemes = _.merge(Themes, userThemes); const listThemes = (renderer)=>{ return _.map(_.values(mergedThemes[renderer]), (theme)=>{ @@ -206,7 +207,7 @@ const MetadataEditor = ({ }; const currentRenderer = metadata.renderer; - const currentThemeDisplay = props.themeBundle?.name ? `${props.themeBundle.author ?? currentRenderer} : ${props.themeBundle.name}` : 'No Theme Selected'; + const currentThemeDisplay = themeBundle?.name ? `${themeBundle.author ?? currentRenderer} : ${themeBundle.name}` : 'No Theme Selected'; let dropdown; if(currentRenderer == 'legacy') { From 4b01edd4be82420b813d8ed89cfbba16ffa3ace2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Tue, 20 Oct 2026 12:46:49 +0200 Subject: [PATCH 05/35] reorder props to match schema --- .../editor/metadataEditor/metadataEditor.jsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.jsx b/client/homebrew/editor/metadataEditor/metadataEditor.jsx index 6dd88fc22..01d9b081a 100644 --- a/client/homebrew/editor/metadataEditor/metadataEditor.jsx +++ b/client/homebrew/editor/metadataEditor/metadataEditor.jsx @@ -20,17 +20,18 @@ const callIfExists = (val, fn, ...args)=>{ const MetadataEditor = ({ metadata = { - editId : null, shareId : null, + editId : null, title : '', description : '', - thumbnail : '', tags : [], - published : false, - authors : [], - renderer : 'legacy', - theme : '5ePHB', lang : 'en', + renderer : 'legacy', + authors : [], + published : false, + thumbnail : '', + theme : '5ePHB', + }, onChange = ()=>{}, reportError = ()=>{}, From edae652e528203644f89f626c6e3617c0f394b3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Tue, 20 Oct 2026 12:52:22 +0200 Subject: [PATCH 06/35] lint 1 --- .../homebrew/editor/metadataEditor/metadataEditor.jsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.jsx b/client/homebrew/editor/metadataEditor/metadataEditor.jsx index 01d9b081a..d5c4387e9 100644 --- a/client/homebrew/editor/metadataEditor/metadataEditor.jsx +++ b/client/homebrew/editor/metadataEditor/metadataEditor.jsx @@ -31,7 +31,6 @@ const MetadataEditor = ({ published : false, thumbnail : '', theme : '5ePHB', - }, onChange = ()=>{}, reportError = ()=>{}, @@ -85,13 +84,11 @@ const MetadataEditor = ({ const handleTheme = (theme)=>{ metadata.renderer = theme.renderer; metadata.theme = theme.path; - onChange(metadata, 'theme'); }; const handleThemeWritein = (e)=>{ const shareId = e.target.value.split('/').pop(); //Extract just the ID if a URL was pasted in metadata.theme = shareId; - onChange(metadata, 'theme'); }; const handleLanguage = (languageCode)=>{ @@ -354,7 +351,6 @@ const MetadataEditor = ({ {showThumbnail && } -
@@ -370,17 +366,12 @@ const MetadataEditor = ({ />
- {renderLanguageDropdown()} - {renderThemeDropdown()} - {renderRenderOptions()}

Authors

- {renderAuthors()} -
@@ -400,7 +391,6 @@ const MetadataEditor = ({

Privacy

-
@@ -412,7 +402,6 @@ const MetadataEditor = ({
- {metadata.editId &&
From eeb8fa9c92b48986519bf19acbf1d32fed2ffa30 Mon Sep 17 00:00:00 2001 From: David Bolack Date: Tue, 20 Oct 2026 11:41:17 -0500 Subject: [PATCH 07/35] Add a force option to ru scripts that deletes the .vite caches --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index f6dcaf2b9..602c5e078 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "scripts": { "start": "node server.js", "build": "vite build", + "force": "rm -rf .vite node_modules/.vite && vite build", "lint": "eslint --fix", "lint:dry": "eslint", "stylelint": "stylelint --fix **/*.{less}", From e9c4ee8563aaa62e8f1bf77248b0d6498090a91a Mon Sep 17 00:00:00 2001 From: David Bolack Date: Tue, 20 Oct 2026 14:51:23 -0500 Subject: [PATCH 08/35] Multiplatform force --- package-lock.json | 11 +++++++++++ package.json | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index fc60d253a..62148f787 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,6 +61,7 @@ "react-dom": "^19.3.1", "react-frame-component": "^5.3.2", "react-router": "^8.4.0", + "run-script-os": "^1.1.6", "sanitize-filename": "1.6.4", "superagent": "^10.4.1" }, @@ -13399,6 +13400,16 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/run-script-os": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/run-script-os/-/run-script-os-1.1.6.tgz", + "integrity": "sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw==", + "license": "MIT", + "bin": { + "run-os": "index.js", + "run-script-os": "index.js" + } + }, "node_modules/safe-array-concat": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", diff --git a/package.json b/package.json index 602c5e078..c4eb55950 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "scripts": { "start": "node server.js", "build": "vite build", - "force": "rm -rf .vite node_modules/.vite && vite build", + "force:win32": "rm .vite node_modules/.vite -r -force && vite --help", + "force:default": "rm -rf .vite node_modules/.vite && vite --help", "lint": "eslint --fix", "lint:dry": "eslint", "stylelint": "stylelint --fix **/*.{less}", @@ -140,6 +141,7 @@ "react-dom": "^19.3.1", "react-frame-component": "^5.3.2", "react-router": "^8.4.0", + "run-script-os": "^1.1.6", "sanitize-filename": "1.6.4", "superagent": "^10.4.1" }, From a9d40ae754731caf234ad008bb2274d644f7f385 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Tue, 20 Oct 2026 16:16:04 -0400 Subject: [PATCH 09/35] Bump marked.hbfm --- package-lock.json | 40 ++++++++++++++++++++-------------------- package.json | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index fc60d253a..d0e22d4ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,7 @@ "jwt-simple": "^0.5.6", "less": "^4.9.1", "lodash": "^4.18.1", - "marked-hbfm": "^1.0.1", + "marked-hbfm": "^1.0.2", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.31.0", "mongoose": "^9.11.2", @@ -11538,9 +11538,9 @@ } }, "node_modules/marked-emoji": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/marked-emoji/-/marked-emoji-2.0.3.tgz", - "integrity": "sha512-fChW/AfUqCHgoEC1nFDgiw3OR/qsi71/QXH/HTo05yd6B5+T+VHh1SqCpn/HpeGLDxkA+MK4+hr4eULB2/A8Jw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/marked-emoji/-/marked-emoji-3.0.0.tgz", + "integrity": "sha512-4IODef6jiQfuRsWOXSH/q9YiXmpVemvm1raj4WmObkWeAPg5bMMPTvbDpxbn4BIvbM245a1Dhq6RAzCS0WFGOw==", "license": "MIT", "peerDependencies": { "marked": ">=4 <19" @@ -11568,25 +11568,25 @@ } }, "node_modules/marked-hbfm": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/marked-hbfm/-/marked-hbfm-1.0.1.tgz", - "integrity": "sha512-mqz1ALIk0POeRCGjAGDTRhyKrMe+i5ycsxLV/8MlG13xC/oTEXA66VHZvIMNLotdM8jNNnLC9TFLCWECN4QHug==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/marked-hbfm/-/marked-hbfm-1.0.2.tgz", + "integrity": "sha512-3dXUoPaS33nJ0T7Urjx72DiAteyd5XuYV7yur/+yF59NfFjAWrifbAVxQSHxev2apc8SoQQFiQpmZc4/rRG3/Q==", "license": "MIT", "dependencies": { "eslint-plugin-jest": "^29.16.2", "eslint-plugin-react": "^7.37.5", "lodash": "^4.18.1", "marked": ">=3 <19", - "marked-alignment-paragraphs": "^1.0.0", - "marked-definition-lists": "^1.0.1", - "marked-diagrams-markdeep": "^1.0.1", - "marked-emoji": "^2.0.3", - "marked-extended-tables": "^2.0.1", - "marked-gfm-heading-id": "^4.1.4", - "marked-nonbreaking-spaces": "^1.0.1", - "marked-smartypants-lite": "^1.0.3", - "marked-subsuper-text": "^1.0.4", - "marked-variables": "^1.0.7" + "marked-alignment-paragraphs": "1.0.0", + "marked-definition-lists": "1.0.1", + "marked-diagrams-markdeep": "1.0.1", + "marked-emoji": "3.0.0", + "marked-extended-tables": "2.0.1", + "marked-gfm-heading-id": "4.1.4", + "marked-nonbreaking-spaces": "1.0.1", + "marked-smartypants-lite": "1.0.3", + "marked-subsuper-text": "1.0.4", + "marked-variables": "2.0.0" } }, "node_modules/marked-nonbreaking-spaces": { @@ -11617,9 +11617,9 @@ } }, "node_modules/marked-variables": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/marked-variables/-/marked-variables-1.0.7.tgz", - "integrity": "sha512-b7ZxKQJ9TEF/xi7dpP//IyIvK10minaYqmQNX7K91K9189jSQ8grpxY9yOIAXpPmVfReY0aZbfxugolTBIpXAA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/marked-variables/-/marked-variables-2.0.0.tgz", + "integrity": "sha512-U3LQ7mXfXpM0H2jExenENOvuFrP4JzSksRUr0yyENxZEkK3jmSlUEdT9LBuYxjOqWlMMhqiFVPOmtm1/QJnS+A==", "license": "MIT", "dependencies": { "expr-eval": "^2.0.2", diff --git a/package.json b/package.json index f6dcaf2b9..f79e71881 100644 --- a/package.json +++ b/package.json @@ -127,7 +127,7 @@ "jwt-simple": "^0.5.6", "less": "^4.9.1", "lodash": "^4.18.1", - "marked-hbfm": "^1.0.1", + "marked-hbfm": "^1.0.2", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.31.0", "mongoose": "^9.11.2", From 5e5007e3a869e57bac0fcb5c96922e6b2e880fb1 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Tue, 20 Oct 2026 16:16:22 -0400 Subject: [PATCH 10/35] Update changelog --- changelog.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 698cbb128..08b9314f9 100644 --- a/changelog.md +++ b/changelog.md @@ -90,9 +90,30 @@ pre { } ``` + ## changelog For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery). +### Tuesday 10/20/2026 - v3.24.2 +{{taskList +##### G-Ambatte +* [x] Hotfix for memory leak on server + +Fixes issue [#5090](https://github.com/naturalcrit/homebrewery/issues/5090) + +##### Calculuschild +* [x] Fix brew variables not working across pages + +* [x] Allow brew variable definitions to be on adjacent lines (no blank line required anymore) + +##### 5e-Cleric +* [x] Partial rollout of some site UI changes (includes alternate views and darkmode in {{openSans **BREWS** :fas_beer_mug_empty:}} page, and changes to {{openSans **VAULT** :fas_dungeon:}}) + +##### Abquintic, Calculuschild, 5e-Cleric +* [x] Multiple background fixes and code cleanups +}} + + ### Tuesday 9/29/2026 - v3.24.1 {{taskList ##### calculuschild @@ -169,7 +190,7 @@ Fixes issue [#3840](https://github.com/naturalcrit/homebrewery/issues/3840) Fixes issue [#2037](https://github.com/naturalcrit/homebrewery/issues/2037) -* [x] Remember editor theme across tabs +* [x] Remember editor theme across tabs, and new {{openSans **SETTINGS :fas_gear:**}} tab with editor settings Fixes issue [#4858](https://github.com/naturalcrit/homebrewery/issues/4858) From 1f6a03db80326ad28072f1a757318b295962d494 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Tue, 20 Oct 2026 16:18:03 -0400 Subject: [PATCH 11/35] 3.24.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d0e22d4ec..427696fa6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "homebrewery", - "version": "3.24.1", + "version": "3.24.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "homebrewery", - "version": "3.24.1", + "version": "3.24.2", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index f79e71881..c922910ac 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.24.1", + "version": "3.24.2", "type": "module", "engines": { "npm": ">=10.8 <12", From e5af86d72d654e038e9f2a5a826ebc9bd9c0c5bb Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Tue, 20 Oct 2026 16:44:53 -0400 Subject: [PATCH 12/35] Fix hbfm import --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 427696fa6..8374fb57a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,7 @@ "jwt-simple": "^0.5.6", "less": "^4.9.1", "lodash": "^4.18.1", - "marked-hbfm": "^1.0.2", + "marked-hbfm": "^1.0.3", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.31.0", "mongoose": "^9.11.2", @@ -11568,9 +11568,9 @@ } }, "node_modules/marked-hbfm": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/marked-hbfm/-/marked-hbfm-1.0.2.tgz", - "integrity": "sha512-3dXUoPaS33nJ0T7Urjx72DiAteyd5XuYV7yur/+yF59NfFjAWrifbAVxQSHxev2apc8SoQQFiQpmZc4/rRG3/Q==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/marked-hbfm/-/marked-hbfm-1.0.3.tgz", + "integrity": "sha512-j+TLQqHv1PGb/ypxh8r+kGlgto1t3373GxxDn1gTBk473QC85uV4PteZfLLq8U5H2OLODbbyEGzxVvYdnOT0lQ==", "license": "MIT", "dependencies": { "eslint-plugin-jest": "^29.16.2", diff --git a/package.json b/package.json index c922910ac..7f2f4fae6 100644 --- a/package.json +++ b/package.json @@ -127,7 +127,7 @@ "jwt-simple": "^0.5.6", "less": "^4.9.1", "lodash": "^4.18.1", - "marked-hbfm": "^1.0.2", + "marked-hbfm": "^1.0.3", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.31.0", "mongoose": "^9.11.2", From 0bc538f588b633443c41c9c7d9dd2a7f499175ab Mon Sep 17 00:00:00 2001 From: David Bolack Date: Wed, 21 Oct 2026 11:07:27 -0500 Subject: [PATCH 13/35] Replace with team preferred fix. --- package-lock.json | 11 ----------- package.json | 4 +--- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 62148f787..fc60d253a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,7 +61,6 @@ "react-dom": "^19.3.1", "react-frame-component": "^5.3.2", "react-router": "^8.4.0", - "run-script-os": "^1.1.6", "sanitize-filename": "1.6.4", "superagent": "^10.4.1" }, @@ -13400,16 +13399,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/run-script-os": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/run-script-os/-/run-script-os-1.1.6.tgz", - "integrity": "sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw==", - "license": "MIT", - "bin": { - "run-os": "index.js", - "run-script-os": "index.js" - } - }, "node_modules/safe-array-concat": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", diff --git a/package.json b/package.json index c4eb55950..0a96093b6 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,7 @@ "scripts": { "start": "node server.js", "build": "vite build", - "force:win32": "rm .vite node_modules/.vite -r -force && vite --help", - "force:default": "rm -rf .vite node_modules/.vite && vite --help", + "build:clean": "node -e \"const fs=require('fs'); fs.rmSync('.vite',{recursive:true,force:true}); fs.rmSync('node_modules/.vite',{recursive:true,force:true});\" && vite build", "lint": "eslint --fix", "lint:dry": "eslint", "stylelint": "stylelint --fix **/*.{less}", @@ -141,7 +140,6 @@ "react-dom": "^19.3.1", "react-frame-component": "^5.3.2", "react-router": "^8.4.0", - "run-script-os": "^1.1.6", "sanitize-filename": "1.6.4", "superagent": "^10.4.1" }, From 554374dbf91e16d954460534001ac9c85346f38f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 21 Oct 2026 18:33:52 +0200 Subject: [PATCH 14/35] fix shortcuts in foldall and unfoldall functions --- client/components/codeEditor/codeEditor.jsx | 52 ++++++++++--------- .../codeEditor/extensions/customKeyMaps.js | 4 +- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/client/components/codeEditor/codeEditor.jsx b/client/components/codeEditor/codeEditor.jsx index bda7e3b73..230a30e1f 100644 --- a/client/components/codeEditor/codeEditor.jsx +++ b/client/components/codeEditor/codeEditor.jsx @@ -156,6 +156,31 @@ const CodeEditor = forwardRef( return folds; }; + const foldAll = (view = viewRef.current)=>{ + if(!view) return false; + + const doc = view.state.doc; + const pages = pageMap.current; + + const effects = pages.map((start, i)=>{ + const next = pages[i + 1] || doc.length; + const from = i ? doc.line(doc.lineAt(start).number + 1).from : 0; + const to = doc.line(doc.lineAt(next).number).from - 1; + + return to > from ? foldEffect.of({ from, to }) : null; + }).filter(Boolean); + + view.dispatch({ effects }); + return true; + }; + + const unfoldAll = (view = viewRef.current)=>{ + if(!view) return false; + + view.dispatch(unfoldAllCmd(view)); + return true; + }; + const createExtensions = ({ onChange, language, editorTheme })=>{ const setEventListeners = EditorView.updateListener.of((update)=>{ if(update.docChanged) { @@ -199,7 +224,7 @@ const CodeEditor = forwardRef( //keyboard shortcut keymap.of([...defaultKeymap, foldKeymap, ...searchKeymap]), - generalKeymap, + generalKeymap(foldAll, unfoldAll), ...(tab === 'brewStyles' ? [cssKeymap] : [markdownKeymap]), //multiple cursors and selections @@ -404,29 +429,8 @@ const CodeEditor = forwardRef( undone : redoDepth(view.state), }; }, - - foldAll : ()=>{ - const view = viewRef.current; - if(!view) return; - - const doc = view.state.doc; - const pages = pageMap.current; - - const effects = pages.map((start, i)=>{ - const next = pages[i + 1] || doc.length; - const from = i ? doc.line(doc.lineAt(start).number + 1).from : 0; - const to = doc.line(doc.lineAt(next).number).from - 1; - - return to > from ? foldEffect.of({ from, to }) : null; - }).filter(Boolean); - - view.dispatch({ effects }); - }, - unfoldAll : ()=>{ - const view = viewRef.current; - if(!view) return; - view.dispatch(unfoldAllCmd(view)); - }, + foldAll : ()=>foldAll(), + unfoldAll : ()=>unfoldAll(), focus : ()=>viewRef.current.focus(), })); diff --git a/client/components/codeEditor/extensions/customKeyMaps.js b/client/components/codeEditor/extensions/customKeyMaps.js index af7fa7131..e6adec71e 100644 --- a/client/components/codeEditor/extensions/customKeyMaps.js +++ b/client/components/codeEditor/extensions/customKeyMaps.js @@ -215,13 +215,15 @@ const newBreak = (type)=>(view)=>forEachSelection(view, (state, from, to, select }; }); -export const generalKeymap = Prec.high(keymap.of([ +export const generalKeymap = (foldAll, unfoldAll) => Prec.high(keymap.of([ { key: 'Tab', run: insertTab }, { key: 'Shift-Tab', run: indentLess }, { key: 'Mod-z', run: undo }, //it may be unnecessary { key: 'Mod-Shift-z', run: redo }, { key: 'Mod-y', run: redo }, //user asked, so double keybind { key: 'Mod-d', run: deleteLine }, //annoyingly overrides "selectNextOccurrence" because users asked + { key: 'Mod-Shift-[', run: foldAll }, + { key: 'Mod-Shift-]', run: unfoldAll }, ])); export const cssKeymap = Prec.highest(keymap.of([ From efdf95b44b232a3c0192bd58b5c427e476fb3736 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 21 Oct 2026 17:42:21 +0000 Subject: [PATCH 15/35] Simplify failedPatch count --- server/homebrew.api.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index aa1f1f84a..cc27eeca2 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -406,9 +406,9 @@ const api = { const patches = parsePatch(brewFromClient.patches); // Patch to a throwaway variable while parallelizing - we're more concerned with error/no error. result = applyPatches(patches, encodeURI(brewFromServer.text)); - const failedPatches = patches.map((patch, index)=>{if(!result[1][index]){ return patch; }}); - if(failedPatches > 0){ - throw (`Patch failure: ${failedPatches}/${result[1].length} did not apply`); + const failedPatchCount = result[1].filter((patchApplied)=>!patchApplied).length; //Count how many patches failed to apply + if(failedPatchCount){ + throw (`Patch failure: ${failedPatchCount}/${result[1].length} did not apply`); } if(decodeURI(result[0]) != brewFromClient.text){ throw ('Patches did not apply cleanly, text mismatch detected'); From be144848774090c42b1acd4b65097db5237c2b38 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 21 Oct 2026 18:07:21 +0000 Subject: [PATCH 16/35] Simplify "excludeXYZProps" functions --- server/homebrew.api.js | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index cc27eeca2..b3a0e8332 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -238,28 +238,14 @@ const api = { return (tokens.find((token)=>token.type === 'heading' || token.type === 'paragraph')?.text || 'No Title') .slice(0, MAX_TITLE_LENGTH); }, - excludePropsFromUpdate : (brew)=>{ - // Remove undesired properties - const modified = _.clone(brew); - const propsToExclude = ['_id', 'views', 'lastViewed']; - for (const prop of propsToExclude) { - delete modified[prop]; - } - return modified; + excludePropsFromUpdate : (brew)=>{ //Remove props that should not update/overwrite when editing + return _.omit(brew, ['_id', 'views', 'lastViewed']); }, - excludeGoogleProps : (brew)=>{ - const modified = _.clone(brew); - const propsToExclude = ['version', 'tags', 'systems', 'published', 'authors', 'owner', 'views', 'thumbnail']; - for (const prop of propsToExclude) { - delete modified[prop]; - } - return modified; + excludeGoogleProps : (brew)=>{ //Remove props that should not be saved to Google (only stored to Homebrewery stub) + return _.omit(brew, ['version', 'tags', 'systems', 'published', 'authors', 'owner', 'views', 'thumbnail']); }, - excludeStubProps : (brew)=>{ - const propsToExclude = ['text', 'textBin']; - for (const prop of propsToExclude) { - brew[prop] = undefined; - } + excludeStubProps : (brew)=>{ //Remove props that should not be saved to stub-only brews + brew.text = brew.textBin = undefined; // Can't use _.omit because this brew is a Mongoose object return brew; }, beforeNewSave : (account, brew)=>{ @@ -292,7 +278,7 @@ const api = { api.beforeNewSave(req.account, brew); - const newHomebrew = new HomebrewModel(brew); + let newHomebrew = new HomebrewModel(brew); newHomebrew.editId = nanoid(12); newHomebrew.shareId = nanoid(12); @@ -301,7 +287,7 @@ const api = { googleId = await api.newGoogleBrew(req.account, newHomebrew, res); if(!googleId) return; - api.excludeStubProps(newHomebrew); + newHomebrew = api.excludeStubProps(newHomebrew); newHomebrew.googleId = googleId; } else { // Compress brew text to binary before saving @@ -463,7 +449,7 @@ const api = { if(brew.googleId) { // If the google id exists after all those actions, exclude the props that are stored in google and aren't needed for rendering the brew items - api.excludeStubProps(brew); + brew = api.excludeStubProps(brew); } else { // Compress brew text to binary before saving brew.textBin = zlib.deflateRawSync(brew.text); From d200d4a48ac12cade9f4ce100693f6851aeb2c49 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 21 Oct 2026 18:52:49 +0000 Subject: [PATCH 17/35] Small cleanup of migrateSystemsToTags --- server/homebrew.api.js | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index b3a0e8332..d76969fb5 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -27,17 +27,8 @@ const isStaticTheme = (renderer, themeName)=>{ return Themes[renderer]?.[themeName] !== undefined; }; -// const getTopBrews = (cb) => { -// HomebrewModel.find().sort({ views: -1 }).limit(5).exec(function(err, brews) { -// cb(brews); -// }); -// }; - - const migrateSystemsToTags = (brew)=>{ - if(!('systems' in brew)) return brew; - - if(!Array.isArray(brew.systems) || brew.systems.length === 0) { + if(!('systems' in brew) || !Array.isArray(brew.systems) || brew.systems.length === 0) { brew.systems = undefined; return brew; } @@ -189,10 +180,7 @@ const api = { stub.renderer = stub.renderer || undefined; // Clear empty strings stub = _.defaults(stub, DEFAULT_BREW_LOAD); // Fill in blank fields - - - const fixedStub = migrateSystemsToTags(stub); - req.brew = fixedStub; + req.brew = migrateSystemsToTags(stub); splitTextStyleAndMetadata(req.brew); next(); }; @@ -412,10 +400,9 @@ const api = { // throw err; // rethrow to preserve the 500 behavior } - let brew = _.assign(brewFromServer, brewFromClient); - - migrateSystemsToTags(brew); + let brew = _.assign(brewFromServer, brewFromClient); + brew = migrateSystemsToTags(brew); brew.title = brew.title.trim(); brew.description = brew.description.trim() || ''; brew.text = api.mergeBrewText(brew); From dfe4634d389c7c6ee5e4487adf18ceb0935d3e43 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 21 Oct 2026 19:28:42 +0000 Subject: [PATCH 18/35] Random cleanups in homebrew.api --- server/homebrew.api.js | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index d76969fb5..2fd81e4f2 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -99,19 +99,17 @@ const api = { const brews = await HomebrewModel.getByUser(username, true, fields, { tags: { $in: ['meta:theme', 'meta:Theme'] } }); - if(brews) { - for (const brew of brews) { - userThemes[brew.renderer] ??= {}; - userThemes[brew.renderer][brew.shareId] = { - name : brew.title, - renderer : brew.renderer, - baseTheme : brew.theme, - baseSnippets : false, - author : brew.authors[0], - path : brew.shareId, - thumbnail : brew.thumbnail || '/assets/naturalCritLogoWhite.svg' - }; - } + for (const brew of brews) { + userThemes[brew.renderer] ??= {}; + userThemes[brew.renderer][brew.shareId] = { + name : brew.title, + renderer : brew.renderer, + baseTheme : brew.theme, + baseSnippets : false, + author : brew.authors[0], + path : brew.shareId, + thumbnail : brew.thumbnail || '/assets/naturalCritLogoWhite.svg' + }; } return userThemes; @@ -187,8 +185,7 @@ const api = { }, getCSS : async (req, res)=>{ const { brew } = req; - if(!brew) return res.status(404).send(''); - if(!brew.style) return res.status(404).send(''); + if(!brew?.style) return res.status(404).send(''); res.set({ 'Cache-Control' : 'no-cache', @@ -204,16 +201,12 @@ const api = { `\`\`\`\n\n` + `${text}`; } - const metadata = _.pick(brew, ['title', 'description', 'tags', 'renderer', 'theme']); + const metadata = _.pick(brew, ['title', 'description', 'tags', 'renderer', 'theme', 'columns', 'columnGutter', 'license', 'legalAuthors']); const snippetsArray = brewSnippetsToJSON('brew_snippets', brew.snippets, null, false).snippets; metadata.snippets = snippetsArray.length > 0 ? snippetsArray : undefined; metadata.bleedSize = { top: brew?.bleedSize?.top, bottom: brew?.bleedSize?.bottom, inner: brew?.bleedSize?.inner, outer: brew?.bleedSize?.outer }; metadata.safetySpace = { top: brew?.safetySpace?.top, bottom: brew?.safetySpace?.bottom, outer: brew?.safetySpace?.outer, inner: brew?.safetySpace?.inner }; metadata.trimSize = { width: brew?.trimSize?.width, height: brew?.trimSize?.height }; - metadata.columns = brew?.columns; - metadata.columnGutter = brew?.columnGutter; - metadata.license = brew?.license; - metadata.legalAuthors = brew?.legalAuthors; text = `\`\`\`metadata\n` + `${yaml.dump(metadata)}\n` + @@ -252,9 +245,7 @@ const api = { newGoogleBrew : async (account, brew, res)=>{ const oAuth2Client = GoogleActions.authCheck(account, res); - const newBrew = api.excludeGoogleProps(brew); - - return await GoogleActions.newGoogleBrew(oAuth2Client, newBrew); + return await GoogleActions.newGoogleBrew(oAuth2Client, api.excludeGoogleProps(brew)); }, newBrew : async (req, res)=>{ const brew = req.body; From d65aa2d7f080919ec8351366697f1772c80e3417 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 21 Oct 2026 19:47:57 +0000 Subject: [PATCH 19/35] Unwind stub/nostub saving logic --- server/homebrew.api.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 2fd81e4f2..8f18543dc 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -447,16 +447,14 @@ const api = { console.error(err); res.status(err.status || 500).send(err.message || 'Unable to save brew to Homebrewery database'); }; - let saved; - if(!brew._id) { - // if the brew does not have a stub id, create and save it, then write the new value back to the brew. - saved = await new HomebrewModel(brew).save().catch(saveError); - } else { - // if the brew does have a stub id, update it using the stub id as the key. + + if(brew._id) // if the brew has a stub id, fetch and merge into Mongo stub document brew = _.assign(await HomebrewModel.findOne({ _id: brew._id }), brew); - saved = await brew.save() - .catch(saveError); - } + else // if not, create a new Mongo document + brew = new HomebrewModel(brew); + + let saved = await brew.save().catch(saveError); + if(!saved) return; // Call and wait for afterSave to complete const after = await afterSave(); From e1ddac766ff798711a5f9fd956eb6a75869bb0d2 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 21 Oct 2026 20:07:45 +0000 Subject: [PATCH 20/35] Unwind google deletion logic --- server/homebrew.api.js | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 8f18543dc..77a2dff6d 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -398,20 +398,12 @@ const api = { brew.description = brew.description.trim() || ''; brew.text = api.mergeBrewText(brew); + //=====----- Update to or remove from Google Drive -----=========// const googleId = brew.googleId; const { saveToGoogle, removeFromGoogle } = req.query; - let afterSave = async ()=>true; if(brew.googleId && removeFromGoogle) { - // If the google id exists and we're removing it from google, set afterSave to delete the google brew and mark the brew's google id as undefined - afterSave = async ()=>{ - return await api.deleteGoogleBrew(req.account, googleId, brew.editId, res) - .catch((err)=>{ - console.error(err); - res.status(err?.status || err?.response?.status || 500).send(err.message || err); - }); - }; - + // If the google id exists and we're removing it from google, mark the brew's google id as undefined brew.googleId = undefined; } else if(!brew.googleId && saveToGoogle) { // If we don't have a google id and the user wants to save to google, create the google brew and set the google id on the brew @@ -425,8 +417,9 @@ const api = { if(!updated) return; } + //=====----- Update to Mongo -----=====// if(brew.googleId) { - // If the google id exists after all those actions, exclude the props that are stored in google and aren't needed for rendering the brew items + // If google id exists after above actions, Mongo will store only a stub; exclude non-stub properties from update brew = api.excludeStubProps(brew); } else { // Compress brew text to binary before saving @@ -454,11 +447,17 @@ const api = { brew = new HomebrewModel(brew); let saved = await brew.save().catch(saveError); - if(!saved) return; - // Call and wait for afterSave to complete - const after = await afterSave(); - if(!after) return; + + if(googleId && removeFromGoogle) { + const deleted = await api.deleteGoogleBrew(req.account, googleId, brew.editId, res) + .catch((err)=>{ + console.error(err); + res.status(err?.status || err?.response?.status || 500).send(err.message || err); + }); + + if(!deleted) return; + } saved.textBin = undefined; // Remove textBin from the saved object to save bandwidth From a8312ba3ff7f50881a0030db41307c9509f19b52 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 21 Oct 2026 20:08:41 +0000 Subject: [PATCH 21/35] Remove redundant excludeGoogleProps from newGoogleBrew call --- 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 77a2dff6d..5715378ae 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -407,7 +407,7 @@ const api = { brew.googleId = undefined; } else if(!brew.googleId && saveToGoogle) { // If we don't have a google id and the user wants to save to google, create the google brew and set the google id on the brew - brew.googleId = await api.newGoogleBrew(req.account, api.excludeGoogleProps(brew), res); + brew.googleId = await api.newGoogleBrew(req.account, brew, res); if(!brew.googleId) return; } else if(brew.googleId) { From 7bd6e7b7ab998391b8535389528a3603e957bc68 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 21 Oct 2026 20:24:33 +0000 Subject: [PATCH 22/35] Group brew-normalizing steps together --- server/homebrew.api.js | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 5715378ae..53f3c6e11 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -397,23 +397,26 @@ const api = { brew.title = brew.title.trim(); brew.description = brew.description.trim() || ''; brew.text = api.mergeBrewText(brew); + brew.updatedAt = new Date(); + brew.version = (brew.version || 1) + 1; + if(req.account) { + brew.authors = _.uniq(_.concat(brew.authors, req.account.username)); + brew.invitedAuthors = _.uniq(_.filter(brew.invitedAuthors, (a)=>req.account.username !== a)); + } //=====----- Update to or remove from Google Drive -----=========// const googleId = brew.googleId; const { saveToGoogle, removeFromGoogle } = req.query; - if(brew.googleId && removeFromGoogle) { - // If the google id exists and we're removing it from google, mark the brew's google id as undefined + if(brew.googleId && removeFromGoogle) { // If removing from google, remove the brew's googleId brew.googleId = undefined; - } else if(!brew.googleId && saveToGoogle) { - // If we don't have a google id and the user wants to save to google, create the google brew and set the google id on the brew + } + else if(!brew.googleId && saveToGoogle) { // If adding to google, create new google document and record the googleId brew.googleId = await api.newGoogleBrew(req.account, brew, res); - if(!brew.googleId) return; - } else if(brew.googleId) { - // If the google id exists and no other actions are being performed, update the google brew + } + else if(brew.googleId) { // If already on google, update the google brew const updated = await GoogleActions.updateGoogleBrew(api.excludeGoogleProps(brew), req.ip); - if(!updated) return; } @@ -427,13 +430,6 @@ const api = { // Delete the non-binary text field since it's not needed anymore brew.text = undefined; } - brew.updatedAt = new Date(); - brew.version = (brew.version || 1) + 1; - - if(req.account) { - brew.authors = _.uniq(_.concat(brew.authors, req.account.username)); - brew.invitedAuthors = _.uniq(_.filter(brew.invitedAuthors, (a)=>req.account.username !== a)); - } // define a function to catch our save errors const saveError = (err)=>{ From a430a5c3ef9dad0c2a7f9de935764ff136973636 Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 21 Oct 2026 20:40:04 +0000 Subject: [PATCH 23/35] Inline the "saveError" function --- server/homebrew.api.js | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 53f3c6e11..6bcd293b0 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -223,7 +223,7 @@ const api = { return _.omit(brew, ['_id', 'views', 'lastViewed']); }, excludeGoogleProps : (brew)=>{ //Remove props that should not be saved to Google (only stored to Homebrewery stub) - return _.omit(brew, ['version', 'tags', 'systems', 'published', 'authors', 'owner', 'views', 'thumbnail']); + return _.omit(brew, ['version', 'tags', 'systems', 'published', 'authors', 'invitedAuthors', 'owner', 'views', 'thumbnail']); }, excludeStubProps : (brew)=>{ //Remove props that should not be saved to stub-only brews brew.text = brew.textBin = undefined; // Can't use _.omit because this brew is a Mongoose object @@ -345,7 +345,6 @@ const api = { return res.status(200).send(returnObj); }, updateBrew : async (req, res)=>{ - // 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; @@ -405,7 +404,7 @@ const api = { } //=====----- Update to or remove from Google Drive -----=========// - const googleId = brew.googleId; + const originalGoogleId = brew.googleId; const { saveToGoogle, removeFromGoogle } = req.query; if(brew.googleId && removeFromGoogle) { // If removing from google, remove the brew's googleId @@ -431,22 +430,21 @@ const api = { brew.text = undefined; } - // define a function to catch our save errors - const saveError = (err)=>{ - console.error(err); - res.status(err.status || 500).send(err.message || 'Unable to save brew to Homebrewery database'); - }; - if(brew._id) // if the brew has a stub id, fetch and merge into Mongo stub document brew = _.assign(await HomebrewModel.findOne({ _id: brew._id }), brew); else // if not, create a new Mongo document brew = new HomebrewModel(brew); - let saved = await brew.save().catch(saveError); + let saved = await brew.save() + .catch((err)=>{ + console.error(err); + res.status(err.status || 500).send(err.message || 'Unable to save brew to Homebrewery database'); + }); + if(!saved) return; - if(googleId && removeFromGoogle) { - const deleted = await api.deleteGoogleBrew(req.account, googleId, brew.editId, res) + if(originalGoogleId && removeFromGoogle) { + const deleted = await api.deleteGoogleBrew(req.account, originalGoogleId, brew.editId, res) .catch((err)=>{ console.error(err); res.status(err?.status || err?.response?.status || 500).send(err.message || err); From 9e7eef14e37bd99392542b77d27066fa6c6a7ddb Mon Sep 17 00:00:00 2001 From: Trevor Buckner Date: Wed, 21 Oct 2026 17:06:06 -0400 Subject: [PATCH 24/35] Fix patch text mismatch logging Was comparing the old Server text to new Client text, instead of comparing *patched* server to client. This should give a better idea of what characters are not matching. --- server/homebrew.api.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 2fd81e4f2..194dcd3fa 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -375,12 +375,13 @@ const api = { if(failedPatchCount){ throw (`Patch failure: ${failedPatchCount}/${result[1].length} did not apply`); } - if(decodeURI(result[0]) != brewFromClient.text){ + patchedText = decodeURI(result[0]); + if(patchedText != brewFromClient.text){ + debugTextMismatch(brewFromClient.text, patchedText, `edit/${brewFromClient.editId}`); 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, // result : result, From 25c583c9df49a13c2a47aa14c0a863f96edad85d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 21 Oct 2026 23:07:29 +0200 Subject: [PATCH 25/35] add title, description and tags to new brew metadata --- .../pages/basePages/editPage/editorPage.jsx | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/client/homebrew/pages/basePages/editPage/editorPage.jsx b/client/homebrew/pages/basePages/editPage/editorPage.jsx index b9fb34597..536f4e34b 100644 --- a/client/homebrew/pages/basePages/editPage/editorPage.jsx +++ b/client/homebrew/pages/basePages/editPage/editorPage.jsx @@ -112,12 +112,15 @@ const EditorPage = (props)=>{ const snipStorage = localStorage.getItem(SNIPKEY); const metaStorage = JSON.parse(localStorage.getItem(METAKEY)); - brew.text = brewStorage ?? brew.text; - brew.style = styleStorage ?? brew.style; - brew.snippets = snipStorage ?? brew.snippets; - brew.renderer = metaStorage?.renderer ?? brew.renderer; - brew.theme = metaStorage?.theme ?? brew.theme; - brew.lang = metaStorage?.lang ?? brew.lang; + brew.title = metaStorage?.title ?? brew.title; + brew.description = metaStorage?.description ?? brew.description; + brew.tags = metaStorage?.tags ?? brew.tags; + brew.text = brewStorage ?? brew.text; + brew.style = styleStorage ?? brew.style; + brew.snippets = snipStorage ?? brew.snippets; + brew.renderer = metaStorage?.renderer ?? brew.renderer; + brew.theme = metaStorage?.theme ?? brew.theme; + brew.lang = metaStorage?.lang ?? brew.lang; } const SAVEKEY = `${SAVEKEY_PREFIX}${global.account?.username}`; @@ -130,7 +133,7 @@ const EditorPage = (props)=>{ localStorage.setItem(BREWKEY, brew.text); if(brew.style) localStorage.setItem(STYLEKEY, brew.style); if(brew.snippets) localStorage.setItem(SNIPKEY, brew.snippets); - localStorage.setItem(METAKEY, JSON.stringify({ renderer: brew.renderer, theme: brew.theme, lang: brew.lang })); + localStorage.setItem(METAKEY, JSON.stringify({ title: brew.title, description: brew.description, tags: brew.tags, renderer: brew.renderer, theme: brew.theme, lang: brew.lang })); if(window.location.pathname !== '/new') window.history.replaceState({}, window.location.title, '/new/'); }; @@ -177,9 +180,12 @@ const EditorPage = (props)=>{ if(field == 'style') localStorage.setItem(STYLEKEY, value); if(field == 'snippets') localStorage.setItem(SNIPKEY, value); if(field == 'metadata') localStorage.setItem(METAKEY, JSON.stringify({ - renderer : value.renderer, - theme : value.theme, - lang : value.lang + title : value.title, + description : value.description, + tags : value.tags, + renderer : value.renderer, + theme : value.theme, + lang : value.lang })); } }; From e38e92c170fc2ea3051095890d3bb991177fd795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 21 Oct 2026 23:30:11 +0200 Subject: [PATCH 26/35] reorder routes by basePage and lint them --- client/homebrew/homebrew.jsx | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/client/homebrew/homebrew.jsx b/client/homebrew/homebrew.jsx index 1ee5efa2a..57e35a9d2 100644 --- a/client/homebrew/homebrew.jsx +++ b/client/homebrew/homebrew.jsx @@ -69,25 +69,28 @@ const Homebrew = (props)=>{ ); } - return (
- } /> - } /> - } /> - } /> - } /> - }/> - } /> - } /> - } /> - } /> + } /> + } /> } /> - } /> - } /> - } /> + + } /> + } /> + } /> + + } /> + } /> + } /> + } /> + + } /> + } /> + } /> + + }/>
From 42d2ff48db5f1b190d7f1164243ceab5f898cf48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Losada=20Hern=C3=A1ndez?= Date: Wed, 21 Oct 2026 23:32:57 +0200 Subject: [PATCH 27/35] lint index.html --- index.html | 3 --- 1 file changed, 3 deletions(-) diff --git a/index.html b/index.html index f30a42c91..1f79c174f 100644 --- a/index.html +++ b/index.html @@ -6,14 +6,11 @@ name="viewport" content="width=device-width, initial-scale=1, height=device-height, interactive-widget=resizes-visual" /> - The Homebrewery - NaturalCrit -
-