diff --git a/.github/actions/limit-pull-requests/action.yml b/.github/actions/limit-pull-requests/action.yml new file mode 100644 index 000000000..413fa0624 --- /dev/null +++ b/.github/actions/limit-pull-requests/action.yml @@ -0,0 +1,103 @@ +name: Limit pull requests +description: > + Limit the number of open pull requests to the repository created by a user +author: ZhongRuoyu (from Homebrew repository) +branding: + icon: alert-triangle + color: yellow + +inputs: + token: + description: GitHub token + required: false + default: ${{ github.token }} + except-users: + description: The users exempted from the limit, one per line + required: false + # https://docs.github.com/en/graphql/reference/enums#commentauthorassociation + except-author-associations: + description: The author associations exempted from the limit, one per line + required: false + comment-limit: + description: > + Post the comment when the user's number of open pull requests exceeds this + number and `comment` is not empty + required: true + default: "10" + comment: + description: The comment to post when the limit is reached + required: false + close-limit: + description: > + Close the pull request when the user's number of open pull requests + exceeds this number and `close` is set to `true` + required: true + default: "50" + close: + description: Whether to close the pull request when the limit is reached + required: true + default: "false" + +runs: + using: composite + steps: + - name: Check the number of pull requests + id: count-pull-requests + run: | + # If the user is exempted, assume they have no pull requests. + if grep -Fiqx '${{ github.actor }}' <<<"$EXCEPT_USERS"; then + echo "::notice::@${{ github.actor }} is exempted from the limit." + echo "count=0" >>"$GITHUB_OUTPUT" + exit 0 + fi + if grep -Fiqx '${{ github.event.pull_request.author_association }}' <<<"$EXCEPT_AUTHOR_ASSOCIATIONS"; then + echo "::notice::@{{ github.actor }} is a ${{ github.event.pull_request.author_association }} exempted from the limit." + echo "count=0" >>"$GITHUB_OUTPUT" + exit 0 + fi + + count="$( + gh api \ + --method GET \ + --header 'Accept: application/vnd.github+json' \ + --header 'X-GitHub-Api-Version: 2022-11-28' \ + --field state=open \ + --paginate \ + '/repos/{owner}/{repo}/pulls' | + jq \ + --raw-output \ + --arg USER '${{ github.actor }}' \ + 'map(select(.user.login == $USER)) | length' + )" + echo "::notice::@${{ github.actor }} has $count open pull request(s)." + echo "count=$count" >>"$GITHUB_OUTPUT" + env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ inputs.token }} + EXCEPT_USERS: ${{ inputs.except-users }} + EXCEPT_AUTHOR_ASSOCIATIONS: ${{ inputs.except-author-associations }} + shell: bash + + - name: Comment on pull request + if: > + fromJSON(steps.count-pull-requests.outputs.count) > fromJSON(inputs.comment-limit) && + inputs.comment != '' + run: | + gh pr comment '${{ github.event.pull_request.number }}' \ + --body="${COMMENT_BODY}" + env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ inputs.token }} + COMMENT_BODY: ${{ inputs.comment }} + shell: bash + + - name: Close pull request + if: > + fromJSON(steps.count-pull-requests.outputs.count) > fromJSON(inputs.close-limit) && + inputs.close == 'true' + run: | + gh pr close '${{ github.event.pull_request.number }}' + env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ inputs.token }} + shell: bash diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml new file mode 100644 index 000000000..e5adb2561 --- /dev/null +++ b/.github/workflows/pr-check.yml @@ -0,0 +1,29 @@ +name: PR Check +on: pull_request_target +env: + GH_REPO: ${{ github.repository }} + GH_NO_UPDATE_NOTIFIER: 1 + GH_PROMPT_DISABLED: 1 +permissions: + contents: read + issues: write + pull-requests: write + statuses: write +jobs: + limit-pull-requests: + if: always() && github.repository_owner == 'naturalcrit' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name : Run limit-pull-requests action + uses: ./.github/actions/limit-pull-requests + with: + except-users: | + dependabot + comment-limit: 3 + comment: | + Hi, thanks for your contribution to the Homebrewery! You already have >=3 open pull requests. Consider completing some of your existing PRs before opening new ones. Thanks! + close-limit: 5 + close: false diff --git a/client/homebrew/brewRenderer/brewRenderer.less b/client/homebrew/brewRenderer/brewRenderer.less index edc8d503b..0406cad29 100644 --- a/client/homebrew/brewRenderer/brewRenderer.less +++ b/client/homebrew/brewRenderer/brewRenderer.less @@ -17,11 +17,24 @@ &::-webkit-scrollbar { width: 20px; + &:horizontal{ + height: 20px; + width:auto; + } + &-thumb { + background: linear-gradient(90deg, #d3c1af 15px, #00000000 15px); + &:horizontal{ + background: linear-gradient(0deg, #d3c1af 15px, #00000000 15px); + } + } + &-corner { + visibility: hidden; + } } - &::-webkit-scrollbar-thumb { - width:20px; - background: linear-gradient(90deg, #d3c1af 15px, #00000000 15px); - } + + + + } .pane { position : relative; } diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index a9efdb245..7cc800b78 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -81,7 +81,7 @@ const Editor = createClass({ updateEditorSize : function() { if(this.refs.codeEditor) { let paneHeight = this.refs.main.parentNode.clientHeight; - paneHeight -= SNIPPETBAR_HEIGHT + 1; + paneHeight -= SNIPPETBAR_HEIGHT; this.refs.codeEditor.codeMirror.setSize(null, paneHeight); } }, diff --git a/client/homebrew/editor/snippetbar/snippetbar.less b/client/homebrew/editor/snippetbar/snippetbar.less index be6ebe11a..e0a24fac9 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.less +++ b/client/homebrew/editor/snippetbar/snippetbar.less @@ -130,6 +130,8 @@ height : 1.2em; margin-right : 8px; font-size : 1.2em; + min-width: 25px; + text-align: center; & ~ i { margin-right : 0; margin-left : 5px; @@ -138,7 +140,7 @@ &.font { height : auto; &::before { - font-size : 1.4em; + font-size : 1em; content : 'ABC'; } diff --git a/client/homebrew/homebrew.jsx b/client/homebrew/homebrew.jsx index 257128766..90e8ad717 100644 --- a/client/homebrew/homebrew.jsx +++ b/client/homebrew/homebrew.jsx @@ -78,7 +78,7 @@ const Homebrew = createClass({ }/> } /> } /> - } /> + } /> } /> } /> } /> diff --git a/client/homebrew/homebrew.less b/client/homebrew/homebrew.less index ed86a8c28..828de796f 100644 --- a/client/homebrew/homebrew.less +++ b/client/homebrew/homebrew.less @@ -18,10 +18,19 @@ &::-webkit-scrollbar { width: 20px; - } - &::-webkit-scrollbar-thumb { - width:20px; - background: linear-gradient(90deg, #d3c1af 15px, #00000000 15px); + &:horizontal{ + height: 20px; + width:auto; + } + &-thumb { + background: linear-gradient(90deg, #d3c1af 15px, #00000000 15px); + &:horizontal{ + background: linear-gradient(0deg, #d3c1af 15px, #00000000 15px); + } + } + &-corner { + visibility: hidden; + } } } } diff --git a/client/homebrew/pages/accountPage/accountPage.jsx b/client/homebrew/pages/accountPage/accountPage.jsx index d08832427..598683504 100644 --- a/client/homebrew/pages/accountPage/accountPage.jsx +++ b/client/homebrew/pages/accountPage/accountPage.jsx @@ -1,102 +1,82 @@ -const React = require('react'); -const createClass = require('create-react-class'); -const _ = require('lodash'); -const cx = require('classnames'); +const React = require('react'); const moment = require('moment'); - const UIPage = require('../basePages/uiPage/uiPage.jsx'); - -const Nav = require('naturalcrit/nav/nav.jsx'); -const Navbar = require('../../navbar/navbar.jsx'); - -const RecentNavItem = require('../../navbar/recent.navitem.jsx').both; -const Account = require('../../navbar/account.navitem.jsx'); -const NewBrew = require('../../navbar/newbrew.navitem.jsx'); -const HelpNavItem = require('../../navbar/help.navitem.jsx'); - const NaturalCritIcon = require('naturalcrit/svg/naturalcrit.svg.jsx'); let SAVEKEY = ''; -const AccountPage = createClass({ - displayName : 'AccountPage', - getDefaultProps : function() { - return { - brew : {}, - uiItems : {} - }; - }, - getInitialState : function() { - return { - uiItems : this.props.uiItems - }; - }, - componentDidMount : function(){ - if(!this.state.saveLocation && this.props.uiItems.username) { - SAVEKEY = `HOMEBREWERY-DEFAULT-SAVE-LOCATION-${this.props.uiItems.username}`; - let saveLocation = window.localStorage.getItem(SAVEKEY); - saveLocation = saveLocation ?? (this.state.uiItems.googleId ? 'GOOGLE-DRIVE' : 'HOMEBREWERY'); - this.makeActive(saveLocation); +const AccountPage = (props)=>{ + // destructure props and set state for save location + const { accountDetails, brew } = props; + const [saveLocation, setSaveLocation] = React.useState(''); + + // initialize save location from local storage based on user id + React.useEffect(()=>{ + if(!saveLocation && accountDetails.username) { + SAVEKEY = `HOMEBREWERY-DEFAULT-SAVE-LOCATION-${accountDetails.username}`; + // if no SAVEKEY in local storage, default save location to Google Drive if user has Google account. + let saveLocation = window.localStorage.getItem(SAVEKEY); + saveLocation = saveLocation ?? (accountDetails.googleId ? 'GOOGLE-DRIVE' : 'HOMEBREWERY'); + setActiveSaveLocation(saveLocation); } - }, + }, []); - makeActive : function(newSelection){ - if(this.state.saveLocation == newSelection) return; + const setActiveSaveLocation = (newSelection)=>{ + if(saveLocation === newSelection) return; window.localStorage.setItem(SAVEKEY, newSelection); - this.setState({ - saveLocation : newSelection - }); - }, + setSaveLocation(newSelection); + }; - renderButton : function(name, key, shouldRender=true){ - if(!shouldRender) return; - return ; - }, + // todo: should this be a set of radio buttons (well styled) since it's either/or choice? + const renderSaveLocationButton = (name, key, shouldRender = true)=>{ + if(!shouldRender) return null; + return ( + + ); + }; - renderNavItems : function() { - return - - - - - - - ; - }, + // render the entirety of the account page content + const renderAccountPage = ()=>{ + return ( + <> +
+

