+
{/* Apply CSS from Style tab and render pages from Markdown tab */}
{state.isMounted
&&
<>
{renderStyle()}
-
- {renderPageInfo()}
>
);
};
diff --git a/client/homebrew/brewRenderer/brewRenderer.less b/client/homebrew/brewRenderer/brewRenderer.less
index 28ea8005e..dca64c455 100644
--- a/client/homebrew/brewRenderer/brewRenderer.less
+++ b/client/homebrew/brewRenderer/brewRenderer.less
@@ -1,8 +1,9 @@
@import (multiple, less) 'shared/naturalcrit/styles/reset.less';
.brewRenderer {
- will-change : transform;
- overflow-y : scroll;
+ overflow-y : scroll;
+ will-change : transform;
+ padding-top : 30px;
:where(.pages) {
margin : 30px 0px;
& > :where(.page) {
@@ -14,66 +15,31 @@
box-shadow : 1px 4px 14px #000000;
}
}
-
&::-webkit-scrollbar {
- width: 20px;
- &:horizontal{
- height: 20px;
- width:auto;
+ width : 20px;
+ &:horizontal {
+ width : auto;
+ height : 20px;
}
&-thumb {
- background: linear-gradient(90deg, #d3c1af 15px, #00000000 15px);
- &:horizontal{
- background: linear-gradient(0deg, #d3c1af 15px, #00000000 15px);
- }
- }
- &-corner {
- visibility: hidden;
+ background : linear-gradient(90deg, #D3C1AF 15px, #00000000 15px);
+ &:horizontal { background : linear-gradient(0deg, #D3C1AF 15px, #00000000 15px); }
}
+ &-corner { visibility : hidden; }
}
-
-
-
-
-
}
+
.pane { position : relative; }
-.pageInfo {
- position : absolute;
- right : 17px;
- bottom : 0;
- z-index : 1000;
- font-size : 10px;
- font-weight : 800;
- color : white;
- background-color : #333333;
- div {
- display : inline-block;
- padding : 8px 10px;
- &:not(:last-child) { border-right : 1px solid #666666; }
- }
-}
-.ppr_msg {
- position : absolute;
- bottom : 0;
- left : 0px;
- z-index : 1000;
- padding : 8px 10px;
- font-size : 10px;
- font-weight : 800;
- color : white;
- background-color : #333333;
-}
@media print {
+ .toolBar { display : none; }
.brewRenderer {
- height: 100%;
- overflow-y: unset;
+ height : 100%;
+ padding-top : unset;
+ overflow-y : unset;
.pages {
- margin: 0px;
- &>.page {
- box-shadow: unset;
- }
+ margin : 0px;
+ & > .page { box-shadow : unset; }
}
}
}
\ No newline at end of file
diff --git a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.less b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.less
index 26d764aff..2982055c8 100644
--- a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.less
+++ b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.less
@@ -1,9 +1,10 @@
.popups {
position : fixed;
- top : @navbarHeight;
+ top : calc(@navbarHeight + @viewerToolsHeight);
right : 24px;
z-index : 10001;
width : 450px;
+ margin-top : 5px;
}
.notificationPopup {
diff --git a/client/homebrew/brewRenderer/toolBar/toolBar.jsx b/client/homebrew/brewRenderer/toolBar/toolBar.jsx
new file mode 100644
index 000000000..fb3b62067
--- /dev/null
+++ b/client/homebrew/brewRenderer/toolBar/toolBar.jsx
@@ -0,0 +1,162 @@
+require('./toolBar.less');
+const React = require('react');
+const { useState, useEffect } = React;
+const _ = require('lodash');
+
+
+const MAX_ZOOM = 300;
+const MIN_ZOOM = 10;
+
+const ToolBar = ({ onZoomChange, currentPage, onPageChange, totalPages })=>{
+
+ const [zoomLevel, setZoomLevel] = useState(100);
+ const [pageNum, setPageNum] = useState(currentPage);
+
+ useEffect(()=>{
+ onZoomChange(zoomLevel);
+ }, [zoomLevel]);
+
+ useEffect(()=>{
+ setPageNum(currentPage);
+ }, [currentPage]);
+
+ const handleZoomButton = (zoom)=>{
+ setZoomLevel(_.round(_.clamp(zoom, MIN_ZOOM, MAX_ZOOM)));
+ };
+
+ const handlePageInput = (pageInput)=>{
+ if(/[0-9]/.test(pageInput))
+ setPageNum(parseInt(pageInput)); // input type is 'text', so `page` comes in as a string, not number.
+ };
+
+ const scrollToPage = (pageNumber)=>{
+ pageNumber = _.clamp(pageNumber, 1, totalPages);
+ const iframe = document.getElementById('BrewRenderer');
+ const brewRenderer = iframe?.contentWindow?.document.querySelector('.brewRenderer');
+ const page = brewRenderer?.querySelector(`#p${pageNumber}`);
+ page?.scrollIntoView({ block: 'start' });
+ setPageNum(pageNumber);
+ };
+
+
+ const calculateChange = (mode)=>{
+ const iframe = document.getElementById('BrewRenderer');
+ const iframeWidth = iframe.getBoundingClientRect().width;
+ const iframeHeight = iframe.getBoundingClientRect().height;
+ const pages = iframe.contentWindow.document.getElementsByClassName('page');
+
+ let desiredZoom = 0;
+
+ if(mode == 'fill'){
+ // find widest page, in case pages are different widths, so that the zoom is adapted to not cut the widest page off screen.
+ const widestPage = _.maxBy([...pages], 'offsetWidth').offsetWidth;
+
+ desiredZoom = (iframeWidth / widestPage) * 100;
+
+ } else if(mode == 'fit'){
+ // find the page with the largest single dim (height or width) so that zoom can be adapted to fit it.
+ const minDimRatio = [...pages].reduce((minRatio, page) => Math.min(minRatio, iframeWidth / page.offsetWidth, iframeHeight / page.offsetHeight), Infinity);
+
+ desiredZoom = minDimRatio * 100;
+ }
+
+ const margin = 5; // extra space so page isn't edge to edge (not truly "to fill")
+
+ const deltaZoom = (desiredZoom - zoomLevel) - margin;
+ return deltaZoom;
+ };
+
+ return (
+
+ {/*v=====----------------------< Zoom Controls >---------------------=====v*/}
+
+ handleZoomButton(zoomLevel + calculateChange('fill'))}
+ >
+
+
+ handleZoomButton(zoomLevel + calculateChange('fit'))}
+ >
+
+
+ handleZoomButton(zoomLevel - 20)}
+ disabled={zoomLevel <= MIN_ZOOM}
+ >
+
+
+ handleZoomButton(parseInt(e.target.value))}
+ />
+
+
+
+
+ handleZoomButton(zoomLevel + 20)}
+ disabled={zoomLevel >= MAX_ZOOM}
+ >
+
+
+
+
+ {/*v=====----------------------< Page Controls >---------------------=====v*/}
+
+
scrollToPage(pageNum - 1)}
+ disabled={pageNum <= 1}
+ >
+
+
+
+
+ e.target.select()}
+ onChange={(e)=>handlePageInput(e.target.value)}
+ onBlur={()=>scrollToPage(pageNum)}
+ onKeyDown={(e)=>e.key == 'Enter' && scrollToPage(pageNum)}
+ />
+ / {totalPages}
+
+
+
scrollToPage(pageNum + 1)}
+ disabled={pageNum >= totalPages}
+ >
+
+
+
+
+ );
+};
+
+module.exports = ToolBar;
diff --git a/client/homebrew/brewRenderer/toolBar/toolBar.less b/client/homebrew/brewRenderer/toolBar/toolBar.less
new file mode 100644
index 000000000..d565ca7d4
--- /dev/null
+++ b/client/homebrew/brewRenderer/toolBar/toolBar.less
@@ -0,0 +1,103 @@
+@import (less) './client/icons/customIcons.less';
+
+.toolBar {
+ position : absolute;
+ z-index : 1;
+ box-sizing : border-box;
+ display : flex;
+ flex-wrap : wrap;
+ gap : 8px 30px;
+ align-items : center;
+ justify-content : center;
+ width : 100%;
+ height : auto;
+ padding : 2px 0;
+ font-family : 'Open Sans', sans-serif;
+ color : #CCCCCC;
+ background-color : #555555;
+
+ .group {
+ box-sizing : border-box;
+ display : flex;
+ gap : 0 3px;
+ align-items : center;
+ justify-content : center;
+ height : 28px;
+ }
+
+ .tool {
+ display : flex;
+ align-items : center;
+ }
+
+ input {
+ position : relative;
+ height : 1.5em;
+ padding : 2px 5px;
+ font-family : 'Open Sans', sans-serif;
+ color : #000000;
+ background : #EEEEEE;
+ border : 1px solid gray;
+ &:focus { outline : 1px solid #D3D3D3; }
+
+ // `.range-input` if generic to all range inputs, or `#zoom-slider` if only for zoom slider
+ &.range-input {
+ padding : 2px 0;
+ color : #D3D3D3;
+ accent-color : #D3D3D3;
+
+ &::-webkit-slider-thumb, &::-moz-slider-thumb {
+ width : 5px;
+ height : 5px;
+ cursor : pointer;
+ outline : none;
+ }
+
+ &:hover::after {
+ position : absolute;
+ bottom : -30px;
+ left : 50%;
+ z-index : 1;
+ display : grid;
+ place-items : center;
+ width : 4ch;
+ height : 1.2lh;
+ pointer-events : none;
+ content : attr(value);
+ background-color : #555555;
+ border : 1px solid #A1A1A1;
+ transform : translate(-50%, 50%);
+ }
+ }
+
+ // `.text-input` if generic to all range inputs, or `#page-input` if only for current page input
+ page-input {
+ width : 4ch;
+ margin-right : 1ch;
+ text-align : center;
+ }
+ }
+
+ button {
+ box-sizing : content-box;
+ display : flex;
+ align-items : center;
+ justify-content : center;
+ width : auto;
+ min-width : 46px;
+ height : 100%;
+ padding : 0 0px;
+ font-weight : unset;
+ color : inherit;
+ background-color : unset;
+ &:hover { background-color : #444444; }
+ &:focus { outline : 1px solid #D3D3D3; }
+ &:disabled {
+ color : #777777;
+ background-color : unset !important;
+ }
+ i {
+ font-size:1.2em;
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx
index 1ecdcb22b..364ed424a 100644
--- a/client/homebrew/editor/editor.jsx
+++ b/client/homebrew/editor/editor.jsx
@@ -59,6 +59,8 @@ const Editor = createClass({
this.updateEditorSize();
this.highlightCustomMarkdown();
window.addEventListener('resize', this.updateEditorSize);
+ document.getElementById('BrewRenderer').addEventListener('keydown', this.handleControlKeys);
+ document.addEventListener('keydown', this.handleControlKeys);
const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY);
if(editorTheme) {
@@ -82,6 +84,19 @@ const Editor = createClass({
};
},
+ handleControlKeys : function(e){
+ if(!(e.ctrlKey && e.metaKey)) return;
+ const LEFTARROW_KEY = 37;
+ const RIGHTARROW_KEY = 39;
+ if (e.shiftKey && (e.keyCode == RIGHTARROW_KEY)) this.brewJump();
+ if (e.shiftKey && (e.keyCode == LEFTARROW_KEY)) this.sourceJump();
+ if ((e.keyCode == LEFTARROW_KEY) || (e.keyCode == RIGHTARROW_KEY)) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ },
+
+
updateEditorSize : function() {
if(this.codeEditor.current) {
let paneHeight = this.editor.current.parentNode.clientHeight;
@@ -119,8 +134,19 @@ const Editor = createClass({
const codeMirror = this.codeEditor.current.codeMirror;
codeMirror.operation(()=>{ // Batch CodeMirror styling
+
+ const foldLines = [];
+
//reset custom text styles
- const customHighlights = codeMirror.getAllMarks().filter((mark)=>!mark.__isFold); //Don't undo code folding
+ const customHighlights = codeMirror.getAllMarks().filter((mark)=>{
+ // Record details of folded sections
+ if(mark.__isFold) {
+ const fold = mark.find();
+ foldLines.push({from: fold.from?.line, to: fold.to?.line});
+ }
+ return !mark.__isFold;
+ }); //Don't undo code folding
+
for (let i=customHighlights.length - 1;i>=0;i--) customHighlights[i].clear();
let editorPageCount = 2; // start page count from page 2
@@ -132,6 +158,11 @@ const Editor = createClass({
codeMirror.removeLineClass(lineNumber, 'text');
codeMirror.removeLineClass(lineNumber, 'wrap', 'sourceMoveFlash');
+ // Don't process lines inside folded text
+ // If the current lineNumber is inside any folded marks, skip line styling
+ if (foldLines.some(fold => lineNumber >= fold.from && lineNumber <= fold.to))
+ return;
+
// Styling for \page breaks
if((this.props.renderer == 'legacy' && line.includes('\\page')) ||
(this.props.renderer == 'V3' && line.match(/^\\page$/))) {
@@ -244,7 +275,7 @@ const Editor = createClass({
// Iterate over conflicting marks and clear them
var marks = codeMirror.findMarks(startPos, endPos);
marks.forEach(function(marker) {
- marker.clear();
+ if(!marker.__isFold) marker.clear();
});
codeMirror.markText(startPos, endPos, { className: 'emoji' });
}
diff --git a/client/homebrew/navbar/navbar.less b/client/homebrew/navbar/navbar.less
index d0f2f77e8..4525a193e 100644
--- a/client/homebrew/navbar/navbar.less
+++ b/client/homebrew/navbar/navbar.less
@@ -1,6 +1,7 @@
@import 'naturalcrit/styles/colors.less';
@navbarHeight : 28px;
+@viewerToolsHeight : 32px;
@keyframes pinkColoring {
0% { color : pink; }
diff --git a/client/homebrew/pages/errorPage/errors/errorIndex.js b/client/homebrew/pages/errorPage/errors/errorIndex.js
index 7bf2caae1..957991ad6 100644
--- a/client/homebrew/pages/errorPage/errors/errorIndex.js
+++ b/client/homebrew/pages/errorPage/errors/errorIndex.js
@@ -2,6 +2,9 @@ const dedent = require('dedent-tabs').default;
const loginUrl = 'https://www.naturalcrit.com/login';
+//001-050 : Brew errors
+//050-100 : Other pages errors
+
const errorIndex = (props)=>{
return {
// Default catch all
@@ -149,6 +152,14 @@ const errorIndex = (props)=>{
**Brew ID:** ${props.brew.brewId}`,
+ //account page when account is not defined
+ '50' : dedent`
+ ## You are not signed in
+
+ You are trying to access the account page, but are not signed in to an account.
+
+ Please login or signup at our [login page](https://www.naturalcrit.com/login?redirect=https://homebrewery.naturalcrit.com/account).`,
+
// Brew locked by Administrators error
'100' : dedent`
## This brew has been locked.
@@ -163,4 +174,4 @@ const errorIndex = (props)=>{
};
};
-module.exports = errorIndex;
\ No newline at end of file
+module.exports = errorIndex;
diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx
index 89d1df874..5b0f59c00 100644
--- a/client/homebrew/pages/newPage/newPage.jsx
+++ b/client/homebrew/pages/newPage/newPage.jsx
@@ -84,6 +84,9 @@ const NewPage = createClass({
if(brew.style)
localStorage.setItem(STYLEKEY, brew.style);
localStorage.setItem(METAKEY, JSON.stringify({ 'renderer': brew.renderer, 'theme': brew.theme, 'lang': brew.lang }));
+ if(window.location.pathname != '/new') {
+ window.history.replaceState({}, window.location.title, '/new/');
+ }
},
componentWillUnmount : function() {
document.removeEventListener('keydown', this.handleControlKeys);
diff --git a/client/icons/customIcons.less b/client/icons/customIcons.less
index dd6605326..0d462833d 100644
--- a/client/icons/customIcons.less
+++ b/client/icons/customIcons.less
@@ -1,57 +1,75 @@
.fac {
- display : inline-block;
+ display : inline-block;
+ background-color : currentColor;
+ mask-size : contain;
+ mask-repeat : no-repeat;
+ mask-position : center;
+ width : 1em;
+ aspect-ratio : 1;
}
.position-top-left {
- content: url('../icons/position-top-left.svg');
+ mask-image: url('../icons/position-top-left.svg');
}
.position-top-right {
- content: url('../icons/position-top-right.svg');
+ mask-image: url('../icons/position-top-right.svg');
}
.position-bottom-left {
- content: url('../icons/position-bottom-left.svg');
+ mask-image: url('../icons/position-bottom-left.svg');
}
.position-bottom-right {
- content: url('../icons/position-bottom-right.svg');
+ mask-image: url('../icons/position-bottom-right.svg');
}
.position-top {
- content: url('../icons/position-top.svg');
+ mask-image: url('../icons/position-top.svg');
}
.position-right {
- content: url('../icons/position-right.svg');
+ mask-image: url('../icons/position-right.svg');
}
.position-bottom {
- content: url('../icons/position-bottom.svg');
+ mask-image: url('../icons/position-bottom.svg');
}
.position-left {
- content: url('../icons/position-left.svg');
+ mask-image: url('../icons/position-left.svg');
}
.mask-edge {
- content: url('../icons/mask-edge.svg');
+ mask-image: url('../icons/mask-edge.svg');
}
.mask-corner {
- content: url('../icons/mask-corner.svg');
+ mask-image: url('../icons/mask-corner.svg');
}
.mask-center {
- content: url('../icons/mask-center.svg');
+ mask-image: url('../icons/mask-center.svg');
}
.book-front-cover {
- content: url('../icons/book-front-cover.svg');
+ mask-image: url('../icons/book-front-cover.svg');
}
.book-back-cover {
- content: url('../icons/book-back-cover.svg');
+ mask-image: url('../icons/book-back-cover.svg');
}
.book-inside-cover {
- content: url('../icons/book-inside-cover.svg');
+ mask-image: url('../icons/book-inside-cover.svg');
}
.book-part-cover {
- content: url('../icons/book-part-cover.svg');
+ mask-image: url('../icons/book-part-cover.svg');
+}
+.image-wrap-left {
+ mask-image: url('../icons/image-wrap-left.svg');
+}
+.image-wrap-right {
+ mask-image: url('../icons/image-wrap-right.svg');
}
.davek {
- content: url('../icons/Davek.svg');
+ mask-image: url('../icons/Davek.svg');
}
.rellanic {
- content: url('../icons/Rellanic.svg');
+ mask-image: url('../icons/Rellanic.svg');
}
.iokharic {
- content: url('../icons/Iokharic.svg');
+ mask-image: url('../icons/Iokharic.svg');
+}
+.zoom-to-fit {
+ mask-image: url('../icons/zoom-to-fit.svg');
+}
+.fit-width {
+ mask-image: url('../icons/fit-width.svg');
}
diff --git a/client/icons/fit-width.svg b/client/icons/fit-width.svg
new file mode 100644
index 000000000..dd3e52f75
--- /dev/null
+++ b/client/icons/fit-width.svg
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/icons/image-wrap-left.svg b/client/icons/image-wrap-left.svg
new file mode 100644
index 000000000..fe1024e43
--- /dev/null
+++ b/client/icons/image-wrap-left.svg
@@ -0,0 +1,58 @@
+
+
diff --git a/client/icons/image-wrap-right.svg b/client/icons/image-wrap-right.svg
new file mode 100644
index 000000000..336a20b64
--- /dev/null
+++ b/client/icons/image-wrap-right.svg
@@ -0,0 +1,58 @@
+
+
diff --git a/client/icons/zoom-to-fit.svg b/client/icons/zoom-to-fit.svg
new file mode 100644
index 000000000..5179ec45e
--- /dev/null
+++ b/client/icons/zoom-to-fit.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 000000000..25d0395c7
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,71 @@
+import react from "eslint-plugin-react";
+import jest from "eslint-plugin-jest";
+import globals from "globals";
+
+export default [{
+ ignores: ["build/"]
+ },
+ {
+ files : ['**/*.js', '**/*.jsx'],
+ plugins : { react, jest },
+ languageOptions : {
+ ecmaVersion : "latest",
+ sourceType : "module",
+ parserOptions : { ecmaFeatures: { jsx: true } },
+ globals : { ...globals.browser, ...globals.node }
+ },
+ rules: {
+ /** Errors **/
+ "camelcase" : ["error", { properties: "never" }],
+ "no-array-constructor" : "error",
+ "no-iterator" : "error",
+ "no-nested-ternary" : "error",
+ "no-new-object" : "error",
+ "no-proto" : "error",
+ "react/jsx-no-bind" : ["error", { allowArrowFunctions: true }],
+ "react/jsx-uses-react" : "error",
+ "react/prefer-es6-class" : ["error", "never"],
+ "jest/valid-expect" : ["error", { maxArgs: 3 }],
+
+ /** Warnings **/
+ "max-lines" : ["warn", { max: 200, skipComments: true, skipBlankLines: true }],
+ "max-depth" : ["warn", { max: 4 }],
+ "max-params" : ["warn", { max: 5 }],
+ "no-restricted-syntax" : ["warn", "ClassDeclaration", "SwitchStatement"],
+ "no-unused-vars" : ["warn", { vars: "all", args: "none", varsIgnorePattern: "config|_|cx|createClass" }],
+ "react/jsx-uses-vars" : "warn",
+
+ /** Fixable **/
+ "arrow-parens" : ["warn", "always"],
+ "brace-style" : ["warn", "1tbs", { allowSingleLine: true }],
+ "jsx-quotes" : ["warn", "prefer-single"],
+ "no-var" : "warn",
+ "prefer-const" : "warn",
+ "prefer-template" : "warn",
+ "quotes" : ["warn", "single", { allowTemplateLiterals: true }],
+ "semi" : ["warn", "always"],
+
+ /** Whitespace **/
+ "array-bracket-spacing" : ["warn", "never"],
+ "arrow-spacing" : ["warn", { before: false, after: false }],
+ "comma-spacing" : ["warn", { before: false, after: true }],
+ "indent" : ["warn", "tab", { MemberExpression: "off" }],
+ "linebreak-style" : "off",
+ "no-trailing-spaces" : "warn",
+ "no-whitespace-before-property" : "warn",
+ "object-curly-spacing" : ["warn", "always"],
+ "react/jsx-indent-props" : ["warn", "tab"],
+ "space-in-parens" : ["warn", "never"],
+ "template-curly-spacing" : ["warn", "never"],
+ "keyword-spacing" : ["warn", {
+ before : true,
+ after : true,
+ overrides : { if: { before: false, after: false } }
+ }],
+ "key-spacing" : ["warn", {
+ multiLine : { beforeColon: true, afterColon: true, align: "colon" },
+ singleLine : { beforeColon: false, afterColon: true }
+ }]
+ }
+ }
+];
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index e025814ae..6dd97c4c4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,20 +1,20 @@
{
"name": "homebrewery",
- "version": "3.14.0",
+ "version": "3.14.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "homebrewery",
- "version": "3.14.0",
+ "version": "3.14.2",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@babel/core": "^7.25.2",
- "@babel/plugin-transform-runtime": "^7.24.7",
- "@babel/preset-env": "^7.25.3",
+ "@babel/plugin-transform-runtime": "^7.25.4",
+ "@babel/preset-env": "^7.25.4",
"@babel/preset-react": "^7.24.7",
- "@googleapis/drive": "^8.11.0",
+ "@googleapis/drive": "^8.13.1",
"body-parser": "^1.20.2",
"classnames": "^2.5.1",
"codemirror": "^5.65.6",
@@ -33,32 +33,33 @@
"lodash": "^4.17.21",
"marked": "11.2.0",
"marked-emoji": "^1.4.2",
- "marked-extended-tables": "^1.0.8",
+ "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.5.2",
+ "mongoose": "^8.6.0",
"nanoid": "3.3.4",
"nconf": "^0.12.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-frame-component": "^4.1.3",
- "react-router-dom": "6.26.0",
+ "react-router-dom": "6.26.1",
"sanitize-filename": "1.6.3",
- "superagent": "^9.0.2",
+ "superagent": "^10.1.0",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
},
"devDependencies": {
- "@stylistic/stylelint-plugin": "^3.0.0",
- "eslint": "^8.57.0",
+ "@stylistic/stylelint-plugin": "^3.0.1",
+ "eslint": "^9.9.1",
"eslint-plugin-jest": "^28.8.0",
"eslint-plugin-react": "^7.35.0",
+ "globals": "^15.9.0",
"jest": "^29.7.0",
"jest-expect-message": "^1.1.3",
"postcss-less": "^6.0.0",
- "stylelint": "^16.8.0",
- "stylelint-config-recess-order": "^5.0.1",
+ "stylelint": "^16.9.0",
+ "stylelint-config-recess-order": "^5.1.0",
"stylelint-config-recommended": "^14.0.1",
"supertest": "^7.0.0"
},
@@ -94,10 +95,9 @@
}
},
"node_modules/@babel/compat-data": {
- "version": "7.25.2",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz",
- "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz",
+ "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==",
"engines": {
"node": ">=6.9.0"
}
@@ -133,12 +133,11 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.25.0",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz",
- "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.4.tgz",
+ "integrity": "sha512-NFtZmZsyzDPJnk9Zg3BbTfKKc9UlHYzD0E//p2Z3B9nCwwtJW9T0gVbCz8+fBngnn4zf1Dr3IK8PHQQHq0lDQw==",
"dependencies": {
- "@babel/types": "^7.25.0",
+ "@babel/types": "^7.25.4",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
"jsesc": "^2.5.1"
@@ -189,17 +188,16 @@
}
},
"node_modules/@babel/helper-create-class-features-plugin": {
- "version": "7.25.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.0.tgz",
- "integrity": "sha512-GYM6BxeQsETc9mnct+nIIpf63SAyzvyYN7UB/IlTyd+MBg06afFGp0mIeUqGyWgS2mxad6vqbMrHVlaL3m70sQ==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz",
+ "integrity": "sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.24.7",
"@babel/helper-member-expression-to-functions": "^7.24.8",
"@babel/helper-optimise-call-expression": "^7.24.7",
"@babel/helper-replace-supers": "^7.25.0",
"@babel/helper-skip-transparent-expression-wrappers": "^7.24.7",
- "@babel/traverse": "^7.25.0",
+ "@babel/traverse": "^7.25.4",
"semver": "^6.3.1"
},
"engines": {
@@ -437,12 +435,11 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.25.3",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz",
- "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz",
+ "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==",
"dependencies": {
- "@babel/types": "^7.25.2"
+ "@babel/types": "^7.25.4"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -837,15 +834,14 @@
}
},
"node_modules/@babel/plugin-transform-async-generator-functions": {
- "version": "7.25.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.0.tgz",
- "integrity": "sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.4.tgz",
+ "integrity": "sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg==",
"dependencies": {
"@babel/helper-plugin-utils": "^7.24.8",
"@babel/helper-remap-async-to-generator": "^7.25.0",
"@babel/plugin-syntax-async-generators": "^7.8.4",
- "@babel/traverse": "^7.25.0"
+ "@babel/traverse": "^7.25.4"
},
"engines": {
"node": ">=6.9.0"
@@ -902,13 +898,12 @@
}
},
"node_modules/@babel/plugin-transform-class-properties": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz",
- "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz",
+ "integrity": "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==",
"dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.24.7",
- "@babel/helper-plugin-utils": "^7.24.7"
+ "@babel/helper-create-class-features-plugin": "^7.25.4",
+ "@babel/helper-plugin-utils": "^7.24.8"
},
"engines": {
"node": ">=6.9.0"
@@ -935,16 +930,15 @@
}
},
"node_modules/@babel/plugin-transform-classes": {
- "version": "7.25.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.0.tgz",
- "integrity": "sha512-xyi6qjr/fYU304fiRwFbekzkqVJZ6A7hOjWZd+89FVcBqPV3S9Wuozz82xdpLspckeaafntbzglaW4pqpzvtSw==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz",
+ "integrity": "sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.24.7",
- "@babel/helper-compilation-targets": "^7.24.8",
+ "@babel/helper-compilation-targets": "^7.25.2",
"@babel/helper-plugin-utils": "^7.24.8",
"@babel/helper-replace-supers": "^7.25.0",
- "@babel/traverse": "^7.25.0",
+ "@babel/traverse": "^7.25.4",
"globals": "^11.1.0"
},
"engines": {
@@ -954,6 +948,14 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/plugin-transform-classes/node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/@babel/plugin-transform-computed-properties": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz",
@@ -1388,13 +1390,12 @@
}
},
"node_modules/@babel/plugin-transform-private-methods": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz",
- "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz",
+ "integrity": "sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==",
"dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.24.7",
- "@babel/helper-plugin-utils": "^7.24.7"
+ "@babel/helper-create-class-features-plugin": "^7.25.4",
+ "@babel/helper-plugin-utils": "^7.24.8"
},
"engines": {
"node": ">=6.9.0"
@@ -1533,15 +1534,14 @@
}
},
"node_modules/@babel/plugin-transform-runtime": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz",
- "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.4.tgz",
+ "integrity": "sha512-8hsyG+KUYGY0coX6KUCDancA0Vw225KJ2HJO0yCNr1vq5r+lJTleDaJf0K7iOhjw4SWhu03TMBzYTJ9krmzULQ==",
"dependencies": {
"@babel/helper-module-imports": "^7.24.7",
- "@babel/helper-plugin-utils": "^7.24.7",
+ "@babel/helper-plugin-utils": "^7.24.8",
"babel-plugin-polyfill-corejs2": "^0.4.10",
- "babel-plugin-polyfill-corejs3": "^0.10.1",
+ "babel-plugin-polyfill-corejs3": "^0.10.6",
"babel-plugin-polyfill-regenerator": "^0.6.1",
"semver": "^6.3.1"
},
@@ -1676,13 +1676,12 @@
}
},
"node_modules/@babel/plugin-transform-unicode-sets-regex": {
- "version": "7.24.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz",
- "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz",
+ "integrity": "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==",
"dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.24.7",
- "@babel/helper-plugin-utils": "^7.24.7"
+ "@babel/helper-create-regexp-features-plugin": "^7.25.2",
+ "@babel/helper-plugin-utils": "^7.24.8"
},
"engines": {
"node": ">=6.9.0"
@@ -1692,12 +1691,11 @@
}
},
"node_modules/@babel/preset-env": {
- "version": "7.25.3",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz",
- "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.4.tgz",
+ "integrity": "sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw==",
"dependencies": {
- "@babel/compat-data": "^7.25.2",
+ "@babel/compat-data": "^7.25.4",
"@babel/helper-compilation-targets": "^7.25.2",
"@babel/helper-plugin-utils": "^7.24.8",
"@babel/helper-validator-option": "^7.24.8",
@@ -1726,13 +1724,13 @@
"@babel/plugin-syntax-top-level-await": "^7.14.5",
"@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
"@babel/plugin-transform-arrow-functions": "^7.24.7",
- "@babel/plugin-transform-async-generator-functions": "^7.25.0",
+ "@babel/plugin-transform-async-generator-functions": "^7.25.4",
"@babel/plugin-transform-async-to-generator": "^7.24.7",
"@babel/plugin-transform-block-scoped-functions": "^7.24.7",
"@babel/plugin-transform-block-scoping": "^7.25.0",
- "@babel/plugin-transform-class-properties": "^7.24.7",
+ "@babel/plugin-transform-class-properties": "^7.25.4",
"@babel/plugin-transform-class-static-block": "^7.24.7",
- "@babel/plugin-transform-classes": "^7.25.0",
+ "@babel/plugin-transform-classes": "^7.25.4",
"@babel/plugin-transform-computed-properties": "^7.24.7",
"@babel/plugin-transform-destructuring": "^7.24.8",
"@babel/plugin-transform-dotall-regex": "^7.24.7",
@@ -1760,7 +1758,7 @@
"@babel/plugin-transform-optional-catch-binding": "^7.24.7",
"@babel/plugin-transform-optional-chaining": "^7.24.8",
"@babel/plugin-transform-parameters": "^7.24.7",
- "@babel/plugin-transform-private-methods": "^7.24.7",
+ "@babel/plugin-transform-private-methods": "^7.25.4",
"@babel/plugin-transform-private-property-in-object": "^7.24.7",
"@babel/plugin-transform-property-literals": "^7.24.7",
"@babel/plugin-transform-regenerator": "^7.24.7",
@@ -1773,10 +1771,10 @@
"@babel/plugin-transform-unicode-escapes": "^7.24.7",
"@babel/plugin-transform-unicode-property-regex": "^7.24.7",
"@babel/plugin-transform-unicode-regex": "^7.24.7",
- "@babel/plugin-transform-unicode-sets-regex": "^7.24.7",
+ "@babel/plugin-transform-unicode-sets-regex": "^7.25.4",
"@babel/preset-modules": "0.1.6-no-external-plugins",
"babel-plugin-polyfill-corejs2": "^0.4.10",
- "babel-plugin-polyfill-corejs3": "^0.10.4",
+ "babel-plugin-polyfill-corejs3": "^0.10.6",
"babel-plugin-polyfill-regenerator": "^0.6.1",
"core-js-compat": "^3.37.1",
"semver": "^6.3.1"
@@ -1855,16 +1853,15 @@
}
},
"node_modules/@babel/traverse": {
- "version": "7.25.3",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz",
- "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==",
- "license": "MIT",
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz",
+ "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==",
"dependencies": {
"@babel/code-frame": "^7.24.7",
- "@babel/generator": "^7.25.0",
- "@babel/parser": "^7.25.3",
+ "@babel/generator": "^7.25.4",
+ "@babel/parser": "^7.25.4",
"@babel/template": "^7.25.0",
- "@babel/types": "^7.25.2",
+ "@babel/types": "^7.25.4",
"debug": "^4.3.1",
"globals": "^11.1.0"
},
@@ -1872,11 +1869,19 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/types": {
- "version": "7.25.2",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz",
- "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==",
+ "node_modules/@babel/traverse/node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.25.4",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz",
+ "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==",
"dependencies": {
"@babel/helper-string-parser": "^7.24.8",
"@babel/helper-validator-identifier": "^7.24.7",
@@ -1894,9 +1899,9 @@
"license": "MIT"
},
"node_modules/@csstools/css-parser-algorithms": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.7.1.tgz",
- "integrity": "sha512-2SJS42gxmACHgikc1WGesXLIT8d/q2l0UFM7TaEeIzdFCE/FPMtTiizcPGGJtlPo2xuQzY09OhrLTzRxqJqwGw==",
+ "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==",
"dev": true,
"funding": [
{
@@ -1908,18 +1913,17 @@
"url": "https://opencollective.com/csstools"
}
],
- "license": "MIT",
"engines": {
- "node": "^14 || ^16 || >=18"
+ "node": ">=18"
},
"peerDependencies": {
- "@csstools/css-tokenizer": "^2.4.1"
+ "@csstools/css-tokenizer": "^3.0.1"
}
},
"node_modules/@csstools/css-tokenizer": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.4.1.tgz",
- "integrity": "sha512-eQ9DIktFJBhGjioABJRtUucoWR2mwllurfnM8LuNGAqX3ViZXaUchqk+1s7jjtkFiT9ySdACsFEA3etErkALUg==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.1.tgz",
+ "integrity": "sha512-UBqaiu7kU0lfvaP982/o3khfXccVlHPWp0/vwwiIgDF0GmqqqxoiXC/6FCjlS9u92f7CoEz6nXKQnrn1kIAkOw==",
"dev": true,
"funding": [
{
@@ -1931,15 +1935,14 @@
"url": "https://opencollective.com/csstools"
}
],
- "license": "MIT",
"engines": {
- "node": "^14 || ^16 || >=18"
+ "node": ">=18"
}
},
"node_modules/@csstools/media-query-list-parser": {
- "version": "2.1.13",
- "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.13.tgz",
- "integrity": "sha512-XaHr+16KRU9Gf8XLi3q8kDlI18d5vzKSKCY510Vrtc9iNR0NJzbY9hhTmwhzYZj/ZwGL4VmB3TA9hJW0Um2qFA==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz",
+ "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==",
"dev": true,
"funding": [
{
@@ -1951,19 +1954,18 @@
"url": "https://opencollective.com/csstools"
}
],
- "license": "MIT",
"engines": {
- "node": "^14 || ^16 || >=18"
+ "node": ">=18"
},
"peerDependencies": {
- "@csstools/css-parser-algorithms": "^2.7.1",
- "@csstools/css-tokenizer": "^2.4.1"
+ "@csstools/css-parser-algorithms": "^3.0.1",
+ "@csstools/css-tokenizer": "^3.0.1"
}
},
"node_modules/@csstools/selector-specificity": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.1.1.tgz",
- "integrity": "sha512-a7cxGcJ2wIlMFLlh8z2ONm+715QkPHiyJcxwQlKOz/03GPw1COpfhcmC9wm4xlZfp//jWHNNMwzjtqHXVWU9KA==",
+ "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": [
{
@@ -1975,12 +1977,11 @@
"url": "https://opencollective.com/csstools"
}
],
- "license": "MIT-0",
"engines": {
- "node": "^14 || ^16 || >=18"
+ "node": ">=18"
},
"peerDependencies": {
- "postcss-selector-parser": "^6.0.13"
+ "postcss-selector-parser": "^6.1.0"
}
},
"node_modules/@dual-bundle/import-meta-resolve": {
@@ -2020,17 +2021,31 @@
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
+ "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==",
+ "dev": true,
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.4",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
"node_modules/@eslint/eslintrc": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
- "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
+ "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
- "espree": "^9.6.0",
- "globals": "^13.19.0",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.0",
@@ -2038,56 +2053,47 @@
"strip-json-comments": "^3.1.1"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@eslint/eslintrc/node_modules/globals": {
- "version": "13.24.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
- "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "type-fest": "^0.20.2"
- },
"engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
- "dev": true,
- "license": "(MIT OR CC0-1.0)",
- "engines": {
- "node": ">=10"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@eslint/js": {
- "version": "8.57.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
- "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
+ "version": "9.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz",
+ "integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==",
"dev": true,
- "license": "MIT",
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz",
+ "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@googleapis/drive": {
- "version": "8.11.0",
- "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.11.0.tgz",
- "integrity": "sha512-HW6/2oThc4X086mGkZxpdP4P+aHpYbjHa6wr9l1F/R+snpk6G8/EuRXEcTkgQUl2t/NdNz3lj8re0AQBG5faSA==",
- "license": "Apache-2.0",
+ "version": "8.13.1",
+ "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.13.1.tgz",
+ "integrity": "sha512-ODfl4VUIKNox570DFA6AzAEHQcKI1EQs0xzzupeAIa+S/kFan85TItXU7XywK8mDORnfkgqwXQ5N/u/BFBj5lw==",
"dependencies": {
"googleapis-common": "^7.0.0"
},
@@ -2095,22 +2101,6 @@
"node": ">=12.0.0"
}
},
- "node_modules/@humanwhocodes/config-array": {
- "version": "0.11.14",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
- "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
- "deprecated": "Use @eslint/config-array instead",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@humanwhocodes/object-schema": "^2.0.2",
- "debug": "^4.3.1",
- "minimatch": "^3.0.5"
- },
- "engines": {
- "node": ">=10.10.0"
- }
- },
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -2125,13 +2115,19 @@
"url": "https://github.com/sponsors/nzakas"
}
},
- "node_modules/@humanwhocodes/object-schema": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
- "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
- "deprecated": "Use @eslint/object-schema instead",
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz",
+ "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==",
"dev": true,
- "license": "BSD-3-Clause"
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
},
"node_modules/@istanbuljs/load-nyc-config": {
"version": "1.1.0",
@@ -2974,7 +2970,6 @@
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.8.tgz",
"integrity": "sha512-qKwC/M/nNNaKUBMQ0nuzm47b7ZYWQHN3pcXq4IIcoSBc2hOIrflAxJduIvvqmhoz3gR2TacTAs8vlsCVPkiEdQ==",
- "license": "MIT",
"dependencies": {
"sparse-bitfield": "^3.0.3"
}
@@ -3018,9 +3013,9 @@
}
},
"node_modules/@remix-run/router": {
- "version": "1.19.0",
- "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.0.tgz",
- "integrity": "sha512-zDICCLKEwbVYTS6TjYaWtHXxkdoUvD/QXvyVZjGCsWz5vyH7aFeONlPffPdW+Y/t6KT0MgXb2Mfjun9YpWN1dA==",
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz",
+ "integrity": "sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==",
"engines": {
"node": ">=14.0.0"
}
@@ -3053,20 +3048,19 @@
}
},
"node_modules/@stylistic/stylelint-plugin": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.0.0.tgz",
- "integrity": "sha512-GymY+9CSqkPaZ1A3m3w/tvCdpP3qQcaL1FSaoVv9aKL3Tn6GVJWHc2VWVkbNEsYr4QImHjWnlmVZROwgUEjMmQ==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.0.1.tgz",
+ "integrity": "sha512-j3mH8HSw2Rob/KJFWZ627w3CQ8gQqVHtzCdPeEffUg5vOgpz4rgrR+Xw2kU0OQCDcdW8Y1nKfdXKKjM5Rn8X0g==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "@csstools/css-parser-algorithms": "^2.7.1",
- "@csstools/css-tokenizer": "^2.4.1",
- "@csstools/media-query-list-parser": "^2.1.13",
+ "@csstools/css-parser-algorithms": "^3.0.0",
+ "@csstools/css-tokenizer": "^3.0.0",
+ "@csstools/media-query-list-parser": "^3.0.0",
"is-plain-object": "^5.0.0",
- "postcss-selector-parser": "^6.1.1",
+ "postcss-selector-parser": "^6.1.2",
"postcss-value-parser": "^4.2.0",
"style-search": "^0.1.0",
- "stylelint": "^16.8.0"
+ "stylelint": "^16.8.2"
},
"engines": {
"node": "^18.12 || >=20.9"
@@ -3177,14 +3171,12 @@
"node_modules/@types/webidl-conversions": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
- "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==",
- "license": "MIT"
+ "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="
},
"node_modules/@types/whatwg-url": {
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz",
"integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==",
- "license": "MIT",
"dependencies": {
"@types/webidl-conversions": "*"
}
@@ -3207,17 +3199,17 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz",
- "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.2.0.tgz",
+ "integrity": "sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "7.18.0",
- "@typescript-eslint/visitor-keys": "7.18.0"
+ "@typescript-eslint/types": "8.2.0",
+ "@typescript-eslint/visitor-keys": "8.2.0"
},
"engines": {
- "node": "^18.18.0 || >=20.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
@@ -3225,13 +3217,13 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
- "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.2.0.tgz",
+ "integrity": "sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==",
"dev": true,
"license": "MIT",
"engines": {
- "node": "^18.18.0 || >=20.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
@@ -3239,14 +3231,14 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz",
- "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.2.0.tgz",
+ "integrity": "sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
- "@typescript-eslint/types": "7.18.0",
- "@typescript-eslint/visitor-keys": "7.18.0",
+ "@typescript-eslint/types": "8.2.0",
+ "@typescript-eslint/visitor-keys": "8.2.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -3255,7 +3247,7 @@
"ts-api-utils": "^1.3.0"
},
"engines": {
- "node": "^18.18.0 || >=20.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
@@ -3307,53 +3299,46 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz",
- "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.2.0.tgz",
+ "integrity": "sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
- "@typescript-eslint/scope-manager": "7.18.0",
- "@typescript-eslint/types": "7.18.0",
- "@typescript-eslint/typescript-estree": "7.18.0"
+ "@typescript-eslint/scope-manager": "8.2.0",
+ "@typescript-eslint/types": "8.2.0",
+ "@typescript-eslint/typescript-estree": "8.2.0"
},
"engines": {
- "node": "^18.18.0 || >=20.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.56.0"
+ "eslint": "^8.57.0 || ^9.0.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz",
- "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.2.0.tgz",
+ "integrity": "sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "7.18.0",
+ "@typescript-eslint/types": "8.2.0",
"eslint-visitor-keys": "^3.4.3"
},
"engines": {
- "node": "^18.18.0 || >=20.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
- "node_modules/@ungap/structured-clone": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
- "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@@ -3972,13 +3957,12 @@
}
},
"node_modules/babel-plugin-polyfill-corejs3": {
- "version": "0.10.4",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz",
- "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==",
- "license": "MIT",
+ "version": "0.10.6",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz",
+ "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==",
"dependencies": {
- "@babel/helper-define-polyfill-provider": "^0.6.1",
- "core-js-compat": "^3.36.1"
+ "@babel/helper-define-polyfill-provider": "^0.6.2",
+ "core-js-compat": "^3.38.0"
},
"peerDependencies": {
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
@@ -4447,7 +4431,6 @@
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz",
"integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==",
- "license": "Apache-2.0",
"engines": {
"node": ">=16.20.1"
}
@@ -4931,12 +4914,11 @@
}
},
"node_modules/core-js-compat": {
- "version": "3.37.1",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz",
- "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==",
- "license": "MIT",
+ "version": "3.38.1",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz",
+ "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==",
"dependencies": {
- "browserslist": "^4.23.0"
+ "browserslist": "^4.23.3"
},
"funding": {
"type": "opencollective",
@@ -5521,19 +5503,6 @@
"node": ">=8"
}
},
- "node_modules/doctrine": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
- "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "esutils": "^2.0.2"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
"node_modules/domain-browser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
@@ -5581,10 +5550,9 @@
"license": "ISC"
},
"node_modules/elliptic": {
- "version": "6.5.6",
- "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.6.tgz",
- "integrity": "sha512-mpzdtpeCLuS3BmE3pO3Cpp5bbjlOPY2Q0PgoF+Od1XZrHLYI28Xe3ossCmYCQt11FQKEYd9+PF8jymTvtWJSHQ==",
- "license": "MIT",
+ "version": "6.5.7",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz",
+ "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==",
"dependencies": {
"bn.js": "^4.11.9",
"brorand": "^1.1.0",
@@ -5851,42 +5819,37 @@
}
},
"node_modules/eslint": {
- "version": "8.57.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
- "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+ "version": "9.9.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.1.tgz",
+ "integrity": "sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==",
"dev": true,
- "license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.6.1",
- "@eslint/eslintrc": "^2.1.4",
- "@eslint/js": "8.57.0",
- "@humanwhocodes/config-array": "^0.11.14",
+ "@eslint-community/regexpp": "^4.11.0",
+ "@eslint/config-array": "^0.18.0",
+ "@eslint/eslintrc": "^3.1.0",
+ "@eslint/js": "9.9.1",
"@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.3.0",
"@nodelib/fs.walk": "^1.2.8",
- "@ungap/structured-clone": "^1.2.0",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
- "doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^7.2.2",
- "eslint-visitor-keys": "^3.4.3",
- "espree": "^9.6.1",
- "esquery": "^1.4.2",
+ "eslint-scope": "^8.0.2",
+ "eslint-visitor-keys": "^4.0.0",
+ "espree": "^10.1.0",
+ "esquery": "^1.5.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^6.0.1",
+ "file-entry-cache": "^8.0.0",
"find-up": "^5.0.0",
"glob-parent": "^6.0.2",
- "globals": "^13.19.0",
- "graphemer": "^1.4.0",
"ignore": "^5.2.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"is-path-inside": "^3.0.3",
- "js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash.merge": "^4.6.2",
@@ -5900,10 +5863,18 @@
"eslint": "bin/eslint.js"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
- "url": "https://opencollective.com/eslint"
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
}
},
"node_modules/eslint-plugin-jest": {
@@ -5996,9 +5967,9 @@
}
},
"node_modules/eslint-scope": {
- "version": "7.2.2",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
- "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz",
+ "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -6006,7 +5977,7 @@
"estraverse": "^5.2.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -6091,20 +6062,17 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/eslint/node_modules/globals": {
- "version": "13.24.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
- "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "node_modules/eslint/node_modules/eslint-visitor-keys": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
+ "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "type-fest": "^0.20.2"
- },
+ "license": "Apache-2.0",
"engines": {
- "node": ">=8"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/has-flag": {
@@ -6130,32 +6098,32 @@
"node": ">=8"
}
},
- "node_modules/eslint/node_modules/type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
- "dev": true,
- "license": "(MIT OR CC0-1.0)",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/espree": {
- "version": "9.6.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
- "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz",
+ "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
- "acorn": "^8.9.0",
+ "acorn": "^8.12.0",
"acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.4.1"
+ "eslint-visitor-keys": "^4.0.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree/node_modules/eslint-visitor-keys": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
+ "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -6575,16 +6543,16 @@
}
},
"node_modules/file-entry-cache": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
- "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "flat-cache": "^3.0.4"
+ "flat-cache": "^4.0.0"
},
"engines": {
- "node": "^10.12.0 || >=12.0.0"
+ "node": ">=16.0.0"
}
},
"node_modules/file-uri-to-path": {
@@ -6657,18 +6625,17 @@
}
},
"node_modules/flat-cache": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
- "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
"dev": true,
"license": "MIT",
"dependencies": {
"flatted": "^3.2.9",
- "keyv": "^4.5.3",
- "rimraf": "^3.0.2"
+ "keyv": "^4.5.4"
},
"engines": {
- "node": "^10.12.0 || >=12.0.0"
+ "node": ">=16"
}
},
"node_modules/flatted": {
@@ -7051,12 +7018,16 @@
}
},
"node_modules/globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "version": "15.9.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz",
+ "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==",
+ "dev": true,
"license": "MIT",
"engines": {
- "node": ">=4"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/globalthis": {
@@ -7156,13 +7127,6 @@
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"license": "ISC"
},
- "node_modules/graphemer": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/gtoken": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
@@ -7481,11 +7445,10 @@
"license": "BSD-3-Clause"
},
"node_modules/ignore": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
- "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"dev": true,
- "license": "MIT",
"engines": {
"node": ">= 4"
}
@@ -10463,12 +10426,11 @@
}
},
"node_modules/marked-extended-tables": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/marked-extended-tables/-/marked-extended-tables-1.0.8.tgz",
- "integrity": "sha512-GcVQP7EnfQ98o09ooqM4t4M0qfpKdKuk7/z4qZfgkLyXTXsIyFS1eeBmfC36o1NbR6aSq8ynL/LeTz3w4RS27Q==",
- "license": "MIT",
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/marked-extended-tables/-/marked-extended-tables-1.0.10.tgz",
+ "integrity": "sha512-zvRS0GPTkxq8UWawSDecd1Rxd2KD8crrmq2QALGDdrgkcgRNQzHlbnlujBGuXxdgDJg7f6UTv+JpcfejBpKdSg==",
"peerDependencies": {
- "marked": ">=3 <12"
+ "marked": ">=3 <15"
}
},
"node_modules/marked-gfm-heading-id": {
@@ -10546,8 +10508,7 @@
"node_modules/memory-pager": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
- "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
- "license": "MIT"
+ "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="
},
"node_modules/meow": {
"version": "13.2.0",
@@ -10595,11 +10556,10 @@
}
},
"node_modules/micromatch": {
- "version": "4.0.7",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
- "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
- "license": "MIT",
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
@@ -10798,7 +10758,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz",
"integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==",
- "license": "Apache-2.0",
"dependencies": {
"@types/whatwg-url": "^11.0.2",
"whatwg-url": "^13.0.0"
@@ -10808,7 +10767,6 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
"integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==",
- "license": "MIT",
"dependencies": {
"punycode": "^2.3.0"
},
@@ -10820,7 +10778,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
- "license": "BSD-2-Clause",
"engines": {
"node": ">=12"
}
@@ -10829,7 +10786,6 @@
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz",
"integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==",
- "license": "MIT",
"dependencies": {
"tr46": "^4.1.1",
"webidl-conversions": "^7.0.0"
@@ -10839,14 +10795,13 @@
}
},
"node_modules/mongoose": {
- "version": "8.5.2",
- "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.5.2.tgz",
- "integrity": "sha512-GZB4rHMdYfGatV+23IpCrqFbyCOjCNOHXgWbirr92KRwTEncBrtW3kgU9vmpKjsGf7nMmnAy06SwWUv1vhDkSg==",
- "license": "MIT",
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.6.0.tgz",
+ "integrity": "sha512-p6VSbYKvD4ZIabqo8C0kS5eKX1Xpji+opTAIJ9wyuPJ8Y/FblgXSMnFRXnB40bYZLKPQT089K5KU8+bqIXtFdw==",
"dependencies": {
"bson": "^6.7.0",
"kareem": "2.6.3",
- "mongodb": "6.7.0",
+ "mongodb": "6.8.0",
"mpath": "0.9.0",
"mquery": "5.0.0",
"ms": "2.1.3",
@@ -10864,7 +10819,6 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
- "license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
@@ -10878,7 +10832,6 @@
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz",
"integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==",
- "license": "Apache-2.0",
"optional": true,
"peer": true,
"dependencies": {
@@ -10895,7 +10848,6 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz",
"integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==",
- "license": "Apache-2.0",
"optional": true,
"peer": true,
"dependencies": {
@@ -10910,7 +10862,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
- "license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
@@ -10922,10 +10873,9 @@
}
},
"node_modules/mongoose/node_modules/mongodb": {
- "version": "6.7.0",
- "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.7.0.tgz",
- "integrity": "sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==",
- "license": "Apache-2.0",
+ "version": "6.8.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.8.0.tgz",
+ "integrity": "sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==",
"dependencies": {
"@mongodb-js/saslprep": "^1.1.5",
"bson": "^6.7.0",
@@ -11846,9 +11796,9 @@
}
},
"node_modules/postcss": {
- "version": "8.4.40",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz",
- "integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==",
+ "version": "8.4.41",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz",
+ "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==",
"dev": true,
"funding": [
{
@@ -11864,7 +11814,6 @@
"url": "https://github.com/sponsors/ai"
}
],
- "license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.1",
@@ -11888,11 +11837,10 @@
}
},
"node_modules/postcss-resolve-nested-selector": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.4.tgz",
- "integrity": "sha512-R6vHqZWgVnTAPq0C+xjyHfEZqfIYboCBVSy24MjxEDm+tIh1BU4O6o7DP7AA7kHzf136d+Qc5duI4tlpHjixDw==",
- "dev": true,
- "license": "MIT"
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz",
+ "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==",
+ "dev": true
},
"node_modules/postcss-safe-parser": {
"version": "7.0.0",
@@ -11922,11 +11870,10 @@
}
},
"node_modules/postcss-selector-parser": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz",
- "integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==",
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"dev": true,
- "license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -12258,11 +12205,11 @@
"license": "MIT"
},
"node_modules/react-router": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.0.tgz",
- "integrity": "sha512-wVQq0/iFYd3iZ9H2l3N3k4PL8EEHcb0XlU2Na8nEwmiXgIUElEH6gaJDtUQxJ+JFzmIXaQjfdpcGWaM6IoQGxg==",
+ "version": "6.26.1",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.1.tgz",
+ "integrity": "sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==",
"dependencies": {
- "@remix-run/router": "1.19.0"
+ "@remix-run/router": "1.19.1"
},
"engines": {
"node": ">=14.0.0"
@@ -12272,12 +12219,12 @@
}
},
"node_modules/react-router-dom": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.0.tgz",
- "integrity": "sha512-RRGUIiDtLrkX3uYcFiCIxKFWMcWQGMojpYZfcstc63A1+sSnVgILGIm9gNUA6na3Fm1QuPGSBQH2EMbAZOnMsQ==",
+ "version": "6.26.1",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.1.tgz",
+ "integrity": "sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==",
"dependencies": {
- "@remix-run/router": "1.19.0",
- "react-router": "6.26.0"
+ "@remix-run/router": "1.19.1",
+ "react-router": "6.26.1"
},
"engines": {
"node": ">=14.0.0"
@@ -12635,23 +12582,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
- "deprecated": "Rimraf versions prior to v4 are no longer supported",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "glob": "^7.1.3"
- },
- "bin": {
- "rimraf": "bin.js"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/ripemd160": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
@@ -13285,7 +13215,6 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
"integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
- "license": "MIT",
"dependencies": {
"memory-pager": "^1.0.2"
}
@@ -13627,9 +13556,9 @@
"license": "ISC"
},
"node_modules/stylelint": {
- "version": "16.8.1",
- "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.8.1.tgz",
- "integrity": "sha512-O8aDyfdODSDNz/B3gW2HQ+8kv8pfhSu7ZR7xskQ93+vI6FhKKGUJMQ03Ydu+w3OvXXE0/u4hWU4hCPNOyld+OA==",
+ "version": "16.9.0",
+ "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.9.0.tgz",
+ "integrity": "sha512-31Nm3WjxGOBGpQqF43o3wO9L5AC36TPIe6030Lnm13H3vDMTcS21DrLh69bMX+DBilKqMMVLian4iG6ybBoNRQ==",
"dev": true,
"funding": [
{
@@ -13641,12 +13570,11 @@
"url": "https://github.com/sponsors/stylelint"
}
],
- "license": "MIT",
"dependencies": {
- "@csstools/css-parser-algorithms": "^2.7.1",
- "@csstools/css-tokenizer": "^2.4.1",
- "@csstools/media-query-list-parser": "^2.1.13",
- "@csstools/selector-specificity": "^3.1.1",
+ "@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",
"@dual-bundle/import-meta-resolve": "^4.1.0",
"balanced-match": "^2.0.0",
"colord": "^2.9.3",
@@ -13661,24 +13589,24 @@
"globby": "^11.1.0",
"globjoin": "^0.1.4",
"html-tags": "^3.3.1",
- "ignore": "^5.3.1",
+ "ignore": "^5.3.2",
"imurmurhash": "^0.1.4",
"is-plain-object": "^5.0.0",
"known-css-properties": "^0.34.0",
"mathml-tag-names": "^2.1.3",
"meow": "^13.2.0",
- "micromatch": "^4.0.7",
+ "micromatch": "^4.0.8",
"normalize-path": "^3.0.0",
"picocolors": "^1.0.1",
- "postcss": "^8.4.40",
- "postcss-resolve-nested-selector": "^0.1.4",
+ "postcss": "^8.4.41",
+ "postcss-resolve-nested-selector": "^0.1.6",
"postcss-safe-parser": "^7.0.0",
- "postcss-selector-parser": "^6.1.1",
+ "postcss-selector-parser": "^6.1.2",
"postcss-value-parser": "^4.2.0",
"resolve-from": "^5.0.0",
"string-width": "^4.2.3",
"strip-ansi": "^7.1.0",
- "supports-hyperlinks": "^3.0.0",
+ "supports-hyperlinks": "^3.1.0",
"svg-tags": "^1.0.0",
"table": "^6.8.2",
"write-file-atomic": "^5.0.1"
@@ -13691,11 +13619,10 @@
}
},
"node_modules/stylelint-config-recess-order": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/stylelint-config-recess-order/-/stylelint-config-recess-order-5.0.1.tgz",
- "integrity": "sha512-rKbGkoa3h0rINrGln9TFVowvSCLgPJC5O0EuPiqlqWcJMb1lImEtXktcjFCVz+hwtSUiHD3ijJc3vP9muFOgJg==",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/stylelint-config-recess-order/-/stylelint-config-recess-order-5.1.0.tgz",
+ "integrity": "sha512-ddapCF6B/kEtQYIFhQFReQ0dvK1ZdgJDM/SGFtIyeooYDbqaJqcOlGkRRGaVErCQYJY/bPSPsLRS2LdQtLJUVQ==",
"dev": true,
- "license": "ISC",
"dependencies": {
"stylelint-order": "^6.0.4"
},
@@ -13850,10 +13777,9 @@
}
},
"node_modules/superagent": {
- "version": "9.0.2",
- "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz",
- "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==",
- "license": "MIT",
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.1.0.tgz",
+ "integrity": "sha512-JMmik7PbnXGlq7g528Gi6apHbVbTz2vrE3du6fuG4kIPSb2PnLoSOPvfjKn8aQYuJcBWAKW6ZG90qPPsE5jZxQ==",
"dependencies": {
"component-emitter": "^1.3.0",
"cookiejar": "^2.1.4",
@@ -13895,6 +13821,38 @@
"node": ">=14.18.0"
}
},
+ "node_modules/supertest/node_modules/mime": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+ "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+ "dev": true,
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/supertest/node_modules/superagent": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz",
+ "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==",
+ "dev": true,
+ "dependencies": {
+ "component-emitter": "^1.3.0",
+ "cookiejar": "^2.1.4",
+ "debug": "^4.3.4",
+ "fast-safe-stringify": "^2.1.1",
+ "form-data": "^4.0.0",
+ "formidable": "^3.5.1",
+ "methods": "^1.1.2",
+ "mime": "2.6.0",
+ "qs": "^6.11.0"
+ },
+ "engines": {
+ "node": ">=14.18.0"
+ }
+ },
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -13908,17 +13866,19 @@
}
},
"node_modules/supports-hyperlinks": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz",
- "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz",
+ "integrity": "sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==",
"dev": true,
- "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0",
"supports-color": "^7.0.0"
},
"engines": {
"node": ">=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/supports-hyperlinks/node_modules/has-flag": {
@@ -13926,7 +13886,6 @@
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
- "license": "MIT",
"engines": {
"node": ">=8"
}
@@ -13936,7 +13895,6 @@
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
- "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
diff --git a/package.json b/package.json
index 2dbff24ed..b2ad61958 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.14.0",
+ "version": "3.14.2",
"engines": {
"npm": "^10.2.x",
"node": "^20.8.x"
@@ -15,8 +15,8 @@
"quick": "node scripts/quick.js",
"build": "node scripts/buildHomebrew.js && node scripts/buildAdmin.js",
"builddev": "node scripts/buildHomebrew.js --dev",
- "lint": "eslint --fix **/*.{js,jsx}",
- "lint:dry": "eslint **/*.{js,jsx}",
+ "lint": "eslint --fix",
+ "lint:dry": "eslint",
"stylelint": "stylelint --fix **/*.{less}",
"stylelint:dry": "stylelint **/*.less",
"circleci": "npm test && eslint **/*.{js,jsx} --max-warnings=0",
@@ -24,6 +24,7 @@
"test": "jest --runInBand",
"test:api-unit": "jest \"server/.*.spec.js\" --verbose",
"test:api-unit:themes": "jest \"server/.*.spec.js\" -t \"theme bundle\" --verbose",
+ "test:api-unit:css": "jest \"server/.*.spec.js\" -t \"Get CSS\" --verbose",
"test:coverage": "jest --coverage --silent --runInBand",
"test:dev": "jest --verbose --watch",
"test:basic": "jest tests/markdown/basic.test.js --verbose",
@@ -33,6 +34,7 @@
"test:mustache-syntax:block": "jest \".*(mustache-syntax).*\" -t '^Block:.*' --verbose --noStackTrace",
"test:mustache-syntax:injection": "jest \".*(mustache-syntax).*\" -t '^Injection:.*' --verbose --noStackTrace",
"test:definition-lists": "jest tests/markdown/definition-lists.test.js --verbose --noStackTrace",
+ "test:hard-breaks": "jest tests/markdown/hard-breaks.test.js --verbose --noStackTrace",
"test:emojis": "jest tests/markdown/emojis.test.js --verbose --noStackTrace",
"test:route": "jest tests/routes/static-pages.test.js --verbose",
"phb": "node scripts/phb.js",
@@ -84,10 +86,10 @@
},
"dependencies": {
"@babel/core": "^7.25.2",
- "@babel/plugin-transform-runtime": "^7.24.7",
- "@babel/preset-env": "^7.25.3",
+ "@babel/plugin-transform-runtime": "^7.25.4",
+ "@babel/preset-env": "^7.25.4",
"@babel/preset-react": "^7.24.7",
- "@googleapis/drive": "^8.11.0",
+ "@googleapis/drive": "^8.13.1",
"body-parser": "^1.20.2",
"classnames": "^2.5.1",
"codemirror": "^5.65.6",
@@ -106,32 +108,33 @@
"lodash": "^4.17.21",
"marked": "11.2.0",
"marked-emoji": "^1.4.2",
- "marked-extended-tables": "^1.0.8",
+ "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.5.2",
+ "mongoose": "^8.6.0",
"nanoid": "3.3.4",
"nconf": "^0.12.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-frame-component": "^4.1.3",
- "react-router-dom": "6.26.0",
+ "react-router-dom": "6.26.1",
"sanitize-filename": "1.6.3",
- "superagent": "^9.0.2",
+ "superagent": "^10.1.0",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
},
"devDependencies": {
- "@stylistic/stylelint-plugin": "^3.0.0",
- "eslint": "^8.57.0",
+ "@stylistic/stylelint-plugin": "^3.0.1",
+ "eslint": "^9.9.1",
"eslint-plugin-jest": "^28.8.0",
"eslint-plugin-react": "^7.35.0",
+ "globals": "^15.9.0",
"jest": "^29.7.0",
"jest-expect-message": "^1.1.3",
"postcss-less": "^6.0.0",
- "stylelint": "^16.8.0",
- "stylelint-config-recess-order": "^5.0.1",
+ "stylelint": "^16.9.0",
+ "stylelint-config-recess-order": "^5.1.0",
"stylelint-config-recommended": "^14.0.1",
"supertest": "^7.0.0"
}
diff --git a/server/app.js b/server/app.js
index 4e981bd34..3a0031609 100644
--- a/server/app.js
+++ b/server/app.js
@@ -9,11 +9,12 @@ const yaml = require('js-yaml');
const app = express();
const config = require('./config.js');
-const { homebrewApi, getBrew, getUsersBrewThemes } = require('./homebrew.api.js');
+const { homebrewApi, getBrew, getUsersBrewThemes, getCSS } = require('./homebrew.api.js');
const GoogleActions = require('./googleActions.js');
const serveCompressedStaticAssets = require('./static-assets.mv.js');
const sanitizeFilename = require('sanitize-filename');
const asyncHandler = require('express-async-handler');
+const templateFn = require('./../client/template.js');
const { DEFAULT_BREW } = require('./brewDefaults.js');
@@ -201,6 +202,9 @@ app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{
res.status(200).send(brew.text);
});
+//Serve brew styling
+app.get('/css/:id', asyncHandler(getBrew('share')), (req, res)=>{getCSS(req, res);});
+
//User Page
app.get('/user/:username', async (req, res, next)=>{
const ownAccount = req.account && (req.account.username == req.params.username);
@@ -357,6 +361,15 @@ app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, r
app.get('/account', asyncHandler(async (req, res, next)=>{
const data = {};
data.title = 'Account Information Page';
+
+ if(!req.account) {
+ res.set('WWW-Authenticate', 'Bearer realm="Authorization Required"');
+ const error = new Error('No valid account');
+ error.status = 401;
+ error.HBErrorCode = '50';
+ error.page = data.title;
+ return next(error);
+ };
let auth;
let googleCount = [];
@@ -421,8 +434,16 @@ if(isLocalEnvironment){
});
}
+//Send rendered page
+app.use(asyncHandler(async (req, res, next)=>{
+ if (!req.route) return res.redirect('/'); // Catch-all for invalid routes
+
+ const page = await renderPage(req, res);
+ if(!page) return;
+ res.send(page);
+}));
+
//Render the page
-const templateFn = require('./../client/template.js');
const renderPage = async (req, res)=>{
// Create configuration object
const configuration = {
@@ -451,13 +472,6 @@ const renderPage = async (req, res)=>{
return page;
};
-//Send rendered page
-app.use(asyncHandler(async (req, res, next)=>{
- const page = await renderPage(req, res);
- if(!page) return;
- res.send(page);
-}));
-
//v=====----- Error-Handling Middleware -----=====v//
//Format Errors as plain objects so all fields will appear in the string sent
const formatErrors = (key, value)=>{
diff --git a/server/homebrew.api.js b/server/homebrew.api.js
index c314454e2..52fe57360 100644
--- a/server/homebrew.api.js
+++ b/server/homebrew.api.js
@@ -148,6 +148,20 @@ const api = {
next();
};
},
+
+ getCSS : async (req, res)=>{
+ const { brew } = req;
+ if(!brew) return res.status(404).send('');
+ splitTextStyleAndMetadata(brew);
+ if(!brew.style) return res.status(404).send('');
+
+ res.set({
+ 'Cache-Control' : 'no-cache',
+ 'Content-Type' : 'text/css'
+ });
+ return res.status(200).send(brew.style);
+ },
+
mergeBrewText : (brew)=>{
let text = brew.text;
if(brew.style !== undefined) {
diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js
index 5f1739b97..d168c73fb 100644
--- a/server/homebrew.api.spec.js
+++ b/server/homebrew.api.spec.js
@@ -50,6 +50,7 @@ describe('Tests for api', ()=>{
res = {
status : jest.fn(()=>res),
send : jest.fn(()=>{}),
+ set : jest.fn(()=>{}),
setHeader : jest.fn(()=>{})
};
@@ -916,4 +917,66 @@ brew`);
expect(saved.googleId).toEqual(brew.googleId);
});
});
+ describe('Get CSS', ()=>{
+ it('should return brew style content as CSS text', async ()=>{
+ const testBrew = { title: 'test brew', text: '```css\n\nI Have a style!\n````\n\n' };
+
+ const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew }));
+ api.getId = jest.fn(()=>({ id: '1', googleId: undefined }));
+ model.get = jest.fn(()=>toBrewPromise(testBrew));
+
+ const fn = api.getBrew('share', true);
+ const req = { brew: {} };
+ const next = jest.fn();
+ await fn(req, null, next);
+ await api.getCSS(req, res);
+
+ expect(req.brew).toEqual(testBrew);
+ expect(req.brew).toHaveProperty('style', '\nI Have a style!\n');
+ expect(res.status).toHaveBeenCalledWith(200);
+ expect(res.send).toHaveBeenCalledWith("\nI Have a style!\n");
+ expect(res.set).toHaveBeenCalledWith({
+ 'Cache-Control' : 'no-cache',
+ 'Content-Type' : 'text/css'
+ });
+ });
+
+ it('should return 404 when brew has no style content', async ()=>{
+ const testBrew = { title: 'test brew', text: 'I don\'t have a style!' };
+
+ const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew }));
+ api.getId = jest.fn(()=>({ id: '1', googleId: undefined }));
+ model.get = jest.fn(()=>toBrewPromise(testBrew));
+
+ const fn = api.getBrew('share', true);
+ const req = { brew: {} };
+ const next = jest.fn();
+ await fn(req, null, next);
+ await api.getCSS(req, res);
+
+ expect(req.brew).toEqual(testBrew);
+ expect(req.brew).toHaveProperty('style');
+ expect(res.status).toHaveBeenCalledWith(404);
+ expect(res.send).toHaveBeenCalledWith('');
+ });
+
+ it('should return 404 when brew does not exist', async ()=>{
+ const testBrew = { };
+
+ const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew }));
+ api.getId = jest.fn(()=>({ id: '1', googleId: undefined }));
+ model.get = jest.fn(()=>toBrewPromise(testBrew));
+
+ const fn = api.getBrew('share', true);
+ const req = { brew: {} };
+ const next = jest.fn();
+ await fn(req, null, next);
+ await api.getCSS(req, res);
+
+ expect(req.brew).toEqual(testBrew);
+ expect(req.brew).toHaveProperty('style');
+ expect(res.status).toHaveBeenCalledWith(404);
+ expect(res.send).toHaveBeenCalledWith('');
+ });
+ });
});
diff --git a/shared/helpers.js b/shared/helpers.js
index e5c1b7769..ac684b06f 100644
--- a/shared/helpers.js
+++ b/shared/helpers.js
@@ -35,6 +35,7 @@ const printCurrentBrew = ()=>{
};
const fetchThemeBundle = async (obj, renderer, theme)=>{
+ if(!renderer || !theme) return;
const res = await request
.get(`/api/theme/${renderer}/${theme}`)
.catch((err)=>{
diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js
index 9388e912a..205063641 100644
--- a/shared/naturalcrit/markdown.js
+++ b/shared/naturalcrit/markdown.js
@@ -3,7 +3,7 @@ const _ = require('lodash');
const Marked = require('marked');
const MarkedExtendedTables = require('marked-extended-tables');
const { markedSmartypantsLite: MarkedSmartypantsLite } = require('marked-smartypants-lite');
-const { gfmHeadingId: MarkedGFMHeadingId } = require('marked-gfm-heading-id');
+const { gfmHeadingId: MarkedGFMHeadingId, resetHeadings: MarkedGFMResetHeadingIDs } = require('marked-gfm-heading-id');
const { markedEmoji: MarkedEmojis } = require('marked-emoji');
//Icon fonts included so they can appear in emoji autosuggest dropdown
@@ -86,7 +86,7 @@ renderer.link = function (href, title, text) {
if(href[0] == '#') {
self = true;
}
- href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
+ href = cleanUrl(href);
if(href === null) {
return text;
@@ -102,6 +102,20 @@ renderer.link = function (href, title, text) {
return out;
};
+// Expose `src` attribute as `--HB_src` to make the URL accessible via CSS
+renderer.image = function (href, title, text) {
+ href = cleanUrl(href);
+ if (href === null)
+ return text;
+
+ let out = `
';
+ return out;
+}
+
// Disable default reflink behavior, as it steps on our variables extension
tokenizer.def = function () {
return undefined;
@@ -356,6 +370,27 @@ const superSubScripts = {
}
};
+const forcedParagraphBreaks = {
+ name : 'hardBreaks',
+ level : 'block',
+ start(src) { return src.match(/\n:+$/m)?.index; }, // Hint to Marked.js to stop and check for a match
+ tokenizer(src, tokens) {
+ const regex = /^(:+)(?:\n|$)/ym;
+ const match = regex.exec(src);
+ if(match?.length) {
+ return {
+ type : 'hardBreaks', // Should match "name" above
+ raw : match[0], // Text to consume from the source
+ length : match[1].length,
+ text : ''
+ };
+ }
+ },
+ renderer(token) {
+ return `
`.repeat(token.length).concat('\n');
+ }
+};
+
const definitionListsSingleLine = {
name : 'definitionListsSingleLine',
level : 'block',
@@ -400,9 +435,9 @@ const definitionListsSingleLine = {
const definitionListsMultiLine = {
name : 'definitionListsMultiLine',
level : 'block',
- start(src) { return src.match(/\n[^\n]*\n::/m)?.index; }, // Hint to Marked.js to stop and check for a match
+ start(src) { return src.match(/\n[^\n]*\n::[^:\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match
tokenizer(src, tokens) {
- const regex = /(\n?\n?(?!::)[^\n]+?(?=\n::))|\n::(.(?:.|\n)*?(?=(?:\n::)|(?:\n\n)|$))/y;
+ const regex = /(\n?\n?(?!::)[^\n]+?(?=\n::[^:\n]))|\n::([^:\n](?:.|\n)*?(?=(?:\n::)|(?:\n\n)|$))/y;
let match;
let endIndex = 0;
const definitions = [];
@@ -706,34 +741,27 @@ const MarkedEmojiOptions = {
renderer : (token)=>`
`
};
+const tableTerminators = [
+ `:+\\n`, // hardBreak
+ ` *{[^\n]+}`, // blockInjector
+ ` *{{[^{\n]*\n.*?\n}}` // mustacheDiv
+]
+
Marked.use(MarkedVariables());
-Marked.use({ extensions: [definitionListsMultiLine, definitionListsSingleLine, superSubScripts, mustacheSpans, mustacheDivs, mustacheInjectInline] });
+Marked.use({ extensions : [definitionListsMultiLine, definitionListsSingleLine, forcedParagraphBreaks, superSubScripts,
+ mustacheSpans, mustacheDivs, mustacheInjectInline] });
Marked.use(mustacheInjectBlock);
Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
-Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions));
+Marked.use(MarkedExtendedTables(tableTerminators), MarkedGFMHeadingId({ globalSlugs: true }), MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions));
-const nonWordAndColonTest = /[^\w:]/g;
-const cleanUrl = function (sanitize, base, href) {
- if(sanitize) {
- let prot;
- try {
- prot = decodeURIComponent(unescape(href))
- .replace(nonWordAndColonTest, '')
- .toLowerCase();
- } catch (e) {
- return null;
- }
- if(prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
- return null;
- }
- }
- try {
- href = encodeURI(href).replace(/%25/g, '%');
- } catch (e) {
- return null;
- }
- return href;
-};
+function cleanUrl(href) {
+ try {
+ href = encodeURI(href).replace(/%25/g, '%');
+ } catch {
+ return null;
+ }
+ return href;
+}
const escapeTest = /[&<>"']/;
const escapeReplace = /[&<>"']/g;
@@ -828,13 +856,15 @@ let globalPageNumber = 0;
module.exports = {
marked : Marked,
- render : (rawBrewText, pageNumber=1)=>{
- globalVarsList[pageNumber] = {}; //Reset global links for current page, to ensure values are parsed in order
- varsQueue = []; //Could move into MarkedVariables()
- globalPageNumber = pageNumber;
+ render : (rawBrewText, pageNumber=0)=>{
+ globalVarsList[pageNumber] = {}; //Reset global links for current page, to ensure values are parsed in order
+ varsQueue = []; //Could move into MarkedVariables()
+ globalPageNumber = pageNumber;
+ if(pageNumber==0) {
+ MarkedGFMResetHeadingIDs();
+ }
- rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n
\n`)
- .replace(/^(:+)$/gm, (match)=>`${`
`.repeat(match.length)}\n`);
+ rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n
\n`);
const opts = Marked.defaults;
rawBrewText = opts.hooks.preprocess(rawBrewText);
diff --git a/tests/markdown/definition-lists.test.js b/tests/markdown/definition-lists.test.js
index 9f5025d73..9c0bdf6b0 100644
--- a/tests/markdown/definition-lists.test.js
+++ b/tests/markdown/definition-lists.test.js
@@ -88,4 +88,16 @@ describe('Multiline Definition Lists', ()=>{
const rendered = Markdown.render(source).trim();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('
Term 1 Inline definition 1 \nInline definition 2 (no DT) \n');
});
+
+ test('Multiline Definition Term must have at least one non-empty Definition', function() {
+ const source = 'Term 1\n::';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
Term 1
\n
`);
+ });
+
+ test('Multiline Definition List must have at least one non-newline character after ::', function() {
+ const source = 'Term 1\n::\nDefinition 1\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
Term 1
\n
\n
Definition 1
`);
+ });
});
diff --git a/tests/markdown/hard-breaks.test.js b/tests/markdown/hard-breaks.test.js
new file mode 100644
index 000000000..3d0f59a41
--- /dev/null
+++ b/tests/markdown/hard-breaks.test.js
@@ -0,0 +1,47 @@
+/* eslint-disable max-lines */
+
+const Markdown = require('naturalcrit/markdown.js');
+
+describe('Hard Breaks', ()=>{
+ test('Single Break', function() {
+ const source = ':\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ });
+
+ test('Double Break', function() {
+ const source = '::\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ });
+
+ test('Triple Break', function() {
+ const source = ':::\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ });
+
+ test('Many Break', function() {
+ const source = '::::::::::\n\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ });
+
+ test('Multiple sets of Breaks', function() {
+ const source = ':::\n:::\n:::';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
\n
\n
`);
+ });
+
+ test('Break directly between two paragraphs', function() {
+ const source = 'Line 1\n::\nLine 2';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
Line 1
\n
\n
Line 2
`);
+ });
+
+ test('Ignored inside a code block', function() {
+ const source = '```\n\n:\n\n```\n';
+ const rendered = Markdown.render(source).trim();
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
\n:\n`);
+ });
+});
diff --git a/tests/markdown/mustache-syntax.test.js b/tests/markdown/mustache-syntax.test.js
index 3f7f2529b..51284ef2b 100644
--- a/tests/markdown/mustache-syntax.test.js
+++ b/tests/markdown/mustache-syntax.test.js
@@ -322,9 +322,9 @@ describe('Injection: When an injection tag follows an element', ()=>{
});
it('Renders an image element with injected style', function() {
- const source = '{position:absolute}';
+ const source = '{position:absolute}';
const rendered = Markdown.render(source).trimReturns();
- expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('
');
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('
');
});
it('Renders an element modified by only the first of two consecutive injections', function() {
@@ -343,19 +343,19 @@ describe('Injection: When an injection tag follows an element', ()=>{
it('Renders an image with added attributes', function() {
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`;
const rendered = Markdown.render(source).trimReturns();
- expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
});
it('Renders an image with "=" in the url, and added attributes', function() {
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`;
const rendered = Markdown.render(source).trimReturns();
- expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
});
it('Renders an image and added attributes with "=" in the value, ', function() {
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e,otherUrl="url?auth=12345"}`;
const rendered = Markdown.render(source).trimReturns();
- expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
});
});
diff --git a/tests/markdown/variables.test.js b/tests/markdown/variables.test.js
index 2c8db375e..bf778b14d 100644
--- a/tests/markdown/variables.test.js
+++ b/tests/markdown/variables.test.js
@@ -315,21 +315,21 @@ describe('Normal Links and Images', ()=>{
const source = ``;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
-
`.trimReturns());
+
`.trimReturns());
});
it('Renders normal images with a title', function() {
const source = 'An image !';
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
-
An image !
`.trimReturns());
+
An image !
`.trimReturns());
});
it('Applies curly injectors to images', function() {
const source = `{width:100px}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
-
`.trimReturns());
+
`.trimReturns());
});
it('Renders normal links', function() {
diff --git a/themes/V3/Blank/snippets.js b/themes/V3/Blank/snippets.js
index 8d45560c5..8437dab2e 100644
--- a/themes/V3/Blank/snippets.js
+++ b/themes/V3/Blank/snippets.js
@@ -153,6 +153,18 @@ module.exports = [
gen : dedent`
 {width:325px,mix-blend-mode:multiply}`
},
+ {
+ name : 'Image Wrap Left',
+ icon : 'fac image-wrap-left',
+ gen : dedent`
+  {width:280px,margin-right:-3cm,wrapLeft}`
+ },
+ {
+ name : 'Image Wrap Right',
+ icon : 'fac image-wrap-right',
+ gen : dedent`
+  {width:280px,margin-left:-3cm,wrapRight}`
+ },
{
name : 'Background Image',
icon : 'fas fa-tree',
diff --git a/themes/V3/Blank/style.less b/themes/V3/Blank/style.less
index 0f779c38b..0f3766342 100644
--- a/themes/V3/Blank/style.less
+++ b/themes/V3/Blank/style.less
@@ -156,6 +156,19 @@ body { counter-reset : page-numbers; }
break-inside : avoid;
}
+ /* Wrap Text */
+ .wrapLeft {
+ shape-outside : var(--HB_src);
+ float : right;
+ shape-margin : 0.2cm;
+ }
+
+ .wrapRight {
+ shape-outside : var(--HB_src);
+ float : left;
+ shape-margin : 0.2cm;
+ }
+
/* Watermark */
.watermark {
position : absolute;
@@ -236,7 +249,7 @@ body { counter-reset : page-numbers; }
left : 50%;
width : 50%;
height : 50%;
- transform : translateX(-50%) translateY(50%) rotate(calc(-1deg * var(--rotation))) scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY)));
+ transform : translateX(-50%) translateY(50%) scaleX(calc(1 / var(--scaleX))) scaleY(calc(1 / var(--scaleY))) rotate(calc(-1deg * var(--rotation)));
}
& img {
position : absolute;