diff --git a/babel.config.json b/babel.config.json
new file mode 100644
index 000000000..5e768ec31
--- /dev/null
+++ b/babel.config.json
@@ -0,0 +1,10 @@
+{
+ "presets": [
+ "@babel/preset-env",
+ "@babel/preset-react"
+ ],
+ "plugins": [
+ "@babel/plugin-transform-runtime",
+ "babel-plugin-transform-import-meta"
+ ]
+}
diff --git a/changelog.md b/changelog.md
index 1f7815d8d..3736ba9b0 100644
--- a/changelog.md
+++ b/changelog.md
@@ -85,6 +85,52 @@ pre {
## changelog
For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery).
+### Wednesday 11/27/2024 - v3.16.1
+
+{{taskList
+##### 5e-Cleric
+
+* [x] Allow linking to specific HTML IDs via `#ID` at the end of the URL, e.g.: `homebrewery.naturalcrit.com/share/share/a6RCXwaDS58i#p4` to link to Page 4 directly
+
+Fixes issues [#2820](https://github.com/naturalcrit/homebrewery/issues/2820), [#3505](https://github.com/naturalcrit/homebrewery/issues/3505)
+
+* [x] Fix generation of link to certain Google Drive brews
+
+Fixes issue [#3776](https://github.com/naturalcrit/homebrewery/issues/3776)
+
+##### abquintic
+
+* [x] Fix blank pages appearing when pasting text
+
+Fixes issue [#3718](https://github.com/naturalcrit/homebrewery/issues/3718)
+
+##### Gazook89
+
+* [x] Add new brew viewing options to the view toolbar
+- {{fac,single-spread}} {{openSans **SINGLE PAGE**}}
+- {{fac,facing-spread}} {{openSans **TWO PAGE**}}
+- {{fac,flow-spread}} {{openSans **GRID**}}
+
+Fixes issue [#1379](https://github.com/naturalcrit/homebrewery/issues/1379)
+
+* [x] Updates to tag input boxes
+
+##### G-Ambatte
+
+* [x] Admin tools to fix certain corrupted documents
+
+Fixes issue [#3801](https://github.com/naturalcrit/homebrewery/issues/3801)
+
+* [x] Fix print window being affected by document zoom
+
+Fixes issue [#3744](https://github.com/naturalcrit/homebrewery/issues/3744)
+
+
+##### calculuschild, 5e-Cleric, G-Ambatte, Gazook89, abquintic
+
+* [x] Multiple code refactors, cleanups, and security fixes
+}}
+
### Saturday 10/12/2024 - v3.16.0
{{taskList
diff --git a/client/admin/brewUtils/brewLookup/brewLookup.jsx b/client/admin/brewUtils/brewLookup/brewLookup.jsx
index 50a2f2015..e5b585ced 100644
--- a/client/admin/brewUtils/brewLookup/brewLookup.jsx
+++ b/client/admin/brewUtils/brewLookup/brewLookup.jsx
@@ -1,3 +1,5 @@
+require('./brewLookup.less');
+
const React = require('react');
const createClass = require('create-react-class');
const cx = require('classnames');
@@ -12,22 +14,43 @@ const BrewLookup = createClass({
},
getInitialState() {
return {
- query : '',
- foundBrew : null,
- searching : false,
- error : null
+ query : '',
+ foundBrew : null,
+ searching : false,
+ error : null,
+ scriptCount : 0
};
},
handleChange(e){
this.setState({ query: e.target.value });
},
lookup(){
- this.setState({ searching: true, error: null });
+ this.setState({ searching: true, error: null, scriptCount: 0 });
request.get(`/admin/lookup/${this.state.query}`)
- .then((res)=>this.setState({ foundBrew: res.body }))
+ .then((res)=>{
+ const foundBrew = res.body;
+ const scriptCheck = foundBrew?.text.match(/(<\/?s)cript/g);
+ this.setState({
+ foundBrew : foundBrew,
+ scriptCount : scriptCheck?.length || 0,
+ });
+ })
.catch((err)=>this.setState({ error: err }))
- .finally(()=>this.setState({ searching: false }));
+ .finally(()=>{
+ this.setState({
+ searching : false
+ });
+ });
+ },
+
+ async cleanScript(){
+ if(!this.state.foundBrew?.shareId) return;
+
+ await request.put(`/admin/clean/script/${this.state.foundBrew.shareId}`)
+ .catch((err)=>{ this.setState({ error: err }); return; });
+
+ this.lookup();
},
renderFoundBrew(){
@@ -46,12 +69,23 @@ const BrewLookup = createClass({
Share Link
/share/{brew.shareId}
+ Created Time
+ {brew.createdAt ? Moment(brew.createdAt).toLocaleString() : 'No creation date'}
+
Last Updated
{Moment(brew.updatedAt).fromNow()}
Num of Views
{brew.views}
+
+ SCRIPT tags detected
+ {this.state.scriptCount}
+ {this.state.scriptCount > 0 &&
+
+ CLEAN BREW
+
+ }
;
},
diff --git a/client/admin/brewUtils/brewLookup/brewLookup.less b/client/admin/brewUtils/brewLookup/brewLookup.less
new file mode 100644
index 000000000..da15e3a64
--- /dev/null
+++ b/client/admin/brewUtils/brewLookup/brewLookup.less
@@ -0,0 +1,6 @@
+.brewLookup {
+ .cleanButton {
+ display : inline-block;
+ width : 100%;
+ }
+}
\ No newline at end of file
diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx
index 795095f71..879af2a9a 100644
--- a/client/homebrew/brewRenderer/brewRenderer.jsx
+++ b/client/homebrew/brewRenderer/brewRenderer.jsx
@@ -5,7 +5,7 @@ const { useState, useRef, useCallback, useMemo, useEffect } = React;
const _ = require('lodash');
const MarkdownLegacy = require('naturalcrit/markdownLegacy.js');
-const Markdown = require('naturalcrit/markdown.js');
+import Markdown from 'naturalcrit/markdown.js';
const ErrorBar = require('./errorBar/errorBar.jsx');
const ToolBar = require('./toolBar/toolBar.jsx');
@@ -201,7 +201,8 @@ const BrewRenderer = (props)=>{
renderedPages.length = 0;
// Render currently-edited page first so cross-page effects (variables, links) can propagate out first
- renderedPages[props.currentEditorCursorPageNum - 1] = renderPage(rawPages[props.currentEditorCursorPageNum - 1], props.currentEditorCursorPageNum - 1);
+ if(rawPages.length > props.currentEditorCursorPageNum -1)
+ renderedPages[props.currentEditorCursorPageNum - 1] = renderPage(rawPages[props.currentEditorCursorPageNum - 1], props.currentEditorCursorPageNum - 1);
_.forEach(rawPages, (page, index)=>{
if((isInView(index) || !renderedPages[index]) && typeof window !== 'undefined'){
diff --git a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx
index 3ea165d62..0c8fc4b8c 100644
--- a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx
+++ b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx
@@ -1,6 +1,6 @@
require('./notificationPopup.less');
import React, { useEffect, useState } from 'react';
-const request = require('../../utils/request-middleware.js');
+import request from '../../utils/request-middleware.js';
import Dialog from '../../../components/dialog.jsx';
diff --git a/client/homebrew/brewRenderer/toolBar/toolBar.less b/client/homebrew/brewRenderer/toolBar/toolBar.less
index dfbd7a20d..577fd71c2 100644
--- a/client/homebrew/brewRenderer/toolBar/toolBar.less
+++ b/client/homebrew/brewRenderer/toolBar/toolBar.less
@@ -13,6 +13,7 @@
height : auto;
padding : 2px 0;
font-family : 'Open Sans', sans-serif;
+ font-size : 13px;
color : #CCCCCC;
background-color : #555555;
& > *:not(.toggleButton) {
@@ -154,13 +155,6 @@
width : auto;
min-width : 46px;
height : 100%;
- padding : 0 0px;
- font-weight : unset;
- color : inherit;
- background-color : unset;
-
- &:not(button:has(i, svg)) { padding : 0 8px; }
-
&:hover { background-color : #444444; }
&:focus { border : 1px solid #D3D3D3;outline : none;}
&:disabled {
diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx
index 9fef72cbb..bba5f3ad9 100644
--- a/client/homebrew/editor/editor.jsx
+++ b/client/homebrew/editor/editor.jsx
@@ -4,7 +4,7 @@ const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
const dedent = require('dedent-tabs').default;
-const Markdown = require('../../../shared/naturalcrit/markdown.js');
+import Markdown from '../../../shared/naturalcrit/markdown.js';
const CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx');
const SnippetBar = require('./snippetbar/snippetbar.jsx');
diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.jsx b/client/homebrew/editor/metadataEditor/metadataEditor.jsx
index e66fa64e2..bfc3b8b61 100644
--- a/client/homebrew/editor/metadataEditor/metadataEditor.jsx
+++ b/client/homebrew/editor/metadataEditor/metadataEditor.jsx
@@ -3,10 +3,10 @@ require('./metadataEditor.less');
const React = require('react');
const createClass = require('create-react-class');
const _ = require('lodash');
-const request = require('../../utils/request-middleware.js');
+import request from '../../utils/request-middleware.js';
const Nav = require('naturalcrit/nav/nav.jsx');
const Combobox = require('client/components/combobox.jsx');
-const StringArrayEditor = require('../stringArrayEditor/stringArrayEditor.jsx');
+const TagInput = require('../tagInput/tagInput.jsx');
const Themes = require('themes/themes.json');
@@ -341,10 +341,11 @@ const MetadataEditor = createClass({
{this.renderThumbnail()}
- this.handleFieldChange('tags', e)}/>
+ onChange={(e)=>this.handleFieldChange('tags', e)}
+ />
systems
@@ -363,12 +364,13 @@ const MetadataEditor = createClass({
{this.renderAuthors()}
-
!this.props.metadata.authors?.includes(v)]}
placeholder='invite author' unique={true}
values={this.props.metadata.invitedAuthors}
notes={['Invited author usernames are case sensitive.', 'After adding an invited author, send them the edit link. There, they can choose to accept or decline the invitation.']}
- onChange={(e)=>this.handleFieldChange('invitedAuthors', e)}/>
+ onChange={(e)=>this.handleFieldChange('invitedAuthors', e)}
+ />
Privacy
diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.less b/client/homebrew/editor/metadataEditor/metadataEditor.less
index 62ec6b37b..2cff01cfe 100644
--- a/client/homebrew/editor/metadataEditor/metadataEditor.less
+++ b/client/homebrew/editor/metadataEditor/metadataEditor.less
@@ -79,6 +79,7 @@
text-overflow : ellipsis;
}
button {
+ .colorButton();
padding : 0px 5px;
color : white;
background-color : black;
@@ -138,16 +139,16 @@
margin-bottom : 15px;
button { width : 100%; }
button.publish {
- .button(@blueLight);
+ .colorButton(@blueLight);
}
button.unpublish {
- .button(@silver);
+ .colorButton(@silver);
}
}
.delete.field .value {
button {
- .button(@red);
+ .colorButton(@red);
}
}
.authors.field .value {
@@ -271,7 +272,7 @@
&:last-child { border-radius : 0 0.5em 0.5em 0; }
}
- .badge {
+ .tag {
padding : 0.3em;
margin : 2px;
font-size : 0.9em;
diff --git a/client/homebrew/editor/stringArrayEditor/stringArrayEditor.jsx b/client/homebrew/editor/stringArrayEditor/stringArrayEditor.jsx
deleted file mode 100644
index 47ab038cc..000000000
--- a/client/homebrew/editor/stringArrayEditor/stringArrayEditor.jsx
+++ /dev/null
@@ -1,149 +0,0 @@
-const React = require('react');
-const createClass = require('create-react-class');
-const _ = require('lodash');
-
-const StringArrayEditor = createClass({
- displayName : 'StringArrayEditor',
- getDefaultProps : function() {
- return {
- label : '',
- values : [],
- valuePatterns : null,
- validators : [],
- placeholder : '',
- notes : [],
- unique : false,
- cannotEdit : [],
- onChange : ()=>{}
- };
- },
-
- getInitialState : function() {
- return {
- valueContext : !!this.props.values ? this.props.values.map((value)=>({
- value,
- editing : false
- })) : [],
- temporaryValue : '',
- updateValue : ''
- };
- },
-
- componentDidUpdate : function(prevProps) {
- if(!_.eq(this.props.values, prevProps.values)) {
- this.setState({
- valueContext : this.props.values ? this.props.values.map((newValue)=>({
- value : newValue,
- editing : this.state.valueContext.find(({ value })=>value === newValue)?.editing || false
- })) : []
- });
- }
- },
-
- handleChange : function(value) {
- this.props.onChange({
- target : {
- value
- }
- });
- },
-
- addValue : function(value){
- this.handleChange(_.uniq([...this.props.values, value]));
- this.setState({
- temporaryValue : ''
- });
- },
-
- removeValue : function(index){
- this.handleChange(this.props.values.filter((_, i)=>i !== index));
- },
-
- updateValue : function(value, index){
- const valueContext = this.state.valueContext;
- valueContext[index].value = value;
- valueContext[index].editing = false;
- this.handleChange(valueContext.map((context)=>context.value));
- this.setState({ valueContext, updateValue: '' });
- },
-
- editValue : function(index){
- if(!!this.props.cannotEdit && this.props.cannotEdit.includes(this.props.values[index])) {
- return;
- }
- const valueContext = this.state.valueContext.map((context, i)=>{
- context.editing = index === i;
- return context;
- });
- this.setState({ valueContext, updateValue: this.props.values[index] });
- },
-
- valueIsValid : function(value, index) {
- const values = _.clone(this.props.values);
- if(index !== undefined) {
- values.splice(index, 1);
- }
- const matchesPatterns = !this.props.valuePatterns || this.props.valuePatterns.some((pattern)=>!!(value || '').match(pattern));
- const uniqueIfSet = !this.props.unique || !values.includes(value);
- const passesValidators = !this.props.validators || this.props.validators.every((validator)=>validator(value));
- return matchesPatterns && uniqueIfSet && passesValidators;
- },
-
- handleValueInputKeyDown : function(event, index) {
- if(event.key === 'Enter') {
- if(this.valueIsValid(event.target.value, index)) {
- if(index !== undefined) {
- this.updateValue(event.target.value, index);
- } else {
- this.addValue(event.target.value);
- }
- }
- } else if(event.key === 'Escape') {
- this.closeEditInput(index);
- }
- },
-
- closeEditInput : function(index) {
- const valueContext = this.state.valueContext;
- valueContext[index].editing = false;
- this.setState({ valueContext, updateValue: '' });
- },
-
- render : function() {
- const valueElements = Object.values(this.state.valueContext).map((context, i)=>context.editing
- ?
-
-
this.handleValueInputKeyDown(e, i)}
- onChange={(e)=>this.setState({ updateValue: e.target.value })}/>
- {
{ e.stopPropagation(); this.closeEditInput(i); }}>
}
- {this.valueIsValid(this.state.updateValue, i) ?
{ e.stopPropagation(); this.updateValue(this.state.updateValue, i); }}>
: null}
-
-
- : this.editValue(i)}>{context.value}
- {!!this.props.cannotEdit && this.props.cannotEdit.includes(context.value) ? null :
{ e.stopPropagation(); this.removeValue(i); }}>
}
-
- );
-
- return
-
{this.props.label}
-
-
- {valueElements}
-
-
this.handleValueInputKeyDown(e)}
- onChange={(e)=>this.setState({ temporaryValue: e.target.value })}/>
- {this.valueIsValid(this.state.temporaryValue) ?
{ e.stopPropagation(); this.addValue(this.state.temporaryValue); }}>
: null}
-
-
-
- {this.props.notes ? this.props.notes.map((n, index)=>
{n}
) : null}
-
-
;
- }
-});
-
-module.exports = StringArrayEditor;
\ No newline at end of file
diff --git a/client/homebrew/editor/tagInput/tagInput.jsx b/client/homebrew/editor/tagInput/tagInput.jsx
new file mode 100644
index 000000000..816541167
--- /dev/null
+++ b/client/homebrew/editor/tagInput/tagInput.jsx
@@ -0,0 +1,105 @@
+require('./tagInput.less');
+const React = require('react');
+const { useState, useEffect } = React;
+const _ = require('lodash');
+
+const TagInput = ({ unique = true, values = [], ...props }) => {
+ const [tempInputText, setTempInputText] = useState('');
+ const [tagList, setTagList] = useState(values.map((value) => ({ value, editing: false })));
+
+ useEffect(()=>{
+ handleChange(tagList.map((context)=>context.value))
+ }, [tagList])
+
+ const handleChange = (value)=>{
+ props.onChange({
+ target : { value }
+ })
+ };
+
+ const handleInputKeyDown = ({ evt, value, index, options = {} }) => {
+ if (_.includes(['Enter', ','], evt.key)) {
+ evt.preventDefault();
+ submitTag(evt.target.value, value, index);
+ if (options.clear) {
+ setTempInputText('');
+ }
+ }
+ };
+
+ const submitTag = (newValue, originalValue, index) => {
+ setTagList((prevContext) => {
+ // remove existing tag
+ if(newValue === null){
+ return [...prevContext].filter((context, i)=>i !== index);
+ }
+ // add new tag
+ if(originalValue === null){
+ return [...prevContext, { value: newValue, editing: false }]
+ }
+ // update existing tag
+ return prevContext.map((context, i) => {
+ if (i === index) {
+ return { ...context, value: newValue, editing: false };
+ }
+ return context;
+ });
+ });
+ };
+
+ const editTag = (index) => {
+ setTagList((prevContext) => {
+ return prevContext.map((context, i) => {
+ if (i === index) {
+ return { ...context, editing: true };
+ }
+ return { ...context, editing: false };
+ });
+ });
+ };
+
+ const renderReadTag = (context, index) => {
+ return (
+ editTag(index)}>
+ {context.value}
+ {evt.stopPropagation(); submitTag(null, context.value, index)}}>
+
+ );
+ };
+
+ const renderWriteTag = (context, index) => {
+ return (
+ handleInputKeyDown({evt, value: context.value, index: index})}
+ autoFocus
+ />
+ );
+ };
+
+ return (
+
+
{props.label}
+
+
+ {tagList.map((context, index) => { return context.editing ? renderWriteTag(context, index) : renderReadTag(context, index); })}
+
+
+
setTempInputText(e.target.value)}
+ onKeyDown={(evt) => handleInputKeyDown({ evt, value: null, options: { clear: true } })}
+ />
+
+
+ );
+};
+
+module.exports = TagInput;
diff --git a/client/homebrew/editor/tagInput/tagInput.less b/client/homebrew/editor/tagInput/tagInput.less
new file mode 100644
index 000000000..e69de29bb
diff --git a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx
index 039bc98f5..9011564a3 100644
--- a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx
+++ b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx
@@ -2,7 +2,7 @@ require('./brewItem.less');
const React = require('react');
const createClass = require('create-react-class');
const moment = require('moment');
-const request = require('../../../../utils/request-middleware.js');
+import request from '../../../../utils/request-middleware.js';
const googleDriveIcon = require('../../../../googleDrive.svg');
const homebreweryIcon = require('../../../../thumbnail.png');
diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx
index 7ddf0bb06..46502af23 100644
--- a/client/homebrew/pages/editPage/editPage.jsx
+++ b/client/homebrew/pages/editPage/editPage.jsx
@@ -4,7 +4,7 @@ const React = require('react');
const _ = require('lodash');
const createClass = require('create-react-class');
-const request = require('../../utils/request-middleware.js');
+import request from '../../utils/request-middleware.js';
const { Meta } = require('vitreum/headtags');
const Nav = require('naturalcrit/nav/nav.jsx');
@@ -16,6 +16,7 @@ const PrintNavItem = require('../../navbar/print.navitem.jsx');
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
const Account = require('../../navbar/account.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
+const VaultNavItem = require('../../navbar/vault.navitem.jsx');
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
const Editor = require('../../editor/editor.jsx');
@@ -23,7 +24,7 @@ const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
const LockNotification = require('./lockNotification/lockNotification.jsx');
-const Markdown = require('naturalcrit/markdown.js');
+import Markdown from 'naturalcrit/markdown.js';
const { DEFAULT_BREW_LOAD } = require('../../../../server/brewDefaults.js');
const { printCurrentBrew, fetchThemeBundle } = require('../../../../shared/helpers.js');
@@ -417,6 +418,7 @@ const EditPage = createClass({
+
diff --git a/client/homebrew/pages/errorPage/errorPage.jsx b/client/homebrew/pages/errorPage/errorPage.jsx
index 387a99b02..4ac73da8c 100644
--- a/client/homebrew/pages/errorPage/errorPage.jsx
+++ b/client/homebrew/pages/errorPage/errorPage.jsx
@@ -1,7 +1,7 @@
require('./errorPage.less');
const React = require('react');
const UIPage = require('../basePages/uiPage/uiPage.jsx');
-const Markdown = require('../../../../shared/naturalcrit/markdown.js');
+import Markdown from '../../../../shared/naturalcrit/markdown.js';
const ErrorIndex = require('./errors/errorIndex.js');
const ErrorPage = ({ brew })=>{
diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx
index f737506f1..00d0c801d 100644
--- a/client/homebrew/pages/homePage/homePage.jsx
+++ b/client/homebrew/pages/homePage/homePage.jsx
@@ -2,7 +2,7 @@ require('./homePage.less');
const React = require('react');
const createClass = require('create-react-class');
const cx = require('classnames');
-const request = require('../../utils/request-middleware.js');
+import request from '../../utils/request-middleware.js';
const { Meta } = require('vitreum/headtags');
const Nav = require('naturalcrit/nav/nav.jsx');
diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx
index 8216b347c..ee2c67d5f 100644
--- a/client/homebrew/pages/newPage/newPage.jsx
+++ b/client/homebrew/pages/newPage/newPage.jsx
@@ -2,9 +2,9 @@
require('./newPage.less');
const React = require('react');
const createClass = require('create-react-class');
-const request = require('../../utils/request-middleware.js');
+import request from '../../utils/request-middleware.js';
-const Markdown = require('naturalcrit/markdown.js');
+import Markdown from 'naturalcrit/markdown.js';
const Nav = require('naturalcrit/nav/nav.jsx');
const PrintNavItem = require('../../navbar/print.navitem.jsx');
diff --git a/client/homebrew/pages/sharePage/sharePage.jsx b/client/homebrew/pages/sharePage/sharePage.jsx
index 2d96e1ce6..04f0e3a6b 100644
--- a/client/homebrew/pages/sharePage/sharePage.jsx
+++ b/client/homebrew/pages/sharePage/sharePage.jsx
@@ -8,6 +8,7 @@ const Navbar = require('../../navbar/navbar.jsx');
const MetadataNav = require('../../navbar/metadata.navitem.jsx');
const PrintNavItem = require('../../navbar/print.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
+const VaultNavItem = require('../../navbar/vault.navitem.jsx');
const Account = require('../../navbar/account.navitem.jsx');
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
@@ -110,6 +111,7 @@ const SharePage = createClass({
>}
+
diff --git a/client/homebrew/pages/vaultPage/vaultPage.jsx b/client/homebrew/pages/vaultPage/vaultPage.jsx
index fbf15f814..21a8e8363 100644
--- a/client/homebrew/pages/vaultPage/vaultPage.jsx
+++ b/client/homebrew/pages/vaultPage/vaultPage.jsx
@@ -15,7 +15,7 @@ const BrewItem = require('../basePages/listPage/brewItem/brewItem.jsx');
const SplitPane = require('../../../../shared/naturalcrit/splitPane/splitPane.jsx');
const ErrorIndex = require('../errorPage/errors/errorIndex.js');
-const request = require('../../utils/request-middleware.js');
+import request from '../../utils/request-middleware.js';
const VaultPage = (props)=>{
const [pageState, setPageState] = useState(parseInt(props.query.page) || 1);
diff --git a/client/homebrew/pages/vaultPage/vaultPage.less b/client/homebrew/pages/vaultPage/vaultPage.less
index d29a5f4e1..a69bcb33b 100644
--- a/client/homebrew/pages/vaultPage/vaultPage.less
+++ b/client/homebrew/pages/vaultPage/vaultPage.less
@@ -92,49 +92,11 @@
&:invalid { background : rgb(255, 188, 181); }
- &[type='checkbox'] {
- position : relative;
- display : inline-block;
- width : 50px;
- height : 30px;
- font-family : 'WalterTurncoat';
- font-size : 20px;
- font-weight : 800;
- color : white;
- letter-spacing : 2px;
- appearance : none;
- background : red;
- isolation : isolate;
- border-radius : 5px;
-
- &::before,&::after {
- position : absolute;
- inset : 0;
- z-index : 5;
- padding-top : 2px;
- text-align : center;
- }
-
- &::before {
- display : block;
- content : 'No';
- }
-
- &::after {
- display : none;
- content : 'Yes';
- }
-
- &:checked {
- background : green;
-
- &::before { display : none; }
- &::after { display : block; }
- }
- }
+
}
#searchButton {
+ .colorButton(@green);
position : absolute;
right : 20px;
bottom : 0;
@@ -152,7 +114,6 @@
flex-direction : column;
height : 100%;
overflow-y : auto;
- font-family : 'BookInsanityRemake';
font-size : 0.34cm;
h3 {
@@ -356,6 +317,7 @@
}
button {
+ .colorButton(@green);
width : max-content;
&.previousPage { grid-area : previousPage; }
diff --git a/client/homebrew/utils/request-middleware.js b/client/homebrew/utils/request-middleware.js
index f6bc2571b..01a9d2571 100644
--- a/client/homebrew/utils/request-middleware.js
+++ b/client/homebrew/utils/request-middleware.js
@@ -1,4 +1,4 @@
-const request = require('superagent');
+import request from 'superagent';
const addHeader = (request)=>request.set('Homebrewery-Version', global.version);
@@ -9,4 +9,4 @@ const requestMiddleware = {
delete : (path)=>addHeader(request.delete(path)),
};
-module.exports = requestMiddleware;
\ No newline at end of file
+export default requestMiddleware;
\ No newline at end of file
diff --git a/client/template.js b/client/template.js
index c77f953ff..990df785c 100644
--- a/client/template.js
+++ b/client/template.js
@@ -8,6 +8,8 @@ const template = async function(name, title='', props = {}){
});
const ogMetaTags = ogTags.join('\n');
+ const ssrModule = await import(`../build/${name}/ssr.cjs`);
+
return `
@@ -21,7 +23,7 @@ const template = async function(name, title='', props = {}){
${title.length ? `${title} - The Homebrewery`: 'The Homebrewery - NaturalCrit'}
- ${require(`../build/${name}/ssr.js`)(props)}
+ ${ssrModule.default(props)}
@@ -29,4 +31,4 @@ const template = async function(name, title='', props = {}){
`;
};
-module.exports = template;
\ No newline at end of file
+export default template;
\ No newline at end of file
diff --git a/faq.md b/faq.md
index c7254952b..d3b6d24f6 100644
--- a/faq.md
+++ b/faq.md
@@ -69,7 +69,6 @@ pre {
You can check the site status here: [Everyone or Just Me](https://downforeveryoneorjustme.com/homebrewery.naturalcrit.com)
-
### Why am I getting an error when trying to save, and my account is linked to Google?
A sign-in with Google only lasts a year until the authentication expires. You must go [here](https://www.naturalcrit.com/login), click the *Log-out* button, and then sign back in using your Google account.
@@ -82,12 +81,17 @@ If you have linked your account with a Google account, you would change your pas
### Is there a way to restore a previous version of my brew?
-Currently, there is no way to do this through the site yourself. This would take too much of a toll on the amount of storage the homebrewery requires. However, we do have daily backups of our database that we keep for 8 days, and you can contact the moderators on [the subreddit](https://www.reddit.com/r/homebrewery) with your Homebrewery username, the name of the lost brew, and the last known time it was working properly. We can manually look through our backups and restore it if it exists.
+In your brew, there is an icon, :fas_clock_rotate_left:, that button opens up a menu with versions of your brew, stored in order from newer to older, up to a week old. Because of the amount of duplicates this function creates, this information is stored in **your browser**, so if you were to uninstall it or clear your cookies and site data, or change computers, the info will not be there.
+
+Also, we do have daily backups of our database that we keep for 8 days, and you can contact the moderators on [the subreddit](https://www.reddit.com/r/homebrewery) with your Homebrewery username, the name of the lost brew, and the last known time it was working properly. We can manually look through our backups and restore it if it exists.
+
### I worked on a brew for X hours, and suddenly all the text disappeared!
This usually happens if you accidentally drag-select all of your text and then start typing which overwrites the selection. Do not panic, and do not refresh the page or reload your brew quite yet as it is probably auto-saved in this state already. Simply press CTRL+Z as many times as needed to undo your last few changes and you will be back to where you were, then make sure to save your brew in the "good" state.
+You can also load a history version old enough to have all the text, using the :fas_clock_rotate_left: history versions button.
+
\column
### Why is only Chrome supported?
@@ -112,10 +116,7 @@ Once you have an image you would like to use, it is recommended to host it somew
\page
### A particular font does not work for my language, what do I do?
-The fonts used were originally created for use with the English language, though revisions since then have added more support for other languages. They are still not complete sets and may be missing a glyph/character you need. Unfortunately, the volunteer group as it stands at the time of this writing does not have a font guru, so it would be difficult to add more glyphs (especially complicated glyphs). Let us know which glyph is missing on the subreddit, but you may need to search [Google Fonts](https://fonts.google.com) for an alternative font if you need something fast.
-
-### Whenever I click on the "Get PDF" button, instead of getting a download, it opens Print Preview in another tab.
-Yes, this is by design. In the print preview, select "Save as PDF" as the Destination, and then click "Save". There will be a normal download dialog where you can save your brew as a PDF.
+The fonts used were originally created for use with the English language, though revisions since then have added more support for other languages. They are still not complete sets and may be missing a glyph/character you need. Unfortunately, the volunteer group as it stands at the time of this writing does not have a font guru, so it would be difficult to add more glyphs (especially complicated glyphs). Let us know which glyph is missing on the subreddit, but you may need to search [Google Fonts](https://fonts.google.com) for an alternative font if you need something fast.
### I have white borders on the bottom/sides of the print preview.
@@ -126,4 +127,8 @@ The Homebrewery defaults to creating US Letter page sizes. If you are printing
### Typing `#### Adhesion` in the text editor doesn't show the header at all in the completed page?
-Your ad-blocking software is mistakenly assuming your text to be an ad. Whitelist homebrewery.naturalcrit.com in your ad-blocking software.
+Your ad-blocking software is mistakenly assuming your text to be an ad. We recommend whitelisting homebrewery.naturalcrit.com in your ad-blocking software, as we have no ads.
+
+### My username appears as _hidden_ when checking my brews in the Vault, why is that?
+
+Your username is most likely your e-mail adress, and our code is picking that up and protecting your identity. This will remain as is, but you can ask for a name change by contacting the moderators on [the subreddit](https://www.reddit.com/r/homebrewery) with your Homebrewery username, and your desired new name. You will also be asked to provide details about some of your unpublished brews, to verify your identity. No information will be leaked or shared.
diff --git a/package-lock.json b/package-lock.json
index ae99df672..a9cd43629 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,19 +1,19 @@
{
"name": "homebrewery",
- "version": "3.16.0",
+ "version": "3.16.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "homebrewery",
- "version": "3.16.0",
+ "version": "3.16.1",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@babel/core": "^7.26.0",
"@babel/plugin-transform-runtime": "^7.25.9",
"@babel/preset-env": "^7.26.0",
- "@babel/preset-react": "^7.25.9",
+ "@babel/preset-react": "^7.26.3",
"@googleapis/drive": "^8.14.0",
"body-parser": "^1.20.2",
"classnames": "^2.5.1",
@@ -21,11 +21,11 @@
"cookie-parser": "^1.4.7",
"create-react-class": "^15.7.0",
"dedent-tabs": "^0.10.3",
- "dompurify": "^3.1.7",
+ "dompurify": "^3.2.2",
"expr-eval": "^2.0.2",
- "express": "^4.21.1",
+ "express": "^4.21.2",
"express-async-handler": "^1.2.0",
- "express-static-gzip": "2.1.8",
+ "express-static-gzip": "2.2.0",
"fs-extra": "11.2.0",
"idb-keyval": "^6.2.1",
"js-yaml": "^4.1.0",
@@ -33,14 +33,14 @@
"less": "^3.13.1",
"lodash": "^4.17.21",
"marked": "11.2.0",
- "marked-emoji": "^1.4.2",
+ "marked-emoji": "^1.4.3",
"marked-extended-tables": "^1.0.10",
"marked-gfm-heading-id": "^3.2.0",
"marked-smartypants-lite": "^1.0.2",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.30.1",
- "mongoose": "^8.7.3",
- "nanoid": "3.3.4",
+ "mongoose": "^8.8.4",
+ "nanoid": "5.0.9",
"nconf": "^0.12.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@@ -52,15 +52,16 @@
},
"devDependencies": {
"@stylistic/stylelint-plugin": "^3.1.1",
- "eslint": "^9.14.0",
+ "babel-plugin-transform-import-meta": "^2.2.1",
+ "eslint": "^9.16.0",
"eslint-plugin-jest": "^28.9.0",
"eslint-plugin-react": "^7.37.2",
- "globals": "^15.12.0",
+ "globals": "^15.13.0",
"jest": "^29.7.0",
"jest-expect-message": "^1.1.3",
"jsdom-global": "^3.0.2",
"postcss-less": "^6.0.0",
- "stylelint": "^16.10.0",
+ "stylelint": "^16.11.0",
"stylelint-config-recess-order": "^5.1.1",
"stylelint-config-recommended": "^14.0.1",
"supertest": "^7.0.0"
@@ -559,6 +560,7 @@
"version": "7.26.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz",
"integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
},
@@ -1659,9 +1661,10 @@
}
},
"node_modules/@babel/preset-react": {
- "version": "7.25.9",
- "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.9.tgz",
- "integrity": "sha512-D3to0uSPiWE7rBrdIICCd0tJSIGpLaaGptna2+w7Pft5xMqLpA1sz99DK5TZ1TjGbdQ/VI1eCSZ06dv3lT4JOw==",
+ "version": "7.26.3",
+ "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.26.3.tgz",
+ "integrity": "sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw==",
+ "license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
"@babel/helper-validator-option": "^7.25.9",
@@ -1747,9 +1750,9 @@
"license": "MIT"
},
"node_modules/@csstools/css-parser-algorithms": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.1.tgz",
- "integrity": "sha512-lSquqZCHxDfuTg/Sk2hiS0mcSFCEBuj49JfzPHJogDBT0mGCyY5A1AQzBWngitrp7i1/HAZpIgzF/VjhOEIJIg==",
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz",
+ "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==",
"dev": true,
"funding": [
{
@@ -1761,17 +1764,18 @@
"url": "https://opencollective.com/csstools"
}
],
+ "license": "MIT",
"engines": {
"node": ">=18"
},
"peerDependencies": {
- "@csstools/css-tokenizer": "^3.0.1"
+ "@csstools/css-tokenizer": "^3.0.3"
}
},
"node_modules/@csstools/css-tokenizer": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.1.tgz",
- "integrity": "sha512-UBqaiu7kU0lfvaP982/o3khfXccVlHPWp0/vwwiIgDF0GmqqqxoiXC/6FCjlS9u92f7CoEz6nXKQnrn1kIAkOw==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz",
+ "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==",
"dev": true,
"funding": [
{
@@ -1783,6 +1787,7 @@
"url": "https://opencollective.com/csstools"
}
],
+ "license": "MIT",
"engines": {
"node": ">=18"
}
@@ -1810,28 +1815,6 @@
"@csstools/css-tokenizer": "^3.0.1"
}
},
- "node_modules/@csstools/selector-specificity": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-4.0.0.tgz",
- "integrity": "sha512-189nelqtPd8++phaHNwYovKZI0FOzH1vQEE3QhHHkNIGrg5fSs9CbYP3RvfEH5geztnIA9Jwq91wyOIwAW5JIQ==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/csstools"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/csstools"
- }
- ],
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "postcss-selector-parser": "^6.1.0"
- }
- },
"node_modules/@dual-bundle/import-meta-resolve": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz",
@@ -1869,9 +1852,9 @@
}
},
"node_modules/@eslint/config-array": {
- "version": "0.18.0",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz",
- "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==",
+ "version": "0.19.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.0.tgz",
+ "integrity": "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==",
"dev": true,
"dependencies": {
"@eslint/object-schema": "^2.1.4",
@@ -1883,20 +1866,19 @@
}
},
"node_modules/@eslint/core": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz",
- "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==",
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.0.tgz",
+ "integrity": "sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==",
"dev": true,
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/eslintrc": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
- "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz",
+ "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==",
"dev": true,
- "license": "MIT",
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
@@ -1920,7 +1902,6 @@
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
- "license": "MIT",
"engines": {
"node": ">=18"
},
@@ -1929,10 +1910,11 @@
}
},
"node_modules/@eslint/js": {
- "version": "9.14.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.14.0.tgz",
- "integrity": "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==",
+ "version": "9.16.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.16.0.tgz",
+ "integrity": "sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
@@ -1947,9 +1929,9 @@
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz",
- "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==",
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz",
+ "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==",
"dev": true,
"dependencies": {
"levn": "^0.4.1"
@@ -3082,6 +3064,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/trusted-types": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+ "optional": true
+ },
"node_modules/@types/webidl-conversions": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
@@ -3283,7 +3271,6 @@
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"dev": true,
- "license": "MIT",
"peerDependencies": {
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
@@ -3337,7 +3324,6 @@
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
- "license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -3881,6 +3867,27 @@
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
}
},
+ "node_modules/babel-plugin-transform-import-meta": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-import-meta/-/babel-plugin-transform-import-meta-2.2.1.tgz",
+ "integrity": "sha512-AxNh27Pcg8Kt112RGa3Vod2QS2YXKKJ6+nSvRtv7qQTJAdx0MZa4UHZ4lnxHUWA2MNbLuZQv5FVab4P1CoLOWw==",
+ "dev": true,
+ "license": "BSD",
+ "dependencies": {
+ "@babel/template": "^7.4.4",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.10.0"
+ }
+ },
+ "node_modules/babel-plugin-transform-import-meta/node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD"
+ },
"node_modules/babel-preset-current-node-syntax": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
@@ -4327,9 +4334,9 @@
}
},
"node_modules/bson": {
- "version": "6.8.0",
- "resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz",
- "integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==",
+ "version": "6.9.0",
+ "resolved": "https://registry.npmjs.org/bson/-/bson-6.9.0.tgz",
+ "integrity": "sha512-X9hJeyeM0//Fus+0pc5dSUMhhrrmWwQUtdavaQeF3Ta6m69matZkGWV/MrBcnwUeLC8W9kwwc2hfkZgUuCX3Ig==",
"engines": {
"node": ">=16.20.1"
}
@@ -4977,11 +4984,10 @@
}
},
"node_modules/cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
- "license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -5023,12 +5029,13 @@
}
},
"node_modules/css-tree": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.0.0.tgz",
- "integrity": "sha512-o88DVQ6GzsABn1+6+zo2ct801dBO5OASVyxbbvA2W20ue2puSh/VOuqUj90eUeMSX/xqGqBmOKiRQN7tJOuBXw==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.0.1.tgz",
+ "integrity": "sha512-8Fxxv+tGhORlshCdCwnNJytvlvq46sOLSYEx2ZIGurahWvMucSRnyjPA3AmrMq4VPRYbHVpWj5VkiVasrM2H4Q==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "mdn-data": "2.10.0",
+ "mdn-data": "2.12.1",
"source-map-js": "^1.0.1"
},
"engines": {
@@ -5455,9 +5462,13 @@
}
},
"node_modules/dompurify": {
- "version": "3.1.7",
- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.7.tgz",
- "integrity": "sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ=="
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.2.tgz",
+ "integrity": "sha512-YMM+erhdZ2nkZ4fTNRTSI94mb7VG7uVF5vj5Zde7tImgnhZE3R6YW/IACGIHb2ux+QkEXMhe591N+5jWOmL4Zw==",
+ "license": "(MPL-2.0 OR Apache-2.0)",
+ "optionalDependencies": {
+ "@types/trusted-types": "^2.0.7"
+ }
},
"node_modules/duplexer2": {
"version": "0.1.4",
@@ -5489,9 +5500,9 @@
"integrity": "sha512-M+7ph0VGBQqqpTT2YrabjNKSQ2fEl9PVx6AK3N558gDH9NO8O6XN9SXXFWRo9u9PbEg/bWq+tjXQr+eXmxubCw=="
},
"node_modules/elliptic": {
- "version": "6.5.7",
- "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz",
- "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==",
+ "version": "6.6.0",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz",
+ "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==",
"dependencies": {
"bn.js": "^4.11.9",
"brorand": "^1.1.0",
@@ -5761,26 +5772,27 @@
"license": "MIT"
},
"node_modules/eslint": {
- "version": "9.14.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.14.0.tgz",
- "integrity": "sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g==",
+ "version": "9.16.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.16.0.tgz",
+ "integrity": "sha512-whp8mSQI4C8VXd+fLgSM0lh3UlmcFtVwUQjyKCFfsp+2ItAIYhlq/hqGahGqHE6cv9unM41VlqKk2VtKYR2TaA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.18.0",
- "@eslint/core": "^0.7.0",
- "@eslint/eslintrc": "^3.1.0",
- "@eslint/js": "9.14.0",
- "@eslint/plugin-kit": "^0.2.0",
+ "@eslint/config-array": "^0.19.0",
+ "@eslint/core": "^0.9.0",
+ "@eslint/eslintrc": "^3.2.0",
+ "@eslint/js": "9.16.0",
+ "@eslint/plugin-kit": "^0.2.3",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
- "@humanwhocodes/retry": "^0.4.0",
+ "@humanwhocodes/retry": "^0.4.1",
"@types/estree": "^1.0.6",
"@types/json-schema": "^7.0.15",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
- "cross-spawn": "^7.0.2",
+ "cross-spawn": "^7.0.5",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^8.2.0",
@@ -5799,8 +5811,7 @@
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
- "optionator": "^0.9.3",
- "text-table": "^0.2.0"
+ "optionator": "^0.9.3"
},
"bin": {
"eslint": "bin/eslint.js"
@@ -6243,9 +6254,10 @@
"license": "MIT"
},
"node_modules/express": {
- "version": "4.21.1",
- "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
- "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
+ "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
+ "license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
@@ -6266,7 +6278,7 @@
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
- "path-to-regexp": "0.1.10",
+ "path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
@@ -6281,6 +6293,10 @@
},
"engines": {
"node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/express-async-handler": {
@@ -6290,10 +6306,11 @@
"license": "MIT"
},
"node_modules/express-static-gzip": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/express-static-gzip/-/express-static-gzip-2.1.8.tgz",
- "integrity": "sha512-g8tiJuI9Y9Ffy59ehVXvqb0hhP83JwZiLxzanobPaMbkB5qBWA8nuVgd+rcd5qzH3GkgogTALlc0BaADYwnMbQ==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/express-static-gzip/-/express-static-gzip-2.2.0.tgz",
+ "integrity": "sha512-4ZQ0pHX0CAauxmzry2/8XFLM6aZA4NBvg9QezSlsEO1zLnl7vMFa48/WIcjzdfOiEUS4S1npPPKP2NHHYAp6qg==",
"dependencies": {
+ "parseurl": "^1.3.3",
"serve-static": "^1.16.2"
}
},
@@ -6948,10 +6965,11 @@
}
},
"node_modules/globals": {
- "version": "15.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-15.12.0.tgz",
- "integrity": "sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==",
+ "version": "15.13.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.13.0.tgz",
+ "integrity": "sha512-49TewVEz0UxZjr1WYYsWpPrhyC/B/pA8Bq0fUmet2n+eR7yn0IvNzNaoBwnK6mdkzcN+se7Ez9zUgULTz2QH4g==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=18"
},
@@ -10063,8 +10081,7 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
- "dev": true,
- "license": "MIT"
+ "dev": true
},
"node_modules/json-stable-stringify": {
"version": "0.0.1",
@@ -10225,9 +10242,9 @@
}
},
"node_modules/known-css-properties": {
- "version": "0.34.0",
- "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.34.0.tgz",
- "integrity": "sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ==",
+ "version": "0.35.0",
+ "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.35.0.tgz",
+ "integrity": "sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==",
"dev": true,
"license": "MIT"
},
@@ -10487,11 +10504,11 @@
}
},
"node_modules/marked-emoji": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/marked-emoji/-/marked-emoji-1.4.2.tgz",
- "integrity": "sha512-2sP+bp2z76dwbILzQ7ijy2PyjjAJR3iAZCzaNGThD2UijFUBeidkn6MoCdX/j47tPIcWt9nwnjqRQPd01ZrfdA==",
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/marked-emoji/-/marked-emoji-1.4.3.tgz",
+ "integrity": "sha512-HDZx1VOmzu7XT2QNKWfrHGbNRMTWKj9XD78yrcH1madD30HpGLMODPOmKr/e7CA7NKKXkpXXNdndQn++ysXmHg==",
"peerDependencies": {
- "marked": ">=4 <15"
+ "marked": ">=4 <16"
}
},
"node_modules/marked-extended-tables": {
@@ -10559,10 +10576,11 @@
}
},
"node_modules/mdn-data": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.10.0.tgz",
- "integrity": "sha512-qq7C3EtK3yJXMwz1zAab65pjl+UhohqMOctTgcqjLOWABqmwj+me02LSsCuEUxnst9X1lCBpoE0WArGKgdGDzw==",
- "dev": true
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.1.tgz",
+ "integrity": "sha512-rsfnCbOHjqrhWxwt5/wtSLzpoKTzW7OXdT5lLOIH1OTYhWu9rRJveGq0sKvDZODABH7RX+uoR+DYcpFnq4Tf6Q==",
+ "dev": true,
+ "license": "CC0-1.0"
},
"node_modules/media-typer": {
"version": "0.3.0",
@@ -10865,13 +10883,14 @@
}
},
"node_modules/mongoose": {
- "version": "8.7.3",
- "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.7.3.tgz",
- "integrity": "sha512-Xl6+dzU5ZpEcDoJ8/AyrIdAwTY099QwpolvV73PIytpK13XqwllLq/9XeVzzLEQgmyvwBVGVgjmMrKbuezxrIA==",
+ "version": "8.8.4",
+ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.8.4.tgz",
+ "integrity": "sha512-yJbn695qCsqDO+xyPII29x2R7flzXhxCDv09mMZPSGllf0sm4jKw3E9s9uvQ9hjO6bL2xjU8KKowYqcY9eSTMQ==",
+ "license": "MIT",
"dependencies": {
"bson": "^6.7.0",
"kareem": "2.6.3",
- "mongodb": "6.9.0",
+ "mongodb": "~6.10.0",
"mpath": "0.9.0",
"mquery": "5.0.0",
"ms": "2.1.3",
@@ -10943,9 +10962,9 @@
}
},
"node_modules/mongoose/node_modules/mongodb": {
- "version": "6.9.0",
- "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.9.0.tgz",
- "integrity": "sha512-UMopBVx1LmEUbW/QE0Hw18u583PEDVQmUmVzzBRH0o/xtE9DBRA5ZYLOjpLIa03i8FXjzvQECJcqoMvCXftTUA==",
+ "version": "6.10.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.10.0.tgz",
+ "integrity": "sha512-gP9vduuYWb9ZkDM546M+MP2qKVk5ZG2wPF63OvSRuUbqCR+11ZCAE1mOfllhlAG0wcoJY5yDL/rV3OmYEwXIzg==",
"dependencies": {
"@mongodb-js/saslprep": "^1.1.5",
"bson": "^6.7.0",
@@ -11021,15 +11040,21 @@
"optional": true
},
"node_modules/nanoid": {
- "version": "3.3.4",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
- "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.9.tgz",
+ "integrity": "sha512-Aooyr6MXU6HpvvWXKoVoXwKMs/KyVakWwg7xQfv5/S/RIgJMy0Ifa45H9qqYy7pTCszrHzP21Uk4PZq2HpEM8Q==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
"license": "MIT",
"bin": {
- "nanoid": "bin/nanoid.cjs"
+ "nanoid": "bin/nanoid.js"
},
"engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ "node": "^18 || >=20"
}
},
"node_modules/nanomatch": {
@@ -11723,9 +11748,10 @@
}
},
"node_modules/path-to-regexp": {
- "version": "0.1.10",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
- "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
+ "license": "MIT"
},
"node_modules/path-type": {
"version": "4.0.0",
@@ -11754,9 +11780,10 @@
}
},
"node_modules/picocolors": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
- "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw=="
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
},
"node_modules/picomatch": {
"version": "2.3.1",
@@ -11879,9 +11906,9 @@
}
},
"node_modules/postcss": {
- "version": "8.4.47",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
- "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
+ "version": "8.4.49",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
+ "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"dev": true,
"funding": [
{
@@ -11897,9 +11924,10 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
- "picocolors": "^1.1.0",
+ "picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
@@ -13646,9 +13674,9 @@
"license": "ISC"
},
"node_modules/stylelint": {
- "version": "16.10.0",
- "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.10.0.tgz",
- "integrity": "sha512-z/8X2rZ52dt2c0stVwI9QL2AFJhLhbPkyfpDFcizs200V/g7v+UYY6SNcB9hKOLcDDX/yGLDsY/pX08sLkz9xQ==",
+ "version": "16.11.0",
+ "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.11.0.tgz",
+ "integrity": "sha512-zrl4IrKmjJQ+h9FoMp69UMCq5SxeHk0URhxUBj4d3ISzo/DplOFBJZc7t7Dr6otB+1bfbbKNLOmCDpzKSlW+Nw==",
"dev": true,
"funding": [
{
@@ -13660,17 +13688,18 @@
"url": "https://github.com/sponsors/stylelint"
}
],
+ "license": "MIT",
"dependencies": {
- "@csstools/css-parser-algorithms": "^3.0.1",
- "@csstools/css-tokenizer": "^3.0.1",
- "@csstools/media-query-list-parser": "^3.0.1",
- "@csstools/selector-specificity": "^4.0.0",
+ "@csstools/css-parser-algorithms": "^3.0.4",
+ "@csstools/css-tokenizer": "^3.0.3",
+ "@csstools/media-query-list-parser": "^4.0.2",
+ "@csstools/selector-specificity": "^5.0.0",
"@dual-bundle/import-meta-resolve": "^4.1.0",
"balanced-match": "^2.0.0",
"colord": "^2.9.3",
"cosmiconfig": "^9.0.0",
"css-functions-list": "^3.2.3",
- "css-tree": "^3.0.0",
+ "css-tree": "^3.0.1",
"debug": "^4.3.7",
"fast-glob": "^3.3.2",
"fastest-levenshtein": "^1.0.16",
@@ -13682,16 +13711,16 @@
"ignore": "^6.0.2",
"imurmurhash": "^0.1.4",
"is-plain-object": "^5.0.0",
- "known-css-properties": "^0.34.0",
+ "known-css-properties": "^0.35.0",
"mathml-tag-names": "^2.1.3",
"meow": "^13.2.0",
"micromatch": "^4.0.8",
"normalize-path": "^3.0.0",
- "picocolors": "^1.0.1",
- "postcss": "^8.4.47",
+ "picocolors": "^1.1.1",
+ "postcss": "^8.4.49",
"postcss-resolve-nested-selector": "^0.1.6",
"postcss-safe-parser": "^7.0.1",
- "postcss-selector-parser": "^6.1.2",
+ "postcss-selector-parser": "^7.0.0",
"postcss-value-parser": "^4.2.0",
"resolve-from": "^5.0.0",
"string-width": "^4.2.3",
@@ -13756,6 +13785,53 @@
"stylelint": "^14.0.0 || ^15.0.0 || ^16.0.1"
}
},
+ "node_modules/stylelint/node_modules/@csstools/media-query-list-parser": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.2.tgz",
+ "integrity": "sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.4",
+ "@csstools/css-tokenizer": "^3.0.3"
+ }
+ },
+ "node_modules/stylelint/node_modules/@csstools/selector-specificity": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz",
+ "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ }
+ },
"node_modules/stylelint/node_modules/balanced-match": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz",
@@ -13797,6 +13873,20 @@
"node": ">= 4"
}
},
+ "node_modules/stylelint/node_modules/postcss-selector-parser": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz",
+ "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/stylelint/node_modules/resolve-from": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
@@ -14060,13 +14150,6 @@
"node": ">=8"
}
},
- "node_modules/text-table": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
- "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
@@ -14678,7 +14761,6 @@
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"dev": true,
- "license": "BSD-2-Clause",
"dependencies": {
"punycode": "^2.1.0"
}
diff --git a/package.json b/package.json
index a48423f50..47e6cc9c4 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,8 @@
{
"name": "homebrewery",
"description": "Create authentic looking D&D homebrews using only markdown",
- "version": "3.16.0",
+ "version": "3.16.1",
+ "type": "module",
"engines": {
"npm": "^10.2.x",
"node": "^20.18.x"
@@ -56,6 +57,9 @@
"shared",
"server"
],
+ "transformIgnorePatterns": [
+ "node_modules/(?!nanoid/).*"
+ ],
"coveragePathIgnorePatterns": [
"build/*"
],
@@ -77,20 +81,11 @@
"jest-expect-message"
]
},
- "babel": {
- "presets": [
- "@babel/preset-env",
- "@babel/preset-react"
- ],
- "plugins": [
- "@babel/plugin-transform-runtime"
- ]
- },
"dependencies": {
"@babel/core": "^7.26.0",
"@babel/plugin-transform-runtime": "^7.25.9",
"@babel/preset-env": "^7.26.0",
- "@babel/preset-react": "^7.25.9",
+ "@babel/preset-react": "^7.26.3",
"@googleapis/drive": "^8.14.0",
"body-parser": "^1.20.2",
"classnames": "^2.5.1",
@@ -98,11 +93,11 @@
"cookie-parser": "^1.4.7",
"create-react-class": "^15.7.0",
"dedent-tabs": "^0.10.3",
- "dompurify": "^3.1.7",
+ "dompurify": "^3.2.2",
"expr-eval": "^2.0.2",
- "express": "^4.21.1",
+ "express": "^4.21.2",
"express-async-handler": "^1.2.0",
- "express-static-gzip": "2.1.8",
+ "express-static-gzip": "2.2.0",
"fs-extra": "11.2.0",
"idb-keyval": "^6.2.1",
"js-yaml": "^4.1.0",
@@ -110,14 +105,14 @@
"less": "^3.13.1",
"lodash": "^4.17.21",
"marked": "11.2.0",
- "marked-emoji": "^1.4.2",
+ "marked-emoji": "^1.4.3",
"marked-extended-tables": "^1.0.10",
"marked-gfm-heading-id": "^3.2.0",
"marked-smartypants-lite": "^1.0.2",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.30.1",
- "mongoose": "^8.7.3",
- "nanoid": "3.3.4",
+ "mongoose": "^8.8.4",
+ "nanoid": "5.0.9",
"nconf": "^0.12.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@@ -129,15 +124,16 @@
},
"devDependencies": {
"@stylistic/stylelint-plugin": "^3.1.1",
- "eslint": "^9.14.0",
+ "babel-plugin-transform-import-meta": "^2.2.1",
+ "eslint": "^9.16.0",
"eslint-plugin-jest": "^28.9.0",
"eslint-plugin-react": "^7.37.2",
- "globals": "^15.12.0",
+ "globals": "^15.13.0",
"jest": "^29.7.0",
"jest-expect-message": "^1.1.3",
"jsdom-global": "^3.0.2",
"postcss-less": "^6.0.0",
- "stylelint": "^16.10.0",
+ "stylelint": "^16.11.0",
"stylelint-config-recess-order": "^5.1.1",
"stylelint-config-recommended": "^14.0.1",
"supertest": "^7.0.0"
diff --git a/scripts/buildAdmin.js b/scripts/buildAdmin.js
index 1157a063b..9c77315ef 100644
--- a/scripts/buildAdmin.js
+++ b/scripts/buildAdmin.js
@@ -1,13 +1,14 @@
-const fs = require('fs-extra');
-const Proj = require('./project.json');
-const { pack } = require('vitreum');
+import fs from 'fs-extra';
+import Proj from './project.json' with { type: 'json' };
+import vitreum from 'vitreum';
+const { pack } = vitreum;
+
+import lessTransform from 'vitreum/transforms/less.js';
+import assetTransform from 'vitreum/transforms/asset.js';
+
const isDev = !!process.argv.find((arg)=>arg=='--dev');
-const lessTransform = require('vitreum/transforms/less.js');
-const assetTransform = require('vitreum/transforms/asset.js');
-//const Meta = require('vitreum/headtags');
-
const transforms = {
'.less' : lessTransform,
'*' : assetTransform('./build')
@@ -17,7 +18,7 @@ const build = async ({ bundle, render, ssr })=>{
const css = await lessTransform.generate({ paths: './shared' });
await fs.outputFile('./build/admin/bundle.css', css);
await fs.outputFile('./build/admin/bundle.js', bundle);
- await fs.outputFile('./build/admin/ssr.js', ssr);
+ await fs.outputFile('./build/admin/ssr.cjs', ssr);
};
fs.emptyDirSync('./build/admin');
diff --git a/scripts/buildHomebrew.js b/scripts/buildHomebrew.js
index f072b0359..313c92db7 100644
--- a/scripts/buildHomebrew.js
+++ b/scripts/buildHomebrew.js
@@ -1,14 +1,15 @@
-const fs = require('fs-extra');
-const zlib = require('zlib');
-const Proj = require('./project.json');
+import fs from 'fs-extra';
+import zlib from 'zlib';
+import Proj from './project.json' with { type: 'json' };
+import vitreum from 'vitreum';
+const { pack, watchFile, livereload } = vitreum;
-const { pack, watchFile, livereload } = require('vitreum');
-const isDev = !!process.argv.find((arg)=>arg=='--dev');
+import lessTransform from 'vitreum/transforms/less.js';
+import assetTransform from 'vitreum/transforms/asset.js';
+import babel from '@babel/core';
+import less from 'less';
-const lessTransform = require('vitreum/transforms/less.js');
-const assetTransform = require('vitreum/transforms/asset.js');
-const babel = require('@babel/core');
-const less = require('less');
+const isDev = !!process.argv.find((arg) => arg === '--dev');
const babelify = async (code)=>(await babel.transformAsync(code, { presets: [['@babel/preset-env', { 'exclude': ['proposal-dynamic-import'] }], '@babel/preset-react'], plugins: ['@babel/plugin-transform-runtime'] })).code;
@@ -24,7 +25,7 @@ const build = async ({ bundle, render, ssr })=>{
//css = `@layer bundle {\n${css}\n}`;
await fs.outputFile('./build/homebrew/bundle.css', css);
await fs.outputFile('./build/homebrew/bundle.js', bundle);
- await fs.outputFile('./build/homebrew/ssr.js', ssr);
+ await fs.outputFile('./build/homebrew/ssr.cjs', ssr);
await fs.copy('./client/homebrew/favicon.ico', './build/assets/favicon.ico');
@@ -51,7 +52,7 @@ fs.emptyDirSync('./build');
const themes = { Legacy: {}, V3: {} };
let themeFiles = fs.readdirSync('./themes/Legacy');
- for (dir of themeFiles) {
+ for (let dir of themeFiles) {
const themeData = JSON.parse(fs.readFileSync(`./themes/Legacy/${dir}/settings.json`).toString());
themeData.path = dir;
themes.Legacy[dir] = (themeData);
@@ -68,7 +69,7 @@ fs.emptyDirSync('./build');
}
themeFiles = fs.readdirSync('./themes/V3');
- for (dir of themeFiles) {
+ for (let dir of themeFiles) {
const themeData = JSON.parse(fs.readFileSync(`./themes/V3/${dir}/settings.json`).toString());
themeData.path = dir;
themes.V3[dir] = (themeData);
@@ -104,14 +105,14 @@ fs.emptyDirSync('./build');
const editorThemesBuildDir = './build/homebrew/cm-themes';
await fs.copy('./node_modules/codemirror/theme', editorThemesBuildDir);
await fs.copy('./themes/codeMirror/customThemes', editorThemesBuildDir);
- editorThemeFiles = fs.readdirSync(editorThemesBuildDir);
+ const editorThemeFiles = fs.readdirSync(editorThemesBuildDir);
const editorThemeFile = './themes/codeMirror/editorThemes.json';
if(fs.existsSync(editorThemeFile)) fs.rmSync(editorThemeFile);
const stream = fs.createWriteStream(editorThemeFile, { flags: 'a' });
stream.write('[\n"default"');
- for (themeFile of editorThemeFiles) {
+ for (let themeFile of editorThemeFiles) {
stream.write(`,\n"${themeFile.slice(0, -4)}"`);
}
stream.write('\n]\n');
diff --git a/server.js b/server.js
index 6cbe07c4f..fe5a9a363 100644
--- a/server.js
+++ b/server.js
@@ -1,12 +1,12 @@
-const DB = require('./server/db.js');
-const server = require('./server/app.js');
-const config = require('./server/config.js');
+import DB from './server/db.js';
+import server from './server/app.js';
+import config from './server/config.js';
DB.connect(config).then(()=>{
// Ensure that we have successfully connected to the database
// before launching server
const PORT = process.env.PORT || config.get('web_port') || 8000;
- server.app.listen(PORT, ()=>{
+ server.listen(PORT, ()=>{
const reset = '\x1b[0m'; // Reset to default style
const bright = '\x1b[1m'; // Bright (bold) style
const cyan = '\x1b[36m'; // Cyan color
diff --git a/server/admin.api.js b/server/admin.api.js
index fd724b9f1..02cdcb2f7 100644
--- a/server/admin.api.js
+++ b/server/admin.api.js
@@ -1,9 +1,15 @@
-const HomebrewModel = require('./homebrew.model.js').model;
-const NotificationModel = require('./notifications.model.js').model;
-const router = require('express').Router();
-const Moment = require('moment');
-const templateFn = require('../client/template.js');
-const zlib = require('zlib');
+import {model as HomebrewModel } from './homebrew.model.js';
+import {model as NotificationModel } from './notifications.model.js';
+import express from 'express';
+import Moment from 'moment';
+import zlib from 'zlib';
+import templateFn from '../client/template.js';
+
+import HomebrewAPI from './homebrew.api.js';
+import asyncHandler from 'express-async-handler';
+import { splitTextStyleAndMetadata } from '../shared/helpers.js';
+
+const router = express.Router();
process.env.ADMIN_USER = process.env.ADMIN_USER || 'admin';
process.env.ADMIN_PASS = process.env.ADMIN_PASS || 'password3';
@@ -66,23 +72,8 @@ router.post('/admin/cleanup', mw.adminOnly, (req, res)=>{
});
/* Searches for matching edit or share id, also attempts to partial match */
-router.get('/admin/lookup/:id', mw.adminOnly, async (req, res, next)=>{
- HomebrewModel.findOne({
- $or : [
- { editId: { $regex: req.params.id, $options: 'i' } },
- { shareId: { $regex: req.params.id, $options: 'i' } },
- ]
- }).exec()
- .then((brew)=>{
- if(!brew) // No document found
- return res.status(404).json({ error: 'Document not found' });
- else
- return res.json(brew);
- })
- .catch((err)=>{
- console.error(err);
- return res.status(500).json({ error: 'Internal Server Error' });
- });
+router.get('/admin/lookup/:id', mw.adminOnly, asyncHandler(HomebrewAPI.getBrew('admin', false)), async (req, res, next)=>{
+ return res.json(req.brew);
});
/* Find 50 brews that aren't compressed yet */
@@ -100,6 +91,25 @@ router.get('/admin/finduncompressed', mw.adminOnly, (req, res)=>{
});
});
+/* Cleans `