Merge branch 'master' of https://github.com/naturalcrit/homebrewery into fit-listpage-into-vault

This commit is contained in:
Víctor Losada Hernández
2026-10-08 21:53:39 +02:00
6 changed files with 186 additions and 298 deletions
+14 -137
View File
@@ -1,30 +1,15 @@
import './editPage.less';
// Common imports
import React, { useState, useEffect, useRef, useEffectEvent } from 'react';
import request from '../../utils/request-middleware.js';
import _ from 'lodash';
import { DEFAULT_BREW_LOAD } from '../../../../server/brewDefaults.js';
import React from 'react';
import request from '../../utils/request-middleware.js';
import _ from 'lodash';
import useCommonEditPageFunctions from '../../utils/commonEditPageFunctions.jsx';
import Nav from '@navbar/nav.jsx';
import { DEFAULT_BREW_LOAD } from '../../../../server/brewDefaults.js';
// Page specific imports
import { md5 } from 'hash-wasm';
import { gzipSync, strToU8 } from 'fflate';
import { makePatches, stringifyPatches } from '@sanity/diff-match-patch';
import { updateHistory, versionHistoryGarbageCollection } from '../../utils/versionHistory.js';
import googleDriveIcon from '../../googleDrive.svg';
const useLocalStorage = false;
const sandbox = false;
const showFloatingButtons = false;
const showEditorButtons = true;
const pageName = 'editPage';
const EditPage = (props)=>{
props = {
@@ -32,75 +17,19 @@ const EditPage = (props)=>{
...props
};
const [currentBrew, setCurrentBrew] = useState(props.brew);
const [saveGoogle, setSaveGoogle] = useState(!!props.brew.googleId);
const [error, setError] = useState(null);
const [alertTrashedGoogleBrew, setAlertTrashedGoogleBrew] = useState(props.brew.trashed);
const [alertNoGoogleToTransfer, setAlertNoGoogleToTransfer] = useState(false);
const [alertOwnershipToTransfer, setAlertOwnershipToTransfer] = useState(false);
const [confirmGoogleTransfer, setConfirmGoogleTransfer] = useState(false);
const lastSavedBrew = useRef(_.cloneDeep(props.brew));
const handleGoogleClick = ()=>{
if(currentBrew.authors.length > 0 && global.account?.username !== currentBrew.authors[0]) {
setAlertOwnershipToTransfer(true);
return;
}
if(!global.account?.googleId) {
setAlertNoGoogleToTransfer(true);
return;
}
setConfirmGoogleTransfer((prev)=>!prev);
setError(null);
};
const closeAlerts = (e)=>{
e.stopPropagation(); //Only handle click once so alert doesn't reopen
setAlertTrashedGoogleBrew(false);
setAlertNoGoogleToTransfer(false);
setConfirmGoogleTransfer(false);
setAlertOwnershipToTransfer(false);
};
const toggleGoogleStorage = (e)=>{
closeAlerts(e);
const newSaveGoogle = !saveGoogle;
setSaveGoogle((prev)=>!prev);
setError(null);
trySave(true, true, newSaveGoogle);
};
const save = async (brew, saveToGoogle)=>{
await updateHistory(brew).catch(console.error);
await versionHistoryGarbageCollection().catch(console.error);
//Prepare content to send to server
const brewToSave = {
...brew,
text : brew.text.normalize('NFC'),
pageCount : ((brew.renderer === 'legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/gm)) || []).length + 1,
patches : stringifyPatches(makePatches(encodeURI(lastSavedBrew.current.text.normalize('NFC')), encodeURI(brew.text.normalize('NFC')))),
hash : await md5(lastSavedBrew.current.text.normalize('NFC')),
textBin : undefined,
version : lastSavedBrew.current.version
};
const compressedBrew = gzipSync(strToU8(JSON.stringify(brewToSave)));
const compressedBrew = gzipSync(strToU8(JSON.stringify(brew)));
const transfer = saveToGoogle === _.isNil(brew.googleId);
const params = transfer ? `?${saveToGoogle ? 'saveToGoogle' : 'removeFromGoogle'}=true` : '';
const res = await request
.put(`/api/update/${brewToSave.editId}${params}`)
.put(`/api/update/${brew.editId}${params}`)
.set('Content-Encoding', 'gzip')
.set('Content-Type', 'application/json')
.send(compressedBrew)
.catch((err)=>{
console.error('Error Updating Local Brew');
setError(err);
});
if(!res) return;
.send(compressedBrew);
const updatedFields = {
googleId : res.body.googleId ?? null, //TODO: investigate if we can set googleId:null in server/update instead of undefined
@@ -116,70 +45,18 @@ const EditPage = (props)=>{
history.replaceState(null, null, `/edit/${savedBrew.editId}`);;
};
const renderGoogleDriveIcon = ()=>(
<Nav.item className='googleDriveStorage' onClick={handleGoogleClick}>
<img src={googleDriveIcon} className={saveGoogle ? '' : 'inactive'} alt='Google Drive icon' />
{alertOwnershipToTransfer && (
<div className='errorContainer'>
You must be the Owner to transfer between the Homebrewery and Google Drive!
The owner of this file is {currentBrew.authors[0]}.
<br></br>
<div className='confirm' onClick={closeAlerts}> Okay </div>
</div>
)}
{alertNoGoogleToTransfer && (
<div className='errorContainer'>
You must be signed in to a Google account to transfer between the Homebrewery and Google Drive!
<a target='_blank' rel='noopener noreferrer' href={`https://www.naturalcrit.com/login?redirect=${window.location.href}`}>
<div className='confirm' onClick={closeAlerts}> Sign In </div>
</a>
<div className='deny' onClick={closeAlerts}> Not Now </div>
</div>
)}
{alertTrashedGoogleBrew && (
<div className='errorContainer'>
This brew is currently in your Trash folder on Google Drive!<br />
If you want to keep it, make sure to move it before it is deleted permanently!<br />
<div className='confirm' onClick={toggleGoogleStorage}> Save my brew </div>
</div>
)}
{confirmGoogleTransfer && (
<div className='errorContainer'>
{saveGoogle
? 'Would you like to transfer this brew from your Google Drive storage back to the Homebrewery?'
: 'Would you like to transfer this brew from the Homebrewery to your personal Google Drive storage?'}
<br />
<div className='confirm' onClick={toggleGoogleStorage}> Yes </div>
<div className='deny' onClick={closeAlerts}> No </div>
</div>
)}
</Nav.item>
);
const {
renderPanels,
trySave,
} = useCommonEditPageFunctions({
saveGoogle,
setSaveGoogle,
error,
setError,
currentBrew,
setCurrentBrew,
useLocalStorage,
sandbox,
showFloatingButtons,
lastSavedBrew,
useLocalStorage : false,
sandbox : false,
showFloatingButtons : false,
showEditorButtons : true,
pageName : 'newPage',
brew : props.brew,
userThemes : props.userThemes,
save,
onSaveSuccess,
pageName,
showEditorButtons,
renderGoogleDriveIcon,
userThemes : props.userThemes
});
return renderPanels();
+13 -39
View File
@@ -1,20 +1,11 @@
import './homePage.less';
// Common imports
import React, { useState, useEffect, useRef, useEffectEvent } from 'react';
import request from '../../utils/request-middleware.js';
import _ from 'lodash';
import { DEFAULT_BREW } from '../../../../server/brewDefaults.js';
import React from 'react';
import request from '../../utils/request-middleware.js';
import _ from 'lodash';
import useCommonEditPageFunctions from '../../utils/commonEditPageFunctions.jsx';
// Page specific imports
const useLocalStorage = false;
const sandbox = true;
const showFloatingButtons = true;
const showEditorButtons = false;
const pageName = 'homePage';
import { DEFAULT_BREW } from '../../../../server/brewDefaults.js';
const HomePage =(props)=>{
props = {
@@ -22,21 +13,10 @@ const HomePage =(props)=>{
...props
};
const [currentBrew, setCurrentBrew] = useState(props.brew);
const [saveGoogle, setSaveGoogle] = useState(global.account?.googleId ? true : false);
const [error, setError] = useState(undefined);
const lastSavedBrew = useRef(_.cloneDeep(props.brew));
const save = async (brew, saveToGoogle)=>{
const res = await request
.post(`/api${saveGoogle ? '?saveToGoogle=true' : ''}`)
.send(brew)
.catch((err)=>{
console.error('Error Updating Local Brew');
setError(err);
});
if(!res) return;
.post(`/api${saveToGoogle ? '?saveToGoogle=true' : ''}`)
.send(brew);
return res.body;
};
@@ -49,21 +29,15 @@ const HomePage =(props)=>{
const {
renderPanels
} = useCommonEditPageFunctions({
saveGoogle,
setSaveGoogle,
error,
setError,
currentBrew,
setCurrentBrew,
useLocalStorage,
sandbox,
showFloatingButtons,
lastSavedBrew,
useLocalStorage : false,
sandbox : true,
showFloatingButtons : true,
showEditorButtons : false,
pageName : 'homePage',
brew : props.brew,
userThemes : {},
save,
onSaveSuccess,
pageName,
showEditorButtons,
userThemes : {}
});
return renderPanels();
+13 -47
View File
@@ -1,20 +1,11 @@
import './newPage.less';
// Common imports
import React, { useState, useEffect, useRef, useEffectEvent } from 'react';
import request from '../../utils/request-middleware.js';
import _ from 'lodash';
import { DEFAULT_BREW } from '../../../../server/brewDefaults.js';
import React from 'react';
import request from '../../utils/request-middleware.js';
import _ from 'lodash';
import useCommonEditPageFunctions from '../../utils/commonEditPageFunctions.jsx';
// Page specific imports
const useLocalStorage = true;
const sandbox = true;
const showFloatingButtons = false;
const showEditorButtons = true;
const pageName = 'newPage';
import { DEFAULT_BREW } from '../../../../server/brewDefaults.js';
const NewPage = (props)=>{
props = {
@@ -22,29 +13,10 @@ const NewPage = (props)=>{
...props
};
const [currentBrew, setCurrentBrew] = useState(props.brew);
const [saveGoogle, setSaveGoogle] = useState(global.account?.googleId ? true : false);
const [error, setError] = useState(null);
const lastSavedBrew = useRef(_.cloneDeep(props.brew));
const save = async (brew, saveToGoogle)=>{
//Prepare content to send to server
const brewToSave = {
...brew,
text : brew.text.normalize('NFC'),
pageCount : ((brew.renderer === 'legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/gm)) || []).length + 1,
textBin : undefined
};
const res = await request
.post(`/api${saveGoogle ? '?saveToGoogle=true' : ''}`)
.send(brewToSave)
.catch((err)=>{
console.error('Error Updating Local Brew');
setError(err);
});
if(!res) return;
.post(`/api${saveToGoogle ? '?saveToGoogle=true' : ''}`)
.send(brew);
return res.body;
};
@@ -57,21 +29,15 @@ const NewPage = (props)=>{
const {
renderPanels
} = useCommonEditPageFunctions({
saveGoogle,
setSaveGoogle,
error,
setError,
currentBrew,
setCurrentBrew,
useLocalStorage,
sandbox,
showFloatingButtons,
lastSavedBrew,
useLocalStorage : true,
sandbox : true,
showFloatingButtons : false,
showEditorButtons : true,
pageName : 'newPage',
brew : props.brew,
userThemes : props.userThemes,
save,
onSaveSuccess,
pageName,
showEditorButtons,
userThemes : props.userThemes
});
return renderPanels();
+112 -16
View File
@@ -2,6 +2,8 @@ import React, { useState, useEffect, useEffectEvent, useRef } from 'react';
import { printCurrentBrew, fetchThemeBundle } from '@shared/helpers.js';
import _ from 'lodash';
import { hbfm } from 'marked-hbfm';
import { md5 } from 'hash-wasm';
import { makePatches, stringifyPatches } from '@sanity/diff-match-patch';
import Headtags from '@vitreum/headtags.js';
import SplitPane from '@components/splitPane/splitPane.jsx';
@@ -22,6 +24,7 @@ import PrintNavItem from '@navbar/print.navitem.jsx';
import ShareNavItem from '@navbar/share.navitem.jsx';
import RecentNavItems from '@navbar/recent.navitem.jsx';
const { both: RecentNavItem } = RecentNavItems;
import googleDriveIcon from '../googleDrive.svg';
const AUTOSAVE_KEY = 'HB_editor_autoSaveOn';
const BREWKEY = 'HB_newPage_content';
@@ -36,24 +39,18 @@ const UNSAVED_WARNING_POPUP_TIMEOUT = 4000; //Show the warning for 4 seconds
export default function useCommonEditPageFunctions(dependencies) {
const {
saveGoogle,
setSaveGoogle,
error,
setError,
currentBrew,
setCurrentBrew,
brew,
useLocalStorage,
sandbox,
showFloatingButtons,
lastSavedBrew,
save,
onSaveSuccess,
pageName,
renderGoogleDriveIcon = ()=>{},
showEditorButtons,
userThemes = {}
} = dependencies;
const [currentBrew, setCurrentBrew] = useState(brew);
const [isSaving, setIsSaving] = useState(false);
const [lastSavedTime, setLastSavedTime] = useState(new Date());
const [autoSaveEnabled, setAutoSaveEnabled] = useState(!sandbox);
@@ -61,6 +58,13 @@ export default function useCommonEditPageFunctions(dependencies) {
const [unsavedChanges, setUnsavedChanges] = useState(false);
const [themeBundle, setThemeBundle] = useState({});
const [HTMLErrors, setHTMLErrors] = useState(hbfm.validate(currentBrew.text));
const [saveGoogle, setSaveGoogle] = useState(currentBrew.googleId);
const [error, setError] = useState(null);
const [alertTrashedGoogleBrew, setAlertTrashedGoogleBrew] = useState(currentBrew.trashed);
const [alertNoGoogleToTransfer, setAlertNoGoogleToTransfer] = useState(false);
const [alertOwnershipToTransfer, setAlertOwnershipToTransfer] = useState(false);
const [confirmGoogleTransfer, setConfirmGoogleTransfer] = useState(false);
const [currentEditorViewPageNum, setCurrentEditorViewPageNum] = useState(1);
const [currentEditorCursorPageNum, setCurrentEditorCursorPageNum] = useState(1);
@@ -68,6 +72,7 @@ export default function useCommonEditPageFunctions(dependencies) {
const unsavedChangesRef = useRef(unsavedChanges); // onBeforeUnload lives outside React and needs ref to unsavedChanges
const warnUnsavedTimeout = useRef(null); // timers live outside React and need ref to consistently track time
const lastSavedBrew = useRef(_.cloneDeep(currentBrew));
const saveTimeout = useRef(null);
const editorRef = useRef(null);
@@ -202,12 +207,27 @@ export default function useCommonEditPageFunctions(dependencies) {
setIsSaving(true);
setError(null);
setHTMLErrors(hbfm.validate(currentBrew.text));
const brewToSave = currentBrew;
let savedBrew = await save(brewToSave, saveToGoogle)
.catch((err)=>{setError(err);});
//Prepare content to send to server
const snapshotBrewBeforeSave = currentBrew;
const brewToSave = {
...currentBrew,
text : currentBrew.text.normalize('NFC'),
pageCount : ((currentBrew.renderer === 'legacy' ? currentBrew.text.match(/\\page/g) : currentBrew.text.match(/^(?=\\page(?:break)?(?: *{[^\n{}]*})?$)/gm)) || []).length + 1,
patches : stringifyPatches(makePatches(encodeURI(lastSavedBrew.current.text.normalize('NFC')), encodeURI(currentBrew.text.normalize('NFC')))),
hash : await md5(lastSavedBrew.current.text.normalize('NFC')),
textBin : undefined,
version : lastSavedBrew.current.version
};
const savedBrew = await save(brewToSave, saveToGoogle)
.catch((err)=>{
console.error('Error Updating Local Brew');
setError(err);
});
if(savedBrew) {
lastSavedBrew.current = {
...brewToSave,
...snapshotBrewBeforeSave,
...savedBrew
};
@@ -217,11 +237,11 @@ export default function useCommonEditPageFunctions(dependencies) {
}));
if(useLocalStorage) clearLocalStorage();
setLastSavedTime(new Date());
if(!autoSaveEnabled) resetWarnUnsavedTimer();
onSaveSuccess(savedBrew);
}
setIsSaving(false);
setLastSavedTime(new Date());
if(!autoSaveEnabled) resetWarnUnsavedTimer();
}, newTimeout);
});
@@ -262,18 +282,94 @@ export default function useCommonEditPageFunctions(dependencies) {
snippets : newData.snippets
}));
//======----- Google Toggle Button -----======
const closeAlerts = (e)=>{
e.stopPropagation(); //Only handle click once so alert doesn't reopen
setAlertTrashedGoogleBrew(false);
setAlertNoGoogleToTransfer(false);
setConfirmGoogleTransfer(false);
setAlertOwnershipToTransfer(false);
};
const handleGoogleClick = ()=>{
if(currentBrew.authors.length > 0 && global.account?.username !== currentBrew.authors[0]) {
setAlertOwnershipToTransfer(true);
return;
}
if(!global.account?.googleId) {
setAlertNoGoogleToTransfer(true);
return;
}
setConfirmGoogleTransfer((prev)=>!prev);
setError(null);
};
const toggleGoogleStorage = (e)=>{
closeAlerts(e);
const newSaveGoogle = !saveGoogle;
setSaveGoogle((prev)=>!prev);
setError(null);
trySave(true, true, newSaveGoogle);
};
const renderGoogleDriveIcon = ()=>(
<Nav.item className='googleDriveStorage' onClick={handleGoogleClick}>
<img src={googleDriveIcon} className={saveGoogle ? '' : 'inactive'} alt='Google Drive icon' />
{alertOwnershipToTransfer && (
<div className='errorContainer'>
You must be the Owner to transfer between the Homebrewery and Google Drive!
The owner of this file is {currentBrew.authors[0]}.
<br></br>
<div className='confirm' onClick={closeAlerts}> Okay </div>
</div>
)}
{alertNoGoogleToTransfer && (
<div className='errorContainer'>
You must be signed in to a Google account to transfer between the Homebrewery and Google Drive!
<a target='_blank' rel='noopener noreferrer' href={`https://www.naturalcrit.com/login?redirect=${window.location.href}`}>
<div className='confirm' onClick={closeAlerts}> Sign In </div>
</a>
<div className='deny' onClick={closeAlerts}> Not Now </div>
</div>
)}
{alertTrashedGoogleBrew && (
<div className='errorContainer'>
This brew is currently in your Trash folder on Google Drive!<br />
If you want to keep it, make sure to move it before it is deleted permanently!<br />
<div className='confirm' onClick={toggleGoogleStorage}> Save my brew </div>
</div>
)}
{confirmGoogleTransfer && (
<div className='errorContainer'>
{saveGoogle
? 'Would you like to transfer this brew from your Google Drive storage back to the Homebrewery?'
: 'Would you like to transfer this brew from the Homebrewery to your personal Google Drive storage?'}
<br />
<div className='confirm' onClick={toggleGoogleStorage}> Yes </div>
<div className='deny' onClick={closeAlerts}> No </div>
</div>
)}
</Nav.item>
);
//======----- Navbar -----======
const renderNavbar = ()=>(
<Navbar>
<Nav.section>
<Nav.item className='brewTitle'>{currentBrew.title}</Nav.item>
</Nav.section>
<Nav.section>
{renderGoogleDriveIcon()}
{(pageName == 'editPage') && renderGoogleDriveIcon()}
{error
? <ErrorNavItem error={error} clearError={clearError} />
: <Nav.dropdown className='save-menu'>
{renderSaveButton()}
{pageName == 'editPage' && renderAutoSaveButton()}
{(pageName == 'editPage') && renderAutoSaveButton()}
</Nav.dropdown>}
<NewBrewItem />
<PrintNavItem />
+28 -53
View File
@@ -25,7 +25,7 @@
"@codemirror/search": "^6.6.0",
"@codemirror/state": "^6.7.6",
"@codemirror/view": "^6.43.13",
"@googleapis/drive": "^26.0.1",
"@googleapis/drive": "^26.0.2",
"@lezer/highlight": "^1.2.4",
"@oddbird/css-anchor-positioning": "^0.10.2",
"@sanity/diff-match-patch": "^3.2.1",
@@ -52,7 +52,7 @@
"marked-hbfm": "^1.0.1",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.31.0",
"mongoose": "^9.10.2",
"mongoose": "^9.10.4",
"nanoid": "6.0.1",
"nconf": "^0.13.0",
"node": "^26.10.0",
@@ -65,7 +65,7 @@
"superagent": "^10.4.1"
},
"devDependencies": {
"@stylistic/stylelint-plugin": "^5.3.0",
"@stylistic/stylelint-plugin": "^5.3.1",
"babel-jest": "^30.5.2",
"babel-plugin-transform-import-meta": "^3.0.0",
"eslint": "9.7",
@@ -77,11 +77,11 @@
"jsdom": "^30.1.1",
"jsdom-global": "^3.0.2",
"postcss-less": "^6.0.0",
"stylelint": "^17.15.0",
"stylelint": "^17.16.0",
"stylelint-config-recess-order": "^7.7.0",
"stylelint-config-recommended": "^18.0.0",
"supertest": "^7.3.0",
"vite": "^8.3.1"
"supertest": "^7.3.1",
"vite": "^8.3.2"
},
"engines": {
"node": ">=26.4.0",
@@ -2516,9 +2516,9 @@
"license": "MIT"
},
"node_modules/@googleapis/drive": {
"version": "26.0.1",
"resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-26.0.1.tgz",
"integrity": "sha512-q1U+WNS2OPAS3Prn3FD0otYNudnv+feXX9DP8qrDqGGwIVU90OBnP6NV66aTc0qJrqeJvnrCYcnjddjm4h23iQ==",
"version": "26.0.2",
"resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-26.0.2.tgz",
"integrity": "sha512-VeKVZvzgH4ioczJZrZ5sy73RJbHrYLI1XZIZ3ZqGKQhC4U03XsOOOiBHKqvH4F9vbXrxR2LawmktoIJuTJhzYg==",
"license": "Apache-2.0",
"dependencies": {
"googleapis-common": "^9.0.0"
@@ -4339,9 +4339,9 @@
"license": "MIT"
},
"node_modules/@stylistic/stylelint-plugin": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-5.3.0.tgz",
"integrity": "sha512-xbyxPeaO9Vns6A8/0MQV6PzZ5nNZCoGV4q1ERaNd5EBAKlWt+YcpaPapNYNUkZaiQg98+xPbikdA58ehquJG3g==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-5.3.1.tgz",
"integrity": "sha512-w6WFLYzQS6nhbcoq8pwYP+OJYYhsdGEiCxY+eINi2+ouDwRLXTucXZj6IrDzkY4di+vsGOPVvJ4/gXChSeOtlw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -11858,9 +11858,9 @@
}
},
"node_modules/mongoose": {
"version": "9.10.2",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.10.2.tgz",
"integrity": "sha512-oFcFL3dsX5tDzMwBD+GGoDqKIj6RNG3bu3oqlvcUIgPulHP14/6N7c07j3s0XgbrlT7U/lbiXOD89hmPtEXnfQ==",
"version": "9.10.4",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.10.4.tgz",
"integrity": "sha512-p1hx2MSD4B+4shFTpRcd/uTwD6DtZeDJ+YxM9ja8/VBDH7vqqLynwGLA6rWa/tD7voOBoGqQR1wM3+/LcUoEfA==",
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.1.0",
@@ -11879,31 +11879,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==",
"extraneous": true,
"license": "Apache-2.0",
"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==",
"extraneous": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18"
}
},
"node_modules/mongoose/node_modules/mongodb": {
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.6.0.tgz",
@@ -13765,9 +13740,9 @@
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz",
"integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -14103,9 +14078,9 @@
"license": "ISC"
},
"node_modules/stylelint": {
"version": "17.15.0",
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.15.0.tgz",
"integrity": "sha512-mWIkesYQvQjf4Kvdeu9ns0IL8/K/wtjaGxPqsPd6DlLZvaQxGysJsocxUDrBcyHQ5VleAk4uSMat4yMrVED7PA==",
"version": "17.16.0",
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.16.0.tgz",
"integrity": "sha512-ejlWnbJ13x1GU32+zZXa6sYQJsKKkwSdv2B4rAdO3OWdEmvhSJKcXi5pOphQYSzyaj1kXpuGJr7pcMBNfDIuPw==",
"dev": true,
"funding": [
{
@@ -14305,9 +14280,9 @@
}
},
"node_modules/supertest": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/supertest/-/supertest-7.3.0.tgz",
"integrity": "sha512-UwwmWq3xLhyU96c521wYNaBg7IqX78Wpj4FKs6Katp6vdklX42zU5ESvX3KeZRLFyO44RwFp4yvxYMNZzCcX9g==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/supertest/-/supertest-7.3.1.tgz",
"integrity": "sha512-vNaNCFoCR/edDbErQZp5KLNT8R4dSPHX7rlDLoE8hWLAm/878j6ShMbe8HK/E6RwwuhlSXVelXAIg9AxR5WWPw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -15104,15 +15079,15 @@
}
},
"node_modules/vite": {
"version": "8.3.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.3.1.tgz",
"integrity": "sha512-/bvH9E9tmCXRGp2uXY3WbOldqpTwFkbha/8ANaEQ6VkxhH60KyqLwgZq6lG2y+4uT55x9+9eUHMpQ7uGnOCKjA==",
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.3.2.tgz",
"integrity": "sha512-SQr1x6W5vVSbROg7vsyXIaxK9b0G7zsT68acdWWRmnBUsgDieLCRG+Rep9WdZgcposvv/GSnr4GUUBqB3vXq6w==",
"license": "MIT",
"dependencies": {
"lightningcss": "^1.33.0",
"picomatch": "^4.0.7",
"postcss": "^8.5.28",
"rolldown": "~1.2.9",
"rolldown": "~1.2.11",
"tinyglobby": "^0.2.17"
},
"bin": {
+6 -6
View File
@@ -102,7 +102,7 @@
"@codemirror/search": "^6.6.0",
"@codemirror/state": "^6.7.6",
"@codemirror/view": "^6.43.13",
"@googleapis/drive": "^26.0.1",
"@googleapis/drive": "^26.0.2",
"@lezer/highlight": "^1.2.4",
"@oddbird/css-anchor-positioning": "^0.10.2",
"@sanity/diff-match-patch": "^3.2.1",
@@ -129,7 +129,7 @@
"marked-hbfm": "^1.0.1",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.31.0",
"mongoose": "^9.10.2",
"mongoose": "^9.10.4",
"nanoid": "6.0.1",
"nconf": "^0.13.0",
"node": "^26.10.0",
@@ -142,7 +142,7 @@
"superagent": "^10.4.1"
},
"devDependencies": {
"@stylistic/stylelint-plugin": "^5.3.0",
"@stylistic/stylelint-plugin": "^5.3.1",
"babel-jest": "^30.5.2",
"babel-plugin-transform-import-meta": "^3.0.0",
"eslint": "9.7",
@@ -154,10 +154,10 @@
"jsdom": "^30.1.1",
"jsdom-global": "^3.0.2",
"postcss-less": "^6.0.0",
"stylelint": "^17.15.0",
"stylelint": "^17.16.0",
"stylelint-config-recess-order": "^7.7.0",
"stylelint-config-recommended": "^18.0.0",
"supertest": "^7.3.0",
"vite": "^8.3.1"
"supertest": "^7.3.1",
"vite": "^8.3.2"
}
}