Account Information

+

Username: {accountDetails.username || 'No user currently logged in'}

+

Last Login: {moment(accountDetails.issued).format('dddd, MMMM Do YYYY, h:mm:ss a ZZ') || '-'}

+
+
+

Homebrewery Information

+

Brews on Homebrewery: {accountDetails.mongoCount}

+
+
+

Google Information

+

Linked to Google: {accountDetails.googleId ? 'YES' : 'NO'}

+ {accountDetails.googleId && ( +

+ Brews on Google Drive: {accountDetails.googleCount ?? ( + <> + Unable to retrieve files - follow these steps to renew your Google credentials. + + )} +

+ )} +
+
+

Default Save Location

+ {renderSaveLocationButton('Homebrewery', 'HOMEBREWERY')} + {renderSaveLocationButton('Google Drive', 'GOOGLE-DRIVE', accountDetails.googleId)} +
+ + ); + }; - renderUiItems : function() { - return <> -
-

Account Information

-

Username: {this.props.uiItems.username || 'No user currently logged in'}

-

Last Login: {moment(this.props.uiItems.issued).format('dddd, MMMM Do YYYY, h:mm:ss a ZZ') || '-'}

-
-
-

Homebrewery Information

-

Brews on Homebrewery: {this.props.uiItems.mongoCount}

-
-
-

Google Information

-

Linked to Google: {this.props.uiItems.googleId ? 'YES' : 'NO'}

- {this.props.uiItems.googleId && -

- Brews on Google Drive: {this.props.uiItems.googleCount ?? <>Unable to retrieve files - follow these steps to renew your Google credentials.} -

- } -
-
-

Default Save Location

- {this.renderButton('Homebrewery', 'HOMEBREWERY')} - {this.renderButton('Google Drive', 'GOOGLE-DRIVE', this.state.uiItems.googleId)} -
- ; - }, - - render : function(){ - return - {this.renderUiItems()} - ; - } -}); + // return the account page inside the base layout wrapper (with navbar etc). + return ( + + {renderAccountPage()} + ); +}; module.exports = AccountPage; diff --git a/client/homebrew/pages/errorPage/errorPage.jsx b/client/homebrew/pages/errorPage/errorPage.jsx index 33da05017..387a99b02 100644 --- a/client/homebrew/pages/errorPage/errorPage.jsx +++ b/client/homebrew/pages/errorPage/errorPage.jsx @@ -1,41 +1,25 @@ require('./errorPage.less'); -const React = require('react'); -const createClass = require('create-react-class'); -const _ = require('lodash'); -const cx = require('classnames'); - -const UIPage = require('../basePages/uiPage/uiPage.jsx'); - -const Markdown = require('../../../../shared/naturalcrit/markdown.js'); - +const React = require('react'); +const UIPage = require('../basePages/uiPage/uiPage.jsx'); +const Markdown = require('../../../../shared/naturalcrit/markdown.js'); const ErrorIndex = require('./errors/errorIndex.js'); -const ErrorPage = createClass({ - displayName : 'ErrorPage', +const ErrorPage = ({ brew })=>{ + // Retrieving the error text based on the brew's error code from ErrorIndex + const errorText = ErrorIndex({ brew })[brew.HBErrorCode.toString()] || ''; - getDefaultProps : function() { - return { - ver : '0.0.0', - errorId : '', - text : '# Oops \n We could not find a brew with that id. **Sorry!**', - error : {} - }; - }, - - render : function(){ - const errorText = ErrorIndex(this.props)[this.props.brew.HBErrorCode.toString()] || ''; - - return + return ( +
-

{`Error ${this.props.brew.status || '000'}`}

-

{this.props.brew.text || 'No error text'}

+

{`Error ${brew?.status || '000'}`}

+

{brew?.text || 'No error text'}


