diff --git a/.stylelintrc.json b/.stylelintrc.json index 2c7a9afdf..b5f2e7712 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -1,48 +1,48 @@ { - "extends": [ - "stylelint-config-recess-order", - "stylelint-config-recommended"], - "plugins": [ - "@stylistic/stylelint-plugin", - "./stylelint_plugins/declaration-colon-align.js", - "./stylelint_plugins/declaration-colon-min-space-before", - "./stylelint_plugins/declaration-block-multi-line-min-declarations" - ], - "customSyntax": "postcss-less", - "rules": { - "no-descending-specificity" : null, - "at-rule-no-unknown" : null, - "function-no-unknown" : null, - "font-family-no-missing-generic-family-keyword" : null, - "font-weight-notation" : "named-where-possible", - "font-family-name-quotes" : "always-unless-keyword", - "@stylistic/indentation" : "tab", - "no-duplicate-selectors" : true, - "@stylistic/color-hex-case" : "upper", - "color-hex-length" : "long", - "@stylistic/selector-combinator-space-after" : "always", - "@stylistic/selector-combinator-space-before" : "always", - "@stylistic/selector-attribute-operator-space-before" : "never", - "@stylistic/selector-attribute-operator-space-after" : "never", - "@stylistic/selector-attribute-brackets-space-inside" : "never", - "selector-attribute-quotes" : "always", - "selector-pseudo-element-colon-notation" : "double", - "@stylistic/selector-pseudo-class-parentheses-space-inside" : "never", - "@stylistic/block-opening-brace-space-before" : "always", - "naturalcrit/declaration-colon-min-space-before" : 1, - "@stylistic/declaration-block-trailing-semicolon" : "always", - "@stylistic/declaration-colon-space-after" : "always", - "@stylistic/number-leading-zero" : "always", - "function-url-quotes" : ["always", { "except": ["empty"] }], - "function-url-scheme-disallowed-list" : ["data","http"], - "comment-whitespace-inside" : "always", - "@stylistic/string-quotes" : "single", - "@stylistic/media-feature-range-operator-space-before" : "always", - "@stylistic/media-feature-range-operator-space-after" : "always", - "@stylistic/media-feature-parentheses-space-inside" : "never", - "@stylistic/media-feature-colon-space-before" : "always", - "@stylistic/media-feature-colon-space-after" : "always", - "naturalcrit/declaration-colon-align" : true, - "naturalcrit/declaration-block-multi-line-min-declarations": 1 - } + "extends": [ + "stylelint-config-recess-order", + "stylelint-config-recommended"], + "plugins": [ + "@stylistic/stylelint-plugin", + "./stylelint_plugins/declaration-colon-align.js", + "./stylelint_plugins/declaration-colon-min-space-before", + "./stylelint_plugins/declaration-block-multi-line-min-declarations" + ], + "customSyntax": "postcss-less", + "rules": { + "no-descending-specificity" : null, + "at-rule-no-unknown" : null, + "function-no-unknown" : null, + "font-family-no-missing-generic-family-keyword" : null, + "font-weight-notation" : "named-where-possible", + "font-family-name-quotes" : "always-unless-keyword", + "@stylistic/indentation" : "tab", + "no-duplicate-selectors" : true, + "@stylistic/color-hex-case" : "upper", + "color-hex-length" : "long", + "@stylistic/selector-combinator-space-after" : "always", + "@stylistic/selector-combinator-space-before" : "always", + "@stylistic/selector-attribute-operator-space-before" : "never", + "@stylistic/selector-attribute-operator-space-after" : "never", + "@stylistic/selector-attribute-brackets-space-inside" : "never", + "selector-attribute-quotes" : "always", + "selector-pseudo-element-colon-notation" : "double", + "@stylistic/selector-pseudo-class-parentheses-space-inside" : "never", + "@stylistic/block-opening-brace-space-before" : "always", + "naturalcrit/declaration-colon-min-space-before" : 1, + "@stylistic/declaration-block-trailing-semicolon" : "always", + "@stylistic/declaration-colon-space-after" : "always", + "@stylistic/number-leading-zero" : "always", + "function-url-quotes" : ["always", { "except": ["empty"] }], + "function-url-scheme-disallowed-list" : ["data","http"], + "comment-whitespace-inside" : "always", + "@stylistic/string-quotes" : "single", + "@stylistic/media-feature-range-operator-space-before" : "always", + "@stylistic/media-feature-range-operator-space-after" : "always", + "@stylistic/media-feature-parentheses-space-inside" : "never", + "@stylistic/media-feature-colon-space-before" : "always", + "@stylistic/media-feature-colon-space-after" : "always", + "naturalcrit/declaration-colon-align" : true, + "naturalcrit/declaration-block-multi-line-min-declarations" : 1 + } } diff --git a/changelog.md b/changelog.md index 3736ba9b0..8cfb499f6 100644 --- a/changelog.md +++ b/changelog.md @@ -77,14 +77,100 @@ pre { } .varSyntaxTable th:first-of-type { - width:6cm; + width:6cm; +} + +.page .exampleTable td,th { + border:1px dashed #00000030; } ``` - ## changelog For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery). +### Thursday 01/30/2024 - v3.17.0 + +{{taskList +##### 5e-Cleric + +* [x] Update FAQ + +* [x] Fix styling for Vault buttons and checkboxes + +* [x] Improve navigation bar styling + +* [x] Add feature to change username at https://www.naturalcrit.com/account + +* [x] Fix Reddit link crash when title has non-latin chars + +##### dbolack + +* [x] Fix page shadows toolbar option + +Fixes issue [#3919](https://github.com/naturalcrit/homebrewery/issues/3919) + +* [x] Add `:>>>` syntax for horizontal :>>>>> spaces + +* [x] Update Docker install instructions + +Fixes issue [#1930](https://github.com/naturalcrit/homebrewery/issues/1930) + +* [x] Allow styling pages via `\page{myStyles}` (with calculuschild) + +Fixes issue [#3901](https://github.com/naturalcrit/homebrewery/issues/3901) + +* [x] Update Ubuntu install instructions + +Fixes issue [#1952](https://github.com/naturalcrit/homebrewery/issues/1952) + +* [x] Add `:-:` `:-` `-:` syntax for paragraph alignment, similar to table column alignment; for example: + +-: -: Right-aligned + +:-: :-: Centered + +* [x] Add `:-- 50% --:` syntax to allow setting table column widths by percentage; for example: +``` +| Narrow | Wide | +|:- 10% -:|:-90%--:| +| Cell | Cell | +``` + + +| Narrow | Wide | +|:- 10% -:|:-90%--:| +|Cell | Cell | +{exampleTable} + +##### G-Ambatte + +* [x] Fix crash when opening brew Properties tab + +Fixes issue [#3927](https://github.com/naturalcrit/homebrewery/issues/3927) + +* [x] Update error pages with steps to refresh credentials + +Fixes issue [#3955](https://github.com/naturalcrit/homebrewery/issues/3955) + +* [x] Add {{openSans :fas_rectangle_list: **NAVIGATION**}} menu to the viewer toolbar + +##### calculuschild + +* [x] Reduce display lag on large brews + +##### Gazook89 + +* [x] Smarter detection of current page number + +Fixes issue [#3824](https://github.com/naturalcrit/homebrewery/issues/3824) + +##### All +* [x] Update dependencies and scripts +* [x] Refactor components and fix various errors +}} + +\column + ### Wednesday 11/27/2024 - v3.16.1 {{taskList @@ -131,6 +217,8 @@ Fixes issue [#3744](https://github.com/naturalcrit/homebrewery/issues/3744) * [x] Multiple code refactors, cleanups, and security fixes }} +\page + ### Saturday 10/12/2024 - v3.16.0 {{taskList @@ -2053,4 +2141,4 @@ Massive changelog incoming: * Added `phb.standalone.css` plus a build system for creating it * Added page numbers and footer text -* Page accent now flips each page +* Page accent now flips each page \ No newline at end of file diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 17f261c2d..a82ea8b34 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -17,10 +17,9 @@ const dedent = require('dedent-tabs').default; const { printCurrentBrew } = require('../../../shared/helpers.js'); import HeaderNav from './headerNav/headerNav.jsx'; - import { safeHTML } from './safeHTML.js'; - +const PAGEBREAK_REGEX_V3 = /^(?=\\page(?: *{[^\n{}]*})?$)/m; const PAGE_HEIGHT = 1056; const INITIAL_CONTENT = dedent` @@ -78,7 +77,7 @@ const BrewPage = (props)=>{ }; }, []); - return
+ return
; }; @@ -126,7 +125,7 @@ const BrewRenderer = (props)=>{ if(props.renderer == 'legacy') { rawPages = props.text.split('\\page'); } else { - rawPages = props.text.split(/^\\page$/gm); + rawPages = props.text.split(PAGEBREAK_REGEX_V3); } const handlePageVisibilityChange = (pageNum, isVisible, isCenter)=>{ @@ -173,20 +172,34 @@ const BrewRenderer = (props)=>{ const renderPage = (pageText, index)=>{ - const styles = { + let styles = { ...(!displayOptions.pageShadows ? { boxShadow: 'none' } : {}) // Add more conditions as needed }; + let classes = 'page'; + let attributes = {}; if(props.renderer == 'legacy') { const html = MarkdownLegacy.render(pageText); return ; } else { + if(pageText.startsWith('\\page')) { + const firstLineTokens = Markdown.marked.lexer(pageText.split('\n', 1)[0])[0].tokens; + const injectedTags = firstLineTokens.find((obj)=>obj.injectedTags !== undefined)?.injectedTags; + if(injectedTags) { + styles = { ...styles, ...injectedTags.styles }; + styles = _.mapKeys(styles, (v, k) => k.startsWith('--') ? k : _.camelCase(k)); // Convert CSS to camelCase for React + classes = [classes, injectedTags.classes].join(' ').trim(); + attributes = injectedTags.attributes; + } + pageText = pageText.includes('\n') ? pageText.substring(pageText.indexOf('\n') + 1) : ''; // Remove the \page line + } + pageText += `\n\n \n\\column\n `; //Artificial column break at page end to emulate column-fill:auto (until `wide` is used, when column-fill:balance will reappear) const html = Markdown.render(pageText, index); - return ; + return ; } }; diff --git a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx index b2045f13d..38a85e0c7 100644 --- a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx +++ b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx @@ -1,6 +1,7 @@ require('./notificationPopup.less'); import React, { useEffect, useState } from 'react'; import request from '../../utils/request-middleware.js'; +import Markdown from 'naturalcrit/markdown.js'; import Dialog from '../../../components/dialog.jsx'; @@ -40,11 +41,10 @@ const NotificationPopup = ()=>{ const renderNotificationsList = ()=>{ if(error) return
{error}
; - return notifications.map((notification)=>(
  • {notification.title}
    -

    +

  • )); }; diff --git a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.less b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.less index 79edf37b2..be642f0fe 100644 --- a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.less +++ b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.less @@ -48,17 +48,46 @@ } ul { margin-top : 15px; - font-size : 0.8em; + font-size : 0.9em; list-style-position : outside; list-style-type : disc; li { - margin-top : 1.4em; - font-size : 0.8em; - line-height : 1.4em; - em { - text-transform:capitalize; - font-weight : 800; + padding-left : 1em; + margin-top : 1.5em; + font-size : 0.9em; + line-height : 1.5em; + em { + font-weight : 800; + text-transform : capitalize; + } + li { + margin-top : 0; + line-height : 1.2em; + list-style-type : square; } } + ul ul,ol ol,ul ol,ol ul { + margin-bottom : 0px; + margin-left : 1.5em; + } } -} + + /* Markdown styling */ + code { + padding : 0.1em 0.5em; + font-family : 'Courier New', 'Courier', monospace; + overflow-wrap : break-word; + white-space : pre-wrap; + background : #08115A; + border-radius : 2px; + } + pre code { + display : inline-block; + width : 100%; + } + .blank { + height : 1em; + margin-top : 0; + & + * { margin-top : 0; } + } +} \ No newline at end of file diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 3e37aba57..9e6178f3e 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -12,7 +12,8 @@ const MetadataEditor = require('./metadataEditor/metadataEditor.jsx'); const EDITOR_THEME_KEY = 'HOMEBREWERY-EDITOR-THEME'; -const SNIPPETBAR_HEIGHT = 25; +const PAGEBREAK_REGEX_V3 = /^(?=\\page(?: *{[^\n{}]*})?$)/m; +const SNIPPETBAR_HEIGHT = 25; const DEFAULT_STYLE_TEXT = dedent` /*=======--- Example CSS styling ---=======*/ /* Any CSS here will apply to your document! */ @@ -126,15 +127,15 @@ const Editor = createClass({ }, updateCurrentCursorPage : function(cursor) { - const lines = this.props.brew.text.split('\n').slice(0, cursor.line + 1); - const pageRegex = this.props.brew.renderer == 'V3' ? /^\\page$/ : /\\page/; + const lines = this.props.brew.text.split('\n').slice(1, cursor.line + 1); + const pageRegex = this.props.brew.renderer == 'V3' ? PAGEBREAK_REGEX_V3 : /\\page/; const currentPage = lines.reduce((count, line)=>count + (pageRegex.test(line) ? 1 : 0), 1); this.props.onCursorPageChange(currentPage); }, updateCurrentViewPage : function(topScrollLine) { - const lines = this.props.brew.text.split('\n').slice(0, topScrollLine + 1); - const pageRegex = this.props.brew.renderer == 'V3' ? /^\\page$/ : /\\page/; + const lines = this.props.brew.text.split('\n').slice(1, topScrollLine + 1); + const pageRegex = this.props.brew.renderer == 'V3' ? PAGEBREAK_REGEX_V3 : /\\page/; const currentPage = lines.reduce((count, line)=>count + (pageRegex.test(line) ? 1 : 0), 1); this.props.onViewPageChange(currentPage); }, @@ -174,7 +175,7 @@ const Editor = createClass({ for (let i=customHighlights.length - 1;i>=0;i--) customHighlights[i].clear(); - let editorPageCount = 2; // start page count from page 2 + let editorPageCount = 1; // start page count from page 1 _.forEach(this.props.brew.text.split('\n'), (line, lineNumber)=>{ @@ -190,7 +191,10 @@ const Editor = createClass({ // Styling for \page breaks if((this.props.renderer == 'legacy' && line.includes('\\page')) || - (this.props.renderer == 'V3' && line.match(/^\\page$/))) { + (this.props.renderer == 'V3' && line.match(PAGEBREAK_REGEX_V3))) { + + if(lineNumber > 0) // Since \page is optional on first line of document, + editorPageCount += 1; // don't use it to increment page count; stay at 1 // add back the original class 'background' but also add the new class '.pageline' codeMirror.addLineClass(lineNumber, 'background', 'pageLine'); @@ -199,8 +203,6 @@ const Editor = createClass({ textContent : editorPageCount }); codeMirror.setBookmark({ line: lineNumber, ch: line.length }, pageCountElement); - - editorPageCount += 1; }; // New Codemirror styling for V3 renderer @@ -358,7 +360,7 @@ const Editor = createClass({ if(!this.isText() || isJumping) return; - const textSplit = this.props.renderer == 'V3' ? /^\\page$/gm : /\\page/; + const textSplit = this.props.renderer == 'V3' ? PAGEBREAK_REGEX_V3 : /\\page/; const textString = this.props.brew.text.split(textSplit).slice(0, targetPage-1).join(textSplit); const targetLine = textString.match('\n') ? textString.split('\n').length - 1 : -1; diff --git a/client/homebrew/pages/errorPage/errors/errorIndex.js b/client/homebrew/pages/errorPage/errors/errorIndex.js index ccdd86768..f7614a37a 100644 --- a/client/homebrew/pages/errorPage/errors/errorIndex.js +++ b/client/homebrew/pages/errorPage/errors/errorIndex.js @@ -2,6 +2,11 @@ const dedent = require('dedent-tabs').default; const loginUrl = 'https://www.naturalcrit.com/login'; +// Prevent parsing text (e.g. document titles) as markdown +const escape = (text) => { + return text.split('').map(char => `&#${char.charCodeAt(0)};`).join(''); +}; + //001-050 : Brew errors //050-100 : Other pages errors @@ -89,7 +94,7 @@ const errorIndex = (props)=>{ : - **Brew Title:** ${props.brew.brewTitle || 'Unable to show title'} + **Brew Title:** ${escape(props.brew.brewTitle) || 'Unable to show title'} **Current Authors:** ${props.brew.authors?.map((author)=>{return `[${author}](/user/${author})`;}).join(', ') || 'Unable to list authors'} @@ -104,7 +109,7 @@ const errorIndex = (props)=>{ : - **Brew Title:** ${props.brew.brewTitle || 'Unable to show title'} + **Brew Title:** ${escape(props.brew.brewTitle) || 'Unable to show title'} **Current Authors:** ${props.brew.authors?.map((author)=>{return `[${author}](/user/${author})`;}).join(', ') || 'Unable to list authors'} @@ -181,7 +186,7 @@ const errorIndex = (props)=>{ **Brew ID:** ${props.brew.brewId} - **Brew Title:** ${props.brew.brewTitle}`, + **Brew Title:** ${escape(props.brew.brewTitle)}`, // ####### Admin page error ####### '52' : dedent` diff --git a/install/README.UBUNTU.md b/install/README.UBUNTU.md index d14cfef46..13fa7631d 100644 --- a/install/README.UBUNTU.md +++ b/install/README.UBUNTU.md @@ -24,12 +24,16 @@ These instructions assume that you are installing to a completely new, fresh Ubu These installation instructions have been tested on the following Ubuntu releases: -- *ubuntu-20.04.3-desktop-amd64* + - *ubuntu-24.04.1-desktop-amd64* + - *ubuntu-22.04.5-desktop-amd64* + - *ubuntu-20.04.6-desktop-amd64* ## Final Notes While this installation process works successfully at the time of writing (December 19, 2021), it relies on all of the Node.JS packages used in the HomeBrewery project retaining their cross-platform capabilities to continue to function. This is one of the inherent advantages of Node.JS, but it is by no means guaranteed and as such, functionality or even installation may fail without warning at some point in the future. +Earlier versions of Ubuntu may requier an alternate Mongo setup, see https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-ubuntu/ for assistance. + Regards, G December 19, 2021 diff --git a/install/ubuntu/etc/systemd/system/homebrewery.service b/install/ubuntu/etc/systemd/system/homebrewery.service index 939d11fb8..defa85a31 100644 --- a/install/ubuntu/etc/systemd/system/homebrewery.service +++ b/install/ubuntu/etc/systemd/system/homebrewery.service @@ -3,7 +3,8 @@ Description=Homebrewery Web Server [Service] User=root -After=mongodb +BindsTo=mongod.service +After=mongod.service Environment=NODE_ENV=local WorkingDirectory=/usr/local/homebrewery ExecStart=node server.js diff --git a/install/ubuntu/install.sh b/install/ubuntu/install.sh index ebad7f3f2..a0fcc8c17 100644 --- a/install/ubuntu/install.sh +++ b/install/ubuntu/install.sh @@ -1,14 +1,60 @@ #!/bin/sh +# Detect Ubuntu Version +export DISTRO=$(grep "^NAME=" /etc/os-release | awk -F '=' '{print $2}' | sed 's/"//g') +export DISTRO_VER=$(grep "VERSION_ID=" /etc/os-release | awk -F '=' '{print $2}' | sed 's/"//g') +export MATCHED="Yes" + +if [ "${DISTRO}" != "Ubuntu" ]; +then + echo :: Ubuntu not detected. Are you using an alternate spin or derivative? + echo :: Detected - ${DISTRO} + read -p [y/N] YESNO + if [ "${YESNO}" != "Y" ] && [ ]"${YESNO}" != "y" ]; then + exit + fi + + MATCHED="No" +fi + # Install CURL and add required NodeJS source to package repo echo ::Install CURL apt install -y curl echo ::Add NodeJS source to package repo -curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash - +curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - +# Add Mongo CE Source +if [ ${DISTRO} = "Ubuntu" ]; + then + echo ::Add Mongo CE source to package repo + curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | \ + sudo gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg \ + --dearmor + if [ "${DISTRO_VER}" == "24.04" ]; then + echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu noble/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list + elif [ "${DISTRO_VER}" == "22.04" ]; then + echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list + elif [ "${DISTRO_VER}" == "20.04" ]; then + echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list + else + MATCHED="No" + fi + sudo apt-get update +fi + +if [ ${MATCHED} == "No" ]; then + echo :: WARNING + echo :: Unable to determine Ubuntu version for Mongo installation purposes. + echo :: Please check your spin/distro documentation to install Mongo CE and enable it on startup. +fi + # Install required packages echo ::Install Homebrewery requirements -apt satisfy -y git nodejs npm mongodb +apt satisfy -y git nodejs npm mongodb-org + +# Enable and start Mongo +systemctl enable mongod +systemctl start mongod # Clone Homebrewery repo echo ::Get Homebrewery files diff --git a/package-lock.json b/package-lock.json index 6cf876186..643d1f0e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,18 @@ { "name": "homebrewery", - "version": "3.16.1", + "version": "3.17.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "homebrewery", - "version": "3.16.1", + "version": "3.17.0", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.26.0", + "@babel/core": "^7.26.7", "@babel/plugin-transform-runtime": "^7.25.9", - "@babel/preset-env": "^7.26.0", + "@babel/preset-env": "^7.26.7", "@babel/preset-react": "^7.26.3", "@googleapis/drive": "^8.14.0", "body-parser": "^1.20.2", @@ -23,7 +23,7 @@ "cors": "^2.8.5", "create-react-class": "^15.7.0", "dedent-tabs": "^0.10.3", - "dompurify": "^3.2.3", + "dompurify": "^3.2.4", "expr-eval": "^2.0.2", "express": "^4.21.2", "express-async-handler": "^1.2.0", @@ -34,11 +34,11 @@ "jwt-simple": "^0.5.6", "less": "^3.13.1", "lodash": "^4.17.21", - "marked": "11.2.0", + "marked": "13.0.3", "marked-emoji": "^1.4.3", "marked-extended-tables": "^1.1.0", - "marked-gfm-heading-id": "^3.2.0", - "marked-smartypants-lite": "^1.0.2", + "marked-gfm-heading-id": "^4.0.1", + "marked-smartypants-lite": "^1.0.3", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", "mongoose": "^8.9.5", @@ -47,7 +47,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-frame-component": "^4.1.3", - "react-router": "^7.1.3", + "react-router": "^7.1.4", "sanitize-filename": "1.6.3", "superagent": "^10.1.1", "vitreum": "git+https://git@github.com/calculuschild/vitreum.git" @@ -55,7 +55,7 @@ "devDependencies": { "@stylistic/stylelint-plugin": "^3.1.1", "babel-plugin-transform-import-meta": "^2.3.2", - "eslint": "^9.18.0", + "eslint": "^9.19.0", "eslint-plugin-jest": "^28.11.0", "eslint-plugin-react": "^7.37.4", "globals": "^15.14.0", @@ -63,7 +63,7 @@ "jest-expect-message": "^1.1.3", "jsdom-global": "^3.0.2", "postcss-less": "^6.0.0", - "stylelint": "^16.13.2", + "stylelint": "^16.14.1", "stylelint-config-recess-order": "^6.0.0", "stylelint-config-recommended": "^15.0.0", "supertest": "^7.0.0" @@ -87,9 +87,10 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz", - "integrity": "sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", @@ -100,28 +101,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.0.tgz", - "integrity": "sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.7.tgz", + "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", + "@babel/helpers": "^7.26.7", + "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/traverse": "^7.26.7", + "@babel/types": "^7.26.7", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -137,12 +140,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.0.tgz", - "integrity": "sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", + "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.0", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -162,24 +166,13 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", - "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.9", + "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -293,9 +286,10 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -332,18 +326,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", - "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", @@ -394,23 +376,25 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", + "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", + "license": "MIT", "dependencies": { "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", - "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", + "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -786,11 +770,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", - "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -958,11 +943,11 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", - "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", + "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", + "license": "MIT", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { @@ -1089,13 +1074,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", - "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", + "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-simple-access": "^7.25.9" + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1166,11 +1151,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", - "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1494,11 +1480,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", - "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1567,13 +1554,14 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", - "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.7.tgz", + "integrity": "sha512-Ycg2tnXwixaXOVb29rana8HNPgLVBof8qqtNQ9LE22IoyZboQbGSxI6ZySMdW3K5nAe6gu35IaJefUJflhUFTQ==", + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/compat-data": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", @@ -1587,7 +1575,7 @@ "@babel/plugin-transform-arrow-functions": "^7.25.9", "@babel/plugin-transform-async-generator-functions": "^7.25.9", "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.26.5", "@babel/plugin-transform-block-scoping": "^7.25.9", "@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-class-static-block": "^7.26.0", @@ -1598,7 +1586,7 @@ "@babel/plugin-transform-duplicate-keys": "^7.25.9", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.26.3", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-for-of": "^7.25.9", "@babel/plugin-transform-function-name": "^7.25.9", @@ -1607,12 +1595,12 @@ "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", "@babel/plugin-transform-member-expression-literals": "^7.25.9", "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/plugin-transform-modules-systemjs": "^7.25.9", "@babel/plugin-transform-modules-umd": "^7.25.9", "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", "@babel/plugin-transform-numeric-separator": "^7.25.9", "@babel/plugin-transform-object-rest-spread": "^7.25.9", "@babel/plugin-transform-object-super": "^7.25.9", @@ -1629,7 +1617,7 @@ "@babel/plugin-transform-spread": "^7.25.9", "@babel/plugin-transform-sticky-regex": "^7.25.9", "@babel/plugin-transform-template-literals": "^7.25.9", - "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.26.7", "@babel/plugin-transform-unicode-escapes": "^7.25.9", "@babel/plugin-transform-unicode-property-regex": "^7.25.9", "@babel/plugin-transform-unicode-regex": "^7.25.9", @@ -1707,15 +1695,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", + "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/types": "^7.26.7", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1733,9 +1722,10 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", + "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -1915,10 +1905,11 @@ } }, "node_modules/@eslint/js": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", - "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", + "version": "9.19.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.19.0.tgz", + "integrity": "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -3846,16 +3837,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, "node_modules/bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", @@ -5332,9 +5313,10 @@ } }, "node_modules/dompurify": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.3.tgz", - "integrity": "sha512-U1U5Hzc2MO0oW3DF+G9qYN0aT7atAou4AgI0XjWz061nyBPbdxkfdhfy5uMgGn6+oLFCfn44ZGbdDqCzVmlOWA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", + "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", + "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" } @@ -5650,17 +5632,18 @@ "license": "MIT" }, "node_modules/eslint": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz", - "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", + "version": "9.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz", + "integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.10.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.18.0", + "@eslint/js": "9.19.0", "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -6345,13 +6328,6 @@ "node": ">=16.0.0" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT", - "optional": true - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -6525,20 +6501,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -9827,9 +9789,9 @@ } }, "node_modules/marked": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-11.2.0.tgz", - "integrity": "sha512-HR0m3bvu0jAPYiIvLUUQtdg1g6D247//lvcekpHO1WMvbwDlwSkZAX9Lw4F4YHE1T0HaaNve0tuAWuV1UJ6vtw==", + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz", + "integrity": "sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -9855,24 +9817,24 @@ } }, "node_modules/marked-gfm-heading-id": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/marked-gfm-heading-id/-/marked-gfm-heading-id-3.2.0.tgz", - "integrity": "sha512-Xfxpr5lXLDLY10XqzSCA9l2dDaiabQUgtYM9hw8yunyVsB/xYBRpiic6BOiY/EAJw1ik1eWr1ET1HKOAPZBhXg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/marked-gfm-heading-id/-/marked-gfm-heading-id-4.1.1.tgz", + "integrity": "sha512-EeQZieAQmsI6c2tWLx0ETd0VjPwLV8qi+HT0dIsfVMERm0rCIuXfRvZXJbo1SgUi++lmuR1LVY+QzgNiLNvVpw==", "license": "MIT", "dependencies": { "github-slugger": "^2.0.0" }, "peerDependencies": { - "marked": ">=4 <13" + "marked": ">=13 <16" } }, "node_modules/marked-smartypants-lite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/marked-smartypants-lite/-/marked-smartypants-lite-1.0.2.tgz", - "integrity": "sha512-cEANts+s3+gnTzXPvPT2z4V8NfbMEL9QooKUviug0DkaKkXQWrUwDAmFnQAkLSJCw2BQcD8YPDyxu0HJ3mg36w==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/marked-smartypants-lite/-/marked-smartypants-lite-1.0.3.tgz", + "integrity": "sha512-OOL8cjFog8KtgUFpkihRu6G42Dz2QPOU4k2xfKmMJ0CdoX+BHBDmdesHUdoQKM0mlwTsRSU3shpMeM/LWuUokg==", "license": "MIT", "peerDependencies": { - "marked": ">=4 <12" + "marked": ">=4 <16" } }, "node_modules/markedLegacy": { @@ -10373,13 +10335,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "node_modules/nan": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", - "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", - "license": "MIT", - "optional": true - }, "node_modules/nanoid": { "version": "5.0.9", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.9.tgz", @@ -11246,9 +11201,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "dev": true, "funding": [ { @@ -11266,7 +11221,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -11350,9 +11305,9 @@ "license": "MIT" }, "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -11654,9 +11609,10 @@ "license": "MIT" }, "node_modules/react-router": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.3.tgz", - "integrity": "sha512-EezYymLY6Guk/zLQ2vRA8WvdUhWFEj5fcE3RfWihhxXBW7+cd1LsIiA3lmx+KCmneAGQuyBv820o44L2+TtkSA==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.4.tgz", + "integrity": "sha512-aJWVrKoLI0nIK1lfbTU3d5al1ZEUiwtSus/xjYL8K5sv2hyPesiOIojHM7QnaNLVtroOB1McZsWk37fMQVoc6A==", + "license": "MIT", "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^1.0.1", @@ -13038,9 +12994,9 @@ "license": "ISC" }, "node_modules/stylelint": { - "version": "16.13.2", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.13.2.tgz", - "integrity": "sha512-wDlgh0mRO9RtSa3TdidqHd0nOG8MmUyVKl+dxA6C1j8aZRzpNeEgdhFmU5y4sZx4Fc6r46p0fI7p1vR5O2DZqA==", + "version": "16.14.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.14.1.tgz", + "integrity": "sha512-oqCL7AC3786oTax35T/nuLL8p2C3k/8rHKAooezrPGRvUX0wX+qqs5kMWh5YYT4PHQgVDobHT4tw55WgpYG6Sw==", "dev": true, "funding": [ { @@ -13052,6 +13008,7 @@ "url": "https://github.com/sponsors/stylelint" } ], + "license": "MIT", "dependencies": { "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", @@ -13071,7 +13028,7 @@ "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.3.1", - "ignore": "^7.0.1", + "ignore": "^7.0.3", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", "known-css-properties": "^0.35.0", @@ -13080,7 +13037,7 @@ "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.1.1", - "postcss": "^8.4.49", + "postcss": "^8.5.1", "postcss-resolve-nested-selector": "^0.1.6", "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^7.0.0", @@ -14436,25 +14393,6 @@ "node": ">=0.10.0" } }, - "node_modules/watchify/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, "node_modules/watchify/node_modules/glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", diff --git a/package.json b/package.json index 40f6aad81..cd43bb5e2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "homebrewery", "description": "Create authentic looking D&D homebrews using only markdown", - "version": "3.16.1", + "version": "3.17.0", "type": "module", "engines": { "npm": "^10.2.x", @@ -85,9 +85,9 @@ ] }, "dependencies": { - "@babel/core": "^7.26.0", + "@babel/core": "^7.26.7", "@babel/plugin-transform-runtime": "^7.25.9", - "@babel/preset-env": "^7.26.0", + "@babel/preset-env": "^7.26.7", "@babel/preset-react": "^7.26.3", "@googleapis/drive": "^8.14.0", "body-parser": "^1.20.2", @@ -98,7 +98,7 @@ "cors": "^2.8.5", "create-react-class": "^15.7.0", "dedent-tabs": "^0.10.3", - "dompurify": "^3.2.3", + "dompurify": "^3.2.4", "expr-eval": "^2.0.2", "express": "^4.21.2", "express-async-handler": "^1.2.0", @@ -109,11 +109,11 @@ "jwt-simple": "^0.5.6", "less": "^3.13.1", "lodash": "^4.17.21", - "marked": "11.2.0", + "marked": "13.0.3", "marked-emoji": "^1.4.3", "marked-extended-tables": "^1.1.0", - "marked-gfm-heading-id": "^3.2.0", - "marked-smartypants-lite": "^1.0.2", + "marked-gfm-heading-id": "^4.0.1", + "marked-smartypants-lite": "^1.0.3", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", "mongoose": "^8.9.5", @@ -122,7 +122,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-frame-component": "^4.1.3", - "react-router": "^7.1.3", + "react-router": "^7.1.4", "sanitize-filename": "1.6.3", "superagent": "^10.1.1", "vitreum": "git+https://git@github.com/calculuschild/vitreum.git" @@ -130,7 +130,7 @@ "devDependencies": { "@stylistic/stylelint-plugin": "^3.1.1", "babel-plugin-transform-import-meta": "^2.3.2", - "eslint": "^9.18.0", + "eslint": "^9.19.0", "eslint-plugin-jest": "^28.11.0", "eslint-plugin-react": "^7.37.4", "globals": "^15.14.0", @@ -138,7 +138,7 @@ "jest-expect-message": "^1.1.3", "jsdom-global": "^3.0.2", "postcss-less": "^6.0.0", - "stylelint": "^16.13.2", + "stylelint": "^16.14.1", "stylelint-config-recess-order": "^6.0.0", "stylelint-config-recommended": "^15.0.0", "supertest": "^7.0.0" diff --git a/stylelint_plugins/declaration-block-multi-line-min-declarations.js b/stylelint_plugins/declaration-block-multi-line-min-declarations.js index ba40ab5f6..517fd27ec 100644 --- a/stylelint_plugins/declaration-block-multi-line-min-declarations.js +++ b/stylelint_plugins/declaration-block-multi-line-min-declarations.js @@ -1,5 +1,5 @@ -const stylelint = require('stylelint'); -const { isNumber } = require('stylelint/lib/utils/validateTypes.cjs'); +import stylelint from 'stylelint'; +import { isNumber } from 'stylelint/lib/utils/validateTypes.mjs'; const { report, ruleMessages, validateOptions } = stylelint.utils; const ruleName = 'naturalcrit/declaration-block-multi-line-min-declarations'; @@ -7,9 +7,8 @@ const messages = ruleMessages(ruleName, { expected : (decls)=>`Rule with ${decls} declaration${decls == 1 ? '' : 's'} should be single line`, }); - -module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOption, secondaryOptionObject, context) { - return function lint(postcssRoot, postcssResult) { +const ruleFunction = (primaryOption, secondaryOptionObject, context)=>{ + return (postcssRoot, postcssResult)=>{ const validOptions = validateOptions( postcssResult, @@ -20,26 +19,23 @@ module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOpti } ); - if(!validOptions) { //If the options are invalid, don't lint + if(!validOptions) //If the options are invalid, don't lint return; - } + const isAutoFixing = Boolean(context.fix); postcssRoot.walkRules((rule)=>{ //Iterate CSS rules //Apply rule only if all children are decls (no further nested rules) - if(rule.nodes.length > primaryOption || !rule.nodes.every((node)=>node.type === 'decl')) { + if(rule.nodes.length > primaryOption || !rule.nodes.every((node)=>node.type === 'decl')) return; - } //Ignore if already one line if(!rule.nodes.some((node)=>node.raws.before.includes('\n')) && !rule.raws.after.includes('\n')) return; if(isAutoFixing) { //We are in “fix” mode - rule.each((decl)=>{ - decl.raws.before = ' '; - }); + rule.each((decl)=>decl.raws.before = ' '); rule.raws.after = ' '; } else { report({ @@ -52,7 +48,9 @@ module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOpti } }); }; -}); +}; -module.exports.ruleName = ruleName; -module.exports.messages = messages; +ruleFunction.ruleName = ruleName; +ruleFunction.messages = messages; + +export default stylelint.createPlugin(ruleName, ruleFunction); diff --git a/stylelint_plugins/declaration-colon-align.js b/stylelint_plugins/declaration-colon-align.js index f1f5269d3..aeb92fcd8 100644 --- a/stylelint_plugins/declaration-colon-align.js +++ b/stylelint_plugins/declaration-colon-align.js @@ -1,32 +1,29 @@ -const stylelint = require('stylelint'); - +import stylelint from 'stylelint'; const { report, ruleMessages, validateOptions } = stylelint.utils; + const ruleName = 'naturalcrit/declaration-colon-align'; const messages = ruleMessages(ruleName, { expected : (rule)=>`Expected colons aligned within rule "${rule}"`, }); - -module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOption, secondaryOptionObject, context) { - return function lint(postcssRoot, postcssResult) { +const ruleFunction = (primaryOption, secondaryOptionObject, context)=>{ + return (postcssRoot, postcssResult)=>{ const validOptions = validateOptions( postcssResult, ruleName, { actual : primaryOption, - possible : [ - true, - false - ] + possible : [true, false] } ); - if(!validOptions) { //If the options are invalid, don't lint + if(!validOptions) // If the options are invalid, don't lint return; - } + const isAutoFixing = Boolean(context.fix); - postcssRoot.walkRules((rule)=>{ //Iterate CSS rules + + postcssRoot.walkRules((rule)=>{ // Iterate CSS rules let maxColonPos = 0; let misaligned = false; @@ -36,33 +33,37 @@ module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOpti return; const colonPos = declaration.prop.length + declaration.raws.between.indexOf(':'); - if(maxColonPos > 0 && colonPos != maxColonPos) { + + if(maxColonPos > 0 && colonPos != maxColonPos) misaligned = true; - } + maxColonPos = Math.max(maxColonPos, colonPos); }); - if(misaligned) { - if(isAutoFixing) { //We are in “fix” mode - rule.each((declaration)=>{ - if(declaration.type != 'decl') - return; + if(!misaligned) + return; - declaration.raws.between = `${' '.repeat(maxColonPos - declaration.prop.length)}:${declaration.raws.between.split(':')[1]}`; - }); - } else { //We are in “report only” mode - report({ - ruleName, - result : postcssResult, - message : messages.expected(rule.selector), // Build the reported message - node : rule, // Specify the reported node - word : rule.selector, // Which exact word caused the error? This positions the error properly - }); - } + if(isAutoFixing) { // We are in “fix” mode + rule.each((declaration)=>{ + if(declaration.type != 'decl') + return; + + declaration.raws.between = `${' '.repeat(maxColonPos - declaration.prop.length)}:${declaration.raws.between.split(':')[1]}`; + }); + } else { // We are in “report only” mode + report({ + ruleName, + result : postcssResult, + message : messages.expected(rule.selector), // Build the reported message + node : rule, // Specify the reported node + word : rule.selector, // Which exact word caused the error? This positions the error properly + }); } }); }; -}); +}; -module.exports.ruleName = ruleName; -module.exports.messages = messages; +ruleFunction.ruleName = ruleName; +ruleFunction.messages = messages; + +export default stylelint.createPlugin(ruleName, ruleFunction); \ No newline at end of file diff --git a/stylelint_plugins/declaration-colon-min-space-before.js b/stylelint_plugins/declaration-colon-min-space-before.js index e75c035a6..7776c6fef 100644 --- a/stylelint_plugins/declaration-colon-min-space-before.js +++ b/stylelint_plugins/declaration-colon-min-space-before.js @@ -1,5 +1,5 @@ -const stylelint = require('stylelint'); -const { isNumber } = require('stylelint/lib/utils/validateTypes.cjs'); +import stylelint from 'stylelint'; +import { isNumber } from 'stylelint/lib/utils/validateTypes.mjs'; const { report, ruleMessages, validateOptions } = stylelint.utils; const ruleName = 'naturalcrit/declaration-colon-min-space-before'; @@ -7,9 +7,8 @@ const messages = ruleMessages(ruleName, { expected : (num)=>`Expected at least ${num} space${num == 1 ? '' : 's'} before ":"` }); - -module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOption, secondaryOptionObject, context) { - return function lint(postcssRoot, postcssResult) { +const ruleFunction = (primaryOption, secondaryOptionObject, context)=>{ + return (postcssRoot, postcssResult)=>{ const validOptions = validateOptions( postcssResult, @@ -30,9 +29,9 @@ module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOpti const between = decl.raws.between; const colonIndex = between.indexOf(':'); - if(between.slice(0, colonIndex).length >= primaryOption) { + if(between.slice(0, colonIndex).length >= primaryOption) return; - } + if(isAutoFixing) { //We are in “fix” mode decl.raws.between = between.slice(0, colonIndex).replace(/\s*$/, ' '.repeat(primaryOption)) + between.slice(colonIndex); } else { @@ -46,7 +45,9 @@ module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOpti } }); }; -}); +}; -module.exports.ruleName = ruleName; -module.exports.messages = messages; +ruleFunction.ruleName = ruleName; +ruleFunction.messages = messages; + +export default stylelint.createPlugin(ruleName, ruleFunction); \ No newline at end of file