diff --git a/Dockerfile b/Dockerfile index 82b13ac86..84652fbf9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:18-alpine +FROM node:20-alpine RUN apk --no-cache add git ENV NODE_ENV=docker diff --git a/changelog.md b/changelog.md index b331fc4f0..0cdb89c29 100644 --- a/changelog.md +++ b/changelog.md @@ -84,6 +84,34 @@ pre { ## changelog For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery). +### Monday 7/29/2024 - v3.14.0 +{{taskList + +##### abquintic, calculuschild + +* [x] Alternative Brew Themes, including importing other brews as a base theme. + +- In the :fas_circle_info: **Properties** menu, find the new {{openSans **THEME**}} dropdown. It lists Brew Themes, including a new **Blank** theme as a simpler basis for custom styling. +- Brews tagged with `meta:theme` will appear in the Brew Themes list. Selecting one loads its :fas_paintbrush: **Style** tab contents as the CSS basis for the current brew, allowing one brew to style multiple documents. +- Brews with `meta:theme` can also select their own Theme, i.e. layering Themes on top of each other. +- The next goal is to make **Published** Themes shareable between users. + + +Fixes issues [#1899](https://github.com/naturalcrit/homebrewery/issues/1899), [#3085](https://github.com/naturalcrit/homebrewery/issues/3085) + +##### G-Ambatte + +* [x] Fix Drop-cap font becoming corrupted when Bold + +Fixes issues [#3551](https://github.com/naturalcrit/homebrewery/issues/3551) + +* [x] Fixes to UI styling + +Fixes issues [#3568](https://github.com/naturalcrit/homebrewery/issues/3568) + +}} + + ### Saturday 6/7/2024 - v3.13.1 {{taskList @@ -131,8 +159,6 @@ Fixes issue [#3298](https://github.com/naturalcrit/homebrewery/issues/3298) Fixes issue [#3397](https://github.com/naturalcrit/homebrewery/issues/3397) }} -\column - ### Monday 18/3/2024 - v3.12.0 {{taskList diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 3c36244c1..4b82c6bc0 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -18,8 +18,6 @@ const { printCurrentBrew } = require('../../../shared/helpers.js'); const DOMPurify = require('dompurify'); const purifyConfig = { FORCE_BODY: true, SANITIZE_DOM: false }; -const Themes = require('themes/themes.json'); - const PAGE_HEIGHT = 1056; const INITIAL_CONTENT = dedent` @@ -57,6 +55,7 @@ const BrewRenderer = (props)=>{ lang : '', errors : [], currentEditorPage : 0, + themeBundle : {}, ...props }; @@ -125,10 +124,9 @@ const BrewRenderer = (props)=>{ }; const renderStyle = ()=>{ - if(!props.style) return; const cleanStyle = props.style; //DOMPurify.sanitize(props.style, purifyConfig); - //return
@layer styleTab {\n${sanitizeScriptTags(props.style)}\n} ` }} />; - return
${cleanStyle} ` }} />; + const themeStyles = props.themeBundle?.joinedStyles ?? ''; + return
${cleanStyle} ` }} />; }; const renderPage = (pageText, index)=>{ @@ -188,10 +186,6 @@ const BrewRenderer = (props)=>{ document.dispatchEvent(new MouseEvent('click')); }; - const rendererPath = props.renderer == 'V3' ? 'V3' : 'Legacy'; - const themePath = props.theme ?? '5ePHB'; - const baseThemePath = Themes[rendererPath][themePath].baseTheme; - return ( <> {/*render dummy page while iFrame is mounting.*/} @@ -220,13 +214,6 @@ const BrewRenderer = (props)=>{ onKeyDown={handleControlKeys} tabIndex={-1} style={{ height: state.height }}> - - - {baseThemePath && - - } - - {/* Apply CSS from Style tab and render pages from Markdown tab */} {state.isMounted && diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index efcc9c861..cb5d122f1 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -381,7 +381,8 @@ const Editor = createClass({ + reportError={this.props.reportError} + userThemes={this.props.userThemes}/> ; } }, @@ -424,6 +425,7 @@ const Editor = createClass({ historySize={this.historySize()} currentEditorTheme={this.state.editorTheme} updateEditorTheme={this.updateEditorTheme} + snippetBundle={this.props.snippetBundle} cursorPos={this.codeEditor.current?.getCursorPosition() || {}} /> {this.renderEditor()} diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.jsx b/client/homebrew/editor/metadataEditor/metadataEditor.jsx index a59a50f74..4b48c2617 100644 --- a/client/homebrew/editor/metadataEditor/metadataEditor.jsx +++ b/client/homebrew/editor/metadataEditor/metadataEditor.jsx @@ -8,6 +8,7 @@ const Nav = require('naturalcrit/nav/nav.jsx'); const Combobox = require('client/components/combobox.jsx'); const StringArrayEditor = require('../stringArrayEditor/stringArrayEditor.jsx'); + const Themes = require('themes/themes.json'); const validations = require('./validations.js'); @@ -98,7 +99,7 @@ const MetadataEditor = createClass({ if(renderer == 'legacy') this.props.metadata.theme = '5ePHB'; } - this.props.onChange(this.props.metadata); + this.props.onChange(this.props.metadata, 'renderer'); }, handlePublish : function(val){ this.props.onChange({ @@ -110,7 +111,7 @@ const MetadataEditor = createClass({ handleTheme : function(theme){ this.props.metadata.renderer = theme.renderer; this.props.metadata.theme = theme.path; - this.props.onChange(this.props.metadata); + this.props.onChange(this.props.metadata, 'theme'); }, handleLanguage : function(languageCode){ @@ -191,37 +192,41 @@ const MetadataEditor = createClass({ renderThemeDropdown : function(){ if(!global.enable_themes) return; + const mergedThemes = _.merge(Themes, this.props.userThemes); + const listThemes = (renderer)=>{ - return _.map(_.values(Themes[renderer]), (theme)=>{ - return
this.handleTheme(theme)} title={''}> - {`${theme.renderer} : ${theme.name}`} - + return _.map(_.values(mergedThemes[renderer]), (theme)=>{ + const preview = theme.thumbnail || `/themes/${theme.renderer}/${theme.path}/dropdownPreview.png`; + const texture = theme.thumbnail || `/themes/${theme.renderer}/${theme.path}/dropdownTexture.png`; + return
this.handleTheme(theme)} title={''}> + {theme.author ?? renderer} : {theme.name} +
+ +
-
{`${theme.name}`} preview
- +
{theme.name} preview
+
; }); }; - const currentTheme = Themes[`${_.upperFirst(this.props.metadata.renderer)}`][this.props.metadata.theme]; + const currentRenderer = this.props.metadata.renderer; + const currentTheme = mergedThemes[`${_.upperFirst(this.props.metadata.renderer)}`][this.props.metadata.theme] + ?? { name: `!!! THEME MISSING !!! ID=${this.props.metadata.theme}` }; let dropdown; - if(this.props.metadata.renderer == 'legacy') { + if(currentRenderer == 'legacy') { dropdown = -
- {`Themes are not supported in the Legacy Renderer`} -
+
{`Themes are not supported in the Legacy Renderer`}
; } else { dropdown = -
- {`${_.upperFirst(currentTheme.renderer)} : ${currentTheme.name}`} -
- {/*listThemes('Legacy')*/} - {listThemes('V3')} +
{currentTheme.author ?? _.upperFirst(currentRenderer)} : {currentTheme.name}
+ + {listThemes(currentRenderer)}
; } diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.less b/client/homebrew/editor/metadataEditor/metadataEditor.less index 7f7ce3060..5d1d8ae9f 100644 --- a/client/homebrew/editor/metadataEditor/metadataEditor.less +++ b/client/homebrew/editor/metadataEditor/metadataEditor.less @@ -191,6 +191,13 @@ color : white; } } + .navDropdown .item > p { + width: 45%; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + height: 1.1em; + } .navDropdown { box-shadow : 0px 5px 10px rgba(0, 0, 0, 0.3); position : absolute; @@ -230,14 +237,23 @@ &:hover > .preview { opacity: 1; } - >img { - mask-image : linear-gradient(90deg, transparent, black 20%); - -webkit-mask-image : linear-gradient(90deg, transparent, black 20%); - position : absolute; - right : 0; - top : 0px; - width : 50%; - height : 100%; + .texture-container { + position: absolute; + width: 100%; + height: 100%; + min-height: 100%; + top: 0; + left: 0; + overflow: hidden; + > img { + mask-image : linear-gradient(90deg, transparent, black 20%); + -webkit-mask-image : linear-gradient(90deg, transparent, black 20%); + position : absolute; + right : 0; + top : 0px; + width : 50%; + min-height : 100%; + } } } } diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index 80a97f49e..af493c961 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -6,9 +6,6 @@ const _ = require('lodash'); const cx = require('classnames'); //Import all themes - -const Themes = require('themes/themes.json'); - const ThemeSnippets = {}; ThemeSnippets['Legacy_5ePHB'] = require('themes/Legacy/5ePHB/snippets.js'); ThemeSnippets['V3_5ePHB'] = require('themes/V3/5ePHB/snippets.js'); @@ -40,7 +37,8 @@ const Snippetbar = createClass({ foldCode : ()=>{}, unfoldCode : ()=>{}, updateEditorTheme : ()=>{}, - cursorPos : {} + cursorPos : {}, + snippetBundle : [] }; }, @@ -53,21 +51,15 @@ const Snippetbar = createClass({ }, componentDidMount : async function() { - const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy'; - const themePath = this.props.theme ?? '5ePHB'; - let snippets = _.cloneDeep(ThemeSnippets[`${rendererPath}_${themePath}`]); - snippets = this.compileSnippets(rendererPath, themePath, snippets); + const snippets = this.compileSnippets(); this.setState({ snippets : snippets }); }, componentDidUpdate : async function(prevProps) { - if(prevProps.renderer != this.props.renderer || prevProps.theme != this.props.theme) { - const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy'; - const themePath = this.props.theme ?? '5ePHB'; - let snippets = _.cloneDeep(ThemeSnippets[`${rendererPath}_${themePath}`]); - snippets = this.compileSnippets(rendererPath, themePath, snippets); + if(prevProps.renderer != this.props.renderer || prevProps.theme != this.props.theme || prevProps.snippetBundle != this.props.snippetBundle) { + const snippets = this.compileSnippets(); this.setState({ snippets : snippets }); @@ -75,26 +67,26 @@ const Snippetbar = createClass({ }, - mergeCustomizer : function(valueA, valueB, key) { + mergeCustomizer : function(oldValue, newValue, key) { if(key == 'snippets') { - const result = _.reverse(_.unionBy(_.reverse(valueB), _.reverse(valueA), 'name')); // Join snippets together, with preference for the current theme over the base theme + const result = _.reverse(_.unionBy(_.reverse(newValue), _.reverse(oldValue), 'name')); // Join snippets together, with preference for the child theme over the parent theme return _.filter(result, 'gen'); //Only keep snippets with a 'gen' property. } }, - compileSnippets : function(rendererPath, themePath, snippets) { - let compiledSnippets = snippets; - const baseSnippetsPath = Themes[rendererPath][themePath].baseSnippets; + compileSnippets : function() { + let compiledSnippets = []; - const objB = _.keyBy(compiledSnippets, 'groupName'); + let oldSnippets = _.keyBy(compiledSnippets, 'groupName'); - if(baseSnippetsPath) { - const objA = _.keyBy(_.cloneDeep(ThemeSnippets[`${rendererPath}_${baseSnippetsPath}`]), 'groupName'); - compiledSnippets = _.values(_.mergeWith(objA, objB, this.mergeCustomizer)); - compiledSnippets = this.compileSnippets(rendererPath, baseSnippetsPath, _.cloneDeep(compiledSnippets)); - } else { - const objA = _.keyBy(_.cloneDeep(ThemeSnippets[`${rendererPath}_Blank`]), 'groupName'); - compiledSnippets = _.values(_.mergeWith(objA, objB, this.mergeCustomizer)); + for (let snippets of this.props.snippetBundle) { + if(typeof(snippets) == 'string') // load staticThemes as needed; they were sent as just a file name + snippets = ThemeSnippets[snippets]; + + const newSnippets = _.keyBy(_.cloneDeep(snippets), 'groupName'); + compiledSnippets = _.values(_.mergeWith(oldSnippets, newSnippets, this.mergeCustomizer)); + + oldSnippets = _.keyBy(compiledSnippets, 'groupName'); } return compiledSnippets; }, diff --git a/client/homebrew/homebrew.jsx b/client/homebrew/homebrew.jsx index 2489bc1ca..8299cfe87 100644 --- a/client/homebrew/homebrew.jsx +++ b/client/homebrew/homebrew.jsx @@ -66,13 +66,14 @@ const Homebrew = createClass({
- } /> + } /> } /> - } /> - } /> + } /> + } /> } /> } /> } /> + } /> } /> } /> } /> diff --git a/client/homebrew/navbar/error-navitem.jsx b/client/homebrew/navbar/error-navitem.jsx index 59e05a253..5dd5c1eb9 100644 --- a/client/homebrew/navbar/error-navitem.jsx +++ b/client/homebrew/navbar/error-navitem.jsx @@ -104,6 +104,18 @@ const ErrorNavItem = createClass({ ; } + if(HBErrorCode === '09') { + return + Oops! +
+ Looks like there was a problem retreiving + the theme, or a theme that it inherits, + for this brew. Verify that brew + {response.body.brewId} still exists! +
+
; + } + return Oops!
diff --git a/client/homebrew/navbar/error-navitem.less b/client/homebrew/navbar/error-navitem.less index 7e7dab772..be138dca4 100644 --- a/client/homebrew/navbar/error-navitem.less +++ b/client/homebrew/navbar/error-navitem.less @@ -21,6 +21,9 @@ font-size : 10px; font-weight : 800; text-transform : uppercase; + .lowercase { + text-transform : none; + } a{ color : @teal; } diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 48d0f3fe5..39a6d1931 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -25,7 +25,7 @@ const LockNotification = require('./lockNotification/lockNotification.jsx'); const Markdown = require('naturalcrit/markdown.js'); const { DEFAULT_BREW_LOAD } = require('../../../../server/brewDefaults.js'); -const { printCurrentBrew } = require('../../../../shared/helpers.js'); +const { printCurrentBrew, fetchThemeBundle } = require('../../../../shared/helpers.js'); const googleDriveIcon = require('../../googleDrive.svg'); @@ -55,7 +55,8 @@ const EditPage = createClass({ autoSaveWarning : false, unsavedTime : new Date(), currentEditorPage : 0, - displayLockMessage : this.props.brew.lock || false + displayLockMessage : this.props.brew.lock || false, + themeBundle : {} }; }, @@ -87,6 +88,8 @@ const EditPage = createClass({ htmlErrors : Markdown.validate(prevState.brew.text) })); + fetchThemeBundle(this, this.props.brew.renderer, this.props.brew.theme); + document.addEventListener('keydown', this.handleControlKeys); }, componentWillUnmount : function() { @@ -130,7 +133,10 @@ const EditPage = createClass({ }), ()=>{if(this.state.autoSave) this.trySave();}); }, - handleMetaChange : function(metadata){ + handleMetaChange : function(metadata, field=undefined){ + if(field == 'theme' || field == 'renderer') // Fetch theme bundle only if theme or renderer was changed + fetchThemeBundle(this, metadata.renderer, metadata.theme); + this.setState((prevState)=>({ brew : { ...prevState.brew, @@ -138,7 +144,6 @@ const EditPage = createClass({ }, isPending : true, }), ()=>{if(this.state.autoSave) this.trySave();}); - }, hasChanges : function(){ @@ -406,12 +411,15 @@ const EditPage = createClass({ onMetaChange={this.handleMetaChange} reportError={this.errorReported} renderer={this.state.brew.renderer} + userThemes={this.props.userThemes} + snippetBundle={this.state.themeBundle.snippets} /> { **Brew ID:** ${props.brew.brewId}`, + // Theme load error + '09' : dedent` + ## No Homebrewery theme document could be found. + + The server could not locate the Homebrewery document. It was likely deleted by + its owner. + + : + + **Requested access:** ${props.brew.accessType} + + **Brew ID:** ${props.brew.brewId}`, + // Brew locked by Administrators error '100' : dedent` ## This brew has been locked. diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx index 1aa816df2..490b22596 100644 --- a/client/homebrew/pages/homePage/homePage.jsx +++ b/client/homebrew/pages/homePage/homePage.jsx @@ -13,6 +13,7 @@ const HelpNavItem = require('../../navbar/help.navitem.jsx'); const RecentNavItem = require('../../navbar/recent.navitem.jsx').both; const AccountNavItem = require('../../navbar/account.navitem.jsx'); const ErrorNavItem = require('../../navbar/error-navitem.jsx'); +const { fetchThemeBundle } = require('../../../../shared/helpers.js'); const SplitPane = require('naturalcrit/splitPane/splitPane.jsx'); @@ -34,12 +35,17 @@ const HomePage = createClass({ brew : this.props.brew, welcomeText : this.props.brew.text, error : undefined, - currentEditorPage : 0 + currentEditorPage : 0, + themeBundle : {} }; }, editor : React.createRef(null), + componentDidMount : function() { + fetchThemeBundle(this, this.props.brew.renderer, this.props.brew.theme); + }, + handleSave : function(){ request.post('/api') .send(this.state.brew) @@ -89,12 +95,14 @@ const HomePage = createClass({ onTextChange={this.handleTextChange} renderer={this.state.brew.renderer} showEditButtons={false} + snippetBundle={this.state.themeBundle.snippets} />
diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index f2525c425..89d1df874 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -19,7 +19,7 @@ const Editor = require('../../editor/editor.jsx'); const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx'); const { DEFAULT_BREW } = require('../../../../server/brewDefaults.js'); -const { printCurrentBrew } = require('../../../../shared/helpers.js'); +const { printCurrentBrew, fetchThemeBundle } = require('../../../../shared/helpers.js'); const BREWKEY = 'homebrewery-new'; const STYLEKEY = 'homebrewery-new-style'; @@ -44,7 +44,8 @@ const NewPage = createClass({ saveGoogle : (global.account && global.account.googleId ? true : false), error : null, htmlErrors : Markdown.validate(brew.text), - currentEditorPage : 0 + currentEditorPage : 0, + themeBundle : {} }; }, @@ -77,6 +78,8 @@ const NewPage = createClass({ saveGoogle : (saveStorage == 'GOOGLE-DRIVE' && this.state.saveGoogle) }); + fetchThemeBundle(this, this.props.brew.renderer, this.props.brew.theme); + localStorage.setItem(BREWKEY, brew.text); if(brew.style) localStorage.setItem(STYLEKEY, brew.style); @@ -122,7 +125,10 @@ const NewPage = createClass({ localStorage.setItem(STYLEKEY, style); }, - handleMetaChange : function(metadata){ + handleMetaChange : function(metadata, field=undefined){ + if(field == 'theme' || field == 'renderer') // Fetch theme bundle only if theme or renderer was changed + fetchThemeBundle(this, metadata.renderer, metadata.theme); + this.setState((prevState)=>({ brew : { ...prevState.brew, ...metadata }, }), ()=>{ @@ -142,8 +148,6 @@ const NewPage = createClass({ isSaving : true }); - console.log('saving new brew'); - let brew = this.state.brew; // Split out CSS to Style if CSS codefence exists if(brew.text.startsWith('```css') && brew.text.indexOf('```\n\n') > 0) { @@ -153,12 +157,10 @@ const NewPage = createClass({ } brew.pageCount=((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1; - const res = await request .post(`/api${this.state.saveGoogle ? '?saveToGoogle=true' : ''}`) .send(brew) .catch((err)=>{ - console.log(err); this.setState({ isSaving: false, error: err }); }); if(!res) return; @@ -214,12 +216,15 @@ const NewPage = createClass({ onStyleChange={this.handleStyleChange} onMetaChange={this.handleMetaChange} renderer={this.state.brew.renderer} + userThemes={this.props.userThemes} + snippetBundle={this.state.themeBundle.snippets} />
diff --git a/config/default.json b/config/default.json index 70c90593e..12b35e6cf 100644 --- a/config/default.json +++ b/config/default.json @@ -4,6 +4,7 @@ "secret" : "secret", "web_port" : 8000, "enable_v3" : true, + "enable_themes" : true, "local_environments" : ["docker", "local"], "publicUrl" : "https://homebrewery.naturalcrit.com" } diff --git a/package-lock.json b/package-lock.json index cce4d5577..284841d76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,18 @@ { "name": "homebrewery", - "version": "3.13.1", + "version": "3.14.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "homebrewery", - "version": "3.13.1", + "version": "3.14.0", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.24.7", + "@babel/core": "^7.25.2", "@babel/plugin-transform-runtime": "^7.24.7", - "@babel/preset-env": "^7.24.7", + "@babel/preset-env": "^7.25.3", "@babel/preset-react": "^7.24.7", "@googleapis/drive": "^8.11.0", "body-parser": "^1.20.2", @@ -21,7 +21,7 @@ "cookie-parser": "^1.4.6", "create-react-class": "^15.7.0", "dedent-tabs": "^0.10.3", - "dompurify": "^3.1.5", + "dompurify": "^3.1.6", "expr-eval": "^2.0.2", "express": "^4.19.2", "express-async-handler": "^1.2.0", @@ -38,13 +38,13 @@ "marked-smartypants-lite": "^1.0.2", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^8.4.5", + "mongoose": "^8.5.2", "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.24.1", + "react-router-dom": "6.25.1", "sanitize-filename": "1.6.3", "superagent": "^9.0.2", "vitreum": "git+https://git@github.com/calculuschild/vitreum.git" @@ -52,7 +52,7 @@ "devDependencies": { "eslint": "^8.57.0", "eslint-plugin-jest": "^28.6.0", - "eslint-plugin-react": "^7.34.3", + "eslint-plugin-react": "^7.35.0", "jest": "^29.7.0", "jest-expect-message": "^1.1.3", "postcss-less": "^6.0.0", @@ -101,28 +101,28 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", + "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==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -143,11 +143,11 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", "dependencies": { - "@babel/types": "^7.24.7", + "@babel/types": "^7.25.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -193,13 +193,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -230,9 +230,9 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", - "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz", + "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", "regexpu-core": "^5.3.1", @@ -283,24 +283,13 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz", - "integrity": "sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", + "integrity": "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -319,15 +308,14 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", "@babel/helper-module-imports": "^7.24.7", "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" }, "engines": { "node": ">=6.9.0" @@ -348,21 +336,21 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", - "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz", + "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-wrap-function": "^7.24.7" + "@babel/helper-wrap-function": "^7.25.0", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -372,13 +360,13 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", - "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", + "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7" + "@babel/helper-member-expression-to-functions": "^7.24.8", + "@babel/helper-optimise-call-expression": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -423,9 +411,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "engines": { "node": ">=6.9.0" } @@ -439,34 +427,33 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", - "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz", + "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==", "dependencies": { - "@babel/helper-function-name": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", + "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -487,9 +474,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", + "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "dependencies": { + "@babel/types": "^7.25.2" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -498,12 +488,26 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.7.tgz", - "integrity": "sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", + "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz", + "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -513,11 +517,11 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.7.tgz", - "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz", + "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -543,12 +547,12 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.7.tgz", - "integrity": "sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz", + "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -841,14 +845,14 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", - "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", + "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==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@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" }, "engines": { "node": ">=6.9.0" @@ -888,11 +892,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz", - "integrity": "sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz", + "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -933,17 +937,15 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz", - "integrity": "sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==", + "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==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-replace-supers": "^7.25.0", + "@babel/traverse": "^7.25.0", "globals": "^11.1.0" }, "engines": { @@ -969,11 +971,11 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz", - "integrity": "sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz", + "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1011,6 +1013,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz", + "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-dynamic-import": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", @@ -1072,13 +1089,13 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz", - "integrity": "sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==", + "version": "7.25.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", + "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.1" }, "engines": { "node": ">=6.9.0" @@ -1103,11 +1120,11 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", + "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1161,12 +1178,12 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz", - "integrity": "sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz", + "integrity": "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==", "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-module-transforms": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-simple-access": "^7.24.7" }, "engines": { @@ -1177,14 +1194,14 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", - "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz", + "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==", "dependencies": { - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1315,11 +1332,11 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.7.tgz", - "integrity": "sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz", + "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, @@ -1557,11 +1574,11 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.7.tgz", - "integrity": "sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz", + "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1630,18 +1647,19 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz", - "integrity": "sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz", + "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==", "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", + "@babel/compat-data": "^7.25.2", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -1662,29 +1680,30 @@ "@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.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.0", "@babel/plugin-transform-async-to-generator": "^7.24.7", "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", "@babel/plugin-transform-class-properties": "^7.24.7", "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.24.7", + "@babel/plugin-transform-classes": "^7.25.0", "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", "@babel/plugin-transform-dotall-regex": "^7.24.7", "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", "@babel/plugin-transform-dynamic-import": "^7.24.7", "@babel/plugin-transform-exponentiation-operator": "^7.24.7", "@babel/plugin-transform-export-namespace-from": "^7.24.7", "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", + "@babel/plugin-transform-literals": "^7.25.2", "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", "@babel/plugin-transform-member-expression-literals": "^7.24.7", "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-modules-systemjs": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-modules-systemjs": "^7.25.0", "@babel/plugin-transform-modules-umd": "^7.24.7", "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", "@babel/plugin-transform-new-target": "^7.24.7", @@ -1693,7 +1712,7 @@ "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-object-super": "^7.24.7", "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^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-property-in-object": "^7.24.7", @@ -1704,7 +1723,7 @@ "@babel/plugin-transform-spread": "^7.24.7", "@babel/plugin-transform-sticky-regex": "^7.24.7", "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.7", + "@babel/plugin-transform-typeof-symbol": "^7.24.8", "@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", @@ -1713,7 +1732,7 @@ "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.4", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.31.0", + "core-js-compat": "^3.37.1", "semver": "^6.3.1" }, "engines": { @@ -1772,31 +1791,28 @@ } }, "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", + "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.2", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1805,11 +1821,11 @@ } }, "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", + "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", "dependencies": { - "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, @@ -2821,9 +2837,9 @@ } }, "node_modules/@mongodb-js/saslprep": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.7.tgz", - "integrity": "sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.8.tgz", + "integrity": "sha512-qKwC/M/nNNaKUBMQ0nuzm47b7ZYWQHN3pcXq4IIcoSBc2hOIrflAxJduIvvqmhoz3gR2TacTAs8vlsCVPkiEdQ==", "dependencies": { "sparse-bitfield": "^3.0.3" } @@ -2864,9 +2880,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.17.1.tgz", - "integrity": "sha512-mCOMec4BKd6BRGBZeSnGiIgwsbLGp3yhVqAD8H+PxiRNEHgDpZb8J1TnrSDlg97t0ySKMQJTHCWBCmBpSmkF6Q==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.18.0.tgz", + "integrity": "sha512-L3jkqmqoSVBVKHfpGZmLrex0lxR5SucGA0sUfFzGctehw+S/ggL9L/0NnC5mw6P8HUWpFZ3nQw3cRApjjWx9Sw==", "engines": { "node": ">=14.0.0" } @@ -3494,18 +3510,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.toreversed": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", - "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, "node_modules/array.prototype.tosorted": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", @@ -4291,9 +4295,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.23.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", + "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", "funding": [ { "type": "opencollective", @@ -4309,10 +4313,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", + "caniuse-lite": "^1.0.30001640", + "electron-to-chromium": "^1.4.820", "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -4331,9 +4335,9 @@ } }, "node_modules/bson": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.7.0.tgz", - "integrity": "sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz", + "integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==", "engines": { "node": ">=16.20.1" } @@ -4478,9 +4482,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001599", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001599.tgz", - "integrity": "sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA==", + "version": "1.0.30001643", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz", + "integrity": "sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==", "funding": [ { "type": "opencollective", @@ -4841,9 +4845,9 @@ } }, "node_modules/core-js-compat": { - "version": "3.36.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz", - "integrity": "sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA==", + "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==", "dependencies": { "browserslist": "^4.23.0" }, @@ -5471,9 +5475,9 @@ } }, "node_modules/dompurify": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.5.tgz", - "integrity": "sha512-lwG+n5h8QNpxtyrJW/gJWckL+1/DQiYMX8f7t8Z2AZTPw1esVrqjI63i7Zc2Gz0aKzLVMYC1V1PL/ky+aY/NgA==" + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz", + "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==" }, "node_modules/duplexer2": { "version": "0.1.4", @@ -5524,9 +5528,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.711", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.711.tgz", - "integrity": "sha512-hRg81qzvUEibX2lDxnFlVCHACa+LtrCPIsWAxo161LDYIB3jauf57RGsMZV9mvGwE98yGH06icj3zBEoOkxd/w==" + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.3.tgz", + "integrity": "sha512-QNdYSS5i8D9axWp/6XIezRObRHqaav/ur9z1VzCDUCH1XIFOr9WQk5xmgunhsTpjjgDy3oLxO/WMOVZlpUQrlA==" }, "node_modules/elliptic": { "version": "6.5.4", @@ -5750,9 +5754,9 @@ } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "engines": { "node": ">=6" } @@ -5851,35 +5855,35 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.34.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.3.tgz", - "integrity": "sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==", + "version": "7.35.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz", + "integrity": "sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==", "dev": true, "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.2", - "array.prototype.toreversed": "^1.1.2", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", + "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.8", "object.fromentries": "^2.0.8", - "object.hasown": "^1.1.4", "object.values": "^1.2.0", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11" + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "node_modules/eslint-plugin-react/node_modules/doctrine": { @@ -10659,13 +10663,13 @@ } }, "node_modules/mongoose": { - "version": "8.4.5", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.4.5.tgz", - "integrity": "sha512-E5KjBThxST2uFSKKXuiMa9H9Zx4DLTSLuxodAnIzJRixNwc1ARTlJUK1m0a80EB+ZKGP4QNTasyUYRG9DUSHOA==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.5.2.tgz", + "integrity": "sha512-GZB4rHMdYfGatV+23IpCrqFbyCOjCNOHXgWbirr92KRwTEncBrtW3kgU9vmpKjsGf7nMmnAy06SwWUv1vhDkSg==", "dependencies": { "bson": "^6.7.0", "kareem": "2.6.3", - "mongodb": "6.6.2", + "mongodb": "6.7.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", @@ -10737,9 +10741,9 @@ } }, "node_modules/mongoose/node_modules/mongodb": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.6.2.tgz", - "integrity": "sha512-ZF9Ugo2JCG/GfR7DEb4ypfyJJyiKbg5qBYKRintebj8+DNS33CyGMkWbrS9lara+u+h+yEOGSRiLhFO/g1s1aw==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.7.0.tgz", + "integrity": "sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==", "dependencies": { "@mongodb-js/saslprep": "^1.1.5", "bson": "^6.7.0", @@ -11279,23 +11283,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -11582,9 +11569,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -12093,11 +12080,11 @@ "dev": true }, "node_modules/react-router": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.24.1.tgz", - "integrity": "sha512-PTXFXGK2pyXpHzVo3rR9H7ip4lSPZZc0bHG5CARmj65fTT6qG7sTngmb6lcYu1gf3y/8KxORoy9yn59pGpCnpg==", + "version": "6.25.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.25.1.tgz", + "integrity": "sha512-u8ELFr5Z6g02nUtpPAggP73Jigj1mRePSwhS/2nkTrlPU5yEkH1vYzWNyvSnSzeeE2DNqWdH+P8OhIh9wuXhTw==", "dependencies": { - "@remix-run/router": "1.17.1" + "@remix-run/router": "1.18.0" }, "engines": { "node": ">=14.0.0" @@ -12107,12 +12094,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.24.1.tgz", - "integrity": "sha512-U19KtXqooqw967Vw0Qcn5cOvrX5Ejo9ORmOtJMzYWtCT4/WOfFLIZGGsVLxcd9UkBO0mSTZtXqhZBsWlHr7+Sg==", + "version": "6.25.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.25.1.tgz", + "integrity": "sha512-0tUDpbFvk35iv+N89dWNrJp+afLgd+y4VtorJZuOCXK0kkCWjEvb3vTJM++SYvMEpbVwXKf3FjeVveVEb6JpDQ==", "dependencies": { - "@remix-run/router": "1.17.1", - "react-router": "6.24.1" + "@remix-run/router": "1.18.0", + "react-router": "6.25.1" }, "engines": { "node": ">=14.0.0" @@ -13428,6 +13415,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -14435,9 +14432,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "funding": [ { "type": "opencollective", @@ -14453,8 +14450,8 @@ } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js" diff --git a/package.json b/package.json index 321f9afbe..984ff58f3 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.13.1", + "version": "3.14.0", "engines": { "npm": "^10.2.x", "node": "^20.8.x" @@ -22,7 +22,8 @@ "circleci": "npm test && eslint **/*.{js,jsx} --max-warnings=0", "verify": "npm run lint && npm test", "test": "jest --runInBand", - "test:api-unit": "jest server/*.spec.js --verbose", + "test:api-unit": "jest \"server/.*.spec.js\" --verbose", + "test:api-unit:themes": "jest \"server/.*.spec.js\" -t \"theme bundle\" --verbose", "test:coverage": "jest --coverage --silent --runInBand", "test:dev": "jest --verbose --watch", "test:basic": "jest tests/markdown/basic.test.js --verbose", @@ -56,15 +57,15 @@ ], "coverageThreshold": { "global": { - "statements": 25, - "branches": 10, - "functions": 22, - "lines": 25 + "statements": 50, + "branches": 40, + "functions": 40, + "lines": 50 }, "server/homebrew.api.js": { - "statements": 65, + "statements": 70, "branches": 50, - "functions": 60, + "functions": 65, "lines": 70 } }, @@ -82,9 +83,9 @@ ] }, "dependencies": { - "@babel/core": "^7.24.7", + "@babel/core": "^7.25.2", "@babel/plugin-transform-runtime": "^7.24.7", - "@babel/preset-env": "^7.24.7", + "@babel/preset-env": "^7.25.3", "@babel/preset-react": "^7.24.7", "@googleapis/drive": "^8.11.0", "body-parser": "^1.20.2", @@ -93,7 +94,7 @@ "cookie-parser": "^1.4.6", "create-react-class": "^15.7.0", "dedent-tabs": "^0.10.3", - "dompurify": "^3.1.5", + "dompurify": "^3.1.6", "expr-eval": "^2.0.2", "express": "^4.19.2", "express-async-handler": "^1.2.0", @@ -110,13 +111,13 @@ "marked-smartypants-lite": "^1.0.2", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^8.4.5", + "mongoose": "^8.5.2", "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.24.1", + "react-router-dom": "6.25.1", "sanitize-filename": "1.6.3", "superagent": "^9.0.2", "vitreum": "git+https://git@github.com/calculuschild/vitreum.git" @@ -124,7 +125,7 @@ "devDependencies": { "eslint": "^8.57.0", "eslint-plugin-jest": "^28.6.0", - "eslint-plugin-react": "^7.34.3", + "eslint-plugin-react": "^7.35.0", "jest": "^29.7.0", "jest-expect-message": "^1.1.3", "postcss-less": "^6.0.0", diff --git a/server/app.js b/server/app.js index 141b48796..c12916a59 100644 --- a/server/app.js +++ b/server/app.js @@ -9,7 +9,7 @@ const yaml = require('js-yaml'); const app = express(); const config = require('./config.js'); -const { homebrewApi, getBrew } = require('./homebrew.api.js'); +const { homebrewApi, getBrew, getUsersBrewThemes } = require('./homebrew.api.js'); const GoogleActions = require('./googleActions.js'); const serveCompressedStaticAssets = require('./static-assets.mv.js'); const sanitizeFilename = require('sanitize-filename'); @@ -81,7 +81,8 @@ app.get('/robots.txt', (req, res)=>{ app.get('/', (req, res, next)=>{ req.brew = { text : welcomeText, - renderer : 'V3' + renderer : 'V3', + theme : '5ePHB' }, req.ogMeta = { ...defaultMetaTags, @@ -97,7 +98,8 @@ app.get('/', (req, res, next)=>{ app.get('/legacy', (req, res, next)=>{ req.brew = { text : welcomeTextLegacy, - renderer : 'legacy' + renderer : 'legacy', + theme : '5ePHB' }, req.ogMeta = { ...defaultMetaTags, @@ -113,7 +115,8 @@ app.get('/legacy', (req, res, next)=>{ app.get('/migrate', (req, res, next)=>{ req.brew = { text : migrateText, - renderer : 'V3' + renderer : 'V3', + theme : '5ePHB' }, req.ogMeta = { ...defaultMetaTags, @@ -130,7 +133,8 @@ app.get('/changelog', async (req, res, next)=>{ req.brew = { title : 'Changelog', text : changelogText, - renderer : 'V3' + renderer : 'V3', + theme : '5ePHB' }, req.ogMeta = { ...defaultMetaTags, @@ -147,7 +151,8 @@ app.get('/faq', async (req, res, next)=>{ req.brew = { title : 'FAQ', text : faqText, - renderer : 'V3' + renderer : 'V3', + theme : '5ePHB' }, req.ogMeta = { ...defaultMetaTags, @@ -284,9 +289,11 @@ app.get('/user/:username', async (req, res, next)=>{ }); //Edit Page -app.get('/edit/:id', asyncHandler(getBrew('edit')), (req, res, next)=>{ +app.get('/edit/:id', asyncHandler(getBrew('edit')), asyncHandler(async(req, res, next)=>{ req.brew = req.brew.toObject ? req.brew.toObject() : req.brew; + req.userThemes = await(getUsersBrewThemes(req.account?.username)); + req.ogMeta = { ...defaultMetaTags, title : req.brew.title || 'Untitled Brew', description : req.brew.description || 'No description.', @@ -298,10 +305,10 @@ app.get('/edit/:id', asyncHandler(getBrew('edit')), (req, res, next)=>{ splitTextStyleAndMetadata(req.brew); res.header('Cache-Control', 'no-cache, no-store'); //reload the latest saved brew when pressing back button, not the cached version before save. return next(); -}); +})); -//New Page -app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{ +//New Page from ID +app.get('/new/:id', asyncHandler(getBrew('share')), asyncHandler(async(req, res, next)=>{ sanitizeBrew(req.brew, 'share'); splitTextStyleAndMetadata(req.brew); const brew = { @@ -311,17 +318,31 @@ app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{ style : req.brew.style, renderer : req.brew.renderer, theme : req.brew.theme, - tags : req.brew.tags + tags : req.brew.tags, }; req.brew = _.defaults(brew, DEFAULT_BREW); + req.userThemes = await(getUsersBrewThemes(req.account?.username)); + req.ogMeta = { ...defaultMetaTags, title : 'New', description : 'Start crafting your homebrew on the Homebrewery!' }; return next(); -}); +})); + +//New Page +app.get('/new', asyncHandler(async(req, res, next)=>{ + req.userThemes = await(getUsersBrewThemes(req.account?.username)); + + req.ogMeta = { ...defaultMetaTags, + title : 'New', + description : 'Start crafting your homebrew on the Homebrewery!' + }; + + return next(); +})); //Share Page app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{ @@ -437,7 +458,8 @@ const renderPage = async (req, res)=>{ enable_v3 : config.get('enable_v3'), enable_themes : config.get('enable_themes'), config : configuration, - ogMeta : req.ogMeta + ogMeta : req.ogMeta, + userThemes : req.userThemes }; const title = req.brew ? req.brew.title : ''; const page = await templateFn('homebrew', title, props) diff --git a/server/homebrew.api.js b/server/homebrew.api.js index f755c8f23..c314454e2 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -8,9 +8,16 @@ const Markdown = require('../shared/naturalcrit/markdown.js'); const yaml = require('js-yaml'); const asyncHandler = require('express-async-handler'); const { nanoid } = require('nanoid'); +const { splitTextStyleAndMetadata } = require('../shared/helpers.js'); const { DEFAULT_BREW, DEFAULT_BREW_LOAD } = require('./brewDefaults.js'); +const Themes = require('../themes/themes.json'); + +const isStaticTheme = (renderer, themeName)=>{ + return Themes[renderer]?.[themeName] !== undefined; +}; + // const getTopBrews = (cb) => { // HomebrewModel.find().sort({ views: -1 }).limit(5).exec(function(err, brews) { // cb(brews); @@ -37,6 +44,43 @@ const api = { } return { id, googleId }; }, + //Get array of any of this user's brews tagged with `meta:theme` + getUsersBrewThemes : async (username)=>{ + if(!username) + return {}; + + const fields = [ + 'title', + 'tags', + 'shareId', + 'thumbnail', + 'textBin', + 'text', + 'authors', + 'renderer' + ]; + + const userThemes = {}; + + const brews = await HomebrewModel.getByUser(username, true, fields, { tags: { $in: ['meta:theme', 'meta:Theme'] } }); + + if(brews) { + for (const brew of brews) { + userThemes[brew.renderer] ??= {}; + userThemes[brew.renderer][brew.shareId] = { + name : brew.title, + renderer : brew.renderer, + baseTheme : brew.theme, + baseSnippets : false, + author : brew.authors[0], + path : brew.shareId, + thumbnail : brew.thumbnail || '/assets/naturalCritLogoWhite.svg' + }; + } + } + + return userThemes; + }, getBrew : (accessType, stubOnly = false)=>{ // Create middleware with the accessType passed in as part of the scope return async (req, res, next)=>{ @@ -142,7 +186,7 @@ const api = { return modified; }, excludeStubProps : (brew)=>{ - const propsToExclude = ['text', 'textBin', 'renderer', 'pageCount']; + const propsToExclude = ['text', 'textBin']; for (const prop of propsToExclude) { brew[prop] = undefined; } @@ -209,6 +253,58 @@ const api = { res.status(200).send(saved); }, + getThemeBundle : async(req, res)=>{ + /* getThemeBundle: Collects the theme and all parent themes + returns an object containing an array of css, and an array of snippets, in render order + + req.params.id : The shareId ( User theme ) or name ( static theme ) + req.params.renderer : The Markdown renderer used for this theme */ + + req.params.renderer = _.upperFirst(req.params.renderer); + let currentTheme; + const completeStyles = []; + const completeSnippets = []; + + while (req.params.id) { + //=== User Themes ===// + if(!isStaticTheme(req.params.renderer, req.params.id)) { + await api.getBrew('share')(req, res, ()=>{}) + .catch((err)=>{ + if(err.HBErrorCode == '05') + err = { ...err, name: 'ThemeLoad Error', message: 'Theme Not Found', HBErrorCode: '09' }; + throw err; + }); + + currentTheme = req.brew; + splitTextStyleAndMetadata(currentTheme); + + // If there is anything in the snippets or style members, append them to the appropriate array + if(currentTheme?.snippets) completeSnippets.push(JSON.parse(currentTheme.snippets)); + if(currentTheme?.style) completeStyles.push(`/* From Brew: ${req.protocol}://${req.get('host')}/share/${req.params.id} */\n\n${currentTheme.style}`); + + req.params.id = currentTheme.theme; + req.params.renderer = currentTheme.renderer; + } + //=== Static Themes ===// + else { + const localSnippets = `${req.params.renderer}_${req.params.id}`; // Just log the name for loading on client + const localStyle = `@import url(\"/themes/${req.params.renderer}/${req.params.id}/style.css\");`; + completeSnippets.push(localSnippets); + completeStyles.push(`/* From Theme ${req.params.id} */\n\n${localStyle}`); + + req.params.id = Themes[req.params.renderer][req.params.id].baseTheme; + } + } + + const returnObj = { + // Reverse the order of the arrays so they are listed oldest parent to youngest child. + styles : completeStyles.reverse(), + snippets : completeSnippets.reverse() + }; + + res.setHeader('Content-Type', 'application/json'); + return res.status(200).send(returnObj); + }, updateBrew : async (req, res)=>{ // Initialize brew from request and body, destructure query params, and set the initial value for the after-save method const brewFromClient = api.excludePropsFromUpdate(req.body); @@ -369,5 +465,6 @@ router.put('/api/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api router.put('/api/update/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew)); router.delete('/api/:id', asyncHandler(api.deleteBrew)); router.get('/api/remove/:id', asyncHandler(api.deleteBrew)); +router.get('/api/theme/:renderer/:id', asyncHandler(api.getThemeBundle)); module.exports = api; diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index c8539bf63..5f1739b97 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -14,6 +14,9 @@ describe('Tests for api', ()=>{ let saved; beforeEach(()=>{ + jest.resetModules(); + jest.restoreAllMocks(); + saved = undefined; saveFunc = jest.fn(async function() { saved = { ...this, _id: '1' }; @@ -45,8 +48,9 @@ describe('Tests for api', ()=>{ model.mockImplementation((brew)=>modelBrew(brew)); res = { - status : jest.fn(()=>res), - send : jest.fn(()=>{}) + status : jest.fn(()=>res), + send : jest.fn(()=>{}), + setHeader : jest.fn(()=>{}) }; api = require('./homebrew.api'); @@ -81,10 +85,6 @@ describe('Tests for api', ()=>{ }; }); - afterEach(()=>{ - jest.restoreAllMocks(); - }); - describe('getId', ()=>{ it('should return only id if google id is not present', ()=>{ const { id, googleId } = api.getId({ @@ -408,8 +408,8 @@ brew`); expect(sent).not.toEqual(googleBrew); expect(result.text).toBeUndefined(); expect(result.textBin).toBeUndefined(); - expect(result.renderer).toBeUndefined(); - expect(result.pageCount).toBeUndefined(); + expect(result.renderer).toBe('v3'); + expect(result.pageCount).toBe(1); }); }); @@ -540,9 +540,9 @@ brew`); description : '', editId : expect.any(String), gDrive : false, - pageCount : undefined, + pageCount : 1, published : false, - renderer : undefined, + renderer : 'V3', lang : 'en', shareId : expect.any(String), googleId : expect.any(String), @@ -581,6 +581,121 @@ brew`); }); }); + describe('Theme bundle', ()=>{ + it('should return Theme Bundle for a User Theme', async ()=>{ + const brews = { + userThemeAID : { title: 'User Theme A', renderer: 'V3', theme: null, shareId: 'userThemeAID', style: 'User Theme A Style' } + }; + + const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew })); + model.get = jest.fn((getParams)=>toBrewPromise(brews[getParams.shareId])); + const req = { params: { renderer: 'V3', id: 'userThemeAID' }, get: ()=>{ return 'localhost'; }, protocol: 'https' }; + + await api.getThemeBundle(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.send).toHaveBeenCalledWith({ + styles : ['/* From Brew: https://localhost/share/userThemeAID */\n\nUser Theme A Style'], + snippets : [] + }); + }); + + it('should return Theme Bundle for nested User Themes', async ()=>{ + const brews = { + userThemeAID : { title: 'User Theme A', renderer: 'V3', theme: 'userThemeBID', shareId: 'userThemeAID', style: 'User Theme A Style' }, + userThemeBID : { title: 'User Theme B', renderer: 'V3', theme: 'userThemeCID', shareId: 'userThemeBID', style: 'User Theme B Style' }, + userThemeCID : { title: 'User Theme C', renderer: 'V3', theme: null, shareId: 'userThemeCID', style: 'User Theme C Style' } + }; + + const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew })); + model.get = jest.fn((getParams)=>toBrewPromise(brews[getParams.shareId])); + const req = { params: { renderer: 'V3', id: 'userThemeAID' }, get: ()=>{ return 'localhost'; }, protocol: 'https' }; + + await api.getThemeBundle(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.send).toHaveBeenCalledWith({ + styles : [ + '/* From Brew: https://localhost/share/userThemeCID */\n\nUser Theme C Style', + '/* From Brew: https://localhost/share/userThemeBID */\n\nUser Theme B Style', + '/* From Brew: https://localhost/share/userThemeAID */\n\nUser Theme A Style' + ], + snippets : [] + }); + }); + + it('should return Theme Bundle for a Static Theme', async ()=>{ + const req = { params: { renderer: 'V3', id: '5ePHB' }, get: ()=>{ return 'localhost'; }, protocol: 'https' }; + + await api.getThemeBundle(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.send).toHaveBeenCalledWith({ + styles : [ + `/* From Theme Blank */\n\n@import url("/themes/V3/Blank/style.css");`, + `/* From Theme 5ePHB */\n\n@import url("/themes/V3/5ePHB/style.css");` + ], + snippets : [ + 'V3_Blank', + 'V3_5ePHB' + ] + }); + }); + + it('should return Theme Bundle for nested User and Static Themes together', async ()=>{ + const brews = { + userThemeAID : { title: 'User Theme A', renderer: 'V3', theme: 'userThemeBID', shareId: 'userThemeAID', style: 'User Theme A Style' }, + userThemeBID : { title: 'User Theme B', renderer: 'V3', theme: 'userThemeCID', shareId: 'userThemeBID', style: 'User Theme B Style' }, + userThemeCID : { title: 'User Theme C', renderer: 'V3', theme: '5eDMG', shareId: 'userThemeCID', style: 'User Theme C Style' } + }; + + const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew })); + model.get = jest.fn((getParams)=>toBrewPromise(brews[getParams.shareId])); + const req = { params: { renderer: 'V3', id: 'userThemeAID' }, get: ()=>{ return 'localhost'; }, protocol: 'https' }; + + await api.getThemeBundle(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.send).toHaveBeenCalledWith({ + styles : [ + `/* From Theme Blank */\n\n@import url("/themes/V3/Blank/style.css");`, + `/* From Theme 5ePHB */\n\n@import url("/themes/V3/5ePHB/style.css");`, + `/* From Theme 5eDMG */\n\n@import url("/themes/V3/5eDMG/style.css");`, + '/* From Brew: https://localhost/share/userThemeCID */\n\nUser Theme C Style', + '/* From Brew: https://localhost/share/userThemeBID */\n\nUser Theme B Style', + '/* From Brew: https://localhost/share/userThemeAID */\n\nUser Theme A Style' + ], + snippets : [ + 'V3_Blank', + 'V3_5ePHB', + 'V3_5eDMG' + ] + }); + }); + + it('should fail for an invalid Theme in the chain', async()=>{ + const brews = { + userThemeAID : { title: 'User Theme A', renderer: 'V3', theme: 'missingTheme', shareId: 'userThemeAID', style: 'User Theme A Style' }, + }; + + const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew })); + model.get = jest.fn((getParams)=>toBrewPromise(brews[getParams.shareId])); + const req = { params: { renderer: 'V3', id: 'userThemeAID' }, get: ()=>{ return 'localhost'; }, protocol: 'https' }; + + let err; + await api.getThemeBundle(req, res) + .catch((e)=>err = e); + + expect(err).toEqual({ + HBErrorCode : '09', + accessType : 'share', + brewId : 'missingTheme', + message : 'Theme Not Found', + name : 'ThemeLoad Error', + status : 404 }); + }); + }); + describe('deleteBrew', ()=>{ it('should handle case where fetching the brew returns an error', async ()=>{ api.getBrew = jest.fn(()=>async ()=>{ throw { message: 'err', HBErrorCode: '02' }; }); diff --git a/server/homebrew.model.js b/server/homebrew.model.js index 36c9aa192..c8db8fdcc 100644 --- a/server/homebrew.model.js +++ b/server/homebrew.model.js @@ -50,8 +50,8 @@ HomebrewSchema.statics.get = async function(query, fields=null){ return brew; }; -HomebrewSchema.statics.getByUser = async function(username, allowAccess=false, fields=null){ - const query = { authors: username, published: true }; +HomebrewSchema.statics.getByUser = async function(username, allowAccess=false, fields=null, filter=null){ + const query = { authors: username, published: true, ...filter }; if(allowAccess){ delete query.published; } diff --git a/shared/helpers.js b/shared/helpers.js index 8ca185046..e5c1b7769 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -1,5 +1,6 @@ const _ = require('lodash'); const yaml = require('js-yaml'); +const request = require('../client/homebrew/utils/request-middleware.js'); const splitTextStyleAndMetadata = (brew)=>{ brew.text = brew.text.replaceAll('\r\n', '\n'); @@ -15,6 +16,11 @@ const splitTextStyleAndMetadata = (brew)=>{ brew.style = brew.text.slice(7, index - 1); brew.text = brew.text.slice(index + 5); } + if(brew.text.startsWith('```snippets')) { + const index = brew.text.indexOf('```\n\n'); + brew.snippets = brew.text.slice(11, index - 1); + brew.text = brew.text.slice(index + 5); + } }; const printCurrentBrew = ()=>{ @@ -28,7 +34,24 @@ const printCurrentBrew = ()=>{ } }; +const fetchThemeBundle = async (obj, renderer, theme)=>{ + const res = await request + .get(`/api/theme/${renderer}/${theme}`) + .catch((err)=>{ + obj.setState({ error: err }); + }); + if(!res) return; + + const themeBundle = res.body; + themeBundle.joinedStyles = themeBundle.styles.map((style)=>``).join('\n\n'); + obj.setState((prevState)=>({ + ...prevState, + themeBundle : themeBundle + })); +}; + module.exports = { splitTextStyleAndMetadata, - printCurrentBrew + printCurrentBrew, + fetchThemeBundle, }; diff --git a/themes/V3/5ePHB/settings.json b/themes/V3/5ePHB/settings.json index 499096a05..53329ce4a 100644 --- a/themes/V3/5ePHB/settings.json +++ b/themes/V3/5ePHB/settings.json @@ -1,6 +1,6 @@ { "name" : "5e PHB", "renderer" : "V3", - "baseTheme" : false, + "baseTheme" : "Blank", "baseSnippets" : false } diff --git a/themes/V3/5ePHB/snippets.js b/themes/V3/5ePHB/snippets.js index d5f37ac65..4daa05c51 100644 --- a/themes/V3/5ePHB/snippets.js +++ b/themes/V3/5ePHB/snippets.js @@ -349,7 +349,7 @@ module.exports = [ /* Ink Friendly */ *:is(.page,.monster,.note,.descriptive) { background : white !important; - filter : drop-shadow(0px 0px 3px #888) !important; + box-shadow : 1px 4px 14px #888 !important; } .page img { diff --git a/themes/V3/5ePHB/snippets/tableOfContents.gen.js b/themes/V3/5ePHB/snippets/tableOfContents.gen.js index b212dea36..3aea01735 100644 --- a/themes/V3/5ePHB/snippets/tableOfContents.gen.js +++ b/themes/V3/5ePHB/snippets/tableOfContents.gen.js @@ -35,7 +35,7 @@ const getTOC = (pages)=>{ const ToCExclude = getComputedStyle(heading).getPropertyValue('--TOC'); if(ToCExclude != 'exclude') { - recursiveAdd(heading.innerText.trim(), onPage, headerDepth.indexOf(heading.tagName), res); + recursiveAdd(heading.textContent.trim(), onPage, headerDepth.indexOf(heading.tagName), res); } }); return res; diff --git a/themes/V3/5ePHB/style.less b/themes/V3/5ePHB/style.less index f8a14f46e..d5511f049 100644 --- a/themes/V3/5ePHB/style.less +++ b/themes/V3/5ePHB/style.less @@ -382,6 +382,14 @@ .useColumns(0.96, @fillMode: balance); } + //only for IOS devices + @supports (-webkit-touch-callout: none) { + .page .monster.frame { + background-repeat : no-repeat; + background-size : cover; + } + } + // ***************************** // * FOOTER // *****************************/ @@ -459,6 +467,7 @@ margin-left : 1.5em; } } + // ***************************** // * SPELL LIST // *****************************/ @@ -907,6 +916,10 @@ h6, .page h1 + * { margin-top : 0; } +.page .descriptive.wide + * { + margin-top: 0; +} + //***************************** // * RUNE TABLE // *****************************/ diff --git a/themes/V3/Journal/settings.json b/themes/V3/Journal/settings.json index 069bdb270..74700cc8c 100644 --- a/themes/V3/Journal/settings.json +++ b/themes/V3/Journal/settings.json @@ -1,6 +1,6 @@ { "name" : "Journal", "renderer" : "V3", - "baseTheme" : false, + "baseTheme" : "Blank", "baseSnippets" : "5ePHB" } diff --git a/themes/assets/assets.less b/themes/assets/assets.less index cdef32c7c..f880bce77 100644 --- a/themes/assets/assets.less +++ b/themes/assets/assets.less @@ -7,6 +7,7 @@ @noteBorderImage : url('/assets/noteBorder.png'); @descriptiveBoxImage : url('/assets/descriptiveBorder.png'); @monsterBlockBackground : url('/assets/parchmentBackgroundGrayscale.jpg'); +@monsterBlockOverlay : url('/assets/parchmentBackgroundOverlayed.jpg'); @monsterBorderImage : url('/assets/monsterBorderFancy.png'); @codeBorderImage : url('/assets/codeBorder.png'); @classTableDecoration : url('/assets/classTableDecoration.png'); diff --git a/themes/themes.json b/themes/themes.json index 0d28c7394..16a4b9b13 100644 --- a/themes/themes.json +++ b/themes/themes.json @@ -18,7 +18,7 @@ "5ePHB": { "name": "5e PHB", "renderer": "V3", - "baseTheme": false, + "baseTheme": "Blank", "baseSnippets": false, "path": "5ePHB" }, @@ -32,7 +32,7 @@ "Journal": { "name": "Journal", "renderer": "V3", - "baseTheme": false, + "baseTheme": "Blank", "baseSnippets": "5ePHB", "path": "Journal" }