- ; - } -}); + + ); +}; module.exports = ErrorPage; diff --git a/client/homebrew/pages/errorPage/errors/errorIndex.js b/client/homebrew/pages/errorPage/errors/errorIndex.js index 7c7a3ae7f..f9d52c109 100644 --- a/client/homebrew/pages/errorPage/errors/errorIndex.js +++ b/client/homebrew/pages/errorPage/errors/errorIndex.js @@ -73,9 +73,11 @@ const errorIndex = (props)=>{ **Properties** tab, and adding your username to the "invited authors" list. You can then try to access this document again. + : + **Brew Title:** ${props.brew.brewTitle || 'Unable to show title'} - **Current Authors:** ${props.brew.authors?.map((author)=>{return `${author}`;}).join(', ') || 'Unable to list authors'} + **Current Authors:** ${props.brew.authors?.map((author)=>{return `[${author}](/user/${author})`;}).join(', ') || 'Unable to list authors'} [Click here to be redirected to the brew's share page.](/share/${props.brew.shareId})`, @@ -86,9 +88,14 @@ const errorIndex = (props)=>{ You must be logged in to one of the accounts listed as an author of this brew. User is not logged in. Please log in [here](${loginUrl}). + : + **Brew Title:** ${props.brew.brewTitle || 'Unable to show title'} - **Current Authors:** ${props.brew.authors?.map((author)=>{return `${author}`;}).join(', ') || 'Unable to list authors'}`, + **Current Authors:** ${props.brew.authors?.map((author)=>{return `[${author}](/user/${author})`;}).join(', ') || 'Unable to list authors'} + + [Click here to be redirected to the brew's share page.](/share/${props.brew.shareId})`, + // Brew load error '05' : dedent` @@ -97,6 +104,8 @@ const errorIndex = (props)=>{ 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}`, @@ -113,6 +122,8 @@ const errorIndex = (props)=>{ An error occurred while attempting to remove the Homebrewery document. + : + **Brew ID:** ${props.brew.brewId}`, // Author delete error @@ -121,6 +132,8 @@ const errorIndex = (props)=>{ An error occurred while attempting to remove the user from the Homebrewery document author list! + : + **Brew ID:** ${props.brew.brewId}`, // Brew locked by Administrators error @@ -129,6 +142,8 @@ const errorIndex = (props)=>{ Please contact the Administrators to unlock this document. + : + **Brew ID:** ${props.brew.brewId} **Brew Title:** ${props.brew.brewTitle}`, diff --git a/client/homebrew/pages/sharePage/sharePage.jsx b/client/homebrew/pages/sharePage/sharePage.jsx index 981ad0126..b2f9a2068 100644 --- a/client/homebrew/pages/sharePage/sharePage.jsx +++ b/client/homebrew/pages/sharePage/sharePage.jsx @@ -47,6 +47,19 @@ const SharePage = createClass({ this.props.brew.shareId; }, + renderEditLink : function(){ + if(!this.props.brew.editId) return; + + let editLink = this.props.brew.editId; + if(this.props.brew.googleId && !this.props.brew.stubbed) { + editLink = this.props.brew.googleId + editLink; + } + + return + edit + ; + }, + render : function(){ return
@@ -64,13 +77,14 @@ const SharePage = createClass({ source - + view - + {this.renderEditLink()} + download - + clone to new diff --git a/package-lock.json b/package-lock.json index e2e0ca109..79f4e34f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,11 +10,11 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.24.3", + "@babel/core": "^7.24.5", "@babel/plugin-transform-runtime": "^7.24.3", - "@babel/preset-env": "^7.24.3", + "@babel/preset-env": "^7.24.5", "@babel/preset-react": "^7.24.1", - "@googleapis/drive": "^8.7.0", + "@googleapis/drive": "^8.8.0", "body-parser": "^1.20.2", "classnames": "^2.5.1", "codemirror": "^5.65.6", @@ -31,25 +31,26 @@ "less": "^3.13.1", "lodash": "^4.17.21", "marked": "11.2.0", + "marked-emoji": "^1.4.0", "marked-extended-tables": "^1.0.8", "marked-gfm-heading-id": "^3.1.3", "marked-smartypants-lite": "^1.0.2", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^8.2.3", + "mongoose": "^8.3.3", "nanoid": "3.3.4", "nconf": "^0.12.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-frame-component": "^4.1.3", - "react-router-dom": "6.22.3", + "react-router-dom": "6.23.0", "sanitize-filename": "1.6.3", - "superagent": "^8.1.2", + "superagent": "^9.0.2", "vitreum": "git+https://git@github.com/calculuschild/vitreum.git" }, "devDependencies": { "eslint": "^8.57.0", - "eslint-plugin-jest": "^27.9.0", + "eslint-plugin-jest": "^28.5.0", "eslint-plugin-react": "^7.34.1", "jest": "^29.7.0", "jest-expect-message": "^1.1.3", @@ -58,7 +59,7 @@ "stylelint-config-recess-order": "^4.6.0", "stylelint-config-recommended": "^13.0.0", "stylelint-stylistic": "^0.4.3", - "supertest": "^6.3.4" + "supertest": "^7.0.0" }, "engines": { "node": "^20.8.x", @@ -99,28 +100,28 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz", - "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", + "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.3.tgz", - "integrity": "sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz", + "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.1", + "@babel/generator": "^7.24.5", "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.1", - "@babel/parser": "^7.24.1", + "@babel/helper-module-transforms": "^7.24.5", + "@babel/helpers": "^7.24.5", + "@babel/parser": "^7.24.5", "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.1", - "@babel/types": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -141,11 +142,11 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/@babel/generator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz", - "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", + "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", "dependencies": { - "@babel/types": "^7.24.0", + "@babel/types": "^7.24.5", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -205,18 +206,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.1.tgz", - "integrity": "sha512-1yJa9dX9g//V6fDebXoEfEsxkZHk3Hcbm+zLhyu6qVgYFLvmTALTeV+jNU9e5RnYtioBrGEOdoI2joMSNQ/+aA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.5.tgz", + "integrity": "sha512-uRc4Cv8UQWnE4NXlYTIIdM7wfFkOqlFztcC/gVXDKohKoVB3OyonfelUBaJzSwpBntZ2KYGF/9S7asCHsXwW6g==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.24.5", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-replace-supers": "^7.24.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-split-export-declaration": "^7.24.5", "semver": "^6.3.1" }, "engines": { @@ -289,11 +290,11 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.5.tgz", + "integrity": "sha512-4owRteeihKWKamtqg4JmWSsEZU445xpFRXPEwp44HbgbxdWlUV1b4Agg4lkA806Lil5XM/e+FJyS0vj5T6vmcA==", "dependencies": { - "@babel/types": "^7.23.0" + "@babel/types": "^7.24.5" }, "engines": { "node": ">=6.9.0" @@ -311,15 +312,15 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz", + "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==", "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-module-imports": "^7.24.3", + "@babel/helper-simple-access": "^7.24.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.5" }, "engines": { "node": ">=6.9.0" @@ -340,9 +341,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", - "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz", + "integrity": "sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==", "engines": { "node": ">=6.9.0" } @@ -380,11 +381,11 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz", + "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.5" }, "engines": { "node": ">=6.9.0" @@ -402,28 +403,28 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", + "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", "engines": { "node": ">=6.9.0" } @@ -450,13 +451,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.1.tgz", - "integrity": "sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz", + "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==", "dependencies": { "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.1", - "@babel/types": "^7.24.0" + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5" }, "engines": { "node": ">=6.9.0" @@ -477,9 +478,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", - "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -487,6 +488,21 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.5.tgz", + "integrity": "sha512-LdXRi1wEMTrHVR4Zc9F8OewC3vdm5h4QB6L71zy6StmYeqGi1b3ttIO8UC+BfZKcH9jdr4aI249rBkm+3+YvHw==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.24.1", "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.1.tgz", @@ -863,11 +879,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.1.tgz", - "integrity": "sha512-h71T2QQvDgM2SmT29UYU6ozjMlAt7s7CSs5Hvy8f8cf/GM/Z4a2zMfN+fjVGaieeCrXR3EdQl6C4gQG+OgmbKw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.5.tgz", + "integrity": "sha512-sMfBc3OxghjC95BkYrYocHL3NaOplrcaunblzwXhGmlPwpmfsxr4vK+mBBt49r+S240vahmv+kUxkeKgs+haCw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.5" }, "engines": { "node": ">=6.9.0" @@ -892,11 +908,11 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.1.tgz", - "integrity": "sha512-FUHlKCn6J3ERiu8Dv+4eoz7w8+kFLSyeVG4vDAikwADGjUCoHw/JHokyGtr8OR4UjpwPVivyF+h8Q5iv/JmrtA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.4.tgz", + "integrity": "sha512-B8q7Pz870Hz/q9UgP8InNpY01CSLDSCyqX7zcRuv3FcPl87A2G17lASroHWaCtbdIcbYzOZ7kWmXFKbijMSmFg==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-create-class-features-plugin": "^7.24.4", "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, @@ -908,17 +924,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.1.tgz", - "integrity": "sha512-ZTIe3W7UejJd3/3R4p7ScyyOoafetUShSf4kCqV0O7F/RiHxVj/wRaRnQlrGwflvcehNA8M42HkAiEDYZu2F1Q==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.5.tgz", + "integrity": "sha512-gWkLP25DFj2dwe9Ck8uwMOpko4YsqyfZJrOmqqcegeDYEbp7rmn4U6UQZNj08UF6MaX39XenSpKRCvpDRBtZ7Q==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.5", "@babel/helper-replace-supers": "^7.24.1", - "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-split-export-declaration": "^7.24.5", "globals": "^11.1.0" }, "engines": { @@ -944,11 +960,11 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.1.tgz", - "integrity": "sha512-ow8jciWqNxR3RYbSNVuF4U2Jx130nwnBnhRw6N6h1bOejNkABmcI5X5oz29K4alWX7vf1C+o6gtKXikzRKkVdw==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.5.tgz", + "integrity": "sha512-SZuuLyfxvsm+Ah57I/i1HVjveBENYK9ue8MJ7qkc7ndoNjqquJiElzA7f5yaAXjyW2hKojosOTAQQRX50bPSVg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.5" }, "engines": { "node": ">=6.9.0" @@ -1243,14 +1259,14 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.1.tgz", - "integrity": "sha512-XjD5f0YqOtebto4HGISLNfiNMTTs6tbkFf2TOqJlYKYmbo+mN9Dnpl4SRoofiziuOWMIyq3sZEUqLo3hLITFEA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.5.tgz", + "integrity": "sha512-7EauQHszLGM3ay7a161tTQH7fj+3vVM/gThlz5HpFtnygTxjrlvoeq7MPVA1Vy9Q555OB8SnAOsMkLShNkkrHA==", "dependencies": { "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.5", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.1" + "@babel/plugin-transform-parameters": "^7.24.5" }, "engines": { "node": ">=6.9.0" @@ -1290,11 +1306,11 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.1.tgz", - "integrity": "sha512-n03wmDt+987qXwAgcBlnUUivrZBPZ8z1plL0YvgQalLm+ZE5BMhGm94jhxXtA1wzv1Cu2aaOv1BM9vbVttrzSg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.5.tgz", + "integrity": "sha512-xWCkmwKT+ihmA6l7SSTpk8e4qQl/274iNbSKRRS8mpqFR32ksy36+a+LWY8OXCCEefF8WFlnOHVsaDI2231wBg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, @@ -1306,11 +1322,11 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.1.tgz", - "integrity": "sha512-8Jl6V24g+Uw5OGPeWNKrKqXPDw2YDjLc53ojwfMcKwlEoETKU9rU0mHUtcg9JntWI/QYzGAXNWEcVHZ+fR+XXg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.5.tgz", + "integrity": "sha512-9Co00MqZ2aoky+4j2jhofErthm6QVLKbpQrvz20c3CH9KQCLHyNB+t2ya4/UrRpQGR+Wrwjg9foopoeSdnHOkA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.5" }, "engines": { "node": ">=6.9.0" @@ -1335,13 +1351,13 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.1.tgz", - "integrity": "sha512-pTHxDVa0BpUbvAgX3Gat+7cSciXqUcY9j2VZKTbSB6+VQGpNgNO9ailxTGHSXlqOnX1Hcx1Enme2+yv7VqP9bg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.5.tgz", + "integrity": "sha512-JM4MHZqnWR04jPMujQDTBVRnqxpLLpx2tkn7iPn+Hmsc0Gnb79yvRWOkvqFOx3Z7P7VxiRIR22c4eGSNj87OBQ==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-create-class-features-plugin": "^7.24.5", + "@babel/helper-plugin-utils": "^7.24.5", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { @@ -1532,11 +1548,11 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.1.tgz", - "integrity": "sha512-CBfU4l/A+KruSUoW+vTQthwcAdwuqbpRNB8HQKlZABwHRhsdHZ9fezp4Sn18PeAlYxTNiLMlx4xUBV3AWfg1BA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.5.tgz", + "integrity": "sha512-UTGnhYVZtTAjdwOTzT+sCyXmTn8AhaxOS/MjG9REclZ6ULHWF9KoCZur0HSGU7hk8PdBFKKbYe6+gqdXWz84Jg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.24.5" }, "engines": { "node": ">=6.9.0" @@ -1605,14 +1621,15 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.3.tgz", - "integrity": "sha512-fSk430k5c2ff8536JcPvPWK4tZDwehWLGlBp0wrsBUjZVdeQV6lePbwKWZaZfK2vnh/1kQX1PzAJWsnBmVgGJA==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.5.tgz", + "integrity": "sha512-UGK2ifKtcC8i5AI4cH+sbLLuLc2ktYSFJgBAXorKAsHUZmrQ1q6aQ6i3BvU24wWs2AAKqQB6kq3N9V9Gw1HiMQ==", "dependencies": { - "@babel/compat-data": "^7.24.1", + "@babel/compat-data": "^7.24.4", "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.24.5", "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.5", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1", @@ -1639,12 +1656,12 @@ "@babel/plugin-transform-async-generator-functions": "^7.24.3", "@babel/plugin-transform-async-to-generator": "^7.24.1", "@babel/plugin-transform-block-scoped-functions": "^7.24.1", - "@babel/plugin-transform-block-scoping": "^7.24.1", + "@babel/plugin-transform-block-scoping": "^7.24.5", "@babel/plugin-transform-class-properties": "^7.24.1", - "@babel/plugin-transform-class-static-block": "^7.24.1", - "@babel/plugin-transform-classes": "^7.24.1", + "@babel/plugin-transform-class-static-block": "^7.24.4", + "@babel/plugin-transform-classes": "^7.24.5", "@babel/plugin-transform-computed-properties": "^7.24.1", - "@babel/plugin-transform-destructuring": "^7.24.1", + "@babel/plugin-transform-destructuring": "^7.24.5", "@babel/plugin-transform-dotall-regex": "^7.24.1", "@babel/plugin-transform-duplicate-keys": "^7.24.1", "@babel/plugin-transform-dynamic-import": "^7.24.1", @@ -1664,13 +1681,13 @@ "@babel/plugin-transform-new-target": "^7.24.1", "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1", "@babel/plugin-transform-numeric-separator": "^7.24.1", - "@babel/plugin-transform-object-rest-spread": "^7.24.1", + "@babel/plugin-transform-object-rest-spread": "^7.24.5", "@babel/plugin-transform-object-super": "^7.24.1", "@babel/plugin-transform-optional-catch-binding": "^7.24.1", - "@babel/plugin-transform-optional-chaining": "^7.24.1", - "@babel/plugin-transform-parameters": "^7.24.1", + "@babel/plugin-transform-optional-chaining": "^7.24.5", + "@babel/plugin-transform-parameters": "^7.24.5", "@babel/plugin-transform-private-methods": "^7.24.1", - "@babel/plugin-transform-private-property-in-object": "^7.24.1", + "@babel/plugin-transform-private-property-in-object": "^7.24.5", "@babel/plugin-transform-property-literals": "^7.24.1", "@babel/plugin-transform-regenerator": "^7.24.1", "@babel/plugin-transform-reserved-words": "^7.24.1", @@ -1678,7 +1695,7 @@ "@babel/plugin-transform-spread": "^7.24.1", "@babel/plugin-transform-sticky-regex": "^7.24.1", "@babel/plugin-transform-template-literals": "^7.24.1", - "@babel/plugin-transform-typeof-symbol": "^7.24.1", + "@babel/plugin-transform-typeof-symbol": "^7.24.5", "@babel/plugin-transform-unicode-escapes": "^7.24.1", "@babel/plugin-transform-unicode-property-regex": "^7.24.1", "@babel/plugin-transform-unicode-regex": "^7.24.1", @@ -1759,18 +1776,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", - "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz", + "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==", "dependencies": { - "@babel/code-frame": "^7.24.1", - "@babel/generator": "^7.24.1", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.24.1", - "@babel/types": "^7.24.0", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/types": "^7.24.5", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1779,12 +1796,12 @@ } }, "node_modules/@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1884,9 +1901,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz", - "integrity": "sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, "dependencies": { "eslint-visitor-keys": "^3.3.0" @@ -1967,9 +1984,9 @@ } }, "node_modules/@googleapis/drive": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.7.0.tgz", - "integrity": "sha512-XAi6kfySIU4H3ivX2DpzTDce5UhNke5NxEWCL6tySEdcVqx+cmXJmkMqwfOAHJalEB5s9PPfdLBU29Xd5XlLSQ==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.8.0.tgz", + "integrity": "sha512-EOZ9GZCOUdej9PJVnkai7qu5RPyFLYse8FlpgijzfnZPOACXWFf4XOFuAuMcMw4Zue8xPhAPHu1qYcy8u362Xw==", "dependencies": { "googleapis-common": "^7.0.0" }, @@ -2795,9 +2812,9 @@ } }, "node_modules/@mongodb-js/saslprep": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.4.tgz", - "integrity": "sha512-8zJ8N1x51xo9hwPh6AWnKdLGEC5N3lDa6kms1YHmFBoRhTpJR6HG8wWk0td1MVCu9cD4YBrvjZEtd5Obw0Fbnw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.5.tgz", + "integrity": "sha512-XLNOMH66KhJzUJNwT/qlMnS4WsNDWD5ASdyaSH3EtK+F4r/CFGa3jT4GNi4mfOitGvWXtdLgQJkQjxSVrio+jA==", "dependencies": { "sparse-bitfield": "^3.0.3" } @@ -2838,9 +2855,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", - "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.0.tgz", + "integrity": "sha512-Quz1KOffeEf/zwkCBM3kBtH4ZoZ+pT3xIXBG4PPW/XFtDP7EGhtTiC2+gpL9GnR7+Qdet5Oa6cYSvwKYg6kN9Q==", "engines": { "node": ">=14.0.0" } @@ -2944,9 +2961,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/minimist": { @@ -2968,9 +2985,9 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/stack-utils": { @@ -3008,16 +3025,16 @@ "dev": true }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.58.0.tgz", - "integrity": "sha512-b+w8ypN5CFvrXWQb9Ow9T4/6LC2MikNf1viLkYTiTbkQl46CnR69w7lajz1icW0TBsYmlpg+mRzFJ4LEJ8X9NA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.58.0", - "@typescript-eslint/visitor-keys": "5.58.0" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -3025,12 +3042,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.58.0.tgz", - "integrity": "sha512-JYV4eITHPzVQMnHZcYJXl2ZloC7thuUHrcUmxtzvItyKPvQ50kb9QXBkgNAt90OYMqwaodQh2kHutWZl1fc+1g==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -3038,21 +3055,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.58.0.tgz", - "integrity": "sha512-cRACvGTodA+UxnYM2uwA2KCwRL7VAzo45syNysqlMyNyjw0Z35Icc9ihPJZjIYuA5bXJYiJ2YGUB59BqlOZT1Q==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.58.0", - "@typescript-eslint/visitor-keys": "5.58.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -3064,6 +3082,15 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -3076,10 +3103,25 @@ "node": ">=10" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -3098,51 +3140,28 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "5.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.58.0.tgz", - "integrity": "sha512-gAmLOTFXMXOC+zP1fsqm3VceKSBQJNzV385Ok3+yzlavNHZoedajjS4UyS21gabJYcobuigQPs/z71A9MdJFqQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.58.0", - "@typescript-eslint/types": "5.58.0", - "@typescript-eslint/typescript-estree": "5.58.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" + "eslint": "^7.0.0 || ^8.0.0" } }, "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { @@ -3158,9 +3177,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -3179,16 +3198,16 @@ "dev": true }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.58.0.tgz", - "integrity": "sha512-/fBraTlPj0jwdyTwLyrRTxv/3lnU2H96pNTVM6z3esTWLtA5MZ9ghSMJ7Rb+TtUAdtEw9EyJzJ0EydIMKxQ9gA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.58.0", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -4307,9 +4326,9 @@ } }, "node_modules/bson": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.2.0.tgz", - "integrity": "sha512-ID1cI+7bazPDyL9wYy9GaQ8gEEohWvcUl/Yf0dIdutJxnmInEEyCsb4awy/OiBfall7zBA179Pahi3vCdFze3Q==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.6.0.tgz", + "integrity": "sha512-BVINv2SgcMjL4oYbBuCQTpE3/VKOSxrOA8Cj/wQP7izSzlBGVomdm+TcUd0Pzy0ytLSSDweCKQ6X3f5veM5LQA==", "engines": { "node": ">=16.20.1" } @@ -5730,19 +5749,19 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "27.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz", - "integrity": "sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==", + "version": "28.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.5.0.tgz", + "integrity": "sha512-6np6DGdmNq/eBbA7HOUNV8fkfL86PYwBfwyb8n23FXgJNTR8+ot3smRHjza9LGsBBZRypK3qyF79vMjohIL8eQ==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "^5.10.0" + "@typescript-eslint/utils": "^6.0.0 || ^7.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^16.10.0 || ^18.12.0 || >=20.0.0" }, "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0 || ^7.0.0", - "eslint": "^7.0.0 || ^8.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0", + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", "jest": "*" }, "peerDependenciesMeta": { @@ -6498,14 +6517,13 @@ } }, "node_modules/formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.1.tgz", + "integrity": "sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og==", "dependencies": { "dezalgo": "^1.0.4", "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" + "once": "^1.4.0" }, "funding": { "url": "https://ko-fi.com/tunnckoCore/commissions" @@ -9820,9 +9838,9 @@ } }, "node_modules/kareem": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", - "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", "engines": { "node": ">=12.0.0" } @@ -10127,6 +10145,14 @@ "node": ">= 18" } }, + "node_modules/marked-emoji": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/marked-emoji/-/marked-emoji-1.4.0.tgz", + "integrity": "sha512-/2TJfGzXpiBBq+X3akHHbTrAjZPJDwR+7FV6SyQLECnQEfaoVkrpKZJzHhPTAq3Sl/A1l2frMT0u6b38VBBlNg==", + "peerDependencies": { + "marked": ">=4 <13" + } + }, "node_modules/marked-extended-tables": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/marked-extended-tables/-/marked-extended-tables-1.0.8.tgz", @@ -10529,13 +10555,13 @@ } }, "node_modules/mongoose": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.2.3.tgz", - "integrity": "sha512-ZB8K8AgbVgLCcqjtmZMxaQBEztwEEZCtAIPMx2Q56Uo4WWKmwf5Nu/EEIFo8d/17P946X0z6xzxwIqCxUMKxrA==", + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.3.3.tgz", + "integrity": "sha512-3kSk0db9DM2tLttCdS6WNRqewPleamFEa4Vz/Qldc0dB4Zow/FiZxb9GExHTJjBZQ9T2xiGleQ3GzRrES3hhsA==", "dependencies": { - "bson": "^6.2.0", - "kareem": "2.5.1", - "mongodb": "6.3.0", + "bson": "^6.5.0", + "kareem": "2.6.3", + "mongodb": "6.5.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", @@ -10607,12 +10633,12 @@ } }, "node_modules/mongoose/node_modules/mongodb": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz", - "integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.5.0.tgz", + "integrity": "sha512-Fozq68InT+JKABGLqctgtb8P56pRrJFkbhW0ux+x1mdHeyinor8oNzJqwLjV/t5X5nJGfTlluxfyMnOXNggIUA==", "dependencies": { - "@mongodb-js/saslprep": "^1.1.0", - "bson": "^6.2.0", + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.4.0", "mongodb-connection-string-url": "^3.0.0" }, "engines": { @@ -11919,9 +11945,9 @@ } }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dependencies": { "loose-envify": "^1.1.0" }, @@ -11930,15 +11956,15 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.3.1" } }, "node_modules/react-frame-component": { @@ -11958,11 +11984,11 @@ "dev": true }, "node_modules/react-router": { - "version": "6.22.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", - "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.0.tgz", + "integrity": "sha512-wPMZ8S2TuPadH0sF5irFGjkNLIcRvOSaEe7v+JER8508dyJumm6XZB1u5kztlX0RVq6AzRVndzqcUh6sFIauzA==", "dependencies": { - "@remix-run/router": "1.15.3" + "@remix-run/router": "1.16.0" }, "engines": { "node": ">=14.0.0" @@ -11972,12 +11998,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.22.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", - "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.0.tgz", + "integrity": "sha512-Q9YaSYvubwgbal2c9DJKfx6hTNoBp3iJDsl+Duva/DwxoJH+OTXkxGpql4iUK2sla/8z4RpjAm6EWx1qUDuopQ==", "dependencies": { - "@remix-run/router": "1.15.3", - "react-router": "6.22.3" + "@remix-run/router": "1.16.0", + "react-router": "6.23.0" }, "engines": { "node": ">=14.0.0" @@ -12505,9 +12531,9 @@ } }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "dependencies": { "loose-envify": "^1.1.0" } @@ -13580,34 +13606,22 @@ } }, "node_modules/superagent": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", - "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", + "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==", "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": "^2.1.2", + "formidable": "^3.5.1", "methods": "^1.1.2", "mime": "2.6.0", - "qs": "^6.11.0", - "semver": "^7.3.8" + "qs": "^6.11.0" }, "engines": { - "node": ">=6.4.0 <13 || >=14" - } - }, - "node_modules/superagent/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "node": ">=14.18.0" } }, "node_modules/superagent/node_modules/mime": { @@ -13621,36 +13635,17 @@ "node": ">=4.0.0" } }, - "node_modules/superagent/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/superagent/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/supertest": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.4.tgz", - "integrity": "sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.0.0.tgz", + "integrity": "sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA==", "dev": true, "dependencies": { "methods": "^1.1.2", - "superagent": "^8.1.2" + "superagent": "^9.0.1" }, "engines": { - "node": ">=6.4.0" + "node": ">=14.18.0" } }, "node_modules/supports-color": { @@ -13995,26 +13990,23 @@ "utf8-byte-length": "^1.0.1" } }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, "node_modules/tty-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", @@ -14144,9 +14136,9 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz", + "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==", "dev": true, "peer": true, "bin": { @@ -14154,7 +14146,7 @@ "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=14.17" } }, "node_modules/umd": { diff --git a/package.json b/package.json index 2bdaaabf3..e916467e6 100644 --- a/package.json +++ b/package.json @@ -27,11 +27,11 @@ "test:dev": "jest --verbose --watch", "test:basic": "jest tests/markdown/basic.test.js --verbose", "test:variables": "jest tests/markdown/variables.test.js --verbose", - "test:mustache-syntax": "jest '.*(mustache-syntax).*' --verbose --noStackTrace", - "test:mustache-syntax:inline": "jest '.*(mustache-syntax).*' -t '^Inline:.*' --verbose --noStackTrace", - "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:mustache-syntax": "jest \".*(mustache-syntax).*\" --verbose --noStackTrace", + "test:mustache-syntax:inline": "jest \".*(mustache-syntax).*\" -t '^Inline:.*' --verbose --noStackTrace", + "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:route": "jest tests/routes/static-pages.test.js --verbose", "phb": "node scripts/phb.js", "prod": "set NODE_ENV=production && npm run build", @@ -81,11 +81,11 @@ ] }, "dependencies": { - "@babel/core": "^7.24.3", + "@babel/core": "^7.24.5", "@babel/plugin-transform-runtime": "^7.24.3", - "@babel/preset-env": "^7.24.3", + "@babel/preset-env": "^7.24.5", "@babel/preset-react": "^7.24.1", - "@googleapis/drive": "^8.7.0", + "@googleapis/drive": "^8.8.0", "body-parser": "^1.20.2", "classnames": "^2.5.1", "codemirror": "^5.65.6", @@ -102,25 +102,26 @@ "less": "^3.13.1", "lodash": "^4.17.21", "marked": "11.2.0", + "marked-emoji": "^1.4.0", "marked-extended-tables": "^1.0.8", "marked-gfm-heading-id": "^3.1.3", "marked-smartypants-lite": "^1.0.2", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^8.2.3", + "mongoose": "^8.3.3", "nanoid": "3.3.4", "nconf": "^0.12.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-frame-component": "^4.1.3", - "react-router-dom": "6.22.3", + "react-router-dom": "6.23.0", "sanitize-filename": "1.6.3", - "superagent": "^8.1.2", + "superagent": "^9.0.2", "vitreum": "git+https://git@github.com/calculuschild/vitreum.git" }, "devDependencies": { "eslint": "^8.57.0", - "eslint-plugin-jest": "^27.9.0", + "eslint-plugin-jest": "^28.5.0", "eslint-plugin-react": "^7.34.1", "jest": "^29.7.0", "jest-expect-message": "^1.1.3", @@ -129,6 +130,6 @@ "stylelint-config-recess-order": "^4.6.0", "stylelint-config-recommended": "^13.0.0", "stylelint-stylistic": "^0.4.3", - "supertest": "^6.3.4" + "supertest": "^7.0.0" } } diff --git a/server.js b/server.js index cb58f9bd3..39224bb88 100644 --- a/server.js +++ b/server.js @@ -7,6 +7,14 @@ DB.connect(config).then(()=>{ // before launching server const PORT = process.env.PORT || config.get('web_port') || 8000; server.app.listen(PORT, ()=>{ - console.log(`server on port: ${PORT}`); + const reset = '\x1b[0m'; // Reset to default style + const bright = '\x1b[1m'; // Bright (bold) style + const cyan = '\x1b[36m'; // Cyan color + const underline = '\x1b[4m'; // Underlined style + + console.log(`\n\tserver started at: ${new Date().toLocaleString()}`); + console.log(`\tserver on port: ${PORT}`); + console.log(`\t${bright + cyan}Open in browser: ${reset}${underline + bright + cyan}http://localhost:${PORT}${reset}\n\n`) + }); }); diff --git a/server/app.js b/server/app.js index e82c64e1d..4b691ccbe 100644 --- a/server/app.js +++ b/server/app.js @@ -23,9 +23,9 @@ const { splitTextStyleAndMetadata } = require('../shared/helpers.js'); const sanitizeBrew = (brew, accessType)=>{ brew._id = undefined; brew.__v = undefined; - if(accessType !== 'edit'){ + if(accessType !== 'edit' && accessType !== 'shareAuthor') { brew.editId = undefined; - } + } return brew; }; @@ -308,7 +308,6 @@ app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{ //Share Page app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{ const { brew } = req; - req.ogMeta = { ...defaultMetaTags, title : req.brew.title || 'Untitled Brew', description : req.brew.description || 'No description.', @@ -327,7 +326,8 @@ app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, r await HomebrewModel.increaseView({ shareId: brew.shareId }); } }; - sanitizeBrew(req.brew, 'share'); + + brew.authors.includes(req.account?.username) ? sanitizeBrew(req.brew, 'shareAuthor') : sanitizeBrew(req.brew, 'share'); splitTextStyleAndMetadata(req.brew); return next(); })); @@ -373,7 +373,7 @@ app.get('/account', asyncHandler(async (req, res, next)=>{ console.log(err); }); - data.uiItems = { + data.accountDetails = { username : req.account.username, issued : req.account.issued, googleId : Boolean(req.account.googleId), diff --git a/server/googleActions.js b/server/googleActions.js index a5ff61d62..16537d603 100644 --- a/server/googleActions.js +++ b/server/googleActions.js @@ -7,7 +7,9 @@ const config = require('./config.js'); let serviceAuth; if(!config.get('service_account')){ - console.log('No Google Service Account in config files - Google Drive integration will not be available.'); + const reset = '\x1b[0m'; // Reset to default style + const yellow = '\x1b[33m'; // yellow color + console.warn(`\n${yellow}No Google Service Account in config files - Google Drive integration will not be available.${reset}`); } else { const keys = typeof(config.get('service_account')) == 'string' ? JSON.parse(config.get('service_account')) : @@ -18,7 +20,7 @@ if(!config.get('service_account')){ serviceAuth.scopes = ['https://www.googleapis.com/auth/drive']; } catch (err) { console.warn(err); - console.log('Please make sure the Google Service Account is set up properly in your config files.'); + console.warn('Please make sure the Google Service Account is set up properly in your config files.'); } } diff --git a/server/homebrew.api.js b/server/homebrew.api.js index 567dc9cf7..e73a704a8 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -83,9 +83,9 @@ const api = { if(accessType === 'edit' && (authorsExist && !(isAuthor || isInvited))) { const accessError = { name: 'Access Error', status: 401 }; if(req.account){ - throw { ...accessError, message: 'User is not an Author', HBErrorCode: '03', authors: stub.authors, brewTitle: stub.title, shareId: stub.shareId }; + throw { ...accessError, message: 'User is not an Author', HBErrorCode: '03', authors: stub.authors, brewTitle: stub.title, shareId: stub.shareId}; } - throw { ...accessError, message: 'User is not logged in', HBErrorCode: '04', authors: stub.authors, brewTitle: stub.title }; + throw { ...accessError, message: 'User is not logged in', HBErrorCode: '04', authors: stub.authors, brewTitle: stub.title, shareId: stub.shareId}; } // If after all of that we still don't have a brew, throw an exception diff --git a/shared/naturalcrit/codeEditor/codeEditor.less b/shared/naturalcrit/codeEditor/codeEditor.less index 8ffbcad91..2ab08a5af 100644 --- a/shared/naturalcrit/codeEditor/codeEditor.less +++ b/shared/naturalcrit/codeEditor/codeEditor.less @@ -24,18 +24,13 @@ animation-duration: 0.4s; } - .CodeMirror-sizer { - padding-right: 0 !important; - //this setting must be !important, because CodeMirror sets it inline. Achieves overlay scrollbar - } - .CodeMirror-vscrollbar { &::-webkit-scrollbar { width: 20px; } &::-webkit-scrollbar-thumb { width: 20px; - background: linear-gradient(90deg, #85858599 15px, #80808000 15px); + background: linear-gradient(90deg, #858585 15px, #808080 15px); } } diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js index 8018bf63b..1c44c57ca 100644 --- a/shared/naturalcrit/markdown.js +++ b/shared/naturalcrit/markdown.js @@ -50,7 +50,7 @@ renderer.html = function (html) { return html; }; -// Don't wrap {{ Divs or {{ empty Spans in

tags +// Don't wrap {{ Spans alone on a line, or {{ Divs in

tags renderer.paragraph = function(text){ let match; if(text.startsWith(' `${key}="${value}"`).join(' ')}` : ''}` + + `>${this.parser.parseInline(token.tokens)}`; // parseInline to turn child tokens into HTML } }; @@ -149,13 +156,13 @@ const mustacheDivs = { if(match) { //Find closing delimiter let blockCount = 0; - let tags = ''; + let tags = {}; let endTags = 0; let endToken = 0; let delim; while (delim = blockRegex.exec(match[0])?.[0].trim()) { - if(!tags) { - tags = `${processStyleTags(delim.substring(2))}`; + if(_.isEmpty(tags)) { + tags = processStyleTags(delim.substring(2)); endTags = delim.length + src.indexOf(delim); } if(delim.startsWith('{{')) { @@ -183,7 +190,14 @@ const mustacheDivs = { } }, renderer(token) { - return `

`${key}="${value}"`).join(' ')}` : ''}` + + `>${this.parser.parse(token.tokens)}
`; // parse to turn child tokens into HTML } }; @@ -199,23 +213,39 @@ const mustacheInjectInline = { if(!lastToken || lastToken.type == 'mustacheInjectInline') return false; - const tags = `${processStyleTags(match[1])}`; + const tags = processStyleTags(match[1]); lastToken.originalType = lastToken.type; lastToken.type = 'mustacheInjectInline'; - lastToken.tags = tags; + lastToken.injectedTags = tags; return { - type : 'text', // Should match "name" above + type : 'mustacheInjectInline', // Should match "name" above raw : match[0], // Text to consume from the source text : '' }; } }, renderer(token) { + if(!token.originalType){ + return; + } token.type = token.originalType; const text = this.parser.parseInline([token]); - const openingTag = /(<[^\s<>]+)([^\n<>]*>.*)/s.exec(text); + const originalTags = extractHTMLStyleTags(text); + const injectedTags = token.injectedTags; + const tags = { + id : injectedTags.id || originalTags.id || null, + classes : [originalTags.classes, injectedTags.classes].join(' ').trim() || null, + styles : [originalTags.styles, injectedTags.styles].join(' ').trim() || null, + attributes : Object.assign(originalTags.attributes ?? {}, injectedTags.attributes ?? {}) + }; + const openingTag = /(<[^\s<>]+)[^\n<>]*(>.*)/s.exec(text); if(openingTag) { - return `${openingTag[1]} class="${token.tags}${openingTag[2]}`; + return `${openingTag[1]}` + + `${tags.classes ? ` class="${tags.classes}"` : ''}` + + `${tags.id ? ` id="${tags.id}"` : ''}` + + `${tags.styles ? ` style="${tags.styles}"` : ''}` + + `${!_.isEmpty(tags.attributes) ? ` ${Object.entries(tags.attributes).map(([key, value]) => `${key}="${value}"`).join(' ')}` : ''}` + + `${openingTag[2]}`; // parse to turn child tokens into HTML } return text; } @@ -235,7 +265,7 @@ const mustacheInjectBlock = { return false; lastToken.originalType = 'mustacheInjectBlock'; - lastToken.tags = `${processStyleTags(match[1])}`; + lastToken.injectedTags = processStyleTags(match[1]); return { type : 'mustacheInjectBlock', // Should match "name" above raw : match[0], // Text to consume from the source @@ -249,9 +279,22 @@ const mustacheInjectBlock = { } token.type = token.originalType; const text = this.parser.parse([token]); - const openingTag = /(<[^\s<>]+)([^\n<>]*>.*)/s.exec(text); + const originalTags = extractHTMLStyleTags(text); + const injectedTags = token.injectedTags; + const tags = { + id : injectedTags.id || originalTags.id || null, + classes : [originalTags.classes, injectedTags.classes].join(' ').trim() || null, + styles : [originalTags.styles, injectedTags.styles].join(' ').trim() || null, + attributes : Object.assign(originalTags.attributes ?? {}, injectedTags.attributes ?? {}) + }; + const openingTag = /(<[^\s<>]+)[^\n<>]*(>.*)/s.exec(text); if(openingTag) { - return `${openingTag[1]} class="${token.tags}${openingTag[2]}`; + return `${openingTag[1]}` + + `${tags.classes ? ` class="${tags.classes}"` : ''}` + + `${tags.id ? ` id="${tags.id}"` : ''}` + + `${tags.styles ? ` style="${tags.styles}"` : ''}` + + `${!_.isEmpty(tags.attributes) ? ` ${Object.entries(tags.attributes).map(([key, value]) => `${key}="${value}"`).join(' ')}` : ''}` + + `${openingTag[2]}`; // parse to turn child tokens into HTML } return text; } @@ -294,10 +337,10 @@ const superSubScripts = { } }; -const definitionListsInline = { - name : 'definitionListsInline', +const definitionListsSingleLine = { + name : 'definitionListsSingleLine', level : 'block', - start(src) { return src.match(/^[^\n]*?::[^\n]*/m)?.index; }, // Hint to Marked.js to stop and check for a match + start(src) { return src.match(/\n[^\n]*?::[^\n]*/m)?.index; }, // Hint to Marked.js to stop and check for a match tokenizer(src, tokens) { const regex = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym; let match; @@ -312,7 +355,7 @@ const definitionListsInline = { } if(definitions.length) { return { - type : 'definitionListsInline', + type : 'definitionListsSingleLine', raw : src.slice(0, endIndex), definitions }; @@ -326,10 +369,10 @@ const definitionListsInline = { } }; -const definitionListsMultiline = { - name : 'definitionListsMultiline', +const definitionListsMultiLine = { + name : 'definitionListsMultiLine', level : 'block', - start(src) { return src.match(/^[^\n]*\n::/m)?.index; }, // Hint to Marked.js to stop and check for a match + start(src) { return src.match(/\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; let match; @@ -353,7 +396,7 @@ const definitionListsMultiline = { } if(definitions.length) { return { - type : 'definitionListsMultiline', + type : 'definitionListsMultiLine', raw : src.slice(0, endIndex), definitions }; @@ -617,7 +660,7 @@ function MarkedVariables() { //^=====--------------------< Variable Handling >-------------------=====^// Marked.use(MarkedVariables()); -Marked.use({ extensions: [definitionListsMultiline, definitionListsInline, superSubScripts, mustacheSpans, mustacheDivs, mustacheInjectInline] }); +Marked.use({ extensions: [definitionListsMultiLine, definitionListsSingleLine, superSubScripts, mustacheSpans, mustacheDivs, mustacheInjectInline] }); Marked.use(mustacheInjectBlock); Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false }); Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite()); @@ -687,15 +730,45 @@ const processStyleTags = (string)=>{ //TODO: can we simplify to just split on commas? const tags = string.match(/(?:[^, ":=]+|[:=](?:"[^"]*"|))+/g); - const id = _.remove(tags, (tag)=>tag.startsWith('#')).map((tag)=>tag.slice(1))[0]; - const classes = _.remove(tags, (tag)=>(!tag.includes(':')) && (!tag.includes('='))); - const attributes = _.remove(tags, (tag)=>(tag.includes('='))).map((tag)=>tag.replace(/="?([^"]*)"?/g, '="$1"')); - const styles = tags?.length ? tags.map((tag)=>tag.replace(/:"?([^"]*)"?/g, ':$1;').trim()) : []; + const id = _.remove(tags, (tag)=>tag.startsWith('#')).map((tag)=>tag.slice(1))[0] || null; + const classes = _.remove(tags, (tag)=>(!tag.includes(':')) && (!tag.includes('='))).join(' ') || null; + const attributes = _.remove(tags, (tag)=>(tag.includes('='))).map((tag)=>tag.replace(/="?([^"]*)"?/g, '="$1"')) + ?.filter(attr => !attr.startsWith('class="') && !attr.startsWith('style="') && !attr.startsWith('id="')) + .reduce((obj, attr) => { + let [key, value] = attr.split("="); + value = value.replace(/"/g, ''); + obj[key] = value; + return obj; + }, {}) || null; + const styles = tags?.length ? tags.map((tag)=>tag.replace(/:"?([^"]*)"?/g, ':$1;').trim()).join(' ') : null; - return `${classes?.length ? ` ${classes.join(' ')}` : ''}"` + - `${id ? ` id="${id}"` : ''}` + - `${styles?.length ? ` style="${styles.join(' ')}"` : ''}` + - `${attributes?.length ? ` ${attributes.join(' ')}` : ''}`; + return { + id : id, + classes : classes, + styles : styles, + attributes : _.isEmpty(attributes) ? null : attributes + }; +}; + +const extractHTMLStyleTags = (htmlString)=> { + const id = htmlString.match(/id="([^"]*)"/)?.[1] || null; + const classes = htmlString.match(/class="([^"]*)"/)?.[1] || null; + const styles = htmlString.match(/style="([^"]*)"/)?.[1] || null; + const attributes = htmlString.match(/[a-zA-Z]+="[^"]*"/g) + ?.filter(attr => !attr.startsWith('class="') && !attr.startsWith('style="') && !attr.startsWith('id="')) + .reduce((obj, attr) => { + let [key, value] = attr.split("="); + value = value.replace(/"/g, ''); + obj[key] = value; + return obj; + }, {}) || null; + + return { + id : id, + classes : classes, + styles : styles, + attributes : _.isEmpty(attributes) ? null : attributes + }; }; const globalVarsList = {}; diff --git a/tests/markdown/mustache-syntax.test.js b/tests/markdown/mustache-syntax.test.js index 835bcc575..b32876353 100644 --- a/tests/markdown/mustache-syntax.test.js +++ b/tests/markdown/mustache-syntax.test.js @@ -130,8 +130,8 @@ describe('Inline: When using the Inline syntax {{ }}', ()=>{ describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{ it('Renders a div with text only', function() { const source = dedent`{{ - text - }}`; + text + }}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

text

`); }); @@ -139,14 +139,14 @@ describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{ it('Renders an empty div', function() { const source = dedent`{{ - }}`; + }}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`); }); it('Renders a single paragraph with opening and closing brackets', function() { const source = dedent`{{ - }}`; + }}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

{{}}

`); }); @@ -154,79 +154,79 @@ describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{ it('Renders a div with a single class', function() { const source = dedent`{{cat - }}`; + }}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`); }); it('Renders a div with a single class and text', function() { const source = dedent`{{cat - Sample text. - }}`; + Sample text. + }}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

Sample text.

`); }); it('Renders a div with two classes and text', function() { const source = dedent`{{cat,dog - Sample text. - }}`; + Sample text. + }}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

Sample text.

`); }); it('Renders a div with a style and text', function() { const source = dedent`{{color:red - Sample text. - }}`; + Sample text. + }}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

Sample text.

`); }); it('Renders a div with a style that has a string variable, and text', function() { const source = dedent`{{--stringVariable:"'string'" - Sample text. - }}`; + Sample text. + }}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

Sample text.

`); }); it('Renders a div with a style that has a string variable, and text', function() { const source = dedent`{{--stringVariable:"'string'" - Sample text. - }}`; + Sample text. + }}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

Sample text.

`); }); it('Renders a div with a class, style and text', function() { const source = dedent`{{cat,color:red - Sample text. - }}`; + Sample text. + }}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

Sample text.

`); }); it('Renders a div with an ID, class, style and text (different order)', function() { const source = dedent`{{color:red,cat,#dog - Sample text. - }}`; + Sample text. + }}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

Sample text.

`); }); it('Renders a div with a single ID', function() { const source = dedent`{{#cat,#dog - Sample text. - }}`; + Sample text. + }}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

Sample text.

`); }); it('Renders a div with an ID, class, style and text, and a variable assignment', function() { const source = dedent`{{color:red,cat,#dog,a="b and c",d="e" - Sample text. - }}`; + Sample text. + }}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

Sample text.

`); }); @@ -243,61 +243,91 @@ describe(`Block: When using the Block syntax {{tags\\ntext\\n}}`, ()=>{ describe('Injection: When an injection tag follows an element', ()=>{ // FIXME: Most of these fail because injections currently replace attributes, rather than append to. Or just minor extra whitespace issues. describe('and that element is an inline-block', ()=>{ - it.failing('Renders a span "text" with no injection', function() { + it('Renders a span "text" with no injection', function() { const source = '{{ text}}{}'; const rendered = Markdown.render(source); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('text'); }); - it.failing('Renders a span "text" with injected Class name', function() { + it('Renders a span "text" with injected Class name', function() { const source = '{{ text}}{ClassName}'; const rendered = Markdown.render(source); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('text'); }); - it.failing('Renders a span "text" with injected attribute', function() { + it('Renders a span "text" with injected attribute', function() { const source = '{{ text}}{a="b and c"}'; const rendered = Markdown.render(source); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('text'); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('text'); }); - it.failing('Renders a span "text" with injected style', function() { + it('Renders a span "text" with injected style', function() { const source = '{{ text}}{color:red}'; const rendered = Markdown.render(source); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('text'); }); - it.failing('Renders a span "text" with injected style using a string variable', function() { + it('Renders a span "text" with injected style using a string variable', function() { const source = `{{ text}}{--stringVariable:"'string'"}`; const rendered = Markdown.render(source); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`text`); }); - it.failing('Renders a span "text" with two injected styles', function() { + it('Renders a span "text" with two injected styles', function() { const source = '{{ text}}{color:red,background:blue}'; const rendered = Markdown.render(source); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('text'); }); - it.failing('Renders an emphasis element with injected Class name', function() { + it('Renders a span "text" with its own ID, overwritten with an injected ID', function() { + const source = '{{#oldId text}}{#newId}'; + const rendered = Markdown.render(source); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('text'); + }); + + it('Renders a span "text" with its own attributes, overwritten with an injected attribute, plus a new one', function() { + const source = '{{attrA="old",attrB="old" text}}{attrA="new",attrC="new"}'; + const rendered = Markdown.render(source); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('text'); + }); + + it('Renders a span "text" with its own attributes, overwritten with an injected attribute, ignoring "class", "style", and "id"', function() { + const source = '{{attrA="old",attrB="old" text}}{attrA="new",attrC="new",class="new",style="new",id="new"}'; + const rendered = Markdown.render(source); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('text'); + }); + + it('Renders a span "text" with its own styles, appended with injected styles', function() { + const source = '{{color:blue,height:10px text}}{width:10px,color:red}'; + const rendered = Markdown.render(source); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('text'); + }); + + it('Renders a span "text" with its own classes, appended with injected classes', function() { + const source = '{{classA,classB text}}{classA,classC}'; + const rendered = Markdown.render(source); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('text'); + }); + + it('Renders an emphasis element with injected Class name', function() { const source = '*emphasis*{big}'; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

emphasis

'); }); - it.failing('Renders a code element with injected style', function() { + it('Renders a code element with injected style', function() { const source = '`code`{background:gray}'; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

code

'); }); - it.failing('Renders an image element with injected style', function() { + it('Renders an image element with injected style', function() { const source = '![alt text](http://i.imgur.com/hMna6G0.png){position:absolute}'; const rendered = Markdown.render(source).trimReturns(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

homebrew mug

'); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

alt text

'); }); - it.failing('Renders an element modified by only the first of two consecutive injections', function() { + it('Renders an element modified by only the first of two consecutive injections', function() { const source = '{{ text}}{color:red}{background:blue}'; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

text{background:blue}

'); @@ -306,61 +336,106 @@ describe('Injection: When an injection tag follows an element', ()=>{ it('Renders an image with added attributes', function() { const source = `![homebrew mug](https://i.imgur.com/hMna6G0.png) {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(`

homebrew mug

`); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

homebrew mug

`); }); }); describe('and that element is a block', ()=>{ - it.failing('renders a div "text" with no injection', function() { + it('renders a div "text" with no injection', function() { const source = '{{\ntext\n}}\n{}'; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

text

'); }); - it.failing('renders a div "text" with injected Class name', function() { + it('renders a div "text" with injected Class name', function() { const source = '{{\ntext\n}}\n{ClassName}'; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

text

'); }); - it.failing('renders a div "text" with injected style', function() { + it('renders a div "text" with injected style', function() { const source = '{{\ntext\n}}\n{color:red}'; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

text

'); }); - it.failing('renders a div "text" with two injected styles', function() { + it('renders a div "text" with two injected styles', function() { const source = dedent`{{ - text - }} - {color:red,background:blue}`; + text + }} + {color:red,background:blue}`; const rendered = Markdown.render(source).trimReturns(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

text

`); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

text

`); }); - it.failing('renders a div "text" with injected variable string', function() { + it('renders a div "text" with injected variable string', function() { const source = dedent`{{ - text - }} - {--stringVariable:"'string'"}`; + text + }} + {--stringVariable:"'string'"}`; const rendered = Markdown.render(source).trimReturns(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

text

`); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

text

`); }); - it.failing('renders an h2 header "text" with injected class name', function() { + it('Renders a span "text" with its own ID, overwritten with an injected ID', function() { + const source = dedent`{{#oldId + text + }} + {#newId}`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

text

'); + }); + + it('Renders a span "text" with its own attributes, overwritten with an injected attribute, plus a new one', function() { + const source = dedent`{{attrA="old",attrB="old" + text + }} + {attrA="new",attrC="new"}`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

text

'); + }); + + it('Renders a span "text" with its own attributes, overwritten with an injected attribute, ignoring "class", "style", and "id"', function() { + const source = dedent`{{attrA="old",attrB="old" + text + }} + {attrA="new",attrC="new",class="new",style="new",id="new"}`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

text

'); + }); + + it('Renders a span "text" with its own styles, appended with injected styles', function() { + const source = dedent`{{color:blue,height:10px + text + }} + {width:10px,color:red}`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

text

'); + }); + + it('Renders a span "text" with its own classes, appended with injected classes', function() { + const source = dedent`{{classA,classB + text + }} + {classA,classC}`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

text

'); + }); + + it('renders an h2 header "text" with injected class name', function() { const source = dedent`## text - {ClassName}`; + {ClassName}`; const rendered = Markdown.render(source).trimReturns(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

text

'); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

text

'); }); - it.failing('renders a table with injected class name', function() { + it('renders a table with injected class name', function() { const source = dedent`| Experience Points | Level | - |:------------------|:-----:| - | 0 | 1 | - | 300 | 2 | + |:------------------|:-----:| + | 0 | 1 | + | 300 | 2 | - {ClassName}`; + {ClassName}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
Experience PointsLevel
01
3002
`); }); @@ -376,23 +451,23 @@ describe('Injection: When an injection tag follows an element', ()=>{ // expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`...`); // FIXME: expect this to be injected into
    ? Currently injects into last
  • // }); - it.failing('renders an h2 header "text" with injected class name, and "secondInjection" as regular text on the next line.', function() { + it('renders an h2 header "text" with injected class name, and "secondInjection" as regular text on the next line.', function() { const source = dedent`## text {ClassName} {secondInjection}`; const rendered = Markdown.render(source).trimReturns(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

    text

    {secondInjection}

    '); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

    text

    {secondInjection}

    '); }); - it.failing('renders a div nested into another div, the inner with class=innerDiv and the other class=outerDiv', function() { + it('renders a div nested into another div, the inner with class=innerDiv and the other class=outerDiv', function() { const source = dedent`{{ - outer text - {{ - inner text - }} - {innerDiv} - }} - {outerDiv}`; + outer text + {{ + inner text + }} + {innerDiv} + }} + {outerDiv}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

    outer text

    inner text

    '); }); diff --git a/tests/markdown/variables.test.js b/tests/markdown/variables.test.js index c909dafec..e6018e19f 100644 --- a/tests/markdown/variables.test.js +++ b/tests/markdown/variables.test.js @@ -329,7 +329,7 @@ describe('Normal Links and Images', ()=>{ const source = `![alt text](url){width:100px}`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent` -

    alt text

    `.trimReturns()); +

    alt text

    `.trimReturns()); }); it('Renders normal links', function() { diff --git a/themes/V3/5ePHB/snippets/coverpage.gen.js b/themes/V3/5ePHB/snippets/coverpage.gen.js index 865269f92..b662aaaa6 100644 --- a/themes/V3/5ePHB/snippets/coverpage.gen.js +++ b/themes/V3/5ePHB/snippets/coverpage.gen.js @@ -149,8 +149,6 @@ module.exports = { ![](/assets/naturalCritLogoWhite.svg) Homebrewery.Naturalcrit.com - }} - - \page`; + }}`; } }; diff --git a/themes/V3/5ePHB/style.less b/themes/V3/5ePHB/style.less index a529d591f..57a39e507 100644 --- a/themes/V3/5ePHB/style.less +++ b/themes/V3/5ePHB/style.less @@ -1,6 +1,6 @@ @import (less) './themes/assets/assets.less'; @import (less) './themes/fonts/icon fonts/font-icons.less'; -@import (less) './themes/fonts/icon fonts/dicefont.less'; +@import (less) './themes/fonts/icon fonts/diceFont.less'; :root { //Colors @@ -532,21 +532,19 @@ .page:has(.frontCover) { columns : 1; text-align : center; - &::after { all : unset; } + &::after { display : none; } h1 { margin-top : 1.2cm; margin-bottom : 0; font-family : 'NodestoCapsCondensed'; font-size : 2.245cm; font-weight : normal; - line-height : 0.85em; + line-height : 1.9cm; color : white; text-shadow : unset; text-transform : uppercase; - filter : drop-shadow(0 0 1.5px black) drop-shadow(0 0 0 black) - drop-shadow(0 0 0 black) drop-shadow(0 0 0 black) - drop-shadow(0 0 0 black) drop-shadow(0 0 0 black) - drop-shadow(0 0 0 black) drop-shadow(0 0 0 black); + -webkit-text-stroke: 0.2cm black; + paint-order:stroke; } h2 { font-family : 'NodestoCapsCondensed'; @@ -554,10 +552,8 @@ font-weight : normal; color : white; letter-spacing : 0.1cm; - filter : drop-shadow(0 0 1px black) drop-shadow(0 0 0 black) - drop-shadow(0 0 0 black) drop-shadow(0 0 0 black) - drop-shadow(0 0 0 black) drop-shadow(0 0 0 black) - drop-shadow(0 0 0 black) drop-shadow(0 0 0 black); + -webkit-text-stroke: 0.14cm black; + paint-order:stroke; } hr { position : relative; @@ -603,10 +599,8 @@ font-size : 0.496cm; color : white; text-align : center; - filter : drop-shadow(0 0 0.7px black) drop-shadow(0 0 0 black) - drop-shadow(0 0 0 black) drop-shadow(0 0 0 black) - drop-shadow(0 0 0 black) drop-shadow(0 0 0 black) - drop-shadow(0 0 0 black) drop-shadow(0 0 0 black); + -webkit-text-stroke: 0.1cm black; + paint-order:stroke; } .logo { position : absolute; @@ -626,14 +620,14 @@ .page:has(.insideCover) { columns : 1; text-align : center; - &::after { all : unset; } + &::after { display : none; } h1 { margin-top : 1.2cm; margin-bottom : 0; font-family : 'NodestoCapsCondensed'; font-size : 2.1cm; font-weight : normal; - line-height : 0.85em; + line-height : 1.785cm; text-transform : uppercase; } h2 { @@ -672,7 +666,7 @@ padding : 2.25cm 1.3cm 2cm 1.3cm; color : #FFFFFF; columns : 1; - &::after { all : unset; } + &::after { display : none; } .columnWrapper { width : 7.6cm; } .backCover { position : absolute; @@ -688,7 +682,7 @@ margin-bottom : 0.3cm; font-family : 'NodestoCapsCondensed'; font-size : 1.35cm; - line-height : 0.95em; + line-height : 1.28cm; color : #ED1C24; text-align : center; } @@ -714,7 +708,7 @@ p { font-family : 'Overpass'; font-size : 0.332cm; - line-height : 1.5em; + line-height : 0.35cm; } hr + p { margin-top : 0.6cm; @@ -739,10 +733,10 @@ font-family : 'NodestoCapsWide'; font-size : 0.4cm; line-height : 1em; + line-height : 1.28cm; color : #FFFFFF; text-align : center; text-indent : 0; - letter-spacing : 0.08em; } } } @@ -782,7 +776,7 @@ margin-left : auto; font-family : 'Overpass'; font-size : 0.45cm; - line-height : 1.1em; + line-height : 0.495cm; } } diff --git a/themes/V3/Blank/snippets.js b/themes/V3/Blank/snippets.js index 122666055..272368665 100644 --- a/themes/V3/Blank/snippets.js +++ b/themes/V3/Blank/snippets.js @@ -326,27 +326,27 @@ module.exports = [ gen : dedent`{{font-family:CodeLight Dummy Text}}` }, { - name : 'Scaly Sans Remake', + name : 'Scaly Sans', icon : 'font ScalySansRemake', gen : dedent`{{font-family:ScalySansRemake Dummy Text}}` }, { - name : 'Book Insanity Remake', + name : 'Book Insanity', icon : 'font BookInsanityRemake', gen : dedent`{{font-family:BookInsanityRemake Dummy Text}}` }, { - name : 'Mr Eaves Remake', + name : 'Mr Eaves', icon : 'font MrEavesRemake', gen : dedent`{{font-family:MrEavesRemake Dummy Text}}` }, { - name: 'Solbera Imitation Remake', + name: 'Solbera Imitation', icon: 'font SolberaImitationRemake', gen: dedent`{{font-family:SolberaImitationRemake Dummy Text}}` }, { - name: 'Scaly Sans Small Caps Remake', + name: 'Scaly Sans Small Caps', icon: 'font ScalySansSmallCapsRemake', gen: dedent`{{font-family:ScalySansSmallCapsRemake Dummy Text}}` }, diff --git a/themes/V3/Blank/style.less b/themes/V3/Blank/style.less index 1d8ca6ee4..ec8905630 100644 --- a/themes/V3/Blank/style.less +++ b/themes/V3/Blank/style.less @@ -1,6 +1,6 @@ @import (less) './themes/fonts/5e/fonts.less'; @import (less) './themes/assets/assets.less'; -@import (less) './themes/fonts/icon fonts/dicefont.less'; +@import (less) './themes/fonts/icon fonts/diceFont.less'; :root { //Colors diff --git a/themes/fonts/icon fonts/dicefont.less b/themes/fonts/icon fonts/diceFont.less similarity index 98% rename from themes/fonts/icon fonts/dicefont.less rename to themes/fonts/icon fonts/diceFont.less index 78a88f03a..069f6f769 100644 --- a/themes/fonts/icon fonts/dicefont.less +++ b/themes/fonts/icon fonts/diceFont.less @@ -1,9 +1,9 @@ -/* Icon Font: dicefont */ +/* Icon Font: diceFont */ @font-face { font-family : 'DiceFont'; font-style : normal; font-weight : normal; - src : url('../../../fonts/icon fonts/dicefont.woff2'); + src : url('../../../fonts/icon fonts/diceFont.woff2'); } .df { diff --git a/themes/fonts/icon fonts/dicefont.woff2 b/themes/fonts/icon fonts/diceFont.woff2 similarity index 100% rename from themes/fonts/icon fonts/dicefont.woff2 rename to themes/fonts/icon fonts/diceFont.woff2 diff --git a/themes/fonts/icon fonts/dicefont_license.md b/themes/fonts/icon fonts/diceFont_license.md similarity index 100% rename from themes/fonts/icon fonts/dicefont_license.md rename to themes/fonts/icon fonts/diceFont_license.md