diff --git a/.circleci/config.yml b/.circleci/config.yml index 274ec25ac..c195df81c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,7 +10,7 @@ orbs: jobs: build: docker: - - image: cimg/node:20.8.0 + - image: cimg/node:20.17.0 - image: mongo:4.4 working_directory: ~/homebrewery @@ -27,7 +27,7 @@ jobs: # fallback to using the latest cache if no exact match is found - v1-dependencies- - - run: sudo npm install -g npm@10.2.0 + - run: sudo npm install -g npm@10.8.2 - node/install-packages: app-dir: ~/homebrewery cache-path: node_modules @@ -45,7 +45,7 @@ jobs: test: docker: - - image: cimg/node:20.8.0 + - image: cimg/node:20.17.0 working_directory: ~/homebrewery parallelism: 1 @@ -70,15 +70,30 @@ jobs: - run: name: Test - Hard Breaks command: npm run test:hard-breaks + - run: + name: Test - Non-Breaking Spaces + command: npm run test:non-breaking-spaces + - run: + name: Test - Paragraph Justification + command: npm run test:paragraph-justification - run: name: Test - Variables command: npm run test:variables + - run: + name: Test - Emojis + command: npm run test:emojis - run: name: Test - Routes command: npm run test:route + - run: + name: Test - HTML sanitization + command: npm run test:safehtml - run: name: Test - Coverage command: npm run test:coverage + - run: + name: Test - Content Negotiation + command: npm run test:content-negotiation workflows: build_and_test: diff --git a/.gitattributes b/.gitattributes index 20eac6017..2f90f4172 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,3 @@ -package-lock.json binary \ No newline at end of file +package-lock.json binary + +*.json text eol=lf \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..020653272 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,37 @@ +> [!TIP] +> Before submitting a Pull Request, please consider the following to speed up reviews: +> - πŸ‘·β€β™€οΈ Create small PRs. Large PRs can usually be broken down into incremental PRs. +> - 🚩 Do you already have several open PRs? Consider finishing or asking for help with existing PRs first. +> - πŸ”§ Does your PR reference a discussed and approved issue, especially for personal or edge-case requests? +> - πŸ’‘ Is the solution agreed upon? Save rework time by discussing strategy before coding. + +## Description + +_Describe what your PR accomplishes. Consider walking through the main changes to aid reviewers in following your code, especially if it covers multiple files._ + +## Related Issues or Discussions + +> [!CAUTION] +> If no issue exists yet, create it, and get agreement on the approach (or paste in a previous agreement from chat, etc.) before moving forward. (Experimental PRs are OK without prior discussion, but do not expect to get merged.) + +- Closes # + +## QA Instructions, Screenshots, Recordings + +_Replace this line with instructions on how to test or view your changes, as well as any before/after +screenshots or recordings for UI changes._ + +### Reviewer Checklist + +_Replace the list below with specific features you want reviewers to look at._ + +*Reviewers, refer to this list when testing features, or suggest new items * +- [ ] Verify new features are functional + - [ ] Feature A does X + - [ ] Feature B does Y +- [ ] Verify old features have not broken + - [ ] Feature Z can still be used +- [ ] Test for edge cases / try to break things + - [ ] Feature A handles negative numbers +- [ ] Identify opportunities for simplification and refactoring +- [ ] Check for code legibility and appropriate comments diff --git a/.stylelintrc.json b/.stylelintrc.json index 2c7a9afdf..b5f2e7712 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -1,48 +1,48 @@ { - "extends": [ - "stylelint-config-recess-order", - "stylelint-config-recommended"], - "plugins": [ - "@stylistic/stylelint-plugin", - "./stylelint_plugins/declaration-colon-align.js", - "./stylelint_plugins/declaration-colon-min-space-before", - "./stylelint_plugins/declaration-block-multi-line-min-declarations" - ], - "customSyntax": "postcss-less", - "rules": { - "no-descending-specificity" : null, - "at-rule-no-unknown" : null, - "function-no-unknown" : null, - "font-family-no-missing-generic-family-keyword" : null, - "font-weight-notation" : "named-where-possible", - "font-family-name-quotes" : "always-unless-keyword", - "@stylistic/indentation" : "tab", - "no-duplicate-selectors" : true, - "@stylistic/color-hex-case" : "upper", - "color-hex-length" : "long", - "@stylistic/selector-combinator-space-after" : "always", - "@stylistic/selector-combinator-space-before" : "always", - "@stylistic/selector-attribute-operator-space-before" : "never", - "@stylistic/selector-attribute-operator-space-after" : "never", - "@stylistic/selector-attribute-brackets-space-inside" : "never", - "selector-attribute-quotes" : "always", - "selector-pseudo-element-colon-notation" : "double", - "@stylistic/selector-pseudo-class-parentheses-space-inside" : "never", - "@stylistic/block-opening-brace-space-before" : "always", - "naturalcrit/declaration-colon-min-space-before" : 1, - "@stylistic/declaration-block-trailing-semicolon" : "always", - "@stylistic/declaration-colon-space-after" : "always", - "@stylistic/number-leading-zero" : "always", - "function-url-quotes" : ["always", { "except": ["empty"] }], - "function-url-scheme-disallowed-list" : ["data","http"], - "comment-whitespace-inside" : "always", - "@stylistic/string-quotes" : "single", - "@stylistic/media-feature-range-operator-space-before" : "always", - "@stylistic/media-feature-range-operator-space-after" : "always", - "@stylistic/media-feature-parentheses-space-inside" : "never", - "@stylistic/media-feature-colon-space-before" : "always", - "@stylistic/media-feature-colon-space-after" : "always", - "naturalcrit/declaration-colon-align" : true, - "naturalcrit/declaration-block-multi-line-min-declarations": 1 - } + "extends": [ + "stylelint-config-recess-order", + "stylelint-config-recommended"], + "plugins": [ + "@stylistic/stylelint-plugin", + "./stylelint_plugins/declaration-colon-align.js", + "./stylelint_plugins/declaration-colon-min-space-before", + "./stylelint_plugins/declaration-block-multi-line-min-declarations" + ], + "customSyntax": "postcss-less", + "rules": { + "no-descending-specificity" : null, + "at-rule-no-unknown" : null, + "function-no-unknown" : null, + "font-family-no-missing-generic-family-keyword" : null, + "font-weight-notation" : "named-where-possible", + "font-family-name-quotes" : "always-unless-keyword", + "@stylistic/indentation" : "tab", + "no-duplicate-selectors" : true, + "@stylistic/color-hex-case" : "upper", + "color-hex-length" : "long", + "@stylistic/selector-combinator-space-after" : "always", + "@stylistic/selector-combinator-space-before" : "always", + "@stylistic/selector-attribute-operator-space-before" : "never", + "@stylistic/selector-attribute-operator-space-after" : "never", + "@stylistic/selector-attribute-brackets-space-inside" : "never", + "selector-attribute-quotes" : "always", + "selector-pseudo-element-colon-notation" : "double", + "@stylistic/selector-pseudo-class-parentheses-space-inside" : "never", + "@stylistic/block-opening-brace-space-before" : "always", + "naturalcrit/declaration-colon-min-space-before" : 1, + "@stylistic/declaration-block-trailing-semicolon" : "always", + "@stylistic/declaration-colon-space-after" : "always", + "@stylistic/number-leading-zero" : "always", + "function-url-quotes" : ["always", { "except": ["empty"] }], + "function-url-scheme-disallowed-list" : ["data","http"], + "comment-whitespace-inside" : "always", + "@stylistic/string-quotes" : "single", + "@stylistic/media-feature-range-operator-space-before" : "always", + "@stylistic/media-feature-range-operator-space-after" : "always", + "@stylistic/media-feature-parentheses-space-inside" : "never", + "@stylistic/media-feature-colon-space-before" : "always", + "@stylistic/media-feature-colon-space-after" : "always", + "naturalcrit/declaration-colon-align" : true, + "naturalcrit/declaration-block-multi-line-min-declarations" : 1 + } } diff --git a/Dockerfile b/Dockerfile index 84652fbf9..17d02b01f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20-alpine +FROM node:22-alpine RUN apk --no-cache add git ENV NODE_ENV=docker @@ -9,7 +9,10 @@ WORKDIR /usr/src/app # Copy package.json into the image, then run yarn install # This improves caching so we don't have to download the dependencies every time the code changes COPY package.json ./ +COPY config/docker.json usr/src/app/config # --ignore-scripts tells yarn not to run postbuild. We run it explicitly later +RUN node --version +RUN npm --version RUN npm install --ignore-scripts # Bundle app source and build application diff --git a/README.DOCKER.md b/README.DOCKER.md index 356ac398a..4dfbef045 100644 --- a/README.DOCKER.md +++ b/README.DOCKER.md @@ -1,12 +1,119 @@ -# Running Homebrewery via Docker +# Offline Install Instructions: Docker -The repo includes a Dockerfile and a docker-compose.yml file. +These instructions are for setting up a persistent instance of the Homebrewery application locally using Docker. -To run the application via docker-compose.yml: -`docker-compose up -d` +If you intend to develop with Homebrewery, following the Homebrewery application section of this guide is not recommended. Using docker to deploy MongoDB locally for development is not a bad idea at all, however. -To stop the application: -`docker-compose down` +# Install Docker + +## Docker Desktop (MacOS/Windows) + +Windows and Mac installs use Docker Desktop. Current install instructions are below. + +* [Mac](https://docs.docker.com/desktop/mac/install/) +* [Windows](https://docs.docker.com/desktop/windows/install/) + +You can set up the docker engine to start on boot via the Docker desktop UI. + +## Docker Engine + +Linux installs use Docker Engine. Docker provides installers and instructions for several of the most common distrubutions. If you do not see yours listed, it is very likely supported indirectly by your distribution. + +* [Arch](https://docs.docker.com/desktop/setup/install/linux/archlinux/) +* [CentOS](https://docs.docker.com/engine/install/centos/) +* [Debian](https://docs.docker.com/engine/install/debian/) +* [Fedora](https://docs.docker.com/engine/install/fedora/) +* [RHEL](https://docs.docker.com/engine/install/rhel/) +* [Ubuntu](https://docs.docker.com/engine/install/ubuntu/) + +### Post installation steps +[Manage Docker as a non-root user (highly recommended)](https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user) +[Enable Docker to start on boot (highly recommended)](https://docs.docker.com/engine/install/linux-postinstall/#configure-docker-to-start-on-boot) + +# Build Homebrewery Image + +Next we build the homebrewery docker image. Start by cloning the repository. + +```shell +git clone https://github.com/naturalcrit/homebrewery.git +cd homebrewery +``` + +Make an changes you need to `config/docker.json` then build the image. If it does not exist,the below as a template. + +``` +{ +"host" : "localhost:8000", +"naturalcrit_url" : "local.naturalcrit.com:8010", +"secret" : "secret", +"web_port" : 8000, +"enable_v3" : true, +"mongodb_uri": "mongodb://172.17.0.2/homebrewery", +"enable_themes" : true, +} +``` + +```shell +docker-compose build homebrewery +``` + +# Add Mongo container + +Once docker is installed and running, it is time to set up the containers. First up, Mongo. + +```shell +docker run --name homebrewery-mongodb -d --restart unless-stopped -v mongodata:/data/db -p 27017:27017 mongo:latest +``` + +Older CPUs may run into an issue with AVX support. +``` +WARNING: MongoDB 5.0+ requires a CPU with AVX support, and your current system does not appear to have that! + see https://jira.mongodb.org/browse/SERVER-54407 + see also https://www.mongodb.com/community/forums/t/mongodb-5-0-cpu-intel-g4650-compatibility/116610/2 + see also https://github.com/docker-library/mongo/issues/485#issuecomment-891991814 +``` +If you see a message similar to this, try using the bitnami mongo instead. + +```shell +docker run --name homebrewery-mongodb -d --restart unless-stopped -v mongodata:/data/db -p 27017:27017 bitnami/mongo:latest +``` + +If your distribution is running on an arm device such as a Raspberry Pi, you will need to run the arm-built MongoDB v4.4. + +```shell +docker run --name homebrewery-mongodb -d --restart unless-stopped -v mongodata:/data/db -p 27017:27017 arm64v8/mongo:4.4 +``` + +## Run the Homebrewery Image +```shell +# Make sure you run this in the homebrewery directory +docker run --name homebrewery-app -d --restart unless-stopped -e NODE_ENV=docker -v $(pwd)/config/docker.json:/usr/src/app/config/docker.json -p 8000:8000 docker.io/library/homebrewery:latest +``` + +## Updating the Image + +When Homebrewery code updates, your docker container will not automatically follow the changes. To do so you will need to rebuild your homebrewery image. + +First, return to your homebrewery clone (from Build Homebrewery Image above) or recreate the clone if you deleted your copy of the code. + +First, delete the existing image. + +```shell +docker rm -f homebrewery-app +``` + +Next, update the clone's code to the latest version. + +```shell +cd homebrewery +git checkout master +git pull upstream master +``` + +Finally, rebuild and restart the homebrewery image. + +```shell +docker-compose build homebrewery +docker run --name homebrewery-app -d --restart unless-stopped -e NODE_ENV=docker -v $(pwd)/config/docker.json:/usr/src/app/config/docker.json -p 8000:8000 docker.io/library/homebrewery:latest +``` -To stop the application and remove all data: -`docker-compose down -v` diff --git a/babel.config.json b/babel.config.json new file mode 100644 index 000000000..5e768ec31 --- /dev/null +++ b/babel.config.json @@ -0,0 +1,10 @@ +{ + "presets": [ + "@babel/preset-env", + "@babel/preset-react" + ], + "plugins": [ + "@babel/plugin-transform-runtime", + "babel-plugin-transform-import-meta" + ] +} diff --git a/changelog.md b/changelog.md index 033eee86d..8cfb499f6 100644 --- a/changelog.md +++ b/changelog.md @@ -77,16 +77,322 @@ pre { } .varSyntaxTable th:first-of-type { - width:6cm; + width:6cm; +} + +.page .exampleTable td,th { + border:1px dashed #00000030; } ``` ## changelog For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery). -### Tuesday 8/13/2024 - v3.14.1 -{{taskList +### Thursday 01/30/2024 - v3.17.0 +{{taskList +##### 5e-Cleric + +* [x] Update FAQ + +* [x] Fix styling for Vault buttons and checkboxes + +* [x] Improve navigation bar styling + +* [x] Add feature to change username at https://www.naturalcrit.com/account + +* [x] Fix Reddit link crash when title has non-latin chars + +##### dbolack + +* [x] Fix page shadows toolbar option + +Fixes issue [#3919](https://github.com/naturalcrit/homebrewery/issues/3919) + +* [x] Add `:>>>` syntax for horizontal :>>>>> spaces + +* [x] Update Docker install instructions + +Fixes issue [#1930](https://github.com/naturalcrit/homebrewery/issues/1930) + +* [x] Allow styling pages via `\page{myStyles}` (with calculuschild) + +Fixes issue [#3901](https://github.com/naturalcrit/homebrewery/issues/3901) + +* [x] Update Ubuntu install instructions + +Fixes issue [#1952](https://github.com/naturalcrit/homebrewery/issues/1952) + +* [x] Add `:-:` `:-` `-:` syntax for paragraph alignment, similar to table column alignment; for example: + +-: -: Right-aligned + +:-: :-: Centered + +* [x] Add `:-- 50% --:` syntax to allow setting table column widths by percentage; for example: +``` +| Narrow | Wide | +|:- 10% -:|:-90%--:| +| Cell | Cell | +``` + + +| Narrow | Wide | +|:- 10% -:|:-90%--:| +|Cell | Cell | +{exampleTable} + +##### G-Ambatte + +* [x] Fix crash when opening brew Properties tab + +Fixes issue [#3927](https://github.com/naturalcrit/homebrewery/issues/3927) + +* [x] Update error pages with steps to refresh credentials + +Fixes issue [#3955](https://github.com/naturalcrit/homebrewery/issues/3955) + +* [x] Add {{openSans :fas_rectangle_list: **NAVIGATION**}} menu to the viewer toolbar + +##### calculuschild + +* [x] Reduce display lag on large brews + +##### Gazook89 + +* [x] Smarter detection of current page number + +Fixes issue [#3824](https://github.com/naturalcrit/homebrewery/issues/3824) + +##### All +* [x] Update dependencies and scripts +* [x] Refactor components and fix various errors +}} + +\column + +### Wednesday 11/27/2024 - v3.16.1 + +{{taskList +##### 5e-Cleric + +* [x] Allow linking to specific HTML IDs via `#ID` at the end of the URL, e.g.: `homebrewery.naturalcrit.com/share/share/a6RCXwaDS58i#p4` to link to Page 4 directly + +Fixes issues [#2820](https://github.com/naturalcrit/homebrewery/issues/2820), [#3505](https://github.com/naturalcrit/homebrewery/issues/3505) + +* [x] Fix generation of link to certain Google Drive brews + +Fixes issue [#3776](https://github.com/naturalcrit/homebrewery/issues/3776) + +##### abquintic + +* [x] Fix blank pages appearing when pasting text + +Fixes issue [#3718](https://github.com/naturalcrit/homebrewery/issues/3718) + +##### Gazook89 + +* [x] Add new brew viewing options to the view toolbar +- {{fac,single-spread}} {{openSans **SINGLE PAGE**}} +- {{fac,facing-spread}} {{openSans **TWO PAGE**}} +- {{fac,flow-spread}} {{openSans **GRID**}} + +Fixes issue [#1379](https://github.com/naturalcrit/homebrewery/issues/1379) + +* [x] Updates to tag input boxes + +##### G-Ambatte + +* [x] Admin tools to fix certain corrupted documents + +Fixes issue [#3801](https://github.com/naturalcrit/homebrewery/issues/3801) + +* [x] Fix print window being affected by document zoom + +Fixes issue [#3744](https://github.com/naturalcrit/homebrewery/issues/3744) + + +##### calculuschild, 5e-Cleric, G-Ambatte, Gazook89, abquintic + +* [x] Multiple code refactors, cleanups, and security fixes +}} + +\page + +### Saturday 10/12/2024 - v3.16.0 + +{{taskList +##### 5e-Cleric + +* [x] Added a new API endpoint `/metadata/:shareId` to fetch metadata about individual brews + +Fixes issue [#2638](https://github.com/naturalcrit/homebrewery/issues/2638) + +* [x] Added A3, A5, and Card page size snippets under {{openSans **:fas_paintbrush: STYLE TAB :fas_arrow_right: :fas_print: PRINT**}} + +* [x] Adjust navbar styling for very long titles + +Fixes issue [#2071](https://github.com/naturalcrit/homebrewery/issues/2071) + +* [x] Added some sorting options to the {{openSans **VAULT** {{fas,fa-dungeon}}}} page + +* [x] Fix `language` property not working in share page + +Fixes issue [#3776](https://github.com/naturalcrit/homebrewery/issues/3776) + +##### abquintic + +* [x] New {{openSans **:fas_pencil: TEXT EDITOR :fas_arrow_right: :fas_bookmark: PAGE NUMBER :fas_arrow_right:**}} +{{openSans **:fas_xmark: SKIP PAGE NUMBER**}} and {{openSans **:fas_arrow_rotate_left: RESTART PAGE NUMBER**}} snippets for more control over automatic page numbering. + +Fixes issue [#513](https://github.com/naturalcrit/homebrewery/issues/513) + +* [x] New Table of Contents control options via {{openSans **:fas_pencil: TEXT EDITOR :fas_arrow_right: :fas_book: TABLE OF CONTENTS**}} submenus. By default, H1-H3 is included in the ToC generation, but the new options allow marking `{{blocks}}` to include or exclude specific or ranges of contained headers. Also, a global option to increase the default range of H1-H3 to H1-H4/5/6. After applying these markers, you must regenerate the Table of Contents to see the changes. + +* [x] Added a ":fas_lock: SYNC VIEWS" button onto the divider bar. When locked, scrolling on either panel will sync the other panel to the same page. + +Fixes issue [#241](https://github.com/naturalcrit/homebrewery/issues/241) + +##### Gazook89 + +* [x] Added a :fas_glasses: HIDE button to the page navigation bar + +##### G-Ambatte + +* [x] Automatic local backups of your files, in case of accidental data loss. Stores up to 5 snapshots of each brew edited in your browser, incrementing from a few minutes old to a maximum of several days. Restore a backup by clicking an entry in the new {{openSans **:fas_clock_rotate_left: HISTORY**}} button in the snippet bar. + +Fixes issue [#3070](https://github.com/naturalcrit/homebrewery/issues/3070) + +* [x] Fix issue with legacy brews breaking on Share page + +Fixes issue [#3764](https://github.com/naturalcrit/homebrewery/issues/3764) + +* [x] Fix print size when printing a zoomed document + +Fixes issue [#3744](https://github.com/naturalcrit/homebrewery/issues/3744) + +##### All + +* [x] Background code cleanup, security fixes, dev tool improvements, dependency updates, prep for upcoming features, etc. +}} + +### Wednesday 9/25/2024 - v3.15.1 + +{{taskList +##### calculuschild + +* [x] Background fixes to handle Google Drive issues + +* [x] Remove duplicate error logging + +##### calculuschild, 5e-Cleric + +* [x] Fix links in {{openSans **RECENT BREWS :fas_clock_rotate_left:**}} and user {{openSans **BREWS :fas_beer_mug_empty:**}} pointing to trashed Google Drive files after transferring from Google to Homebrewery storage + +Fixes issue [#3776](https://github.com/naturalcrit/homebrewery/issues/3776) +}} + +\page + +### Wednesday 9/04/2024 - v3.15.0 + +{{taskList +##### 5e-Cleric, abquintic, calculuschild, Gazook89, G-Ambatte, Ericsheid, Kaiburr + +* [x] New {{openSans **VAULT** {{fas,fa-dungeon}}}} page πŸŽ‰πŸŽ‰πŸŽ‰ +: +All **PUBLISHED** brews ({{openSans :fas_circle_info: **Properties**}} menu) will be searchable, by title or author, and filtered by renderer. More features and adjustments will be coming. +: +Note: If any of your own brews are not showing up in search (particularly if stored on Google Drive), please edit and re-save to ensure our database has the data needed from document to be searchable. + +Fixes issue [#697](https://github.com/naturalcrit/homebrewery/issues/697) + +##### Gazook89 + +* [x] Auto-focus on text editor when switching editor tabs +}} + +### Wednesday 8/28/2024 - v3.14.3 + +{{taskList +##### calculuschild, G-Ambatte + +* [x] New {{openSans **IMAGES β†’ {{fac,image-wrap-left}} IMAGE WRAP LEFT/RIGHT**}} snippets + +Fixes issue [#380](https://github.com/naturalcrit/homebrewery/issues/380) + +* [x] Fix v3.14.2 bug with `κž‰κž‰κž‰κž‰` failing after tables + +##### 5e-Cleric + +* [x] Fix Account page crash when not logged in + +Fixes issue [#3605](https://github.com/naturalcrit/homebrewery/issues/3605) + +##### abquintic + +* [x] Fix jump hotkeys conflicting with `CTRL + SHIFT`. Preview and Source movement shortcuts now use `CTRL + SHIFT + META + LEFT\RIGHTARROW` + +##### G-Ambatte + +* [x] Fix display issue with image wrap icons +}} + + +### Tuesday 8/27/2024 - v3.14.2 + +{{taskList +##### calculuschild + +* [x] Reroute invalid urls to homepage + +Fixes issues [#3269](https://github.com/naturalcrit/homebrewery/issues/3629) + +* [x] Background dependency updates + +##### G-Ambatte + +* [x] Add route to get brew styling via `/css/shareId` + +Fixes issues [#1097](https://github.com/naturalcrit/homebrewery/issues/1097) + +* [x] Fix `:emojis:` preventing code folding + +Fixes issues [#3604](https://github.com/naturalcrit/homebrewery/issues/3604) + +* [x] Fix mask image warping when rotated and stretched + +Fixes issues [#3636](https://github.com/naturalcrit/homebrewery/issues/3636) + +* [x] Fix Table of Contents uppercasing + +Fixes issues [#3572](https://github.com/naturalcrit/homebrewery/issues/3572) + +##### abquintic + +* [x] Create globally unique Header IDs across pages + +Fixes issues [#1430](https://github.com/naturalcrit/homebrewery/issues/1430) + +* [x] Fix colon `κž‰κž‰κž‰κž‰` being parsed in codeblocks + +* [x] Prevent crashes when loading undefined renderer or theme bundle + +* [x] Add Jump-To hotkeys + + * Use `CTRL/META + SHIFT + LEFTARROW` to brewJump + * Use `CTRL/META + SHIFT + RIGHTARROW` to sourceJump + +* [x] Prevent reload from clobbering modified fresh clones + +##### 5e-Cleric, Gazook89 + +* [x] Viewer tools for zoom/page navigation +}} + +### Tuesday 8/13/2024 - v3.14.1 + +{{taskList ##### abquintic * [x] Allow Table of Contents to flow across columns @@ -129,16 +435,13 @@ Fixes issues [#3613](https://github.com/naturalcrit/homebrewery/issues/3613) Fixes issues [#3622](https://github.com/naturalcrit/homebrewery/issues/3622) - ##### calculuschild * [x] Fix `/migrate` page using an editor context instead of share context - ##### 5e-Cleric * [x] Fix Monster Stat Blocks losing color in Safari - }} \page @@ -509,7 +812,7 @@ Fixes issue [#2729](https://github.com/naturalcrit/homebrewery/issues/2729), ### Thursday 17/08/2023 - v3.9.2 {{taskList -##### Calculuschild +##### calculuschild * [x] Fix links to certain old Google Drive files @@ -567,7 +870,7 @@ Fixes issue [#1924](https://github.com/naturalcrit/homebrewery/issues/1924) ### Friday 02/06/2023 - v3.9.0 {{taskList -##### Calculuschild +##### calculuschild * [x] Fix some files not showing up on userpage when user has a large number of brews in Google Drive @@ -664,7 +967,7 @@ Fixes issues [#2731](https://github.com/naturalcrit/homebrewery/issues/2731) ### Monday 13/03/2023 - v3.7.2 {{taskList -##### Calculuschild +##### calculuschild * [x] Fix wide Monster Stat Blocks not spanning columns on Legacy }} @@ -687,7 +990,7 @@ Fixes issues [#1569](https://github.com/naturalcrit/homebrewery/issues/1569) * [x] Updated the Google Drive icon * [x] Backend fix to unit tests failing intermittently -##### Calculuschild +##### calculuschild * [x] Fix PDF pixelation on CoverPage text outlines }} @@ -699,7 +1002,7 @@ Fixes issues [#1569](https://github.com/naturalcrit/homebrewery/issues/1569) **NOTE:** Some new snippets will now show a {{beta BETA}} tag. Feel free to use them, but be aware we may change how they work depending on your feedback. }} -##### Calculuschild +##### calculuschild * [x] New {{openSans **IMAGES β†’ WATERCOLOR EDGE** {{fac,mask-edge}} }} and {{openSans **WATERCOLOR CORNER** {{fac,mask-corner}} }} snippets for V3, which adds a stylish watercolor texture to the edge of your images! (Thanks to /u/flamableconcrete on Reddit for providing these image masks!) @@ -843,7 +1146,7 @@ Fixes issues [#1670](https://github.com/naturalcrit/homebrewery/issues/1670) ### Thursday 28/10/2022 - v3.3.1 {{taskList -##### Calculuschild +##### calculuschild * [x] Fixes to several broken CSS styles from v3.3.0 @@ -858,7 +1161,7 @@ Fixes issues [#2468](https://github.com/naturalcrit/homebrewery/issues/2468) ### Friday 19/10/2022 - v3.3.0 {{taskList -##### Calculuschild +##### calculuschild * [x] Fix for tables broken by Chrome v106 @@ -941,7 +1244,7 @@ Fixes issues [#2317](https://github.com/naturalcrit/homebrewery/issues/2317), [ ### Wednesday 31/08/2022 - v3.2.1 {{taskList -##### Calculuschild +##### calculuschild * [x] Reference Links should now work inside tables @@ -967,7 +1270,7 @@ Fixes issues [#2317](https://github.com/naturalcrit/homebrewery/issues/2317), [ ### Saturday 27/08/2022 - v3.2.0 {{taskList -##### Calculuschild +##### calculuschild * [x] The V3 renderer is now the default for new brews. @@ -994,7 +1297,7 @@ Fixes issues [#2317](https://github.com/naturalcrit/homebrewery/issues/2317), [ ### Thursday 09/06/2022 - v3.1.1 {{taskList -##### Calculuschild: +##### calculuschild: * [x] Fixed class table decorations appearing on top of the table in PDF output. diff --git a/client/admin/admin.jsx b/client/admin/admin.jsx index 92e0b2aee..f2f2667a4 100644 --- a/client/admin/admin.jsx +++ b/client/admin/admin.jsx @@ -2,35 +2,44 @@ require('./admin.less'); const React = require('react'); const createClass = require('create-react-class'); +const BrewUtils = require('./brewUtils/brewUtils.jsx'); +const NotificationUtils = require('./notificationUtils/notificationUtils.jsx'); -const BrewCleanup = require('./brewCleanup/brewCleanup.jsx'); -const BrewLookup = require('./brewLookup/brewLookup.jsx'); -const BrewCompress = require ('./brewCompress/brewCompress.jsx'); -const Stats = require('./stats/stats.jsx'); +const tabGroups = ['brew', 'notifications']; const Admin = createClass({ getDefaultProps : function() { return {}; }, + getInitialState : function(){ + return ({ + currentTab : 'brew' + }); + }, + + handleClick : function(newTab){ + if(this.state.currentTab === newTab) return; + this.setState({ + currentTab : newTab + }); + }, + render : function(){ return
-
homebrewery admin
-
- -
- -
- -
- -
+
+ + {this.state.currentTab==='brew' && } + {this.state.currentTab==='notifications' && } +
; } }); diff --git a/client/admin/admin.less b/client/admin/admin.less index a61335835..c6c9b4662 100644 --- a/client/admin/admin.less +++ b/client/admin/admin.less @@ -6,39 +6,95 @@ @import 'font-awesome/css/font-awesome.css'; -html,body, #reactContainer, .naturalCrit{ - min-height : 100%; -} +html,body, #reactContainer, .naturalCrit { min-height : 100%; } @sidebarWidth : 250px; -body{ - background-color : #eee; - font-family : 'Open Sans', sans-serif; - color : #4b5055; - font-weight : 100; - text-rendering : optimizeLegibility; - margin : 0; +body { + height : 100%; padding : 0; - height : 100%; + margin : 0; + font-family : 'Open Sans', sans-serif; + font-weight : 100; + color : #4B5055; + background-color : #EEEEEE; + text-rendering : optimizeLegibility; } -.admin{ +:where(.admin) { - header{ + header { + padding : 20px 0px; + margin-bottom : 30px; + font-size : 2em; + color : white; background-color : @red; - font-size: 2em; - padding : 20px 0px; - color : white; - margin-bottom: 30px; - i{ - margin-right: 30px; + i { margin-right : 30px; } + } + + hr { margin : 30px 0px; } + + :where(.container) { + input { + height : 33px; + padding : 0px 10px; + margin-bottom : 20px; + font-family : monospace; + } + + button { + height : 37px; + vertical-align : middle; + } + + dl { + @maxItemWidth : 132px; + dt { + float : left; + width : @maxItemWidth; + clear : left; + text-align : right; + &::after { content : ' : '; } + } + dd { + height : 1em; + padding : 0 0 0.5em 0; + margin-left : @maxItemWidth + 6px; + } + } + + .tabs button { + margin-right : 3px; + margin-left : 3px; + color : black; + background-color : #EEEEEE; + border : 1px solid #444444; + border-radius : 5px; + &:hover { + color : #EEEEEE; + background-color : #444444; + } + &.active { + margin-right : 2px; + margin-left : 2px; + text-decoration : underline; + background-color : #CCCCCC; + border : 2px solid #444444; + } + } + + .notificationUtils { + display : flex; + gap : 50px; + justify-content : space-between; } } - hr{ - margin : 30px 0px; + .error { + background: rgb(178, 54, 54); + color:white; + font-weight: 900; + margin-block:10px; + padding:10px; } - - } diff --git a/client/admin/brewCleanup/brewCleanup.less b/client/admin/brewCleanup/brewCleanup.less deleted file mode 100644 index ec7582855..000000000 --- a/client/admin/brewCleanup/brewCleanup.less +++ /dev/null @@ -1,10 +0,0 @@ -.BrewCleanup{ - .removeBox{ - margin-top: 20px; - button{ - background-color: @red; - margin-right: 10px; - } - } - -} \ No newline at end of file diff --git a/client/admin/brewCompress/brewCompress.less b/client/admin/brewCompress/brewCompress.less deleted file mode 100644 index 2a2bf42ea..000000000 --- a/client/admin/brewCompress/brewCompress.less +++ /dev/null @@ -1,10 +0,0 @@ -.BrewCompress{ - .removeBox{ - margin-top: 20px; - button{ - background-color: @red; - margin-right: 10px; - } - } - -} \ No newline at end of file diff --git a/client/admin/brewLookup/brewLookup.less b/client/admin/brewLookup/brewLookup.less deleted file mode 100644 index 61eeec424..000000000 --- a/client/admin/brewLookup/brewLookup.less +++ /dev/null @@ -1,30 +0,0 @@ - -.brewLookup{ - input{ - height : 33px; - margin-bottom : 20px; - padding : 0px 10px; - font-family : monospace; - } - button{ - vertical-align : middle; - height : 37px; - } - dl{ - @maxItemWidth : 132px; - dt{ - float : left; - clear : left; - width : @maxItemWidth; - text-align : right; - &::after { - content: " : "; - } - } - dd{ - height : 1em; - margin-left : @maxItemWidth + 6px; - padding : 0 0 0.5em 0; - } - } -} \ No newline at end of file diff --git a/client/admin/brewCleanup/brewCleanup.jsx b/client/admin/brewUtils/brewCleanup/brewCleanup.jsx similarity index 100% rename from client/admin/brewCleanup/brewCleanup.jsx rename to client/admin/brewUtils/brewCleanup/brewCleanup.jsx diff --git a/client/admin/brewUtils/brewCleanup/brewCleanup.less b/client/admin/brewUtils/brewCleanup/brewCleanup.less new file mode 100644 index 000000000..16fc98957 --- /dev/null +++ b/client/admin/brewUtils/brewCleanup/brewCleanup.less @@ -0,0 +1,9 @@ +.BrewCleanup { + .removeBox { + margin-top : 20px; + button { + margin-right : 10px; + background-color : @red; + } + } +} \ No newline at end of file diff --git a/client/admin/brewCompress/brewCompress.jsx b/client/admin/brewUtils/brewCompress/brewCompress.jsx similarity index 100% rename from client/admin/brewCompress/brewCompress.jsx rename to client/admin/brewUtils/brewCompress/brewCompress.jsx diff --git a/client/admin/brewUtils/brewCompress/brewCompress.less b/client/admin/brewUtils/brewCompress/brewCompress.less new file mode 100644 index 000000000..8668e9280 --- /dev/null +++ b/client/admin/brewUtils/brewCompress/brewCompress.less @@ -0,0 +1,9 @@ +.BrewCompress { + .removeBox { + margin-top : 20px; + button { + margin-right : 10px; + background-color : @red; + } + } +} \ No newline at end of file diff --git a/client/admin/brewLookup/brewLookup.jsx b/client/admin/brewUtils/brewLookup/brewLookup.jsx similarity index 63% rename from client/admin/brewLookup/brewLookup.jsx rename to client/admin/brewUtils/brewLookup/brewLookup.jsx index c9212d990..e5b585ced 100644 --- a/client/admin/brewLookup/brewLookup.jsx +++ b/client/admin/brewUtils/brewLookup/brewLookup.jsx @@ -1,4 +1,5 @@ require('./brewLookup.less'); + const React = require('react'); const createClass = require('create-react-class'); const cx = require('classnames'); @@ -13,22 +14,43 @@ const BrewLookup = createClass({ }, getInitialState() { return { - query : '', - foundBrew : null, - searching : false, - error : null + query : '', + foundBrew : null, + searching : false, + error : null, + scriptCount : 0 }; }, handleChange(e){ this.setState({ query: e.target.value }); }, lookup(){ - this.setState({ searching: true, error: null }); + this.setState({ searching: true, error: null, scriptCount: 0 }); request.get(`/admin/lookup/${this.state.query}`) - .then((res)=>this.setState({ foundBrew: res.body })) + .then((res)=>{ + const foundBrew = res.body; + const scriptCheck = foundBrew?.text.match(/(<\/?s)cript/g); + this.setState({ + foundBrew : foundBrew, + scriptCount : scriptCheck?.length || 0, + }); + }) .catch((err)=>this.setState({ error: err })) - .finally(()=>this.setState({ searching: false })); + .finally(()=>{ + this.setState({ + searching : false + }); + }); + }, + + async cleanScript(){ + if(!this.state.foundBrew?.shareId) return; + + await request.put(`/admin/clean/script/${this.state.foundBrew.shareId}`) + .catch((err)=>{ this.setState({ error: err }); return; }); + + this.lookup(); }, renderFoundBrew(){ @@ -47,12 +69,23 @@ const BrewLookup = createClass({
Share Link
/share/{brew.shareId}
+
Created Time
+
{brew.createdAt ? Moment(brew.createdAt).toLocaleString() : 'No creation date'}
+
Last Updated
{Moment(brew.updatedAt).fromNow()}
Num of Views
{brew.views}
+ +
SCRIPT tags detected
+
{this.state.scriptCount}
+ {this.state.scriptCount > 0 && +
+ +
+ } ; }, diff --git a/client/admin/brewUtils/brewLookup/brewLookup.less b/client/admin/brewUtils/brewLookup/brewLookup.less new file mode 100644 index 000000000..da15e3a64 --- /dev/null +++ b/client/admin/brewUtils/brewLookup/brewLookup.less @@ -0,0 +1,6 @@ +.brewLookup { + .cleanButton { + display : inline-block; + width : 100%; + } +} \ No newline at end of file diff --git a/client/admin/brewUtils/brewUtils.jsx b/client/admin/brewUtils/brewUtils.jsx new file mode 100644 index 000000000..de8c29895 --- /dev/null +++ b/client/admin/brewUtils/brewUtils.jsx @@ -0,0 +1,24 @@ +const React = require('react'); +const createClass = require('create-react-class'); + + +const BrewCleanup = require('./brewCleanup/brewCleanup.jsx'); +const BrewLookup = require('./brewLookup/brewLookup.jsx'); +const BrewCompress = require ('./brewCompress/brewCompress.jsx'); +const Stats = require('./stats/stats.jsx'); + +const BrewUtils = createClass({ + render : function(){ + return <> + +
+ +
+ +
+ + ; + } +}); + +module.exports = BrewUtils; diff --git a/client/admin/stats/stats.jsx b/client/admin/brewUtils/stats/stats.jsx similarity index 100% rename from client/admin/stats/stats.jsx rename to client/admin/brewUtils/stats/stats.jsx diff --git a/client/admin/brewUtils/stats/stats.less b/client/admin/brewUtils/stats/stats.less new file mode 100644 index 000000000..b5a4612e1 --- /dev/null +++ b/client/admin/brewUtils/stats/stats.less @@ -0,0 +1,13 @@ + +.Stats { + position : relative; + + .pending { + position : absolute; + top : 0px; + left : 0px; + width : 100%; + height : 100%; + background-color : rgba(238,238,238, 0.5); + } +} \ No newline at end of file diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx new file mode 100644 index 000000000..0cca1047e --- /dev/null +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.jsx @@ -0,0 +1,109 @@ +require('./notificationAdd.less'); +const React = require('react'); +const { useState, useRef } = require('react'); +const request = require('superagent'); + +const NotificationAdd = ()=>{ + const [notificationResult, setNotificationResult] = useState(null); + const [searching, setSearching] = useState(false); + const [error, setError] = useState(null); + + const dismissKeyRef = useRef(null); + const titleRef = useRef(null); + const textRef = useRef(null); + const startAtRef = useRef(null); + const stopAtRef = useRef(null); + + const saveNotification = async ()=>{ + const dismissKey = dismissKeyRef.current.value; + const title = titleRef.current.value; + const text = textRef.current.value; + const startAt = new Date(startAtRef.current.value); + const stopAt = new Date(stopAtRef.current.value); + + // Basic validation + if(!dismissKey || !title || !text || isNaN(startAt.getTime()) || isNaN(stopAt.getTime())) { + setError('All fields are required'); + return; + } + if(startAt >= stopAt) { + setError('End date must be after the start date!'); + return; + } + + const data = { + dismissKey, + title, + text, + startAt : startAt?.toISOString() ?? '', + stopAt : stopAt?.toISOString() ?? '', + }; + + try { + setSearching(true); + setError(null); + const response = await request.post('/admin/notification/add').send(data); + console.log(response.body); + + // Reset form fields + dismissKeyRef.current.value = ''; + titleRef.current.value = ''; + textRef.current.value = ''; + + setNotificationResult('Notification successfully created.'); + setSearching(false); + } catch (err) { + console.log(err.response.body.message); + setError(`Error saving notification: ${err.response.body.message}`); + setSearching(false); + } + }; + + return ( +
+

Add Notification

+ + + + + + + + + + + +
{notificationResult}
+ + + {error &&
{error}
} +
+ ); +}; + +module.exports = NotificationAdd; diff --git a/client/admin/notificationUtils/notificationAdd/notificationAdd.less b/client/admin/notificationUtils/notificationAdd/notificationAdd.less new file mode 100644 index 000000000..878da24c2 --- /dev/null +++ b/client/admin/notificationUtils/notificationAdd/notificationAdd.less @@ -0,0 +1,37 @@ +.notificationAdd { + position : relative; + display : flex; + flex-direction : column; + width : 500px; + + .field { + display : grid; + grid-template-columns : 120px 150px; + align-items : center; + justify-items : stretch; + width : 100%; + margin-bottom : 20px; + + + input { + height : 33px; + padding : 0px 10px; + margin-bottom : unset; + font-family : monospace; + } + + textarea { + width : 50ch; + min-height : 7em; + max-height : 20em; + resize : vertical; + padding : 10px; + } + } + + button { + width: 200px; + + i { margin-right : 10px; } + } +} \ No newline at end of file diff --git a/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx new file mode 100644 index 000000000..05f81b776 --- /dev/null +++ b/client/admin/notificationUtils/notificationLookup/notificationLookup.jsx @@ -0,0 +1,105 @@ +require('./notificationLookup.less'); + +const React = require('react'); +const { useState } = require('react'); +const request = require('superagent'); +const Moment = require('moment'); + +const NotificationDetail = ({ notification, onDelete })=>( + <> +
+
Key
+
{notification.dismissKey}
+ +
Title
+
{notification.title || 'No Title'}
+ +
Created
+
{Moment(notification.createdAt).format('LLLL')}
+ +
Start
+
{Moment(notification.startAt).format('LLLL') || 'No Start Time'}
+ +
Stop
+
{Moment(notification.stopAt).format('LLLL') || 'No End Time'}
+ +
Text
+
{notification.text || 'No Text'}
+
+ + +); + +const NotificationLookup = ()=>{ + const [searching, setSearching] = useState(false); + const [error, setError] = useState(null); + const [notifications, setNotifications] = useState([]); + + const lookupAll = async ()=>{ + setSearching(true); + setError(null); + + try { + const res = await request.get('/admin/notification/all'); + setNotifications(res.body || []); + } catch (err) { + console.log(err); + setError(`Error looking up notifications: ${err.response.body.message}`); + } finally { + setSearching(false); + } + }; + + const deleteNotification = async (dismissKey)=>{ + if(!dismissKey) return; + + const confirmed = window.confirm( + `Really delete notification ${dismissKey}?` + ); + if(!confirmed) { + console.log('Delete notification cancelled'); + return; + } + console.log('Delete notification confirm'); + try { + await request.delete(`/admin/notification/delete/${dismissKey}`); + lookupAll(); + } catch (err) { + console.log(err); + setError(`Error deleting notification: ${err.response.body.message}`); + }; + }; + + const renderNotificationsList = ()=>{ + if(error) + return
{error}
; + + if(notifications.length === 0) + return
No notifications available.
; + + return ( +
    + {notifications.map((notification)=>( +
  • +
    + {notification.title || 'No Title'} + +
    +
  • + ))} +
+ ); + }; + + return ( +
+

Check all Notifications

+ + {renderNotificationsList()} +
+ ); +}; + +module.exports = NotificationLookup; diff --git a/client/admin/notificationUtils/notificationLookup/notificationLookup.less b/client/admin/notificationUtils/notificationLookup/notificationLookup.less new file mode 100644 index 000000000..3f9b78310 --- /dev/null +++ b/client/admin/notificationUtils/notificationLookup/notificationLookup.less @@ -0,0 +1,40 @@ + +.notificationLookup { + width : 450px; + height : fit-content; + + .notificationList { + display : flex; + flex-direction : column; + max-height : 500px; + margin-block : 20px; + overflow : auto; + border : 1px solid; + border-radius : 5px; + + li { + padding : 10px; + background : #CCCCCC; + + &:nth-child(even) { background : #DDDDDD; } + &:first-child { + border-top-left-radius : 5px; + border-top-right-radius : 5px; + } + &:last-child { + border-bottom-right-radius : 5px; + border-bottom-left-radius : 5px; + } + + summary { + font-size : 20px; + font-weight : 900; + } + + dl dt{ + font-weight: 900; + } + } + } + .noNotification { margin-block : 20px; } +} \ No newline at end of file diff --git a/client/admin/notificationUtils/notificationUtils.jsx b/client/admin/notificationUtils/notificationUtils.jsx new file mode 100644 index 000000000..22ea21328 --- /dev/null +++ b/client/admin/notificationUtils/notificationUtils.jsx @@ -0,0 +1,15 @@ +const React = require('react'); + +const NotificationLookup = require('./notificationLookup/notificationLookup.jsx'); +const NotificationAdd = require('./notificationAdd/notificationAdd.jsx'); + +const NotificationUtils = ()=>{ + return ( +
+ + +
+ ); +}; + +module.exports = NotificationUtils; diff --git a/client/admin/stats/stats.less b/client/admin/stats/stats.less deleted file mode 100644 index 5337bf671..000000000 --- a/client/admin/stats/stats.less +++ /dev/null @@ -1,28 +0,0 @@ - -.Stats{ - position : relative; - .pending{ - position : absolute; - top : 0px; - left : 0px; - height : 100%; - width : 100%; - background-color : rgba(238,238,238, 0.5); - } - dl{ - @maxItemWidth : 132px; - dt{ - float : left; - clear : left; - width : @maxItemWidth; - text-align : right; - &::after { - content: " : "; - } - } - dd{ - margin : 0 0 0 @maxItemWidth + 10px; - padding : 0 0 0.5em 0; - } - } -} \ No newline at end of file diff --git a/client/components/Anchored.jsx b/client/components/Anchored.jsx new file mode 100644 index 000000000..87af5a6e1 --- /dev/null +++ b/client/components/Anchored.jsx @@ -0,0 +1,96 @@ +import React, { useState, useRef, forwardRef, useEffect, cloneElement, Children } from 'react'; +import './Anchored.less'; + +// Anchored is a wrapper component that must have as children an and a component. +// AnchoredTrigger must have a unique `id` prop, which is passed up to Anchored, saved in state on mount, and +// then passed down through props into AnchoredBox. The `id` is used for the CSS Anchor Positioning properties. +// **The Anchor Positioning API is not available in Firefox yet** +// So in Firefox the positioning isn't perfect but is likely sufficient, and FF team seems to be working on the API quickly. + +// When Anchor Positioning is added to Firefox, this can also be rewritten using the Popover API-- add the `popover` attribute +// to the container div, which will render the container in the *top level* and give it better interactions like +// click outside to dismiss. **Do not** add without Anchor, though, because positioning is very limited with the `popover` +// attribute. + + +const Anchored = ({ children })=>{ + const [visible, setVisible] = useState(false); + const [anchorId, setAnchorId] = useState(null); + const boxRef = useRef(null); + const triggerRef = useRef(null); + + // promote trigger id to Anchored id (to pass it back down to the box as "anchorId") + useEffect(()=>{ + if(triggerRef.current){ + setAnchorId(triggerRef.current.id); + } + }, []); + + // close box on outside click or Escape key + useEffect(()=>{ + const handleClickOutside = (evt)=>{ + if( + boxRef.current && + !boxRef.current.contains(evt.target) && + triggerRef.current && + !triggerRef.current.contains(evt.target) + ) { + setVisible(false); + } + }; + + const handleEscapeKey = (evt)=>{ + if(evt.key === 'Escape') setVisible(false); + }; + + window.addEventListener('click', handleClickOutside); + window.addEventListener('keydown', handleEscapeKey); + + return ()=>{ + window.removeEventListener('click', handleClickOutside); + window.removeEventListener('keydown', handleEscapeKey); + }; + }, []); + + const toggleVisibility = ()=>setVisible((prev)=>!prev); + + // Map children to inject necessary props + const mappedChildren = Children.map(children, (child)=>{ + if(child.type === AnchoredTrigger) { + return cloneElement(child, { ref: triggerRef, toggleVisibility, visible }); + } + if(child.type === AnchoredBox) { + return cloneElement(child, { ref: boxRef, visible, anchorId }); + } + return child; + }); + + return <>{mappedChildren}; +}; + +// forward ref for AnchoredTrigger +const AnchoredTrigger = forwardRef(({ toggleVisibility, visible, children, className, ...props }, ref)=>( + +)); + +// forward ref for AnchoredBox +const AnchoredBox = forwardRef(({ visible, children, className, anchorId, ...props }, ref)=>( +
+ {children} +
+)); + +export { Anchored, AnchoredTrigger, AnchoredBox }; diff --git a/client/components/Anchored.less b/client/components/Anchored.less new file mode 100644 index 000000000..4f0e2fa8f --- /dev/null +++ b/client/components/Anchored.less @@ -0,0 +1,13 @@ + + +.anchored-box { + position:absolute; + @supports (inset-block-start: anchor(bottom)){ + inset-block-start: anchor(bottom); + } + justify-self: anchor-center; + visibility: hidden; + &.active { + visibility: visible; + } +} \ No newline at end of file diff --git a/client/components/dialog.jsx b/client/components/dialog.jsx index 2057ecb87..e88d06c99 100644 --- a/client/components/dialog.jsx +++ b/client/components/dialog.jsx @@ -1,22 +1,24 @@ // Dialog box, for popups and modal blocking messages -const React = require('react'); +import React from 'react'; const { useRef, useEffect } = React; -function Dialog({ dismissKey, closeText = 'Close', blocking = false, ...rest }) { +function Dialog({ dismisskeys = [], closeText = 'Close', blocking = false, ...rest }) { const dialogRef = useRef(null); useEffect(()=>{ - if(!dismissKey || !localStorage.getItem(dismissKey)) { - blocking ? dialogRef.current?.showModal() : dialogRef.current?.show(); - } + blocking ? dialogRef.current?.showModal() : dialogRef.current?.show(); }, []); const dismiss = ()=>{ - dismissKey && localStorage.setItem(dismissKey, true); + dismisskeys.forEach((key)=>{ + if(key) { + localStorage.setItem(key, 'true'); + } + }); dialogRef.current?.close(); }; - return ( + return ( {rest.children} + ); +}; module.exports = ErrorBar; diff --git a/client/homebrew/brewRenderer/errorBar/errorBar.less b/client/homebrew/brewRenderer/errorBar/errorBar.less index f3f2dbaae..163648533 100644 --- a/client/homebrew/brewRenderer/errorBar/errorBar.less +++ b/client/homebrew/brewRenderer/errorBar/errorBar.less @@ -1,60 +1,58 @@ -.errorBar{ +.errorBar { position : absolute; - z-index : 10000; - box-sizing : border-box; + top : 32px; + z-index : 1; width : 100%; - margin-right : 13px; - padding : 20px; - padding-bottom : 10px; - padding-left : 100px; - background-color : @red; color : white; - i{ - position : absolute; - left : 30px; - opacity : 0.8; - font-size : 3em; - } - h3{ - font-size : 1.1em; - font-weight : 800; - } - ul{ - margin-top : 15px; - font-size : 0.8em; - list-style-position : inside; - list-style-type : disc; - li{ - line-height : 1.6em; + background-color : @red; + border : unset; + + div { + > i { + float : left; + margin-right : 10px; + margin-bottom : 20px; + font-size : 3em; + opacity : 0.8; + } + h2 { font-weight : 800; } + ul { + margin-top : 15px; + font-size : 0.8em; + list-style-position : inside; + list-style-type : disc; + li { line-height : 1.6em; } } } - hr{ - box-sizing : border-box; + hr { height : 2px; - width : 150%; margin-top : 25px; margin-bottom : 15px; - margin-left : -100px; background-color : darken(@red, 8%); border : none; } - small{ - font-size: 0.6em; - opacity: 0.7; + small { + font-size : 0.6em; + opacity : 0.7; } - .protips{ - margin-left : -80px; - font-size : 0.6em; - &>div{ - margin-bottom : 10px; - line-height : 1.2em; - } - h4{ - opacity : 0.8; + .protips { + font-size : 0.6em; + line-height : 1.2em; + h4 { font-weight : 800; line-height : 1.5em; text-transform : uppercase; } } + button.dismiss { + position : absolute; + top : 20px; + right : 30px; + padding : unset; + font-size : 40px; + background-color : transparent; + opacity : 0.6; + &:hover { opacity : 1; } + } } \ No newline at end of file diff --git a/client/homebrew/brewRenderer/headerNav/headerNav.jsx b/client/homebrew/brewRenderer/headerNav/headerNav.jsx new file mode 100644 index 000000000..68963129f --- /dev/null +++ b/client/homebrew/brewRenderer/headerNav/headerNav.jsx @@ -0,0 +1,106 @@ +require('./headerNav.less'); + +import * as React from 'react'; +import * as _ from 'lodash'; + + +const MAX_TEXT_LENGTH = 40; + +const HeaderNav = React.forwardRef(({}, pagesRef)=>{ + + const renderHeaderLinks = ()=>{ + if(!pagesRef.current) return; + + const selector = [ + '.pages > .page', // All page elements, which by definition have IDs + '.page:not(:has(.toc)) > [id]', // All direct children of non-ToC .page with an ID (Legacy) + '.page:not(:has(.toc)) > .columnWrapper > [id]', // All direct children of non-ToC .page > .columnWrapper with an ID (V3) + '.page:not(:has(.toc)) h2', // All non-ToC H2 titles, like Monster frame titles + ]; + const elements = pagesRef.current.querySelectorAll(selector.join(',')); + if(!elements) return; + const navList = []; + + // navList is a list of objects which have the following structure: + // { + // depth : how deeply indented the item should be + // text : the text to display in the nav link + // link : the hyperlink to navigate to when clicked + // className : [optional] the class to apply to the nav link for styling + // } + + elements.forEach((el)=>{ + if(el.className.match(/\bpage\b/)) { + let text = `Page ${el.id.slice(1)}`; // The ID of a page *should* always be equal to `p` followed by the page number + if(el.querySelector('.toc')){ // If the page contains a table of contents, add "- Contents" to the display text + text += ' - Contents'; + }; + navList.push({ + depth : 0, // Pages are always at the least indented level + text : text, + link : el.id, + className : 'pageLink' + }); + return; + } + if(el.localName.match(/^h[1-6]/)){ // Header elements H1 through H6 + navList.push({ + depth : el.localName[1], // Depth is set by the header level + text : el.textContent, // Use `textContent` because `innerText` is affected by rendering, e.g. 'content-visibility: auto' + link : el.id + }); + return; + } + navList.push({ + depth : 7, // All unmatched elements with IDs are set to the maximum depth (7) + text : el.textContent, // Use `textContent` because `innerText` is affected by rendering, e.g. 'content-visibility: auto' + link : el.id + }); + }); + + return _.map(navList, (navItem, index)=>{ + return ; + }); + + }; + + return ; +} +); + +const HeaderNavItem = ({ link, text, depth, className })=>{ + + const trimString = (text, prefixLength = 0)=>{ + // Sanity check nav link strings + let output = text; + + // If the string has a line break, only use the first line + if(text.indexOf('\n')){ + output = text.split('\n')[0]; + } + + // Trim unecessary spaces from string + output = output.trim(); + + // Reduce excessively long strings + const maxLength = MAX_TEXT_LENGTH - prefixLength; + if(output.length > maxLength){ + return `${output.slice(0, maxLength).trim()}...`; + } + return output; + }; + + if(!link || !text) return; + + return
  • + + {trimString(text, depth)} + +
  • ; +}; + +export default HeaderNav; \ No newline at end of file diff --git a/client/homebrew/brewRenderer/headerNav/headerNav.less b/client/homebrew/brewRenderer/headerNav/headerNav.less new file mode 100644 index 000000000..8b35041d9 --- /dev/null +++ b/client/homebrew/brewRenderer/headerNav/headerNav.less @@ -0,0 +1,47 @@ +.headerNav { + position: fixed; + top: 32px; + left: 0px; + padding: 5px 10px; + background-color: #ccc; + border-radius: 5px; + max-height: calc(100vh - 32px); + max-width: 40vw; + overflow-y: auto; + &.active { + padding-bottom: 10px; + .navIcon { + padding-bottom: 10px; + } + } + .navIcon { + cursor: pointer; + } + li { + list-style-type: none; + a { + display: inline-block; + width: 100%; + font-family: 'Open Sans'; + font-size: 12px; + padding: 2px; + color: inherit; + text-decoration: none; + cursor: pointer; + &:hover { + text-decoration: underline; + } + &.pageLink { + font-weight: 900; + } + + @depths: 1,2,3,4,5,6,7; + + each(@depths, { + &.depth-@{value} { + padding-left: ((@value - 1) * 0.5em); + } + }); + } + } +} \ No newline at end of file diff --git a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx index cca60bbec..38a85e0c7 100644 --- a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx +++ b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.jsx @@ -1,43 +1,63 @@ require('./notificationPopup.less'); -const React = require('react'); -const _ = require('lodash'); +import React, { useEffect, useState } from 'react'; +import request from '../../utils/request-middleware.js'; +import Markdown from 'naturalcrit/markdown.js'; import Dialog from '../../../components/dialog.jsx'; -const DISMISS_KEY = 'dismiss_notification12-04-23'; const DISMISS_BUTTON = ; const NotificationPopup = ()=>{ - return + const [notifications, setNotifications] = useState([]); + const [dissmissKeyList, setDismissKeyList] = useState([]); + const [error, setError] = useState(null); + + useEffect(()=>{ + getNotifications(); + }, []); + + const getNotifications = async ()=>{ + setError(null); + try { + const res = await request.get('/admin/notification/all'); + pickActiveNotifications(res.body || []); + } catch (err) { + console.log(err); + setError(`Error looking up notifications: ${err?.response?.body?.message || err.message}`); + } + }; + + const pickActiveNotifications = (notifs)=>{ + const now = new Date(); + const filteredNotifications = notifs.filter((notification)=>{ + const startDate = new Date(notification.startAt); + const stopDate = new Date(notification.stopAt); + const dismissed = localStorage.getItem(notification.dismissKey) ? true : false; + return now >= startDate && now <= stopDate && !dismissed; + }); + setNotifications(filteredNotifications); + setDismissKeyList(filteredNotifications.map((notif)=>notif.dismissKey)); + }; + + const renderNotificationsList = ()=>{ + if(error) return
    {error}
    ; + return notifications.map((notification)=>( +
  • + {notification.title}
    +

    +
  • + )); + }; + + if(!notifications.length) return; + return

    Notice

    This website is always improving and we are still adding new features and squashing bugs. Keep the following in mind:
      -
    • - Don't store IMAGES in Google Drive
      - Google Drive is not an image service, and will block images from being used - in brews if they get more views than expected. Google has confirmed they won't fix - this, so we recommend you look for another image hosting service such as imgur, ImgBB or Google Photos. -
    • - -
    • - Don't delete your Homebrewery folder on Google Drive!
      - We have had several reports of users losing their brews, not realizing - that they had deleted the files on their Google Drive. If you have a Homebrewery folder - on your Google Drive with *.txt files inside, do not delete it! - We cannot help you recover files that you have deleted from your own - Google Drive. -
    • - -
    • - Protect your work!
      - If you opt not to use your Google Drive, keep in mind that we do not save a history of your projects. Please make frequent backups of your brews!  - - See the FAQ - to learn how to avoid losing your work! -
    • + {renderNotificationsList()}
    ; }; diff --git a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.less b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.less index 26d764aff..be642f0fe 100644 --- a/client/homebrew/brewRenderer/notificationPopup/notificationPopup.less +++ b/client/homebrew/brewRenderer/notificationPopup/notificationPopup.less @@ -1,9 +1,10 @@ .popups { position : fixed; - top : @navbarHeight; + top : calc(@navbarHeight + @viewerToolsHeight); right : 24px; z-index : 10001; width : 450px; + margin-top : 5px; } .notificationPopup { @@ -47,14 +48,46 @@ } ul { margin-top : 15px; - font-size : 0.8em; + font-size : 0.9em; list-style-position : outside; list-style-type : disc; li { - margin-top : 1.4em; - font-size : 0.8em; - line-height : 1.4em; - em { font-weight : 800; } + padding-left : 1em; + margin-top : 1.5em; + font-size : 0.9em; + line-height : 1.5em; + em { + font-weight : 800; + text-transform : capitalize; + } + li { + margin-top : 0; + line-height : 1.2em; + list-style-type : square; + } + } + ul ul,ol ol,ul ol,ol ul { + margin-bottom : 0px; + margin-left : 1.5em; } } -} + + /* Markdown styling */ + code { + padding : 0.1em 0.5em; + font-family : 'Courier New', 'Courier', monospace; + overflow-wrap : break-word; + white-space : pre-wrap; + background : #08115A; + border-radius : 2px; + } + pre code { + display : inline-block; + width : 100%; + } + .blank { + height : 1em; + margin-top : 0; + & + * { margin-top : 0; } + } +} \ No newline at end of file diff --git a/client/homebrew/brewRenderer/safeHTML.js b/client/homebrew/brewRenderer/safeHTML.js new file mode 100644 index 000000000..2574f4cfe --- /dev/null +++ b/client/homebrew/brewRenderer/safeHTML.js @@ -0,0 +1,46 @@ +// Derived from the vue-html-secure package, customized for Homebrewery + +let doc = null; +let div = null; + +function safeHTML(htmlString) { + // If the Document interface doesn't exist, exit + if(typeof document == 'undefined') return null; + // If the test document and div don't exist, create them + if(!doc) doc = document.implementation.createHTMLDocument(''); + if(!div) div = doc.createElement('div'); + + // Set the test div contents to the evaluation string + div.innerHTML = htmlString; + // Grab all nodes from the test div + const elements = div.querySelectorAll('*'); + + // Blacklisted tags + const blacklistTags = ['script', 'noscript', 'noembed']; + // Tests to remove attributes + const blacklistAttrs = [ + (test)=>{return test.localName.indexOf('on') == 0;}, + (test)=>{return test.localName.indexOf('type') == 0 && test.value.match(/submit/i);}, + (test)=>{return test.value.replace(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g, '').toLowerCase().trim().indexOf('javascript:') == 0;} + ]; + + + elements.forEach((element)=>{ + // Check each element for blacklisted type + if(blacklistTags.includes(element?.localName?.toLowerCase())) { + element.remove(); + return; + } + // Check remaining elements for blacklisted attributes + for (const attribute of element.attributes){ + if(blacklistAttrs.some((test)=>{return test(attribute);})) { + element.removeAttribute(attribute.localName); + break; + }; + }; + }); + + return div.innerHTML; +}; + +module.exports.safeHTML = safeHTML; \ No newline at end of file diff --git a/client/homebrew/brewRenderer/toolBar/toolBar.jsx b/client/homebrew/brewRenderer/toolBar/toolBar.jsx new file mode 100644 index 000000000..f11d1f127 --- /dev/null +++ b/client/homebrew/brewRenderer/toolBar/toolBar.jsx @@ -0,0 +1,236 @@ +/* eslint-disable max-lines */ +require('./toolBar.less'); +const React = require('react'); +const { useState, useEffect } = React; +const _ = require('lodash'); + +import { Anchored, AnchoredBox, AnchoredTrigger } from '../../../components/Anchored.jsx'; + +const MAX_ZOOM = 300; +const MIN_ZOOM = 10; + +const ToolBar = ({ displayOptions, onDisplayOptionsChange, visiblePages, totalPages, headerState, setHeaderState })=>{ + + const [pageNum, setPageNum] = useState(1); + const [toolsVisible, setToolsVisible] = useState(true); + + useEffect(()=>{ + // format multiple visible pages as a range (e.g. "150-153") + const pageRange = visiblePages.length === 1 ? `${visiblePages[0]}` : `${visiblePages[0]} - ${visiblePages.at(-1)}`; + setPageNum(pageRange); + }, [visiblePages]); + + const handleZoomButton = (zoom)=>{ + handleOptionChange('zoomLevel', _.round(_.clamp(zoom, MIN_ZOOM, MAX_ZOOM))); + }; + + const handleOptionChange = (optionKey, newValue)=>{ + onDisplayOptionsChange({ ...displayOptions, [optionKey]: newValue }); + }; + + const handlePageInput = (pageInput)=>{ + if(/[0-9]/.test(pageInput)) + setPageNum(parseInt(pageInput)); // input type is 'text', so `page` comes in as a string, not number. + }; + + // scroll to a page, used in the Prev/Next Page buttons. + const scrollToPage = (pageNumber)=>{ + if(typeof pageNumber !== 'number') return; + pageNumber = _.clamp(pageNumber, 1, totalPages); + const iframe = document.getElementById('BrewRenderer'); + const brewRenderer = iframe?.contentWindow?.document.querySelector('.brewRenderer'); + const page = brewRenderer?.querySelector(`#p${pageNumber}`); + page?.scrollIntoView({ block: 'start' }); + }; + + const calculateChange = (mode)=>{ + const iframe = document.getElementById('BrewRenderer'); + const iframeWidth = iframe.getBoundingClientRect().width; + const iframeHeight = iframe.getBoundingClientRect().height; + const pages = iframe.contentWindow.document.getElementsByClassName('page'); + + let desiredZoom = 0; + + if(mode == 'fill'){ + // find widest page, in case pages are different widths, so that the zoom is adapted to not cut the widest page off screen. + const widestPage = _.maxBy([...pages], 'offsetWidth').offsetWidth; + + desiredZoom = (iframeWidth / widestPage) * 100; + + } else if(mode == 'fit'){ + let minDimRatio; + // find the page with the largest single dim (height or width) so that zoom can be adapted to fit it. + if(displayOptions.spread === 'facing') + minDimRatio = [...pages].reduce((minRatio, page)=>Math.min(minRatio, iframeWidth / page.offsetWidth / 2), Infinity); // if 'facing' spread, fit two pages in view + else + minDimRatio = [...pages].reduce((minRatio, page)=>Math.min(minRatio, iframeWidth / page.offsetWidth, iframeHeight / page.offsetHeight), Infinity); + + desiredZoom = minDimRatio * 100; + } + + const margin = 5; // extra space so page isn't edge to edge (not truly "to fill") + + const deltaZoom = (desiredZoom - displayOptions.zoomLevel) - margin; + return deltaZoom; + }; + + return ( + + ); +}; + +module.exports = ToolBar; diff --git a/client/homebrew/brewRenderer/toolBar/toolBar.less b/client/homebrew/brewRenderer/toolBar/toolBar.less new file mode 100644 index 000000000..a24731489 --- /dev/null +++ b/client/homebrew/brewRenderer/toolBar/toolBar.less @@ -0,0 +1,189 @@ +@import (less) './client/icons/customIcons.less'; + +.toolBar { + position : absolute; + z-index : 1; + box-sizing : border-box; + display : flex; + flex-wrap : wrap; + gap : 8px 30px; + align-items : center; + justify-content : center; + width : 100%; + height : auto; + padding : 2px 0; + font-family : 'Open Sans', sans-serif; + font-size : 13px; + color : #CCCCCC; + background-color : #555555; + & > *:not(.toggleButton) { + opacity : 1; + transition : all 0.2s ease; + } + + .group { + box-sizing : border-box; + display : flex; + gap : 0 3px; + align-items : center; + justify-content : center; + height : 28px; + } + + .tool { + display : flex; + align-items : center; + } + + .active, [aria-checked='true'] { background-color : #444444; } + + .anchored-trigger { + &.active { background-color : #444444; } + } + + .anchored-box { + --box-color : #555555; + top : 30px; + display : flex; + flex-direction : column; + gap : 5px; + padding : 15px; + margin-top : 10px; + font-size : 0.8em; + color : #CCCCCC; + background-color : var(--box-color); + border-radius : 5px; + + h1 { + padding-bottom : 0.3em; + margin-bottom : 0.5em; + border-bottom : 1px solid currentColor; + } + + h2 { + padding-bottom : 0.3em; + margin : 1em 0 0.5em 0; + color : lightgray; + border-bottom : 1px solid currentColor; + } + + label { + display : flex; + gap : 6px; + align-items : center; + justify-content : space-between; + + } + input { + height : unset; + &[type='range'] { padding : 0; } + } + &::before { + position : absolute; + top : -20px; + left : 50%; + width : 0px; + height : 0px; + pointer-events : none; + content : ''; + border : 10px solid transparent; + border-bottom : 10px solid var(--box-color); + transform : translateX(-50%); + } + + } + + .radio-group:has(button[role='radio']) { + display : flex; + height : 100%; + border : 1px solid #333333; + } + + input { + position : relative; + height : 1.5em; + padding : 2px 5px; + font-family : 'Open Sans', sans-serif; + color : inherit; + background : #3B3B3B; + border : none; + &:focus { outline : 1px solid #D3D3D3; } + + // `.range-input` if generic to all range inputs, or `#zoom-slider` if only for zoom slider + &.range-input { + padding : 2px 0; + color : #D3D3D3; + accent-color : #D3D3D3; + + &::-webkit-slider-thumb, &::-moz-range-thumb { + width : 5px; + height : 5px; + cursor : ew-resize; + outline : none; + } + + &.hover-tooltip[value]:hover::after { + position : absolute; + bottom : -30px; + left : 50%; + z-index : 1; + display : grid; + place-items : center; + width : 4ch; + height : 1.2lh; + pointer-events : none; + content : attr(value); + background-color : #555555; + border : 1px solid #A1A1A1; + transform : translate(-50%, 50%); + } + } + + // `.text-input` if generic to all range inputs, or `#page-input` if only for current page input + &#page-input { + min-width : 5ch; + margin-right : 1ch; + text-align : center; + } + } + + button { + box-sizing : border-box; + display : flex; + align-items : center; + justify-content : center; + width : auto; + min-width : 46px; + height : 100%; + &:hover { background-color : #444444; } + &:focus { border : 1px solid #D3D3D3;outline : none;} + &:disabled { + color : #777777; + background-color : unset !important; + } + i { font-size : 1.2em; } + } + + &.hidden { + flex-wrap : nowrap; + width : 92px; + overflow : hidden; + background-color : unset; + opacity : 0.5; + transition : all 0.3s ease; + & > *:not(.toggleButton) { + opacity : 0; + transition : all 0.2s ease; + } + } +} + +.toggleButton { + position : absolute; + left : 0; + z-index : 5; + width : 32px; + min-width : unset; + height : 100%; + display : flex; +} \ No newline at end of file diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index 41d3f91e4..2d0a26268 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -1,11 +1,10 @@ -/*eslint max-lines: ["warn", {"max": 300, "skipBlankLines": true, "skipComments": true}]*/ +/*eslint max-lines: ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}]*/ require('./editor.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); -const cx = require('classnames'); const dedent = require('dedent-tabs').default; -const Markdown = require('../../../shared/naturalcrit/markdown.js'); +import Markdown from '../../../shared/naturalcrit/markdown.js'; const CodeEditor = require('naturalcrit/codeEditor/codeEditor.jsx'); const SnippetBar = require('./snippetbar/snippetbar.jsx'); @@ -13,7 +12,8 @@ const MetadataEditor = require('./metadataEditor/metadataEditor.jsx'); const EDITOR_THEME_KEY = 'HOMEBREWERY-EDITOR-THEME'; -const SNIPPETBAR_HEIGHT = 25; +const PAGEBREAK_REGEX_V3 = /^(?=\\page(?: *{[^\n{}]*})?$)/m; +const SNIPPETBAR_HEIGHT = 25; const DEFAULT_STYLE_TEXT = dedent` /*=======--- Example CSS styling ---=======*/ /* Any CSS here will apply to your document! */ @@ -22,6 +22,7 @@ const DEFAULT_STYLE_TEXT = dedent` color: black; }`; +let isJumping = false; const Editor = createClass({ displayName : 'Editor', @@ -37,8 +38,15 @@ const Editor = createClass({ onMetaChange : ()=>{}, reportError : ()=>{}, + onCursorPageChange : ()=>{}, + onViewPageChange : ()=>{}, + editorTheme : 'default', - renderer : 'legacy' + renderer : 'legacy', + + currentEditorCursorPageNum : 1, + currentEditorViewPageNum : 1, + currentBrewRendererPageNum : 1, }; }, getInitialState : function() { @@ -56,12 +64,16 @@ const Editor = createClass({ isMeta : function() {return this.state.view == 'meta';}, componentDidMount : function() { + this.updateEditorSize(); this.highlightCustomMarkdown(); window.addEventListener('resize', this.updateEditorSize); document.getElementById('BrewRenderer').addEventListener('keydown', this.handleControlKeys); document.addEventListener('keydown', this.handleControlKeys); + this.codeEditor.current.codeMirror.on('cursorActivity', (cm)=>{this.updateCurrentCursorPage(cm.getCursor());}); + this.codeEditor.current.codeMirror.on('scroll', _.throttle(()=>{this.updateCurrentViewPage(this.codeEditor.current.getTopVisibleLine());}, 200)); + const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY); if(editorTheme) { this.setState({ @@ -75,29 +87,37 @@ const Editor = createClass({ }, componentDidUpdate : function(prevProps, prevState, snapshot) { + this.highlightCustomMarkdown(); - if(prevProps.moveBrew !== this.props.moveBrew) { + if(prevProps.moveBrew !== this.props.moveBrew) this.brewJump(); - }; - if(prevProps.moveSource !== this.props.moveSource) { + + if(prevProps.moveSource !== this.props.moveSource) this.sourceJump(); - }; + + if(this.props.liveScroll) { + if(prevProps.currentBrewRendererPageNum !== this.props.currentBrewRendererPageNum) { + this.sourceJump(this.props.currentBrewRendererPageNum, false); + } else if(prevProps.currentEditorViewPageNum !== this.props.currentEditorViewPageNum) { + this.brewJump(this.props.currentEditorViewPageNum, false); + } else if(prevProps.currentEditorCursorPageNum !== this.props.currentEditorCursorPageNum) { + this.brewJump(this.props.currentEditorCursorPageNum, false); + } + } }, handleControlKeys : function(e){ - if(!(e.ctrlKey || e.metaKey)) return; - console.log(e); + if(!(e.ctrlKey && e.metaKey && e.shiftKey)) return; const LEFTARROW_KEY = 37; const RIGHTARROW_KEY = 39; - if (e.shiftKey && (e.keyCode == RIGHTARROW_KEY)) this.brewJump(); - if (e.shiftKey && (e.keyCode == LEFTARROW_KEY)) this.sourceJump(); - if ((e.keyCode == LEFTARROW_KEY) || (e.keyCode == RIGHTARROW_KEY)) { + if(e.keyCode == RIGHTARROW_KEY) this.brewJump(); + if(e.keyCode == LEFTARROW_KEY) this.sourceJump(); + if(e.keyCode == LEFTARROW_KEY || e.keyCode == RIGHTARROW_KEY) { e.stopPropagation(); e.preventDefault(); } }, - updateEditorSize : function() { if(this.codeEditor.current) { let paneHeight = this.editor.current.parentNode.clientHeight; @@ -106,6 +126,20 @@ const Editor = createClass({ } }, + updateCurrentCursorPage : function(cursor) { + const lines = this.props.brew.text.split('\n').slice(1, cursor.line + 1); + const pageRegex = this.props.brew.renderer == 'V3' ? PAGEBREAK_REGEX_V3 : /\\page/; + const currentPage = lines.reduce((count, line)=>count + (pageRegex.test(line) ? 1 : 0), 1); + this.props.onCursorPageChange(currentPage); + }, + + updateCurrentViewPage : function(topScrollLine) { + const lines = this.props.brew.text.split('\n').slice(1, topScrollLine + 1); + const pageRegex = this.props.brew.renderer == 'V3' ? PAGEBREAK_REGEX_V3 : /\\page/; + const currentPage = lines.reduce((count, line)=>count + (pageRegex.test(line) ? 1 : 0), 1); + this.props.onViewPageChange(currentPage); + }, + handleInject : function(injectText){ this.codeEditor.current?.injectText(injectText, false); }, @@ -114,19 +148,10 @@ const Editor = createClass({ this.props.setMoveArrows(newView === 'text'); this.setState({ view : newView - }, this.updateEditorSize); //TODO: not sure if updateeditorsize needed - }, - - getCurrentPage : function(){ - const lines = this.props.brew.text.split('\n').slice(0, this.codeEditor.current.getCursorPosition().line + 1); - return _.reduce(lines, (r, line)=>{ - if( - (this.props.renderer == 'legacy' && line.indexOf('\\page') !== -1) - || - (this.props.renderer == 'V3' && line.match(/^\\page$/)) - ) r++; - return r; - }, 1); + }, ()=>{ + this.codeEditor.current?.codeMirror.focus(); + this.updateEditorSize(); + }); //TODO: not sure if updateeditorsize needed }, highlightCustomMarkdown : function(){ @@ -136,11 +161,21 @@ const Editor = createClass({ codeMirror.operation(()=>{ // Batch CodeMirror styling + const foldLines = []; + //reset custom text styles - const customHighlights = codeMirror.getAllMarks().filter((mark)=>!mark.__isFold); //Don't undo code folding + const customHighlights = codeMirror.getAllMarks().filter((mark)=>{ + // Record details of folded sections + if(mark.__isFold) { + const fold = mark.find(); + foldLines.push({ from: fold.from?.line, to: fold.to?.line }); + } + return !mark.__isFold; + }); //Don't undo code folding + for (let i=customHighlights.length - 1;i>=0;i--) customHighlights[i].clear(); - let editorPageCount = 2; // start page count from page 2 + let editorPageCount = 1; // start page count from page 1 _.forEach(this.props.brew.text.split('\n'), (line, lineNumber)=>{ @@ -149,9 +184,17 @@ const Editor = createClass({ codeMirror.removeLineClass(lineNumber, 'text'); codeMirror.removeLineClass(lineNumber, 'wrap', 'sourceMoveFlash'); + // Don't process lines inside folded text + // If the current lineNumber is inside any folded marks, skip line styling + if(foldLines.some((fold)=>lineNumber >= fold.from && lineNumber <= fold.to)) + return; + // Styling for \page breaks if((this.props.renderer == 'legacy' && line.includes('\\page')) || - (this.props.renderer == 'V3' && line.match(/^\\page$/))) { + (this.props.renderer == 'V3' && line.match(PAGEBREAK_REGEX_V3))) { + + if(lineNumber > 0) // Since \page is optional on first line of document, + editorPageCount += 1; // don't use it to increment page count; stay at 1 // add back the original class 'background' but also add the new class '.pageline' codeMirror.addLineClass(lineNumber, 'background', 'pageLine'); @@ -160,8 +203,6 @@ const Editor = createClass({ textContent : editorPageCount }); codeMirror.setBookmark({ line: lineNumber, ch: line.length }, pageCountElement); - - editorPageCount += 1; }; // New Codemirror styling for V3 renderer @@ -172,7 +213,7 @@ const Editor = createClass({ // definition lists if(line.includes('::')){ - if(/^:*$/.test(line) == true){ return }; + if(/^:*$/.test(line) == true){ return; }; const regex = /^([^\n]*?:?\s?)(::[^\n]*)(?:\n|$)/ymd; // the `d` flag, for match indices, throws an ESLint error. let match; while ((match = regex.exec(line)) != null){ @@ -180,10 +221,10 @@ const Editor = createClass({ codeMirror.markText({ line: lineNumber, ch: match.indices[1][0] }, { line: lineNumber, ch: match.indices[1][1] }, { className: 'dt-highlight' }); codeMirror.markText({ line: lineNumber, ch: match.indices[2][0] }, { line: lineNumber, ch: match.indices[2][1] }, { className: 'dd-highlight' }); const ddIndex = match.indices[2][0]; - let colons = /::/g; - let colonMatches = colons.exec(match[2]); + const colons = /::/g; + const colonMatches = colons.exec(match[2]); if(colonMatches !== null){ - codeMirror.markText({ line: lineNumber, ch: colonMatches.index + ddIndex }, { line: lineNumber, ch: colonMatches.index + colonMatches[0].length + ddIndex }, { className: 'dl-colon-highlight'} ) + codeMirror.markText({ line: lineNumber, ch: colonMatches.index + ddIndex }, { line: lineNumber, ch: colonMatches.index + colonMatches[0].length + ddIndex }, { className: 'dl-colon-highlight' }); } } } @@ -193,12 +234,12 @@ const Editor = createClass({ let startIndex = line.indexOf('^'); const superRegex = /\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^/gy; const subRegex = /\^\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^\^/gy; - + while (startIndex >= 0) { superRegex.lastIndex = subRegex.lastIndex = startIndex; let isSuper = false; - let match = subRegex.exec(line) || superRegex.exec(line); - if (match) { + const match = subRegex.exec(line) || superRegex.exec(line); + if(match) { isSuper = !subRegex.lastIndex; codeMirror.markText({ line: lineNumber, ch: match.index }, { line: lineNumber, ch: match.index + match[0].length }, { className: isSuper ? 'superscript' : 'subscript' }); } @@ -248,20 +289,20 @@ const Editor = createClass({ while (startIndex >= 0) { emojiRegex.lastIndex = startIndex; - let match = emojiRegex.exec(line); - if (match) { + const match = emojiRegex.exec(line); + if(match) { let tokens = Markdown.marked.lexer(match[0]); - tokens = tokens[0].tokens.filter(t => t.type == 'emoji') - if (!tokens.length) + tokens = tokens[0].tokens.filter((t)=>t.type == 'emoji'); + if(!tokens.length) return; - let startPos = { line: lineNumber, ch: match.index }; - let endPos = { line: lineNumber, ch: match.index + match[0].length }; + const startPos = { line: lineNumber, ch: match.index }; + const endPos = { line: lineNumber, ch: match.index + match[0].length }; // Iterate over conflicting marks and clear them - var marks = codeMirror.findMarks(startPos, endPos); + const marks = codeMirror.findMarks(startPos, endPos); marks.forEach(function(marker) { - marker.clear(); + if(!marker.__isFold) marker.clear(); }); codeMirror.markText(startPos, endPos, { className: 'emoji' }); } @@ -274,75 +315,93 @@ const Editor = createClass({ } }, - brewJump : function(targetPage=this.getCurrentPage()){ - if(!window) return; - // console.log(`Scroll to: p${targetPage}`); + brewJump : function(targetPage=this.props.currentEditorCursorPageNum, smooth=true){ + if(!window || !this.isText() || isJumping) + return; + + // Get current brewRenderer scroll position and calculate target position const brewRenderer = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer')[0]; const currentPos = brewRenderer.scrollTop; const targetPos = window.frames['BrewRenderer'].contentDocument.getElementById(`p${targetPage}`).getBoundingClientRect().top; - const interimPos = targetPos >= 0 ? -30 : 30; - const bounceDelay = 100; - const scrollDelay = 500; - - if(!this.throttleBrewMove) { - this.throttleBrewMove = _.throttle((currentPos, interimPos, targetPos)=>{ - brewRenderer.scrollTo({ top: currentPos + interimPos, behavior: 'smooth' }); - setTimeout(()=>{ - brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'smooth', block: 'start' }); - }, bounceDelay); - }, scrollDelay, { leading: true, trailing: false }); + const checkIfScrollComplete = ()=>{ + let scrollingTimeout; + clearTimeout(scrollingTimeout); // Reset the timer every time a scroll event occurs + scrollingTimeout = setTimeout(()=>{ + isJumping = false; + brewRenderer.removeEventListener('scroll', checkIfScrollComplete); + }, 150); // If 150 ms pass without a brewRenderer scroll event, assume scrolling is done }; - this.throttleBrewMove(currentPos, interimPos, targetPos); - // const hashPage = (page != 1) ? `p${page}` : ''; - // window.location.hash = hashPage; + isJumping = true; + checkIfScrollComplete(); + brewRenderer.addEventListener('scroll', checkIfScrollComplete); + + if(smooth) { + const bouncePos = targetPos >= 0 ? -30 : 30; //Do a little bounce before scrolling + const bounceDelay = 100; + const scrollDelay = 500; + + if(!this.throttleBrewMove) { + this.throttleBrewMove = _.throttle((currentPos, bouncePos, targetPos)=>{ + brewRenderer.scrollTo({ top: currentPos + bouncePos, behavior: 'smooth' }); + setTimeout(()=>{ + brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'smooth', block: 'start' }); + }, bounceDelay); + }, scrollDelay, { leading: true, trailing: false }); + }; + this.throttleBrewMove(currentPos, bouncePos, targetPos); + } else { + brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'instant', block: 'start' }); + } }, - sourceJump : function(targetLine=null){ - if(this.isText()) { - if(targetLine == null) { - targetLine = 0; + sourceJump : function(targetPage=this.props.currentBrewRendererPageNum, smooth=true){ + if(!this.isText() || isJumping) + return; - const pageCollection = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('page'); - const brewRendererHeight = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer').item(0).getBoundingClientRect().height; + const textSplit = this.props.renderer == 'V3' ? PAGEBREAK_REGEX_V3 : /\\page/; + const textString = this.props.brew.text.split(textSplit).slice(0, targetPage-1).join(textSplit); + const targetLine = textString.match('\n') ? textString.split('\n').length - 1 : -1; - let currentPage = 1; - for (const page of pageCollection) { - if(page.getBoundingClientRect().bottom > (brewRendererHeight / 2)) { - currentPage = parseInt(page.id.slice(1)) || 1; - break; - } + let currentY = this.codeEditor.current.codeMirror.getScrollInfo().top; + let targetY = this.codeEditor.current.codeMirror.heightAtLine(targetLine, 'local', true); + + const checkIfScrollComplete = ()=>{ + let scrollingTimeout; + clearTimeout(scrollingTimeout); // Reset the timer every time a scroll event occurs + scrollingTimeout = setTimeout(()=>{ + isJumping = false; + this.codeEditor.current.codeMirror.off('scroll', checkIfScrollComplete); + }, 150); // If 150 ms pass without a scroll event, assume scrolling is done + }; + + isJumping = true; + checkIfScrollComplete(); + this.codeEditor.current.codeMirror.on('scroll', checkIfScrollComplete); + + if(smooth) { + //Scroll 1/10 of the way every 10ms until 1px off. + const incrementalScroll = setInterval(()=>{ + currentY += (targetY - currentY) / 10; + this.codeEditor.current.codeMirror.scrollTo(null, currentY); + + // Update target: target height is not accurate until within +-10 lines of the visible window + if(Math.abs(targetY - currentY > 100)) + targetY = this.codeEditor.current.codeMirror.heightAtLine(targetLine, 'local', true); + + // End when close enough + if(Math.abs(targetY - currentY) < 1) { + this.codeEditor.current.codeMirror.scrollTo(null, targetY); // Scroll any remaining difference + this.codeEditor.current.setCursorPosition({ line: targetLine + 1, ch: 0 }); + this.codeEditor.current.codeMirror.addLineClass(targetLine + 1, 'wrap', 'sourceMoveFlash'); + clearInterval(incrementalScroll); } - - const textSplit = this.props.renderer == 'V3' ? /^\\page$/gm : /\\page/; - const textString = this.props.brew.text.split(textSplit).slice(0, currentPage-1).join(textSplit); - const textPosition = textString.length; - const lineCount = textString.match('\n') ? textString.slice(0, textPosition).split('\n').length : 0; - - targetLine = lineCount - 1; //Scroll to `\page`, which is one line back. - - let currentY = this.codeEditor.current.codeMirror.getScrollInfo().top; - let targetY = this.codeEditor.current.codeMirror.heightAtLine(targetLine, 'local', true); - - //Scroll 1/10 of the way every 10ms until 1px off. - const incrementalScroll = setInterval(()=>{ - currentY += (targetY - currentY) / 10; - this.codeEditor.current.codeMirror.scrollTo(null, currentY); - - // Update target: target height is not accurate until within +-10 lines of the visible window - if(Math.abs(targetY - currentY > 100)) - targetY = this.codeEditor.current.codeMirror.heightAtLine(targetLine, 'local', true); - - // End when close enough - if(Math.abs(targetY - currentY) < 1) { - this.codeEditor.current.codeMirror.scrollTo(null, targetY); // Scroll any remaining difference - this.codeEditor.current.setCursorPosition({ line: targetLine + 1, ch: 0 }); - this.codeEditor.current.codeMirror.addLineClass(targetLine + 1, 'wrap', 'sourceMoveFlash'); - clearInterval(incrementalScroll); - } - }, 10); - } + }, 10); + } else { + this.codeEditor.current.codeMirror.scrollTo(null, targetY); // Scroll any remaining difference + this.codeEditor.current.setCursorPosition({ line: targetLine + 1, ch: 0 }); + this.codeEditor.current.codeMirror.addLineClass(targetLine + 1, 'wrap', 'sourceMoveFlash'); } }, @@ -443,7 +502,9 @@ const Editor = createClass({ currentEditorTheme={this.state.editorTheme} updateEditorTheme={this.updateEditorTheme} snippetBundle={this.props.snippetBundle} - cursorPos={this.codeEditor.current?.getCursorPosition() || {}} /> + cursorPos={this.codeEditor.current?.getCursorPosition() || {}} + updateBrew={this.props.updateBrew} + /> {this.renderEditor()} diff --git a/client/homebrew/editor/editor.less b/client/homebrew/editor/editor.less index f8dc249d6..b2e96683e 100644 --- a/client/homebrew/editor/editor.less +++ b/client/homebrew/editor/editor.less @@ -2,6 +2,7 @@ .editor { position : relative; width : 100%; + container: editor / inline-size; .codeEditor { height : 100%; diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.jsx b/client/homebrew/editor/metadataEditor/metadataEditor.jsx index 0f1f6ad54..bfc3b8b61 100644 --- a/client/homebrew/editor/metadataEditor/metadataEditor.jsx +++ b/client/homebrew/editor/metadataEditor/metadataEditor.jsx @@ -3,10 +3,10 @@ require('./metadataEditor.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); -const request = require('../../utils/request-middleware.js'); +import request from '../../utils/request-middleware.js'; const Nav = require('naturalcrit/nav/nav.jsx'); const Combobox = require('client/components/combobox.jsx'); -const StringArrayEditor = require('../stringArrayEditor/stringArrayEditor.jsx'); +const TagInput = require('../tagInput/tagInput.jsx'); const Themes = require('themes/themes.json'); @@ -304,17 +304,14 @@ const MetadataEditor = createClass({ onChange={(e)=>this.handleRenderer('V3', e)} /> V3 - - - Click here to see the demo page for the old Legacy renderer! - + Click here to see the demo page for the old Legacy renderer! ; }, render : function(){ return
    -

    Brew

    +

    Properties Editor

    @@ -344,10 +341,11 @@ const MetadataEditor = createClass({ {this.renderThumbnail()}
    - this.handleFieldChange('tags', e)}/> + onChange={(e)=>this.handleFieldChange('tags', e)} + />
    @@ -362,28 +360,25 @@ const MetadataEditor = createClass({ {this.renderRenderOptions()} -
    - -

    Authors

    +

    Authors

    {this.renderAuthors()} - !this.props.metadata.authors?.includes(v)]} placeholder='invite author' unique={true} values={this.props.metadata.invitedAuthors} notes={['Invited author usernames are case sensitive.', 'After adding an invited author, send them the edit link. There, they can choose to accept or decline the invitation.']} - onChange={(e)=>this.handleFieldChange('invitedAuthors', e)}/> + onChange={(e)=>this.handleFieldChange('invitedAuthors', e)} + /> -
    - -

    Privacy

    +

    Privacy

    {this.renderPublish()} - Published homebrews will be publicly viewable and searchable (eventually...) + Published brews are searchable in the Vault and visible on your user page. Unpublished brews are not indexed in the Vault or visible on your user page, but can still be shared and indexed by search engines. You can unpublish a brew any time.
    diff --git a/client/homebrew/editor/metadataEditor/metadataEditor.less b/client/homebrew/editor/metadataEditor/metadataEditor.less index 27ebd88c2..2cff01cfe 100644 --- a/client/homebrew/editor/metadataEditor/metadataEditor.less +++ b/client/homebrew/editor/metadataEditor/metadataEditor.less @@ -1,5 +1,6 @@ @import 'naturalcrit/styles/colors.less'; + .metadataEditor { position : absolute; z-index : 5; @@ -9,12 +10,19 @@ padding : 25px; overflow-y : auto; background-color : #999999; + font-size : 13px; - .sectionHead { + h1 { + margin: 0 0 40px; + font-weight: bold; + text-transform: uppercase; + } + + h2 { margin : 20px 0; - font-weight : 1000; - - &:first-of-type { margin-top : 0; } + font-weight : bold; + border-bottom: 2px solid gray; + color: #555; } & > div { margin-bottom : 10px; } @@ -43,15 +51,21 @@ min-width : 200px; & > label { width : 80px; - font-size : 11px; font-weight : 800; line-height : 1.8em; text-transform : uppercase; + font-size: .9em; } & > .value { flex : 1 1 auto; width : 50px; &:invalid { background : #FFB9B9; } + small { + display : block; + font-size : 0.9em; + font-style : italic; + line-height : 1.4em; + } } input[type='text'], textarea { border : 1px solid gray; @@ -65,6 +79,7 @@ text-overflow : ellipsis; } button { + .colorButton(); padding : 0px 5px; color : white; background-color : black; @@ -78,7 +93,6 @@ textarea.value { height : auto; font-family : 'Open Sans', sans-serif; - font-size : 0.8em; resize : none; } } @@ -87,12 +101,6 @@ z-index : 200; max-width : 150px; } - small { - display : inline-block; - font-size : 0.6em; - font-style : italic; - line-height : 1.4em; - } } @@ -113,18 +121,13 @@ display : inline-flex; align-items : center; margin-right : 15px; - font-size : 0.7em; + font-size : 0.9em; font-weight : 800; white-space : nowrap; vertical-align : middle; cursor : pointer; user-select : none; } - a { - display : inline-flex; - font-size : 0.7em; - font-weight : 800; - } input { margin : 3px; vertical-align : middle; @@ -136,25 +139,23 @@ margin-bottom : 15px; button { width : 100%; } button.publish { - .button(@blueLight); + .colorButton(@blueLight); } button.unpublish { - .button(@silver); + .colorButton(@silver); } } .delete.field .value { button { - .button(@red); + .colorButton(@red); } } .authors.field .value { - font-size : 0.8em; line-height : 1.5em; } .themes.field { - font-size : 13.33px; .navDropdownContainer { position : relative; z-index : 100; @@ -165,9 +166,9 @@ background-color : darkgray; } & > div:first-child { - padding : 6px 3px; + padding : 3px 3px; background-color : inherit; - border : 2px solid rgb(118,118,118); + border : 1px solid gray; i { float : right; } &:hover { color : white; @@ -240,6 +241,7 @@ } } } + .field .list { display : flex; flex : 1 0; @@ -258,27 +260,26 @@ color : white; text-align : center; cursor : pointer; - + i { position : relative; top : 50%; transform : translateY(-50%); } - + &:not(:last-child) { border-right : 1px solid black; } - + &:last-child { border-radius : 0 0.5em 0.5em 0; } } - .badge { + .tag { padding : 0.3em; margin : 2px; font-size : 0.9em; background-color : #DDDDDD; border-radius : 0.5em; - .icon { - #groupedIcon; } + .icon { #groupedIcon; } } .input-group { @@ -294,17 +295,30 @@ height : 100%; } - .invalid:focus { background-color : pink; } + .input-group { + height : ~'calc(.9em + 4px + .6em)'; - .icon { - #groupedIcon; - top : -0.54em; - right : 1px; - height : 97%; - font-size : 0.8em; + input { border-radius : 0.5em 0 0 0.5em; } - i { font-size : 1.125em; } + input:last-child { border-radius : 0.5em; } + + .value { + width : 7.5vw; + min-width : 75px; + height : 100%; + } + + .invalid:focus { background-color : pink; } + + .icon { + #groupedIcon; + top : -0.54em; + right : 1px; + height : 97%; + + i { font-size : 1.125em; } + } } } } -} +} \ No newline at end of file diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx index af493c961..f7d9508f8 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.jsx +++ b/client/homebrew/editor/snippetbar/snippetbar.jsx @@ -1,10 +1,12 @@ -/*eslint max-lines: ["warn", {"max": 250, "skipBlankLines": true, "skipComments": true}]*/ +/*eslint max-lines: ["warn", {"max": 350, "skipBlankLines": true, "skipComments": true}]*/ require('./snippetbar.less'); const React = require('react'); const createClass = require('create-react-class'); const _ = require('lodash'); const cx = require('classnames'); +import { loadHistory } from '../../utils/versionHistory.js'; + //Import all themes const ThemeSnippets = {}; ThemeSnippets['Legacy_5ePHB'] = require('themes/Legacy/5ePHB/snippets.js'); @@ -38,7 +40,8 @@ const Snippetbar = createClass({ unfoldCode : ()=>{}, updateEditorTheme : ()=>{}, cursorPos : {}, - snippetBundle : [] + snippetBundle : [], + updateBrew : ()=>{} }; }, @@ -46,31 +49,54 @@ const Snippetbar = createClass({ return { renderer : this.props.renderer, themeSelector : false, - snippets : [] + snippets : [], + showHistory : false, + historyExists : false, + historyItems : [] }; }, - componentDidMount : async function() { + componentDidMount : async function(prevState) { const snippets = this.compileSnippets(); this.setState({ snippets : snippets }); }, - componentDidUpdate : async function(prevProps) { + componentDidUpdate : async function(prevProps, prevState) { if(prevProps.renderer != this.props.renderer || prevProps.theme != this.props.theme || prevProps.snippetBundle != this.props.snippetBundle) { - const snippets = this.compileSnippets(); this.setState({ - snippets : snippets + snippets : this.compileSnippets() + }); + }; + + // Update history list if it has changed + const checkHistoryItems = await loadHistory(this.props.brew); + + // If all items have the noData property, there is no saved data + const checkHistoryExists = !checkHistoryItems.every((historyItem)=>{ + return historyItem?.noData; + }); + if(prevState.historyExists != checkHistoryExists){ + this.setState({ + historyExists : checkHistoryExists + }); + } + + // If any history items have changed, update the list + if(checkHistoryExists && checkHistoryItems.some((historyItem, index)=>{ + return index >= prevState.historyItems.length || !_.isEqual(historyItem, prevState.historyItems[index]); + })){ + this.setState({ + historyItems : checkHistoryItems }); } }, - mergeCustomizer : function(oldValue,Β newValue, key)Β { if(key == 'snippets')Β { const result = _.reverse(_.unionBy(_.reverse(newValue), _.reverse(oldValue), 'name')); // Join snippets together, with preference for the child theme over the parent theme - return _.filter(result, 'gen'); //Only keep snippets with a 'gen' property. + return result.filter((snip)=>snip.gen || snip.subsnippets); } }, @@ -124,70 +150,116 @@ const Snippetbar = createClass({ renderSnippetGroups : function(){ const snippets = this.state.snippets.filter((snippetGroup)=>snippetGroup.view === this.props.view); + if(snippets.length === 0) return null; - return _.map(snippets, (snippetGroup)=>{ - return ; + return
    + {_.map(snippets, (snippetGroup)=>{ + return ; + }) + } +
    ; + }, + + replaceContent : function(item){ + return this.props.updateBrew(item); + }, + + toggleHistoryMenu : function(){ + this.setState({ + showHistory : !this.state.showHistory }); }, + renderHistoryItems : function() { + if(!this.state.historyExists) return; + + return
    + {_.map(this.state.historyItems, (item, index)=>{ + if(item.noData || !item.savedAt) return; + + const saveTime = new Date(item.savedAt); + const diffMs = new Date() - saveTime; + const diffSecs = Math.floor(diffMs / 1000); + + let diffString = `about ${diffSecs} seconds ago`; + + if(diffSecs > 60) diffString = `about ${Math.floor(diffSecs / 60)} minutes ago`; + if(diffSecs > (60 * 60)) diffString = `about ${Math.floor(diffSecs / (60 * 60))} hours ago`; + if(diffSecs > (24 * 60 * 60)) diffString = `about ${Math.floor(diffSecs / (24 * 60 * 60))} days ago`; + if(diffSecs > (7 * 24 * 60 * 60)) diffString = `about ${Math.floor(diffSecs / (7 * 24 * 60 * 60))} weeks ago`; + + return
    {this.replaceContent(item);}} > + + v{item.version} : {diffString} +
    ; + })} +
    ; + }, + renderEditorButtons : function(){ if(!this.props.showEditButtons) return; - let foldButtons; - if(this.props.view == 'text'){ - foldButtons = - <> -
    - -
    -
    - -
    - ; + - } + return ( +
    + {this.props.view !== 'meta' && <>
    +
    + + { this.state.showHistory && this.renderHistoryItems() } +
    +
    + +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    + + {this.state.themeSelector && this.renderThemeSelector()} +
    +
    } + - return
    -
    - -
    -
    - -
    -
    - {foldButtons} -
    - - {this.state.themeSelector && this.renderThemeSelector()} +
    +
    this.props.onViewChange('text')}> + +
    +
    this.props.onViewChange('style')}> + +
    +
    this.props.onViewChange('meta')}> + +
    -
    -
    this.props.onViewChange('text')}> - -
    -
    this.props.onViewChange('style')}> - -
    -
    this.props.onViewChange('meta')}> - -
    -
    ; +
    + ) }, render : function(){ @@ -224,8 +296,9 @@ const SnippetGroup = createClass({ return _.map(snippets, (snippet)=>{ return
    this.handleSnippetClick(e, snippet)}> - {snippet.name} + {snippet.name} {snippet.experimental && beta} + {snippet.disabled && disabled} {snippet.subsnippets && <>
    diff --git a/client/homebrew/editor/snippetbar/snippetbar.less b/client/homebrew/editor/snippetbar/snippetbar.less index e0a24fac9..7d56dc718 100644 --- a/client/homebrew/editor/snippetbar/snippetbar.less +++ b/client/homebrew/editor/snippetbar/snippetbar.less @@ -4,82 +4,119 @@ .snippetBar { @menuHeight : 25px; position : relative; - height : @menuHeight; + display : flex; + flex-wrap : wrap-reverse; + justify-content : space-between; + height : auto; color : black; background-color : #DDDDDD; - .editors { - position : absolute; - top : 0px; - right : 0px; + .snippets { display : flex; - justify-content : space-between; - height : @menuHeight; - & > div { - width : @menuHeight; - height : @menuHeight; - line-height : @menuHeight; - text-align : center; - cursor : pointer; - &:hover,&.selected { background-color : #999999; } - &.text { - .tooltipLeft('Brew Editor'); - } - &.style { - .tooltipLeft('Style Editor'); - } - &.meta { - .tooltipLeft('Properties'); - } - &.undo { - .tooltipLeft('Undo'); - font-size : 0.75em; - color : grey; - &.active { color : inherit; } - } - &.redo { - .tooltipLeft('Redo'); - font-size : 0.75em; - color : grey; - &.active { color : inherit; } - } - &.foldAll { - .tooltipLeft('Fold All'); - font-size : 0.75em; - color : inherit; - } - &.unfoldAll { - .tooltipLeft('Unfold All'); - font-size : 0.75em; - color : inherit; - } - &.editorTheme { - .tooltipLeft('Editor Themes'); - font-size : 0.75em; - color : black; - &.active { - position : relative; - background-color : #999999; + justify-content : flex-start; + min-width : 327.58px; + } + + .editors { + display : flex; + justify-content : flex-end; + min-width : 225px; + + &:only-child { margin-left : auto;min-width:unset;} + + >div { + display : flex; + flex : 1; + justify-content : space-around; + + &:first-child { border-left : none; } + + & > div { + position : relative; + width : @menuHeight; + height : @menuHeight; + line-height : @menuHeight; + text-align : center; + cursor : pointer; + + &.editorTool:not(.active) { + cursor:not-allowed; + } + + &:hover,&.selected { background-color : #999999; } + &.text { + .tooltipLeft('Brew Editor'); + } + &.style { + .tooltipLeft('Style Editor'); + } + &.meta { + .tooltipLeft('Properties'); + } + &.undo { + .tooltipLeft('Undo'); + font-size : 0.75em; + color : grey; + &.active { color : inherit; } + } + &.redo { + .tooltipLeft('Redo'); + font-size : 0.75em; + color : grey; + &.active { color : inherit; } + } + &.foldAll { + .tooltipLeft('Fold All'); + font-size : 0.75em; + color : grey; + &.active { color : inherit; } + } + &.unfoldAll { + .tooltipLeft('Unfold All'); + font-size : 0.75em; + color : grey; + &.active { color : inherit; } + } + &.history { + .tooltipLeft('History'); + position : relative; + font-size : 0.75em; + color : grey; + border : none; + &.active { color : inherit; } + & > .dropdown { + right : -1px; + & > .snippet { padding-right : 10px; } + } + } + &.editorTheme { + .tooltipLeft('Editor Themes'); + font-size : 0.75em; + color : black; + &.active { + position : relative; + background-color : #999999; + } + } + &.divider { + width : 5px; + background : linear-gradient(currentColor, currentColor) no-repeat center/1px 100%; + &:hover { background-color : inherit; } } } - &.divider { - width : 5px; - background : linear-gradient(currentColor, currentColor) no-repeat center/1px 100%; - &:hover { background-color : inherit; } + .themeSelector { + position : absolute; + top : 25px; + right : 0; + z-index : 10; + display : flex; + align-items : center; + justify-content : center; + width : 170px; + height : inherit; + background-color : inherit; } - } - .themeSelector { - position : absolute; - top : 25px; - right : 0; - z-index : 10; - display : flex; - align-items : center; - justify-content : center; - width : 170px; - height : inherit; - background-color : inherit; - } + } } .snippetBarButton { display : inline-block; @@ -89,6 +126,7 @@ font-weight : 800; line-height : @menuHeight; text-transform : uppercase; + text-wrap : nowrap; cursor : pointer; &:hover, &.selected { background-color : #999999; } i { @@ -105,7 +143,7 @@ .tooltipLeft('Edit Brew Properties'); } .snippetGroup { - border-right : 1px solid currentColor; + &:hover { & > .dropdown { visibility : visible; } } @@ -127,11 +165,11 @@ cursor : pointer; .animate(background-color); i { + min-width : 25px; height : 1.2em; margin-right : 8px; font-size : 1.2em; - min-width: 25px; - text-align: center; + text-align : center; & ~ i { margin-right : 0; margin-left : 5px; @@ -164,6 +202,7 @@ } } .name { margin-right : auto; } + .disabled { text-decoration : line-through; } .beta { align-self : center; padding : 4px 6px; @@ -189,4 +228,19 @@ } } } -} \ No newline at end of file +} +@container editor (width < 553px) { + .snippetBar { + .editors { + flex : 1; + justify-content : space-between; + border-bottom : 1px solid; + } + .snippets { + flex : 1; + justify-content : space-evenly; + } + .editors > div.history > .dropdown { right : unset; } + } +} + diff --git a/client/homebrew/editor/stringArrayEditor/stringArrayEditor.jsx b/client/homebrew/editor/stringArrayEditor/stringArrayEditor.jsx deleted file mode 100644 index 8f06ae561..000000000 --- a/client/homebrew/editor/stringArrayEditor/stringArrayEditor.jsx +++ /dev/null @@ -1,149 +0,0 @@ -const React = require('react'); -const createClass = require('create-react-class'); -const _ = require('lodash'); - -const StringArrayEditor = createClass({ - displayName : 'StringArrayEditor', - getDefaultProps : function() { - return { - label : '', - values : [], - valuePatterns : null, - validators : [], - placeholder : '', - notes : [], - unique : false, - cannotEdit : [], - onChange : ()=>{} - }; - }, - - getInitialState : function() { - return { - valueContext : !!this.props.values ? this.props.values.map((value)=>({ - value, - editing : false - })) : [], - temporaryValue : '', - updateValue : '' - }; - }, - - componentDidUpdate : function(prevProps) { - if(!_.eq(this.props.values, prevProps.values)) { - this.setState({ - valueContext : this.props.values ? this.props.values.map((newValue)=>({ - value : newValue, - editing : this.state.valueContext.find(({ value })=>value === newValue)?.editing || false - })) : [] - }); - } - }, - - handleChange : function(value) { - this.props.onChange({ - target : { - value - } - }); - }, - - addValue : function(value){ - this.handleChange(_.uniq([...this.props.values, value])); - this.setState({ - temporaryValue : '' - }); - }, - - removeValue : function(index){ - this.handleChange(this.props.values.filter((_, i)=>i !== index)); - }, - - updateValue : function(value, index){ - const valueContext = this.state.valueContext; - valueContext[index].value = value; - valueContext[index].editing = false; - this.handleChange(valueContext.map((context)=>context.value)); - this.setState({ valueContext, updateValue: '' }); - }, - - editValue : function(index){ - if(!!this.props.cannotEdit && this.props.cannotEdit.includes(this.props.values[index])) { - return; - } - const valueContext = this.state.valueContext.map((context, i)=>{ - context.editing = index === i; - return context; - }); - this.setState({ valueContext, updateValue: this.props.values[index] }); - }, - - valueIsValid : function(value, index) { - const values = _.clone(this.props.values); - if(index !== undefined) { - values.splice(index, 1); - } - const matchesPatterns = !this.props.valuePatterns || this.props.valuePatterns.some((pattern)=>!!(value || '').match(pattern)); - const uniqueIfSet = !this.props.unique || !values.includes(value); - const passesValidators = !this.props.validators || this.props.validators.every((validator)=>validator(value)); - return matchesPatterns && uniqueIfSet && passesValidators; - }, - - handleValueInputKeyDown : function(event, index) { - if(event.key === 'Enter') { - if(this.valueIsValid(event.target.value, index)) { - if(index !== undefined) { - this.updateValue(event.target.value, index); - } else { - this.addValue(event.target.value); - } - } - } else if(event.key === 'Escape') { - this.closeEditInput(index); - } - }, - - closeEditInput : function(index) { - const valueContext = this.state.valueContext; - valueContext[index].editing = false; - this.setState({ valueContext, updateValue: '' }); - }, - - render : function() { - const valueElements = Object.values(this.state.valueContext).map((context, i)=>context.editing - ? -
    - this.handleValueInputKeyDown(e, i)} - onChange={(e)=>this.setState({ updateValue: e.target.value })}/> - {
    { e.stopPropagation(); this.closeEditInput(i); }}>
    } - {this.valueIsValid(this.state.updateValue, i) ?
    { e.stopPropagation(); this.updateValue(this.state.updateValue, i); }}>
    : null} -
    -
    - :
    this.editValue(i)}>{context.value} - {!!this.props.cannotEdit && this.props.cannotEdit.includes(context.value) ? null :
    { e.stopPropagation(); this.removeValue(i); }}>
    } -
    - ); - - return
    - -
    -
    - {valueElements} -
    - this.handleValueInputKeyDown(e)} - onChange={(e)=>this.setState({ temporaryValue: e.target.value })}/> - {this.valueIsValid(this.state.temporaryValue) ?
    { e.stopPropagation(); this.addValue(this.state.temporaryValue); }}>
    : null} -
    -
    - - {this.props.notes ? this.props.notes.map((n, index)=>

    {n}

    ) : null} -
    -
    ; - } -}); - -module.exports = StringArrayEditor; \ No newline at end of file diff --git a/client/homebrew/editor/tagInput/tagInput.jsx b/client/homebrew/editor/tagInput/tagInput.jsx new file mode 100644 index 000000000..816541167 --- /dev/null +++ b/client/homebrew/editor/tagInput/tagInput.jsx @@ -0,0 +1,105 @@ +require('./tagInput.less'); +const React = require('react'); +const { useState, useEffect } = React; +const _ = require('lodash'); + +const TagInput = ({ unique = true, values = [], ...props }) => { + const [tempInputText, setTempInputText] = useState(''); + const [tagList, setTagList] = useState(values.map((value) => ({ value, editing: false }))); + + useEffect(()=>{ + handleChange(tagList.map((context)=>context.value)) + }, [tagList]) + + const handleChange = (value)=>{ + props.onChange({ + target : { value } + }) + }; + + const handleInputKeyDown = ({ evt, value, index, options = {} }) => { + if (_.includes(['Enter', ','], evt.key)) { + evt.preventDefault(); + submitTag(evt.target.value, value, index); + if (options.clear) { + setTempInputText(''); + } + } + }; + + const submitTag = (newValue, originalValue, index) => { + setTagList((prevContext) => { + // remove existing tag + if(newValue === null){ + return [...prevContext].filter((context, i)=>i !== index); + } + // add new tag + if(originalValue === null){ + return [...prevContext, { value: newValue, editing: false }] + } + // update existing tag + return prevContext.map((context, i) => { + if (i === index) { + return { ...context, value: newValue, editing: false }; + } + return context; + }); + }); + }; + + const editTag = (index) => { + setTagList((prevContext) => { + return prevContext.map((context, i) => { + if (i === index) { + return { ...context, editing: true }; + } + return { ...context, editing: false }; + }); + }); + }; + + const renderReadTag = (context, index) => { + return ( +
  • editTag(index)}> + {context.value} + +
  • + ); + }; + + const renderWriteTag = (context, index) => { + return ( + handleInputKeyDown({evt, value: context.value, index: index})} + autoFocus + /> + ); + }; + + return ( +
    + +
    +
      + {tagList.map((context, index) => { return context.editing ? renderWriteTag(context, index) : renderReadTag(context, index); })} +
    + + setTempInputText(e.target.value)} + onKeyDown={(evt) => handleInputKeyDown({ evt, value: null, options: { clear: true } })} + /> +
    +
    + ); +}; + +module.exports = TagInput; diff --git a/client/homebrew/editor/tagInput/tagInput.less b/client/homebrew/editor/tagInput/tagInput.less new file mode 100644 index 000000000..e69de29bb diff --git a/client/homebrew/homebrew.jsx b/client/homebrew/homebrew.jsx index 2226c4f3f..415390498 100644 --- a/client/homebrew/homebrew.jsx +++ b/client/homebrew/homebrew.jsx @@ -1,8 +1,12 @@ +//β•”===--------------- Polyfills --------------===β•—// +import 'core-js/es/string/to-well-formed.js'; +//β•š===--------------- ---------------===╝// + require('./homebrew.less'); const React = require('react'); const createClass = require('create-react-class'); -const { StaticRouter:Router } = require('react-router-dom/server'); -const { Route, Routes, useParams, useSearchParams } = require('react-router-dom'); +const { StaticRouter:Router } = require('react-router'); +const { Route, Routes, useParams, useSearchParams } = require('react-router'); const HomePage = require('./pages/homePage/homePage.jsx'); const EditPage = require('./pages/editPage/editPage.jsx'); @@ -10,6 +14,7 @@ const UserPage = require('./pages/userPage/userPage.jsx'); const SharePage = require('./pages/sharePage/sharePage.jsx'); const NewPage = require('./pages/newPage/newPage.jsx'); const ErrorPage = require('./pages/errorPage/errorPage.jsx'); +const VaultPage = require('./pages/vaultPage/vaultPage.jsx'); const AccountPage = require('./pages/accountPage/accountPage.jsx'); const WithRoute = (props)=>{ @@ -71,6 +76,7 @@ const Homebrew = createClass({ } /> } /> } /> + }/> } /> } /> } /> diff --git a/client/homebrew/navbar/editTitle.navitem.jsx b/client/homebrew/navbar/editTitle.navitem.jsx deleted file mode 100644 index 94ae5d0b0..000000000 --- a/client/homebrew/navbar/editTitle.navitem.jsx +++ /dev/null @@ -1,34 +0,0 @@ -const React = require('react'); -const createClass = require('create-react-class'); -const cx = require('classnames'); -const Nav = require('naturalcrit/nav/nav.jsx'); - -const MAX_TITLE_LENGTH = 50; - - -const EditTitle = createClass({ - displayName : 'EditTitleNavItem', - getDefaultProps : function() { - return { - title : '', - onChange : function(){} - }; - }, - - handleChange : function(e){ - if(e.target.value.length > MAX_TITLE_LENGTH) return; - this.props.onChange(e.target.value); - }, - render : function(){ - return - - -
    = MAX_TITLE_LENGTH })}> - {this.props.title.length}/{MAX_TITLE_LENGTH} -
    -
    ; - }, - -}); - -module.exports = EditTitle; diff --git a/client/homebrew/navbar/error-navitem.jsx b/client/homebrew/navbar/error-navitem.jsx index 5dd5c1eb9..f6788e6d5 100644 --- a/client/homebrew/navbar/error-navitem.jsx +++ b/client/homebrew/navbar/error-navitem.jsx @@ -111,7 +111,7 @@ const ErrorNavItem = createClass({ Looks like there was a problem retreiving the theme, or a theme that it inherits, for this brew. Verify that brew - {response.body.brewId} still exists! + {response.body.brewId} still exists!
    ; } diff --git a/client/homebrew/navbar/navbar.less b/client/homebrew/navbar/navbar.less index d0f2f77e8..ae11c1e7e 100644 --- a/client/homebrew/navbar/navbar.less +++ b/client/homebrew/navbar/navbar.less @@ -1,6 +1,7 @@ @import 'naturalcrit/styles/colors.less'; @navbarHeight : 28px; +@viewerToolsHeight : 32px; @keyframes pinkColoring { 0% { color : pink; } @@ -24,16 +25,20 @@ .homebrew nav { background-color : #333333; - .navContent { - position : relative; - z-index : 2; - display : flex; - justify-content : space-between; - } + position : relative; + z-index : 2; + display : flex; + justify-content : space-between; + .navSection { display : flex; align-items : center; &:last-child .navItem { border-left : 1px solid #666666; } + + &:has(.brewTitle) { + flex-grow : 1; + min-width : 300px; + } } // "NaturalCrit" logo .navLogo { @@ -68,6 +73,10 @@ .navItem { #backgroundColorsHover; .animate(background-color); + display : flex; + align-items : center; + justify-content : center; + height : 100%; padding : 8px 12px; font-size : 10px; font-weight : 800; @@ -93,39 +102,20 @@ animation-duration : 2s; } } - &.editTitle { // this is not needed at all currently - you used to be able to edit the title via the navbar. - padding : 2px 12px; - input { - width : 250px; - padding : 2px; - margin : 0; - font-family : 'Open Sans', sans-serif; - font-size : 12px; - font-weight : 800; - color : white; - text-align : center; - background-color : transparent; - border : 1px solid @blue; - outline : none; - } - .charCount { - display : inline-block; - margin-left : 8px; - color : #666666; - text-align : right; - vertical-align : bottom; - &.max { color : @red; } - } - } &.brewTitle { - flex-grow : 1; + display : block; + width : 100%; + overflow : hidden; font-size : 12px; font-weight : 800; color : white; text-align : center; - text-transform : initial; - background-color : transparent; + text-overflow : ellipsis; + text-transform : initial; + white-space : nowrap; + background-color : transparent; } + // "The Homebrewery" logo &.homebrewLogo { .animate(color); @@ -239,23 +229,25 @@ } .navDropdownContainer { position : relative; + height : 100%; + .navDropdown { - position: absolute; - top: 28px; - right: 0px; - z-index: 10000; - width: max-content; - min-width:100%; - max-height: calc(100vh - 28px); - overflow: hidden auto; - display: flex; - flex-direction: column; - align-items: flex-end; + position : absolute; + //top: 28px; + right : 0px; + z-index : 10000; + display : flex; + flex-direction : column; + align-items : flex-end; + width : max-content; + min-width : 100%; + max-height : calc(100vh - 28px); + overflow : hidden auto; .navItem { position : relative; display : flex; - justify-content : space-between; align-items : center; + justify-content : space-between; width : 100%; border : 1px solid #888888; border-bottom : 0; @@ -277,10 +269,10 @@ overflow : hidden auto; color : white; text-decoration : none; - background-color : #333333; - border-top : 1px solid #888888; scrollbar-color : #666666 #333333; scrollbar-width : thin; + background-color : #333333; + border-top : 1px solid #888888; .clear { position : absolute; top : 50%; diff --git a/client/homebrew/navbar/recent.navitem.jsx b/client/homebrew/navbar/recent.navitem.jsx index 431bdd8df..a6cbbf406 100644 --- a/client/homebrew/navbar/recent.navitem.jsx +++ b/client/homebrew/navbar/recent.navitem.jsx @@ -36,7 +36,7 @@ const RecentItems = createClass({ //== Add current brew to appropriate recent items list (depending on storageKey) ==// if(this.props.storageKey == 'edit'){ let editId = this.props.brew.editId; - if(this.props.brew.googleId){ + if(this.props.brew.googleId && !this.props.brew.stubbed){ editId = `${this.props.brew.googleId}${this.props.brew.editId}`; } edited = _.filter(edited, (brew)=>{ @@ -51,7 +51,7 @@ const RecentItems = createClass({ } if(this.props.storageKey == 'view'){ let shareId = this.props.brew.shareId; - if(this.props.brew.googleId){ + if(this.props.brew.googleId && !this.props.brew.stubbed){ shareId = `${this.props.brew.googleId}${this.props.brew.shareId}`; } viewed = _.filter(viewed, (brew)=>{ @@ -83,7 +83,7 @@ const RecentItems = createClass({ let edited = JSON.parse(localStorage.getItem(EDIT_KEY) || '[]'); if(this.props.storageKey == 'edit') { let prevEditId = prevProps.brew.editId; - if(prevProps.brew.googleId){ + if(prevProps.brew.googleId && !this.props.brew.stubbed){ prevEditId = `${prevProps.brew.googleId}${prevProps.brew.editId}`; } @@ -91,7 +91,7 @@ const RecentItems = createClass({ return brew.id !== prevEditId; }); let editId = this.props.brew.editId; - if(this.props.brew.googleId){ + if(this.props.brew.googleId && !this.props.brew.stubbed){ editId = `${this.props.brew.googleId}${this.props.brew.editId}`; } edited.unshift({ diff --git a/client/homebrew/navbar/reddit.navitem.jsx b/client/homebrew/navbar/reddit.navitem.jsx deleted file mode 100644 index 1d9f95604..000000000 --- a/client/homebrew/navbar/reddit.navitem.jsx +++ /dev/null @@ -1,44 +0,0 @@ -const React = require('react'); -const createClass = require('create-react-class'); -const Nav = require('naturalcrit/nav/nav.jsx'); - -const MAIN_URL = 'https://www.reddit.com/r/UnearthedArcana/submit?selftext=true'; - - -const RedditShare = createClass({ - displayName : 'RedditShareNavItem', - getDefaultProps : function() { - return { - brew : { - title : '', - sharedId : '', - text : '' - } - }; - }, - - getText : function(){ - - }, - - - handleClick : function(){ - const url = [ - MAIN_URL, - `title=${encodeURIComponent(this.props.brew.title ? this.props.brew.title : 'Check out my brew!')}`, - `text=${encodeURIComponent(this.props.brew.text)}` - ].join('&'); - - window.open(url, '_blank'); - }, - - - render : function(){ - return - share on reddit - ; - }, - -}); - -module.exports = RedditShare; diff --git a/client/homebrew/navbar/vault.navitem.jsx b/client/homebrew/navbar/vault.navitem.jsx new file mode 100644 index 000000000..087297011 --- /dev/null +++ b/client/homebrew/navbar/vault.navitem.jsx @@ -0,0 +1,17 @@ +const React = require('react'); + +const Nav = require('naturalcrit/nav/nav.jsx'); + +module.exports = function (props) { + return ( + + Vault + + ); +}; diff --git a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx index bf0624f1c..ef98e8425 100644 --- a/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx +++ b/client/homebrew/pages/basePages/listPage/brewItem/brewItem.jsx @@ -1,177 +1,179 @@ require('./brewItem.less'); const React = require('react'); -const createClass = require('create-react-class'); +const { useCallback } = React; const moment = require('moment'); -const request = require('../../../../utils/request-middleware.js'); +import request from '../../../../utils/request-middleware.js'; const googleDriveIcon = require('../../../../googleDrive.svg'); const homebreweryIcon = require('../../../../thumbnail.png'); const dedent = require('dedent-tabs').default; -const BrewItem = createClass({ - displayName : 'BrewItem', - getDefaultProps : function() { - return { - brew : { - title : '', - description : '', - authors : [], - stubbed : true - }, - updateListFilter : ()=>{}, - reportError : ()=>{} - }; +const BrewItem = ({ + brew = { + title : '', + description : '', + authors : [], + stubbed : true, }, + updateListFilter = ()=>{}, + reportError = ()=>{}, + renderStorage = true, +})=>{ - deleteBrew : function(){ - if(this.props.brew.authors.length <= 1){ - if(!confirm('Are you sure you want to delete this brew? Because you are the only owner of this brew, the document will be deleted permanently.')) return; - if(!confirm('Are you REALLY sure? You will not be able to recover the document.')) return; + const deleteBrew = useCallback(()=>{ + if(brew.authors.length <= 1) { + if(!window.confirm('Are you sure you want to delete this brew? Because you are the only owner of this brew, the document will be deleted permanently.')) return; + if(!window.confirm('Are you REALLY sure? You will not be able to recover the document.')) return; } else { - if(!confirm('Are you sure you want to remove this brew from your collection? This will remove you as an editor, but other owners will still be able to access the document.')) return; - if(!confirm('Are you REALLY sure? You will lose editor access to this document.')) return; + if(!window.confirm('Are you sure you want to remove this brew from your collection? This will remove you as an editor, but other owners will still be able to access the document.')) return; + if(!window.confirm('Are you REALLY sure? You will lose editor access to this document.')) return; } - request.delete(`/api/${this.props.brew.googleId ?? ''}${this.props.brew.editId}`) - .send() - .end((err, res)=>{ - if(err) { - this.props.reportError(err); - } else { - location.reload(); - } - }); - }, + request.delete(`/api/${brew.googleId ?? ''}${brew.editId}`).send().end((err, res)=>{ + if (err) reportError(err); else window.location.reload(); + }); + }, [brew, reportError]); - updateFilter : function(type, term){ - this.props.updateListFilter(type, term); - }, + const updateFilter = useCallback((type, term)=> updateListFilter(type, term), [updateListFilter]); - renderDeleteBrewLink : function(){ - if(!this.props.brew.editId) return; + const renderDeleteBrewLink = ()=>{ + if(!brew.editId) return null; - return - - ; - }, + return ( + + + + ); + }; - renderEditLink : function(){ - if(!this.props.brew.editId) return; + const renderEditLink = ()=>{ + if(!brew.editId) return null; - let editLink = this.props.brew.editId; - if(this.props.brew.googleId && !this.props.brew.stubbed) { - editLink = this.props.brew.googleId + editLink; + let editLink = brew.editId; + if(brew.googleId && !brew.stubbed) editLink = brew.googleId + editLink; + + return ( + + + + ); + }; + + const renderShareLink = ()=>{ + if(!brew.shareId) return null; + + let shareLink = brew.shareId; + if(brew.googleId && !brew.stubbed) { + shareLink = brew.googleId + shareLink; } - return - - ; - }, + return ( + + + + ); + }; - renderShareLink : function(){ - if(!this.props.brew.shareId) return; + const renderDownloadLink = ()=>{ + if(!brew.shareId) return null; - let shareLink = this.props.brew.shareId; - if(this.props.brew.googleId && !this.props.brew.stubbed) { - shareLink = this.props.brew.googleId + shareLink; + let shareLink = brew.shareId; + if(brew.googleId && !brew.stubbed) { + shareLink = brew.googleId + shareLink; } - return - - ; - }, + return ( + + + + ); + }; - renderDownloadLink : function(){ - if(!this.props.brew.shareId) return; - - let shareLink = this.props.brew.shareId; - if(this.props.brew.googleId && !this.props.brew.stubbed) { - shareLink = this.props.brew.googleId + shareLink; + const renderStorageIcon = ()=>{ + if(!renderStorage) return null; + if(brew.googleId) { + return ( + + + googleDriveIcon + + + ); } - return - - ; - }, + return ( + + homebreweryIcon + + ); + }; - renderStorageIcon : function(){ - if(this.props.brew.googleId) { - return - - googleDriveIcon - - ; - } + if(Array.isArray(brew.tags)) { + brew.tags = brew.tags?.filter((tag)=>tag); // remove tags that are empty strings + brew.tags.sort((a, b)=>{ + return a.indexOf(':') - b.indexOf(':') !== 0 ? a.indexOf(':') - b.indexOf(':') : a.toLowerCase().localeCompare(b.toLowerCase()); + }); + } - return - homebreweryIcon - ; - }, + const dateFormatString = 'YYYY-MM-DD HH:mm:ss'; - render : function(){ - const brew = this.props.brew; - if(Array.isArray(brew.tags)) { // temporary fix until dud tags are cleaned - brew.tags = brew.tags?.filter((tag)=>tag); //remove tags that are empty strings - brew.tags.sort((a, b)=>{ - return a.indexOf(':') - b.indexOf(':') != 0 ? a.indexOf(':') - b.indexOf(':') : a.toLowerCase().localeCompare(b.toLowerCase()); - }); - } - const dateFormatString = 'YYYY-MM-DD HH:mm:ss'; - - return
    - {brew.thumbnail && -
    -
    - } + return ( +
    + {brew.thumbnail &&
    }

    {brew.title}

    {brew.description}


    - - {brew.tags?.length ? <> + {brew.tags?.length ? (
    - + {brew.tags.map((tag, idx)=>{ const matches = tag.match(/^(?:([^:]+):)?([^:]+)$/); - return {this.updateFilter(tag);}}>{matches[2]}; + return updateFilter(tag)}>{matches[2]}; })}
    - : <> - } + ) : null} - {brew.authors?.map((author, index)=>( - <> - {author} - {index < brew.authors.length - 1 && ', '} - ))} + {' '} + {brew.authors?.map((author, index)=>( + + {author === 'hidden' ? ( + + {author} + + ) : ({author})} + {index < brew.authors.length - 1 && ', '} + + ))}
    - {brew.views} + {brew.views} - {brew.pageCount && + {brew.pageCount && ( {brew.pageCount} - } - + )} + {moment(brew.updatedAt).fromNow()} - {this.renderStorageIcon()} + {renderStorageIcon()}
    - {this.renderShareLink()} - {this.renderEditLink()} - {this.renderDownloadLink()} - {this.renderDeleteBrewLink()} + {renderShareLink()} + {renderEditLink()} + {renderDownloadLink()} + {renderDeleteBrewLink()}
    -
    ; - } -}); +
    + ); +}; module.exports = BrewItem; diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx index 39a6d1931..ffb6a6b40 100644 --- a/client/homebrew/pages/editPage/editPage.jsx +++ b/client/homebrew/pages/editPage/editPage.jsx @@ -1,9 +1,10 @@ /* eslint-disable max-lines */ require('./editPage.less'); const React = require('react'); -const createClass = require('create-react-class'); const _ = require('lodash'); -const request = require('../../utils/request-middleware.js'); +const createClass = require('create-react-class'); + +import request from '../../utils/request-middleware.js'; const { Meta } = require('vitreum/headtags'); const Nav = require('naturalcrit/nav/nav.jsx'); @@ -15,6 +16,7 @@ const PrintNavItem = require('../../navbar/print.navitem.jsx'); const ErrorNavItem = require('../../navbar/error-navitem.jsx'); const Account = require('../../navbar/account.navitem.jsx'); const RecentNavItem = require('../../navbar/recent.navitem.jsx').both; +const VaultNavItem = require('../../navbar/vault.navitem.jsx'); const SplitPane = require('naturalcrit/splitPane/splitPane.jsx'); const Editor = require('../../editor/editor.jsx'); @@ -22,14 +24,16 @@ const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx'); const LockNotification = require('./lockNotification/lockNotification.jsx'); -const Markdown = require('naturalcrit/markdown.js'); +import Markdown from 'naturalcrit/markdown.js'; const { DEFAULT_BREW_LOAD } = require('../../../../server/brewDefaults.js'); const { printCurrentBrew, fetchThemeBundle } = require('../../../../shared/helpers.js'); +import { updateHistory, versionHistoryGarbageCollection } from '../../utils/versionHistory.js'; + const googleDriveIcon = require('../../googleDrive.svg'); -const SAVE_TIMEOUT = 3000; +const SAVE_TIMEOUT = 10000; const EditPage = createClass({ displayName : 'EditPage', @@ -41,22 +45,24 @@ const EditPage = createClass({ getInitialState : function() { return { - brew : this.props.brew, - isSaving : false, - isPending : false, - alertTrashedGoogleBrew : this.props.brew.trashed, - alertLoginToTransfer : false, - saveGoogle : this.props.brew.googleId ? true : false, - confirmGoogleTransfer : false, - error : null, - htmlErrors : Markdown.validate(this.props.brew.text), - url : '', - autoSave : true, - autoSaveWarning : false, - unsavedTime : new Date(), - currentEditorPage : 0, - displayLockMessage : this.props.brew.lock || false, - themeBundle : {} + brew : this.props.brew, + isSaving : false, + isPending : false, + alertTrashedGoogleBrew : this.props.brew.trashed, + alertLoginToTransfer : false, + saveGoogle : this.props.brew.googleId ? true : false, + confirmGoogleTransfer : false, + error : null, + htmlErrors : Markdown.validate(this.props.brew.text), + url : '', + autoSave : true, + autoSaveWarning : false, + unsavedTime : new Date(), + currentEditorViewPageNum : 1, + currentEditorCursorPageNum : 1, + currentBrewRendererPageNum : 1, + displayLockMessage : this.props.brew.lock || false, + themeBundle : {} }; }, @@ -113,16 +119,27 @@ const EditPage = createClass({ this.editor.current.update(); }, + handleEditorViewPageChange : function(pageNumber){ + this.setState({ currentEditorViewPageNum: pageNumber }); + }, + + handleEditorCursorPageChange : function(pageNumber){ + this.setState({ currentEditorCursorPageNum: pageNumber }); + }, + + handleBrewRendererPageChange : function(pageNumber){ + this.setState({ currentBrewRendererPageNum: pageNumber }); + }, + handleTextChange : function(text){ //If there are errors, run the validator on every change to give quick feedback let htmlErrors = this.state.htmlErrors; if(htmlErrors.length) htmlErrors = Markdown.validate(text); this.setState((prevState)=>({ - brew : { ...prevState.brew, text: text }, - isPending : true, - htmlErrors : htmlErrors, - currentEditorPage : this.editor.current.getCurrentPage() - 1 //Offset index since Marked starts pages at 0 + brew : { ...prevState.brew, text: text }, + isPending : true, + htmlErrors : htmlErrors, }), ()=>{if(this.state.autoSave) this.trySave();}); }, @@ -150,6 +167,16 @@ const EditPage = createClass({ return !_.isEqual(this.state.brew, this.savedBrew); }, + updateBrew : function(newData){ + this.setState((prevState)=>({ + brew : { + ...prevState.brew, + style : newData.style, + text : newData.text + } + })); + }, + trySave : function(immediate=false){ if(!this.debounceSave) this.debounceSave = _.debounce(this.save, SAVE_TIMEOUT); if(this.hasChanges()){ @@ -202,6 +229,9 @@ const EditPage = createClass({ htmlErrors : Markdown.validate(prevState.brew.text) })); + await updateHistory(this.state.brew).catch(console.error); + await versionHistoryGarbageCollection().catch(console.error); + const transfer = this.state.saveGoogle == _.isNil(this.state.brew.googleId); const brew = this.state.brew; @@ -351,7 +381,7 @@ const EditPage = createClass({ **[Homebrewery Link](${global.config.publicUrl}/share/${shareLink})**`; - return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(title)}&text=${encodeURIComponent(text)}`; + return `https://www.reddit.com/r/UnearthedArcana/submit?title=${encodeURIComponent(title.toWellFormed())}&text=${encodeURIComponent(text)}`; }, renderNavbar : function(){ @@ -388,6 +418,7 @@ const EditPage = createClass({ + @@ -400,8 +431,8 @@ const EditPage = createClass({ {this.renderNavbar()} + {this.props.brew.lock && }
    - {this.props.brew.lock && } diff --git a/client/homebrew/pages/errorPage/errorPage.jsx b/client/homebrew/pages/errorPage/errorPage.jsx index 387a99b02..4ac73da8c 100644 --- a/client/homebrew/pages/errorPage/errorPage.jsx +++ b/client/homebrew/pages/errorPage/errorPage.jsx @@ -1,7 +1,7 @@ require('./errorPage.less'); const React = require('react'); const UIPage = require('../basePages/uiPage/uiPage.jsx'); -const Markdown = require('../../../../shared/naturalcrit/markdown.js'); +import Markdown from '../../../../shared/naturalcrit/markdown.js'; const ErrorIndex = require('./errors/errorIndex.js'); const ErrorPage = ({ brew })=>{ diff --git a/client/homebrew/pages/errorPage/errors/errorIndex.js b/client/homebrew/pages/errorPage/errors/errorIndex.js index 7bf2caae1..2c7be5d4b 100644 --- a/client/homebrew/pages/errorPage/errors/errorIndex.js +++ b/client/homebrew/pages/errorPage/errors/errorIndex.js @@ -2,6 +2,14 @@ const dedent = require('dedent-tabs').default; const loginUrl = 'https://www.naturalcrit.com/login'; +// Prevent parsing text (e.g. document titles) as markdown +const escape = (text = '')=>{ + return text.split('').map((char)=>`&#${char.charCodeAt(0)};`).join(''); +}; + +//001-050 : Brew errors +//050-100 : Other pages errors + const errorIndex = (props)=>{ return { // Default catch all @@ -15,7 +23,18 @@ const errorIndex = (props)=>{ '01' : dedent` ## An error occurred while retrieving this brew from Google Drive! - Google reported an error while attempting to retrieve a brew from this link.`, + Google is able to see the brew at this link, but reported an error while attempting to retrieve it. + + ### Refreshing your Google Credentials + + This issue is likely caused by an issue with your Google credentials; if you are the owner of this file, the following steps may resolve the issue: + + - Go to https://www.naturalcrit.com/login and click logout if present (in small text at the bottom of the page). + - Click "Sign In with Google", which will refresh your Google credentials. + - After completing the sign in process, return to Homebrewery and refresh/reload the page so that it can pick up the updated credentials. + - If this was the source of the issue, it should now be resolved. + + If following these steps does not resolve the issue, please let us know!`, // Google Drive - 404 : brew deleted or access denied '02' : dedent` @@ -47,7 +66,7 @@ const errorIndex = (props)=>{ - **The Google Account may be closed.** Google may have removed the account due to inactivity or violating a Google policy. Make sure the owner can still access Google Drive normally and upload/download files to it. - : + If the file isn't found, Google Drive usually puts your file in your Trash folder for 30 days. Assuming the trash hasn't been emptied yet, it might be worth checking. You can also find the Activity tab on the right side of the Google Drive page, which @@ -75,7 +94,7 @@ const errorIndex = (props)=>{ : - **Brew Title:** ${props.brew.brewTitle || 'Unable to show title'} + **Brew Title:** ${escape(props.brew.brewTitle) || 'Unable to show title'} **Current Authors:** ${props.brew.authors?.map((author)=>{return `[${author}](/user/${author})`;}).join(', ') || 'Unable to list authors'} @@ -90,7 +109,7 @@ const errorIndex = (props)=>{ : - **Brew Title:** ${props.brew.brewTitle || 'Unable to show title'} + **Brew Title:** ${escape(props.brew.brewTitle) || 'Unable to show title'} **Current Authors:** ${props.brew.authors?.map((author)=>{return `[${author}](/user/${author})`;}).join(', ') || 'Unable to list authors'} @@ -149,8 +168,16 @@ const errorIndex = (props)=>{ **Brew ID:** ${props.brew.brewId}`, + //account page when account is not defined + '50' : dedent` + ## You are not signed in + + You are trying to access the account page, but are not signed in to an account. + + Please login or signup at our [login page](https://www.naturalcrit.com/login?redirect=https://homebrewery.naturalcrit.com/account).`, + // Brew locked by Administrators error - '100' : dedent` + '51' : dedent` ## This brew has been locked. Only an author may request that this lock is removed. @@ -159,8 +186,18 @@ const errorIndex = (props)=>{ **Brew ID:** ${props.brew.brewId} - **Brew Title:** ${props.brew.brewTitle}`, + **Brew Title:** ${escape(props.brew.brewTitle)}`, + + // ####### Admin page error ####### + '52' : dedent` + ## Access Denied + You need to provide correct administrator credentials to access this page.`, + + '90' : dedent` An unexpected error occurred while looking for these brews. + Try again in a few minutes.`, + + '91' : dedent` An unexpected error occurred while trying to get the total of brews.`, }; }; -module.exports = errorIndex; \ No newline at end of file +module.exports = errorIndex; diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx index 490b22596..00d0c801d 100644 --- a/client/homebrew/pages/homePage/homePage.jsx +++ b/client/homebrew/pages/homePage/homePage.jsx @@ -1,21 +1,20 @@ require('./homePage.less'); const React = require('react'); const createClass = require('create-react-class'); -const _ = require('lodash'); const cx = require('classnames'); -const request = require('../../utils/request-middleware.js'); +import request from '../../utils/request-middleware.js'; const { Meta } = require('vitreum/headtags'); const Nav = require('naturalcrit/nav/nav.jsx'); const Navbar = require('../../navbar/navbar.jsx'); const NewBrewItem = require('../../navbar/newbrew.navitem.jsx'); const HelpNavItem = require('../../navbar/help.navitem.jsx'); +const VaultNavItem = require('../../navbar/vault.navitem.jsx'); const RecentNavItem = require('../../navbar/recent.navitem.jsx').both; const AccountNavItem = require('../../navbar/account.navitem.jsx'); const ErrorNavItem = require('../../navbar/error-navitem.jsx'); const { fetchThemeBundle } = require('../../../../shared/helpers.js'); - const SplitPane = require('naturalcrit/splitPane/splitPane.jsx'); const Editor = require('../../editor/editor.jsx'); const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx'); @@ -32,11 +31,13 @@ const HomePage = createClass({ }, getInitialState : function() { return { - brew : this.props.brew, - welcomeText : this.props.brew.text, - error : undefined, - currentEditorPage : 0, - themeBundle : {} + brew : this.props.brew, + welcomeText : this.props.brew.text, + error : undefined, + currentEditorViewPageNum : 1, + currentEditorCursorPageNum : 1, + currentBrewRendererPageNum : 1, + themeBundle : {} }; }, @@ -61,10 +62,22 @@ const HomePage = createClass({ handleSplitMove : function(){ this.editor.current.update(); }, + + handleEditorViewPageChange : function(pageNumber){ + this.setState({ currentEditorViewPageNum: pageNumber }); + }, + + handleEditorCursorPageChange : function(pageNumber){ + this.setState({ currentEditorCursorPageNum: pageNumber }); + }, + + handleBrewRendererPageChange : function(pageNumber){ + this.setState({ currentBrewRendererPageNum: pageNumber }); + }, + handleTextChange : function(text){ this.setState((prevState)=>({ - brew : { ...prevState.brew, text: text }, - currentEditorPage : this.editor.current.getCurrentPage() - 1 //Offset index since Marked starts pages at 0 + brew : { ...prevState.brew, text: text }, })); }, renderNavbar : function(){ @@ -76,6 +89,7 @@ const HomePage = createClass({ } + @@ -86,27 +100,33 @@ const HomePage = createClass({ return
    {this.renderNavbar()} - -
    - - - - +
    + + + +
    -
    Save current
    diff --git a/client/homebrew/pages/homePage/welcome_msg.md b/client/homebrew/pages/homePage/welcome_msg.md index c7d46149e..49d1918f0 100644 --- a/client/homebrew/pages/homePage/welcome_msg.md +++ b/client/homebrew/pages/homePage/welcome_msg.md @@ -91,13 +91,6 @@ If you are looking for more 5e Homebrew resources check out [r/UnearthedArcana]( \page - - - - - - - ## Markdown+ The Homebrewery aims to make homebrewing as simple as possible, providing a live editor with Markdown syntax that is more human-readable and faster to write with than raw HTML. diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx index 5b0f59c00..ee2c67d5f 100644 --- a/client/homebrew/pages/newPage/newPage.jsx +++ b/client/homebrew/pages/newPage/newPage.jsx @@ -2,9 +2,9 @@ require('./newPage.less'); const React = require('react'); const createClass = require('create-react-class'); -const request = require('../../utils/request-middleware.js'); +import request from '../../utils/request-middleware.js'; -const Markdown = require('naturalcrit/markdown.js'); +import Markdown from 'naturalcrit/markdown.js'; const Nav = require('naturalcrit/nav/nav.jsx'); const PrintNavItem = require('../../navbar/print.navitem.jsx'); @@ -39,13 +39,15 @@ const NewPage = createClass({ const brew = this.props.brew; return { - brew : brew, - isSaving : false, - saveGoogle : (global.account && global.account.googleId ? true : false), - error : null, - htmlErrors : Markdown.validate(brew.text), - currentEditorPage : 0, - themeBundle : {} + brew : brew, + isSaving : false, + saveGoogle : (global.account && global.account.googleId ? true : false), + error : null, + htmlErrors : Markdown.validate(brew.text), + currentEditorViewPageNum : 1, + currentEditorCursorPageNum : 1, + currentBrewRendererPageNum : 1, + themeBundle : {} }; }, @@ -108,15 +110,26 @@ const NewPage = createClass({ this.editor.current.update(); }, + handleEditorViewPageChange : function(pageNumber){ + this.setState({ currentEditorViewPageNum: pageNumber }); + }, + + handleEditorCursorPageChange : function(pageNumber){ + this.setState({ currentEditorCursorPageNum: pageNumber }); + }, + + handleBrewRendererPageChange : function(pageNumber){ + this.setState({ currentBrewRendererPageNum: pageNumber }); + }, + handleTextChange : function(text){ //If there are errors, run the validator on every change to give quick feedback let htmlErrors = this.state.htmlErrors; if(htmlErrors.length) htmlErrors = Markdown.validate(text); this.setState((prevState)=>({ - brew : { ...prevState.brew, text: text }, - htmlErrors : htmlErrors, - currentEditorPage : this.editor.current.getCurrentPage() - 1 //Offset index since Marked starts pages at 0 + brew : { ...prevState.brew, text: text }, + htmlErrors : htmlErrors, })); localStorage.setItem(BREWKEY, text); }, @@ -210,30 +223,38 @@ const NewPage = createClass({ render : function(){ return
    {this.renderNavbar()} -
    - - - - +
    + + + +
    ; } diff --git a/client/homebrew/pages/sharePage/sharePage.jsx b/client/homebrew/pages/sharePage/sharePage.jsx index 9b4f9b73d..e9c5540a2 100644 --- a/client/homebrew/pages/sharePage/sharePage.jsx +++ b/client/homebrew/pages/sharePage/sharePage.jsx @@ -1,6 +1,6 @@ require('./sharePage.less'); const React = require('react'); -const createClass = require('create-react-class'); +const { useState, useEffect, useCallback } = React; const { Meta } = require('vitreum/headtags'); const Nav = require('naturalcrit/nav/nav.jsx'); @@ -14,114 +14,114 @@ const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx'); const { DEFAULT_BREW_LOAD } = require('../../../../server/brewDefaults.js'); const { printCurrentBrew, fetchThemeBundle } = require('../../../../shared/helpers.js'); -const SharePage = createClass({ - displayName : 'SharePage', - getDefaultProps : function() { - return { - brew : DEFAULT_BREW_LOAD, - disableMeta : false - }; - }, +const SharePage = (props)=>{ + const { brew = DEFAULT_BREW_LOAD, disableMeta = false } = props; - getInitialState : function() { - return { - themeBundle : {} - }; - }, + const [state, setState] = useState({ + themeBundle : {}, + currentBrewRendererPageNum : 1, + }); - componentDidMount : function() { - document.addEventListener('keydown', this.handleControlKeys); + const handleBrewRendererPageChange = useCallback((pageNumber)=>{ + setState((prevState)=>({ + currentBrewRendererPageNum : pageNumber, + ...prevState })); + }, []); - fetchThemeBundle(this, this.props.brew.renderer, this.props.brew.theme); - }, - - componentWillUnmount : function() { - document.removeEventListener('keydown', this.handleControlKeys); - }, - - handleControlKeys : function(e){ + const handleControlKeys = (e)=>{ if(!(e.ctrlKey || e.metaKey)) return; const P_KEY = 80; - if(e.keyCode == P_KEY){ - if(e.keyCode == P_KEY) printCurrentBrew(); + if(e.keyCode === P_KEY) { + printCurrentBrew(); e.stopPropagation(); e.preventDefault(); } - }, + }; - processShareId : function() { - return this.props.brew.googleId && !this.props.brew.stubbed ? - this.props.brew.googleId + this.props.brew.shareId : - this.props.brew.shareId; - }, + useEffect(()=>{ + document.addEventListener('keydown', handleControlKeys); + fetchThemeBundle( + { setState }, + brew.renderer, + brew.theme + ); - renderEditLink : function(){ - if(!this.props.brew.editId) return; + return ()=>{ + document.removeEventListener('keydown', handleControlKeys); + }; + }, []); - let editLink = this.props.brew.editId; - if(this.props.brew.googleId && !this.props.brew.stubbed) { - editLink = this.props.brew.googleId + editLink; - } + const processShareId = ()=>{ + return brew.googleId && !brew.stubbed ? brew.googleId + brew.shareId : brew.shareId; + }; - return - edit - ; - }, + const renderEditLink = ()=>{ + if(!brew.editId) return null; - render : function(){ - const titleStyle = this.props.disableMeta ? { cursor: 'default' } : {}; - const titleEl = {this.props.brew.title}; + const editLink = brew.googleId && ! brew.stubbed ? brew.googleId + brew.editId : brew.editId; - return
    + return ( + + edit + + ); + }; + + const titleEl = ( + + {brew.title} + + ); + + return ( +
    - { - this.props.disableMeta ? - titleEl - : - - {titleEl} - - } + {disableMeta ? titleEl : {titleEl}} - {this.props.brew.shareId && <> - - - - source - - - view - - {this.renderEditLink()} - - download - - - clone to new - - - } - + {brew.shareId && ( + <> + + + + source + + + view + + {renderEditLink()} + + download + + + clone to new + + + + )} +
    -
    ; - } -}); +
    + ); +}; module.exports = SharePage; diff --git a/client/homebrew/pages/sharePage/sharePage.less b/client/homebrew/pages/sharePage/sharePage.less index 83e784c49..754108506 100644 --- a/client/homebrew/pages/sharePage/sharePage.less +++ b/client/homebrew/pages/sharePage/sharePage.less @@ -1,5 +1,5 @@ .sharePage{ - .navContent .navSection.titleSection { + nav .navSection.titleSection { flex-grow: 1; justify-content: center; } diff --git a/client/homebrew/pages/userPage/userPage.jsx b/client/homebrew/pages/userPage/userPage.jsx index 01778be44..f6fae639d 100644 --- a/client/homebrew/pages/userPage/userPage.jsx +++ b/client/homebrew/pages/userPage/userPage.jsx @@ -1,80 +1,60 @@ const React = require('react'); -const createClass = require('create-react-class'); -const _ = require('lodash'); +const { useState } = React; +const _ = require('lodash'); const ListPage = require('../basePages/listPage/listPage.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 ErrorNavItem = require('../../navbar/error-navitem.jsx'); +const VaultNavitem = require('../../navbar/vault.navitem.jsx'); -const UserPage = createClass({ - displayName : 'UserPage', - getDefaultProps : function() { - return { - username : '', - brews : [], - query : '', - error : null - }; - }, - getInitialState : function() { - const usernameWithS = this.props.username + (this.props.username.endsWith('s') ? `’` : `’s`); +const UserPage = (props)=>{ + props = { + username : '', + brews : [], + query : '', + ...props + }; - const brews = _.groupBy(this.props.brews, (brew)=>{ - return (brew.published ? 'published' : 'private'); - }); + const [error, setError] = useState(null); - const brewCollection = [ - { - title : `${usernameWithS} published brews`, - class : 'published', - brews : brews.published - } - ]; - if(this.props.username == global.account?.username){ - brewCollection.push( - { - title : `${usernameWithS} unpublished brews`, - class : 'unpublished', - brews : brews.private - } - ); - } + const usernameWithS = props.username + (props.username.endsWith('s') ? `’` : `’s`); + const groupedBrews = _.groupBy(props.brews, (brew)=>brew.published ? 'published' : 'private'); - return { - brewCollection : brewCollection - }; - }, - errorReported : function(error) { - this.setState({ - error - }); - }, + const brewCollection = [ + { + title : `${usernameWithS} published brews`, + class : 'published', + brews : groupedBrews.published || [] + }, + ...(props.username === global.account?.username ? [{ + title : `${usernameWithS} unpublished brews`, + class : 'unpublished', + brews : groupedBrews.private || [] + }] : []) + ]; - navItems : function() { - return + const navItems = ( + - {this.state.error ? - : - null - } + {error && ()} + - ; - }, + + ); - render : function(){ - return ; - } -}); + return ( + setError(err)} /> + ); +}; module.exports = UserPage; diff --git a/client/homebrew/pages/vaultPage/vaultPage.jsx b/client/homebrew/pages/vaultPage/vaultPage.jsx new file mode 100644 index 000000000..21a8e8363 --- /dev/null +++ b/client/homebrew/pages/vaultPage/vaultPage.jsx @@ -0,0 +1,431 @@ +/*eslint max-lines: ["warn", {"max": 400, "skipBlankLines": true, "skipComments": true}]*/ +/*eslint max-params:["warn", { max: 10 }], */ +require('./vaultPage.less'); + +const React = require('react'); +const { useState, useEffect, useRef } = React; + +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 BrewItem = require('../basePages/listPage/brewItem/brewItem.jsx'); +const SplitPane = require('../../../../shared/naturalcrit/splitPane/splitPane.jsx'); +const ErrorIndex = require('../errorPage/errors/errorIndex.js'); + +import request from '../../utils/request-middleware.js'; + +const VaultPage = (props)=>{ + const [pageState, setPageState] = useState(parseInt(props.query.page) || 1); + + const [sortState, setSort] = useState(props.query.sort || 'title'); + const [dirState, setdir] = useState(props.query.dir || 'asc'); + + //Response state + const [brewCollection, setBrewCollection] = useState(null); + const [totalBrews, setTotalBrews] = useState(null); + const [searching, setSearching] = useState(false); + const [error, setError] = useState(null); + + const titleRef = useRef(null); + const authorRef = useRef(null); + const countRef = useRef(null); + const v3Ref = useRef(null); + const legacyRef = useRef(null); + const submitButtonRef = useRef(null); + + useEffect(()=>{ + disableSubmitIfFormInvalid(); + loadPage(pageState, true, props.query.sort, props.query.dir); + }, []); + + const updateStateWithBrews = (brews, page)=>{ + setBrewCollection(brews || null); + setPageState(parseInt(page) || 1); + setSearching(false); + }; + + const updateUrl = (titleValue, authorValue, countValue, v3Value, legacyValue, page, sort, dir)=>{ + const url = new URL(window.location.href); + const urlParams = new URLSearchParams(url.search); + + urlParams.set('title', titleValue); + urlParams.set('author', authorValue); + urlParams.set('count', countValue); + urlParams.set('v3', v3Value); + urlParams.set('legacy', legacyValue); + urlParams.set('page', page); + urlParams.set('sort', sort); + urlParams.set('dir', dir); + + url.search = urlParams.toString(); + window.history.replaceState(null, '', url.toString()); + }; + + const performSearch = async (title, author, count, v3, legacy, page, sort, dir)=>{ + updateUrl(title, author, count, v3, legacy, page, sort, dir); + + const response = await request + .get(`/api/vault?title=${title}&author=${author}&v3=${v3}&legacy=${legacy}&count=${count}&page=${page}&sort=${sort}&dir=${dir}`) + .catch((error)=>{ + console.log('error at loadPage: ', error); + setError(error); + updateStateWithBrews([], 1); + }); + + if(response.ok) + updateStateWithBrews(response.body.brews, page); + }; + + const loadTotal = async (title, author, v3, legacy)=>{ + setTotalBrews(null); + + const response = await request.get(`/api/vault/total?title=${title}&author=${author}&v3=${v3}&legacy=${legacy}`) + .catch((error)=>{ + console.log('error at loadTotal: ', error); + setError(error); + updateStateWithBrews([], 1); + }); + + if(response.ok) + setTotalBrews(response.body.totalBrews); + }; + + const loadPage = async (page, updateTotal, sort, dir)=>{ + if(!validateForm()) return; + + setSearching(true); + setError(null); + + const title = titleRef.current.value || ''; + const author = authorRef.current.value || ''; + const count = countRef.current.value || 10; + const v3 = v3Ref.current.checked != false; + const legacy = legacyRef.current.checked != false; + const sortOption = sort || 'title'; + const dirOption = dir || 'asc'; + const pageProp = page || 1; + + setSort(sortOption); + setdir(dirOption); + + performSearch(title, author, count, v3, legacy, pageProp, sortOption, dirOption); + + if(updateTotal) + loadTotal(title, author, v3, legacy); + }; + + const renderNavItems = ()=>( + + + + Vault: Search for brews + + + + + + + + + + ); + + const validateForm = ()=>{ + //form validity: title or author must be written, and at least one renderer set + const isTitleValid = titleRef.current.validity.valid && titleRef.current.value; + const isAuthorValid = authorRef.current.validity.valid && authorRef.current.value; + const isCheckboxChecked = legacyRef.current.checked || v3Ref.current.checked; + + const isFormValid = (isTitleValid || isAuthorValid) && isCheckboxChecked; + + return isFormValid; + }; + + const disableSubmitIfFormInvalid = ()=>{ + submitButtonRef.current.disabled = !validateForm(); + }; + + const renderForm = ()=>( +
    +

    Brew Lookup

    +
    + + + + + + + + + + + +
    + +

    Tips and tricks

    +
      +
    • + Only published brews are searchable via this tool +
    • +
    • + Usernames are case-sensitive +
    • +
    • + Use "word" to match an exact string, + and - to exclude words (at least one word must not be negated) +
    • +
    • + Some common words like "a", "after", "through", "itself", "here", etc., + are ignored in searches. The full list can be found   + + here + +
    • +
    + New features will be coming, such as filters and search by tags. +
    +
    + ); + + const renderSortOption = (optionTitle, optionValue)=>{ + const oppositeDir = dirState === 'asc' ? 'desc' : 'asc'; + + return ( +
    + + {sortState === optionValue && ( + + )} +
    + ); + }; + + const renderSortBar = ()=>{ + + return ( +
    + {renderSortOption('Title', 'title', props.query.dir)} + {renderSortOption('Created Date', 'createdAt', props.query.dir)} + {renderSortOption('Updated Date', 'updatedAt', props.query.dir)} + {renderSortOption('Views', 'views', props.query.dir)} +
    + ); + }; + + const renderPaginationControls = ()=>{ + if(!totalBrews) return null; + + const countInt = parseInt(props.query.count || 20); + const totalPages = Math.ceil(totalBrews / countInt); + + let startPage, endPage; + if(pageState <= 6) { + startPage = 1; + endPage = Math.min(totalPages, 10); + } else if(pageState + 4 >= totalPages) { + startPage = Math.max(1, totalPages - 9); + endPage = totalPages; + } else { + startPage = pageState - 5; + endPage = pageState + 4; + } + + const pagesAroundCurrent = new Array(endPage - startPage + 1) + .fill() + .map((_, index)=>( + loadPage(startPage + index, false, sortState, dirState)} + > + {startPage + index} + + )); + + return ( +
    + +
      + {startPage > 1 && ( + loadPage(1, false, sortState, dirState)} + > + 1 ... + + )} + {pagesAroundCurrent} + {endPage < totalPages && ( + loadPage(totalPages, false, sortState, dirState)} + > + ... {totalPages} + + )} +
    + +
    + ); + }; + + const renderFoundBrews = ()=>{ + if(searching) { + return ( +
    +

    Searching

    +
    + ); + } + + if(error) { + const errorText = ErrorIndex()[error.HBErrorCode.toString()] || ''; + + return ( +
    +

    Error: {errorText}

    +
    + ); + } + + if(!brewCollection) { + return ( +
    +

    No search yet

    +
    + ); + } + + if(brewCollection.length === 0) { + return ( +
    +

    No brews found

    +
    + ); + } + + return ( +
    + + {`Brews found: `} + {totalBrews} + + {brewCollection.map((brew, index)=>{ + return ( + + ); + })} + {renderPaginationControls()} +
    + ); + }; + + return ( +
    + + + {renderNavItems()} +
    + +
    {renderForm()}
    +
    + {renderSortBar()} + {renderFoundBrews()} +
    +
    +
    +
    + ); +}; + +module.exports = VaultPage; diff --git a/client/homebrew/pages/vaultPage/vaultPage.less b/client/homebrew/pages/vaultPage/vaultPage.less new file mode 100644 index 000000000..a69bcb33b --- /dev/null +++ b/client/homebrew/pages/vaultPage/vaultPage.less @@ -0,0 +1,358 @@ +.vaultPage { + height : 100%; + overflow-y : hidden; + background-color : #2C3E50; + + *:not(input) { user-select : none; } + + .content .dataGroup { + width : 100%; + height : 100%; + background : white; + + &.form .brewLookup { + position : relative; + padding : 50px clamp(20px, 4vw, 50px); + + small { + font-size : 10pt; + color : #555555; + + a { color : #333333; } + } + + code { + padding-inline : 5px; + font-family : monospace; + background : lightgrey; + border-radius : 5px; + } + + h1, h2, h3, h4 { + font-family : 'CodeBold'; + letter-spacing : 2px; + } + + legend { + h3 { + margin-block : 30px 20px; + font-size : 20px; + text-align : center; + border-bottom : 2px solid; + } + ul { + padding-inline : 30px 10px; + li { + margin-block : 5px; + line-height : calc(1em + 5px); + list-style : disc; + } + } + } + + &::after { + position : absolute; + top : 0; + right : 0; + left : 0; + display : block; + padding : 10px; + font-weight : 900; + color : white; + white-space : pre-wrap; + content : 'Error:\A At least one renderer should be enabled to make a search'; + background : rgb(255, 60, 60); + opacity : 0; + transition : opacity 0.5s; + } + &:not(:has(input[type='checkbox']:checked))::after { opacity : 1; } + + .formTitle { + margin : 20px 0; + font-size : 30px; + color : black; + text-align : center; + border-bottom : 2px solid; + } + + .formContents { + position : relative; + display : flex; + flex-direction : column; + + label { + display : flex; + align-items : center; + margin : 10px 0; + } + select { margin : 0 10px; } + + input { + margin : 0 10px; + + &:invalid { background : rgb(255, 188, 181); } + + + } + + #searchButton { + .colorButton(@green); + position : absolute; + right : 20px; + bottom : 0; + + i { + margin-left : 10px; + animation-duration : 1000s; + } + } + } + } + + &.resultsContainer { + display : flex; + flex-direction : column; + height : 100%; + overflow-y : auto; + font-size : 0.34cm; + + h3 { + font-family : 'Open Sans'; + font-weight : 900; + color : white; + } + + .sort-container { + display : flex; + flex-wrap : wrap; + column-gap : 15px; + justify-content : center; + height : 30px; + color : white; + background-color : #555555; + border-top : 1px solid #666666; + border-bottom : 1px solid #666666; + + .sort-option { + display : flex; + align-items : center; + padding : 0 8px; + + &:hover { background-color : #444444; } + + &.active { + background-color : #333333; + + button { + font-weight : 800; + color : white; + + & + .sortDir { padding-left : 5px; } + } + } + + button { + padding : 0; + font-size : 11px; + font-weight : normal; + color : #CCCCCC; + text-transform : uppercase; + background-color : transparent; + + &:hover { background : none; } + } + } + } + + .foundBrews { + position : relative; + width : 100%; + height : 100%; + max-height : 100%; + padding : 50px 50px 70px 50px; + overflow-y : scroll; + background-color : #2C3E50; + + h3 { font-size : 25px; } + + &.noBrews { + display : grid; + place-items : center; + color : white; + } + + &.searching { + display : grid; + place-items : center; + color : white; + + h3 { position : relative; } + + h3.searchAnim::after { + position : absolute; + top : 50%; + right : 0; + width : max-content; + height : 1em; + content : ''; + translate : calc(100% + 5px) -50%; + animation : trailingDots 2s ease infinite; + } + } + + .totalBrews { + position : fixed; + right : 0; + bottom : 0; + z-index : 1000; + padding : 8px 10px; + font-family : 'Open Sans'; + font-size : 11px; + font-weight : 800; + color : white; + background-color : #333333; + + .searchAnim { + position : relative; + display : inline-block; + width : 3ch; + height : 1em; + } + + .searchAnim::after { + position : absolute; + top : 50%; + right : 0; + width : max-content; + height : 1em; + content : ''; + translate : -50% -50%; + animation : trailingDots 2s ease infinite; + } + } + + .brewItem { + width : 47%; + margin-right : 40px; + color : black; + isolation : isolate; + + &::after { + position : absolute; + inset : 0; + z-index : -2; + display : block; + content : ''; + background-image : url('/assets/parchmentBackground.jpg'); + } + + &:nth-child(even of .brewItem) { margin-right : 0; } + + h2 { + font-family : 'MrEavesRemake'; + font-size : 0.75cm; + font-weight : 800; + line-height : 0.988em; + color : var(--HB_Color_HeaderText); + } + .info { + position : relative; + z-index : 2; + font-family : 'ScalySansRemake'; + font-size : 1.2em; + + >span { + margin-right : 12px; + line-height : 1.5em; + } + } + .links { z-index : 2; } + + hr { + margin : 0px; + visibility : hidden; + } + + .thumbnail { z-index : -1; } + } + + .paginationControls { + position : absolute; + left : 50%; + display : grid; + grid-template-areas : 'previousPage currentPage nextPage'; + grid-template-columns : 50px 1fr 50px; + place-items : center; + width : auto; + translate : -50%; + + .pages { + display : flex; + grid-area : currentPage; + justify-content : space-evenly; + width : 100%; + height : 100%; + padding : 5px 8px; + text-align : center; + + .pageNumber { + margin-inline : 1vw; + font-family : 'Open Sans'; + font-weight : 900; + color : white; + text-underline-position : under; + text-wrap : nowrap; + cursor : pointer; + + &.currentPage { + color : gold; + text-decoration : underline; + pointer-events : none; + } + + &.firstPage { margin-right : -5px; } + + &.lastPage { margin-left : -5px; } + } + } + + button { + .colorButton(@green); + width : max-content; + + &.previousPage { grid-area : previousPage; } + + &.nextPage { grid-area : nextPage; } + } + + } + } + } + } + +} + +@keyframes trailingDots { + + 0%, + 32% { content : ' .'; } + + 33%, + 65% { content : ' ..'; } + + 66%, + 100% { content : ' ...'; } +} + +// media query for when the page is smaller than 1079 px in width +@media screen and (max-width : 1079px) { + .vaultPage { + + .dataGroup.form .brewLookup { padding : 1px 20px 20px 10px; } + + .dataGroup.resultsContainer .foundBrews .brewItem { + width : 100%; + margin-inline : auto; + } + } +} diff --git a/client/homebrew/utils/customIDBStore.js b/client/homebrew/utils/customIDBStore.js new file mode 100644 index 000000000..6a3c84400 --- /dev/null +++ b/client/homebrew/utils/customIDBStore.js @@ -0,0 +1,19 @@ +import * as IDB from 'idb-keyval/dist/index.js'; + +export function initCustomStore(db, store){ + const createCustomStore = async ()=>IDB.createStore(db, store); + + return { + entries : async ()=>IDB.entries(await createCustomStore()), + keys : async ()=>IDB.keys(await createCustomStore()), + values : async ()=>IDB.values(await createCustomStore()), + clear : async ()=>IDB.clear(await createCustomStore), + get : async (key)=>IDB.get(key, await createCustomStore()), + getMany : async (keys)=>IDB.getMany(keys, await createCustomStore()), + set : async (key, value)=>IDB.set(key, value, await createCustomStore()), + setMany : async (entries)=>IDB.setMany(entries, await createCustomStore()), + update : async (key, updateFn)=>IDB.update(key, updateFn, await createCustomStore()), + del : async (key)=>IDB.del(key, await createCustomStore()), + delMany : async (keys)=>IDB.delMany(keys, await createCustomStore()) + }; +}; \ No newline at end of file diff --git a/client/homebrew/utils/request-middleware.js b/client/homebrew/utils/request-middleware.js index f6bc2571b..01a9d2571 100644 --- a/client/homebrew/utils/request-middleware.js +++ b/client/homebrew/utils/request-middleware.js @@ -1,4 +1,4 @@ -const request = require('superagent'); +import request from 'superagent'; const addHeader = (request)=>request.set('Homebrewery-Version', global.version); @@ -9,4 +9,4 @@ const requestMiddleware = { delete : (path)=>addHeader(request.delete(path)), }; -module.exports = requestMiddleware; \ No newline at end of file +export default requestMiddleware; \ No newline at end of file diff --git a/client/homebrew/utils/versionHistory.js b/client/homebrew/utils/versionHistory.js new file mode 100644 index 000000000..ec3bd74e1 --- /dev/null +++ b/client/homebrew/utils/versionHistory.js @@ -0,0 +1,119 @@ +import { initCustomStore } from './customIDBStore.js'; + +export const HISTORY_PREFIX = 'HOMEBREWERY-HISTORY'; +export const HISTORY_SLOTS = 5; + +// History values in minutes +const HISTORY_SAVE_DELAYS = { + '0' : 0, + '1' : 2, + '2' : 10, + '3' : 60, + '4' : 12 * 60, + '5' : 2 * 24 * 60 +}; +// const HISTORY_SAVE_DELAYS = { +// '0' : 0, +// '1' : 1, +// '2' : 2, +// '3' : 3, +// '4' : 4, +// '5' : 5 +// }; + +const GARBAGE_COLLECT_DELAY = 28 * 24 * 60; +// const GARBAGE_COLLECT_DELAY = 10; + + +const HB_DB = 'HOMEBREWERY-DB'; +const HB_STORE = 'HISTORY'; + +const IDB = initCustomStore(HB_DB, HB_STORE); + +function getKeyBySlot(brew, slot){ + // Return a string representing the key for this brew and history slot + return `${HISTORY_PREFIX}-${brew.shareId}-${slot}`; +}; + +function parseBrewForStorage(brew, slot = 0) { + // Strip out unneeded object properties + // Returns an array of [ key, brew ] + const archiveBrew = { + title : brew.title, + text : brew.text, + style : brew.style, + version : brew.version, + shareId : brew.shareId, + savedAt : brew?.savedAt || new Date(), + expireAt : new Date() + }; + + archiveBrew.expireAt.setMinutes(archiveBrew.expireAt.getMinutes() + HISTORY_SAVE_DELAYS[slot]); + + const key = getKeyBySlot(brew, slot); + + return [key, archiveBrew]; +} + +export async function loadHistory(brew){ + const DEFAULT_HISTORY_ITEM = { expireAt: '2000-01-01T00:00:00.000Z', shareId: brew.shareId, noData: true }; + + const historyKeys = []; + + // Create array of all history keys + for (let i = 1; i <= HISTORY_SLOTS; i++){ + historyKeys.push(getKeyBySlot(brew, i)); + }; + + // Load all keys from IDB at once + const dataArray = await IDB.getMany(historyKeys); + return dataArray.map((data)=>{ return data ?? DEFAULT_HISTORY_ITEM; }); +} + +export async function updateHistory(brew) { + const history = await loadHistory(brew); + + // Walk each version position + for (let slot = HISTORY_SLOTS - 1; slot >= 0; slot--){ + const storedVersion = history[slot]; + + // If slot has expired, update all lower slots and break + if(new Date() >= new Date(storedVersion.expireAt)){ + + // Create array of arrays : [ [key1, value1], [key2, value2], ..., [keyN, valueN] ] + // to pass to IDB.setMany + const historyUpdate = []; + + for (let updateSlot = slot; updateSlot > 0; updateSlot--){ + // Move data from updateSlot to updateSlot + 1 + if(!history[updateSlot - 1]?.noData) { + historyUpdate.push(parseBrewForStorage(history[updateSlot - 1], updateSlot + 1)); + } + }; + + // Update the most recent brew + historyUpdate.push(parseBrewForStorage(brew, 1)); + + await IDB.setMany(historyUpdate); + + // Break out of data checks because we found an expired value + break; + } + }; +}; + +export async function versionHistoryGarbageCollection(){ + const entries = await IDB.entries(); + + const expiredKeys = []; + for (const [key, value] of entries){ + const expireAt = new Date(value.savedAt); + expireAt.setMinutes(expireAt.getMinutes() + GARBAGE_COLLECT_DELAY); + if(new Date() > expireAt){ + expiredKeys.push(key); + }; + }; + if(expiredKeys.length > 0){ + await IDB.delMany(expiredKeys); + } +}; \ No newline at end of file diff --git a/client/icons/customIcons.less b/client/icons/customIcons.less index dd6605326..1c8d1bd47 100644 --- a/client/icons/customIcons.less +++ b/client/icons/customIcons.less @@ -1,57 +1,84 @@ .fac { - display : inline-block; + display : inline-block; + background-color : currentColor; + mask-size : contain; + mask-repeat : no-repeat; + mask-position : center; + width : 1em; + aspect-ratio : 1; } .position-top-left { - content: url('../icons/position-top-left.svg'); + mask-image: url('../icons/position-top-left.svg'); } .position-top-right { - content: url('../icons/position-top-right.svg'); + mask-image: url('../icons/position-top-right.svg'); } .position-bottom-left { - content: url('../icons/position-bottom-left.svg'); + mask-image: url('../icons/position-bottom-left.svg'); } .position-bottom-right { - content: url('../icons/position-bottom-right.svg'); + mask-image: url('../icons/position-bottom-right.svg'); } .position-top { - content: url('../icons/position-top.svg'); + mask-image: url('../icons/position-top.svg'); } .position-right { - content: url('../icons/position-right.svg'); + mask-image: url('../icons/position-right.svg'); } .position-bottom { - content: url('../icons/position-bottom.svg'); + mask-image: url('../icons/position-bottom.svg'); } .position-left { - content: url('../icons/position-left.svg'); + mask-image: url('../icons/position-left.svg'); } .mask-edge { - content: url('../icons/mask-edge.svg'); + mask-image: url('../icons/mask-edge.svg'); } .mask-corner { - content: url('../icons/mask-corner.svg'); + mask-image: url('../icons/mask-corner.svg'); } .mask-center { - content: url('../icons/mask-center.svg'); + mask-image: url('../icons/mask-center.svg'); } .book-front-cover { - content: url('../icons/book-front-cover.svg'); + mask-image: url('../icons/book-front-cover.svg'); } .book-back-cover { - content: url('../icons/book-back-cover.svg'); + mask-image: url('../icons/book-back-cover.svg'); } .book-inside-cover { - content: url('../icons/book-inside-cover.svg'); + mask-image: url('../icons/book-inside-cover.svg'); } .book-part-cover { - content: url('../icons/book-part-cover.svg'); + mask-image: url('../icons/book-part-cover.svg'); +} +.image-wrap-left { + mask-image: url('../icons/image-wrap-left.svg'); +} +.image-wrap-right { + mask-image: url('../icons/image-wrap-right.svg'); } .davek { - content: url('../icons/Davek.svg'); + mask-image: url('../icons/Davek.svg'); } .rellanic { - content: url('../icons/Rellanic.svg'); + mask-image: url('../icons/Rellanic.svg'); } .iokharic { - content: url('../icons/Iokharic.svg'); + mask-image: url('../icons/Iokharic.svg'); +} +.zoom-to-fit { + mask-image: url('../icons/zoom-to-fit.svg'); +} +.fit-width { + mask-image: url('../icons/fit-width.svg'); +} +.single-spread { + mask-image: url('../icons/single-spread.svg'); +} +.facing-spread { + mask-image: url('../icons/facing-spread.svg'); +} +.flow-spread { + mask-image: url('../icons/flow-spread.svg'); } diff --git a/client/icons/facing-spread.svg b/client/icons/facing-spread.svg new file mode 100644 index 000000000..fbff1c948 --- /dev/null +++ b/client/icons/facing-spread.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/client/icons/fit-width.svg b/client/icons/fit-width.svg new file mode 100644 index 000000000..dd3e52f75 --- /dev/null +++ b/client/icons/fit-width.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/client/icons/flow-spread.svg b/client/icons/flow-spread.svg new file mode 100644 index 000000000..28193cb0b --- /dev/null +++ b/client/icons/flow-spread.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/icons/image-wrap-left.svg b/client/icons/image-wrap-left.svg new file mode 100644 index 000000000..fe1024e43 --- /dev/null +++ b/client/icons/image-wrap-left.svg @@ -0,0 +1,58 @@ + + diff --git a/client/icons/image-wrap-right.svg b/client/icons/image-wrap-right.svg new file mode 100644 index 000000000..336a20b64 --- /dev/null +++ b/client/icons/image-wrap-right.svg @@ -0,0 +1,58 @@ + + diff --git a/client/icons/single-spread.svg b/client/icons/single-spread.svg new file mode 100644 index 000000000..06396e589 --- /dev/null +++ b/client/icons/single-spread.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/client/icons/zoom-to-fit.svg b/client/icons/zoom-to-fit.svg new file mode 100644 index 000000000..5179ec45e --- /dev/null +++ b/client/icons/zoom-to-fit.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/client/template.js b/client/template.js index c77f953ff..990df785c 100644 --- a/client/template.js +++ b/client/template.js @@ -8,6 +8,8 @@ const template = async function(name, title='', props = {}){ }); const ogMetaTags = ogTags.join('\n'); + const ssrModule = await import(`../build/${name}/ssr.cjs`); + return ` @@ -21,7 +23,7 @@ const template = async function(name, title='', props = {}){ ${title.length ? `${title} - The Homebrewery`: 'The Homebrewery - NaturalCrit'} -
    ${require(`../build/${name}/ssr.js`)(props)}
    +
    ${ssrModule.default(props)}
    @@ -29,4 +31,4 @@ const template = async function(name, title='', props = {}){ `; }; -module.exports = template; \ No newline at end of file +export default template; \ No newline at end of file diff --git a/config/default.json b/config/default.json index 12b35e6cf..bea3b2663 100644 --- a/config/default.json +++ b/config/default.json @@ -6,5 +6,7 @@ "enable_v3" : true, "enable_themes" : true, "local_environments" : ["docker", "local"], - "publicUrl" : "https://homebrewery.naturalcrit.com" + "publicUrl" : "https://homebrewery.naturalcrit.com", + "hb_images" : null, + "hb_fonts" : null } diff --git a/docker-compose.yml b/docker-compose.yml index f74fadc0a..d7e5f3b90 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: '2' services: mongodb: image: mongo:latest diff --git a/faq.md b/faq.md index c7254952b..d3b6d24f6 100644 --- a/faq.md +++ b/faq.md @@ -69,7 +69,6 @@ pre { You can check the site status here: [Everyone or Just Me](https://downforeveryoneorjustme.com/homebrewery.naturalcrit.com) - ### Why am I getting an error when trying to save, and my account is linked to Google? A sign-in with Google only lasts a year until the authentication expires. You must go [here](https://www.naturalcrit.com/login), click the *Log-out* button, and then sign back in using your Google account. @@ -82,12 +81,17 @@ If you have linked your account with a Google account, you would change your pas ### Is there a way to restore a previous version of my brew? -Currently, there is no way to do this through the site yourself. This would take too much of a toll on the amount of storage the homebrewery requires. However, we do have daily backups of our database that we keep for 8 days, and you can contact the moderators on [the subreddit](https://www.reddit.com/r/homebrewery) with your Homebrewery username, the name of the lost brew, and the last known time it was working properly. We can manually look through our backups and restore it if it exists. +In your brew, there is an icon, :fas_clock_rotate_left:, that button opens up a menu with versions of your brew, stored in order from newer to older, up to a week old. Because of the amount of duplicates this function creates, this information is stored in **your browser**, so if you were to uninstall it or clear your cookies and site data, or change computers, the info will not be there. + +Also, we do have daily backups of our database that we keep for 8 days, and you can contact the moderators on [the subreddit](https://www.reddit.com/r/homebrewery) with your Homebrewery username, the name of the lost brew, and the last known time it was working properly. We can manually look through our backups and restore it if it exists. + ### I worked on a brew for X hours, and suddenly all the text disappeared! This usually happens if you accidentally drag-select all of your text and then start typing which overwrites the selection. Do not panic, and do not refresh the page or reload your brew quite yet as it is probably auto-saved in this state already. Simply press CTRL+Z as many times as needed to undo your last few changes and you will be back to where you were, then make sure to save your brew in the "good" state. +You can also load a history version old enough to have all the text, using the :fas_clock_rotate_left: history versions button. + \column ### Why is only Chrome supported? @@ -112,10 +116,7 @@ Once you have an image you would like to use, it is recommended to host it somew \page ### A particular font does not work for my language, what do I do? -The fonts used were originally created for use with the English language, though revisions since then have added more support for other languages. They are still not complete sets and may be missing a glyph/character you need. Unfortunately, the volunteer group as it stands at the time of this writing does not have a font guru, so it would be difficult to add more glyphs (especially complicated glyphs). Let us know which glyph is missing on the subreddit, but you may need to search [Google Fonts](https://fonts.google.com) for an alternative font if you need something fast. - -### Whenever I click on the "Get PDF" button, instead of getting a download, it opens Print Preview in another tab. -Yes, this is by design. In the print preview, select "Save as PDF" as the Destination, and then click "Save". There will be a normal download dialog where you can save your brew as a PDF. +The fonts used were originally created for use with the English language, though revisions since then have added more support for other languages. They are still not complete sets and may be missing a glyph/character you need. Unfortunately, the volunteer group as it stands at the time of this writing does not have a font guru, so it would be difficult to add more glyphs (especially complicated glyphs). Let us know which glyph is missing on the subreddit, but you may need to search [Google Fonts](https://fonts.google.com) for an alternative font if you need something fast. ### I have white borders on the bottom/sides of the print preview. @@ -126,4 +127,8 @@ The Homebrewery defaults to creating US Letter page sizes. If you are printing ### Typing `#### Adhesion` in the text editor doesn't show the header at all in the completed page? -Your ad-blocking software is mistakenly assuming your text to be an ad. Whitelist homebrewery.naturalcrit.com in your ad-blocking software. +Your ad-blocking software is mistakenly assuming your text to be an ad. We recommend whitelisting homebrewery.naturalcrit.com in your ad-blocking software, as we have no ads. + +### My username appears as _hidden_ when checking my brews in the Vault, why is that? + +Your username is most likely your e-mail adress, and our code is picking that up and protecting your identity. This will remain as is, but you can ask for a name change by contacting the moderators on [the subreddit](https://www.reddit.com/r/homebrewery) with your Homebrewery username, and your desired new name. You will also be asked to provide details about some of your unpublished brews, to verify your identity. No information will be leaked or shared. diff --git a/install/README.UBUNTU.md b/install/README.UBUNTU.md index d14cfef46..13fa7631d 100644 --- a/install/README.UBUNTU.md +++ b/install/README.UBUNTU.md @@ -24,12 +24,16 @@ These instructions assume that you are installing to a completely new, fresh Ubu These installation instructions have been tested on the following Ubuntu releases: -- *ubuntu-20.04.3-desktop-amd64* + - *ubuntu-24.04.1-desktop-amd64* + - *ubuntu-22.04.5-desktop-amd64* + - *ubuntu-20.04.6-desktop-amd64* ## Final Notes While this installation process works successfully at the time of writing (December 19, 2021), it relies on all of the Node.JS packages used in the HomeBrewery project retaining their cross-platform capabilities to continue to function. This is one of the inherent advantages of Node.JS, but it is by no means guaranteed and as such, functionality or even installation may fail without warning at some point in the future. +Earlier versions of Ubuntu may requier an alternate Mongo setup, see https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-ubuntu/ for assistance. + Regards, G December 19, 2021 diff --git a/install/ubuntu/etc/systemd/system/homebrewery.service b/install/ubuntu/etc/systemd/system/homebrewery.service index 939d11fb8..defa85a31 100644 --- a/install/ubuntu/etc/systemd/system/homebrewery.service +++ b/install/ubuntu/etc/systemd/system/homebrewery.service @@ -3,7 +3,8 @@ Description=Homebrewery Web Server [Service] User=root -After=mongodb +BindsTo=mongod.service +After=mongod.service Environment=NODE_ENV=local WorkingDirectory=/usr/local/homebrewery ExecStart=node server.js diff --git a/install/ubuntu/install.sh b/install/ubuntu/install.sh index ebad7f3f2..a0fcc8c17 100644 --- a/install/ubuntu/install.sh +++ b/install/ubuntu/install.sh @@ -1,14 +1,60 @@ #!/bin/sh +# Detect Ubuntu Version +export DISTRO=$(grep "^NAME=" /etc/os-release | awk -F '=' '{print $2}' | sed 's/"//g') +export DISTRO_VER=$(grep "VERSION_ID=" /etc/os-release | awk -F '=' '{print $2}' | sed 's/"//g') +export MATCHED="Yes" + +if [ "${DISTRO}" != "Ubuntu" ]; +then + echo :: Ubuntu not detected. Are you using an alternate spin or derivative? + echo :: Detected - ${DISTRO} + read -p [y/N] YESNO + if [ "${YESNO}" != "Y" ] && [ ]"${YESNO}" != "y" ]; then + exit + fi + + MATCHED="No" +fi + # Install CURL and add required NodeJS source to package repo echo ::Install CURL apt install -y curl echo ::Add NodeJS source to package repo -curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash - +curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - +# Add Mongo CE Source +if [ ${DISTRO} = "Ubuntu" ]; + then + echo ::Add Mongo CE source to package repo + curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | \ + sudo gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg \ + --dearmor + if [ "${DISTRO_VER}" == "24.04" ]; then + echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu noble/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list + elif [ "${DISTRO_VER}" == "22.04" ]; then + echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list + elif [ "${DISTRO_VER}" == "20.04" ]; then + echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/8.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list + else + MATCHED="No" + fi + sudo apt-get update +fi + +if [ ${MATCHED} == "No" ]; then + echo :: WARNING + echo :: Unable to determine Ubuntu version for Mongo installation purposes. + echo :: Please check your spin/distro documentation to install Mongo CE and enable it on startup. +fi + # Install required packages echo ::Install Homebrewery requirements -apt satisfy -y git nodejs npm mongodb +apt satisfy -y git nodejs npm mongodb-org + +# Enable and start Mongo +systemctl enable mongod +systemctl start mongod # Clone Homebrewery repo echo ::Get Homebrewery files diff --git a/package-lock.json b/package-lock.json index 49ac3f775..649c6597c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,70 +1,75 @@ { "name": "homebrewery", - "version": "3.14.1", + "version": "3.17.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "homebrewery", - "version": "3.14.1", + "version": "3.17.0", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.25.2", - "@babel/plugin-transform-runtime": "^7.25.4", - "@babel/preset-env": "^7.25.4", - "@babel/preset-react": "^7.24.7", - "@googleapis/drive": "^8.13.0", + "@babel/core": "^7.26.7", + "@babel/plugin-transform-runtime": "^7.25.9", + "@babel/preset-env": "^7.26.7", + "@babel/preset-react": "^7.26.3", + "@googleapis/drive": "^8.14.0", "body-parser": "^1.20.2", "classnames": "^2.5.1", "codemirror": "^5.65.6", - "cookie-parser": "^1.4.6", + "cookie-parser": "^1.4.7", + "core-js": "^3.40.0", + "cors": "^2.8.5", "create-react-class": "^15.7.0", "dedent-tabs": "^0.10.3", - "dompurify": "^3.1.6", + "dompurify": "^3.2.4", "expr-eval": "^2.0.2", - "express": "^4.19.2", + "express": "^4.21.2", "express-async-handler": "^1.2.0", - "express-static-gzip": "2.1.7", - "fs-extra": "11.2.0", + "express-static-gzip": "2.2.0", + "fs-extra": "11.3.0", + "idb-keyval": "^6.2.1", "js-yaml": "^4.1.0", "jwt-simple": "^0.5.6", "less": "^3.13.1", "lodash": "^4.17.21", - "marked": "11.2.0", - "marked-emoji": "^1.4.2", - "marked-extended-tables": "^1.0.8", - "marked-gfm-heading-id": "^3.2.0", - "marked-smartypants-lite": "^1.0.2", + "marked": "13.0.3", + "marked-emoji": "^1.4.3", + "marked-extended-tables": "^1.1.0", + "marked-gfm-heading-id": "^4.0.1", + "marked-smartypants-lite": "^1.0.3", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^8.5.3", - "nanoid": "3.3.4", + "mongoose": "^8.9.6", + "nanoid": "5.0.9", "nconf": "^0.12.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-frame-component": "^4.1.3", - "react-router-dom": "6.26.1", + "react-router": "^7.1.5", "sanitize-filename": "1.6.3", - "superagent": "^9.0.2", + "superagent": "^10.1.1", "vitreum": "git+https://git@github.com/calculuschild/vitreum.git" }, "devDependencies": { - "@stylistic/stylelint-plugin": "^3.0.1", - "eslint": "^9.9.0", - "eslint-plugin-jest": "^28.8.0", - "eslint-plugin-react": "^7.35.0", - "globals": "^15.9.0", + "@stylistic/stylelint-plugin": "^3.1.1", + "babel-plugin-transform-import-meta": "^2.3.2", + "eslint": "^9.19.0", + "eslint-plugin-jest": "^28.11.0", + "eslint-plugin-react": "^7.37.4", + "globals": "^15.14.0", "jest": "^29.7.0", "jest-expect-message": "^1.1.3", + "jsdom-global": "^3.0.2", "postcss-less": "^6.0.0", - "stylelint": "^16.8.2", - "stylelint-config-recess-order": "^5.1.0", - "stylelint-config-recommended": "^14.0.1", + "stylelint": "^16.14.1", + "stylelint-config-recess-order": "^6.0.0", + "stylelint-config-recommended": "^15.0.0", "supertest": "^7.0.0" }, "engines": { - "node": "^20.8.x", + "node": "^20.18.x", "npm": "^10.2.x" } }, @@ -82,12 +87,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "license": "MIT", "dependencies": { - "@babel/highlight": "^7.24.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -95,29 +101,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", - "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", - "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.7.tgz", + "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-module-transforms": "^7.25.2", - "@babel/helpers": "^7.25.0", - "@babel/parser": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.2", - "@babel/types": "^7.25.2", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.7", + "@babel/parser": "^7.26.7", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.26.7", + "@babel/types": "^7.26.7", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -133,53 +140,41 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.4.tgz", - "integrity": "sha512-NFtZmZsyzDPJnk9Zg3BbTfKKc9UlHYzD0E//p2Z3B9nCwwtJW9T0gVbCz8+fBngnn4zf1Dr3IK8PHQQHq0lDQw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.25.4", + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz", - "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", - "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.2", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -188,16 +183,16 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz", - "integrity": "sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.8", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.25.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/traverse": "^7.25.4", + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", "semver": "^6.3.1" }, "engines": { @@ -208,13 +203,12 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz", - "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", + "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "regexpu-core": "^5.3.1", + "@babel/helper-annotate-as-pure": "^7.25.9", + "regexpu-core": "^6.1.1", "semver": "^6.3.1" }, "engines": { @@ -241,41 +235,37 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", - "integrity": "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", "dependencies": { - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.8" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", - "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", - "license": "MIT", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.2" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -285,35 +275,33 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz", - "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", - "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz", - "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-wrap-function": "^7.25.0", - "@babel/traverse": "^7.25.0" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -323,14 +311,13 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", - "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", + "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.24.8", - "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/traverse": "^7.25.0" + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -339,107 +326,75 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz", - "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", - "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz", - "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", "dependencies": { - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", - "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", + "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", "license": "MIT", "dependencies": { - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz", - "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", + "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.25.4" + "@babel/types": "^7.26.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -449,13 +404,12 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", - "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.3" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -465,12 +419,11 @@ } }, "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz", - "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -480,12 +433,11 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz", - "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -495,14 +447,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz", - "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -512,13 +463,12 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz", - "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.0" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -543,6 +493,7 @@ "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -568,6 +519,7 @@ "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" @@ -576,52 +528,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", - "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", - "license": "MIT", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -631,12 +543,12 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", - "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -649,6 +561,7 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" @@ -661,6 +574,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -670,12 +584,11 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", - "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -688,6 +601,7 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" @@ -700,6 +614,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -712,6 +627,7 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" @@ -724,6 +640,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -736,6 +653,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -748,6 +666,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" @@ -756,25 +675,11 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" @@ -819,12 +724,11 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz", - "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -834,14 +738,13 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.4.tgz", - "integrity": "sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", + "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-remap-async-to-generator": "^7.25.0", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/traverse": "^7.25.4" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -851,14 +754,13 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", - "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", + "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -868,12 +770,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz", - "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -883,12 +785,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz", - "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -898,12 +799,12 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz", - "integrity": "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.4", - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -913,14 +814,12 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz", - "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==", - "license": "MIT", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -930,15 +829,15 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz", - "integrity": "sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-replace-supers": "^7.25.0", - "@babel/traverse": "^7.25.4", + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", "globals": "^11.1.0" }, "engines": { @@ -957,13 +856,12 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", - "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/template": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -973,12 +871,11 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz", - "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -988,13 +885,12 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz", - "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1004,12 +900,11 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz", - "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1019,13 +914,12 @@ } }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz", - "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.0", - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1035,13 +929,11 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", - "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1051,13 +943,12 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz", - "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", + "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", "license": "MIT", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1067,13 +958,11 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz", - "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1083,13 +972,12 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz", - "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", + "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1099,14 +987,13 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.25.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", - "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", "dependencies": { - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/traverse": "^7.25.1" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1116,13 +1003,11 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz", - "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-json-strings": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1132,12 +1017,11 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", - "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1147,13 +1031,11 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz", - "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1163,12 +1045,11 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz", - "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1178,13 +1059,12 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz", - "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1194,14 +1074,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz", - "integrity": "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", + "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.24.8", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-simple-access": "^7.24.7" + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1211,15 +1090,14 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz", - "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", "dependencies": { - "@babel/helper-module-transforms": "^7.25.0", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.0" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1229,13 +1107,12 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz", - "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1245,13 +1122,12 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz", - "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1261,12 +1137,11 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz", - "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1276,13 +1151,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz", - "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==", + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1292,13 +1166,11 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz", - "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1308,15 +1180,13 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz", - "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.7" + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1326,13 +1196,12 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz", - "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1342,13 +1211,11 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz", - "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1358,14 +1225,12 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz", - "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1375,12 +1240,11 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz", - "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1390,12 +1254,12 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz", - "integrity": "sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.4", - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1405,15 +1269,13 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz", - "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1423,12 +1285,11 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", - "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1438,12 +1299,11 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz", - "integrity": "sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz", + "integrity": "sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1453,16 +1313,15 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.2.tgz", - "integrity": "sha512-KQsqEAVBpU82NM/B/N9j9WOdphom1SZH3R+2V7INrQUH+V9EBFwZsEJl8eBIVeQE62FxJCc70jzEZwqU7RcVqA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", + "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/plugin-syntax-jsx": "^7.24.7", - "@babel/types": "^7.25.2" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-syntax-jsx": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1472,12 +1331,11 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.24.7.tgz", - "integrity": "sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", + "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.24.7" + "@babel/plugin-transform-react-jsx": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1487,13 +1345,12 @@ } }, "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.7.tgz", - "integrity": "sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.9.tgz", + "integrity": "sha512-KQ/Takk3T8Qzj5TppkS1be588lkbTp5uj7w6a0LeQaTMSckU/wK0oJ/pih+T690tkgI5jfmg2TqDJvd41Sj1Cg==", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1503,12 +1360,11 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz", - "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.25.9", "regenerator-transform": "^0.15.2" }, "engines": { @@ -1518,13 +1374,27 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz", - "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==", - "license": "MIT", + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", + "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1534,12 +1404,12 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.4.tgz", - "integrity": "sha512-8hsyG+KUYGY0coX6KUCDancA0Vw225KJ2HJO0yCNr1vq5r+lJTleDaJf0K7iOhjw4SWhu03TMBzYTJ9krmzULQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz", + "integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==", "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", @@ -1553,12 +1423,11 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz", - "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1568,13 +1437,12 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz", - "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1584,12 +1452,11 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz", - "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1599,12 +1466,11 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz", - "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", + "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1614,12 +1480,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz", - "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1629,12 +1495,11 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", - "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1644,13 +1509,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz", - "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1660,13 +1524,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz", - "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1676,12 +1539,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz", - "integrity": "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.2", - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1691,92 +1554,79 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.4.tgz", - "integrity": "sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.7.tgz", + "integrity": "sha512-Ycg2tnXwixaXOVb29rana8HNPgLVBof8qqtNQ9LE22IoyZboQbGSxI6ZySMdW3K5nAe6gu35IaJefUJflhUFTQ==", + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.4", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-validator-option": "^7.24.8", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", + "@babel/compat-data": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.7", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.4", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.25.0", - "@babel/plugin-transform-class-properties": "^7.25.4", - "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.25.4", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.8", - "@babel/plugin-transform-dotall-regex": "^7.24.7", - "@babel/plugin-transform-duplicate-keys": "^7.24.7", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", - "@babel/plugin-transform-dynamic-import": "^7.24.7", - "@babel/plugin-transform-exponentiation-operator": "^7.24.7", - "@babel/plugin-transform-export-namespace-from": "^7.24.7", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.25.1", - "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.25.2", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-member-expression-literals": "^7.24.7", - "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-modules-systemjs": "^7.25.0", - "@babel/plugin-transform-modules-umd": "^7.24.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-new-target": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-object-super": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.8", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.25.4", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-property-literals": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-reserved-words": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.8", - "@babel/plugin-transform-unicode-escapes": "^7.24.7", - "@babel/plugin-transform-unicode-property-regex": "^7.24.7", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.25.4", + "@babel/plugin-transform-arrow-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.25.9", + "@babel/plugin-transform-async-to-generator": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.26.5", + "@babel/plugin-transform-block-scoping": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-class-static-block": "^7.26.0", + "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-computed-properties": "^7.25.9", + "@babel/plugin-transform-destructuring": "^7.25.9", + "@babel/plugin-transform-dotall-regex": "^7.25.9", + "@babel/plugin-transform-duplicate-keys": "^7.25.9", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-dynamic-import": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.26.3", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.25.9", + "@babel/plugin-transform-function-name": "^7.25.9", + "@babel/plugin-transform-json-strings": "^7.25.9", + "@babel/plugin-transform-literals": "^7.25.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", + "@babel/plugin-transform-member-expression-literals": "^7.25.9", + "@babel/plugin-transform-modules-amd": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", + "@babel/plugin-transform-modules-systemjs": "^7.25.9", + "@babel/plugin-transform-modules-umd": "^7.25.9", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-new-target": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", + "@babel/plugin-transform-numeric-separator": "^7.25.9", + "@babel/plugin-transform-object-rest-spread": "^7.25.9", + "@babel/plugin-transform-object-super": "^7.25.9", + "@babel/plugin-transform-optional-catch-binding": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9", + "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/plugin-transform-private-property-in-object": "^7.25.9", + "@babel/plugin-transform-property-literals": "^7.25.9", + "@babel/plugin-transform-regenerator": "^7.25.9", + "@babel/plugin-transform-regexp-modifiers": "^7.26.0", + "@babel/plugin-transform-reserved-words": "^7.25.9", + "@babel/plugin-transform-shorthand-properties": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-sticky-regex": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.26.7", + "@babel/plugin-transform-unicode-escapes": "^7.25.9", + "@babel/plugin-transform-unicode-property-regex": "^7.25.9", + "@babel/plugin-transform-unicode-regex": "^7.25.9", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.37.1", + "core-js-compat": "^3.38.1", "semver": "^6.3.1" }, "engines": { @@ -1801,17 +1651,17 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.7.tgz", - "integrity": "sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.26.3.tgz", + "integrity": "sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-transform-react-display-name": "^7.24.7", - "@babel/plugin-transform-react-jsx": "^7.24.7", - "@babel/plugin-transform-react-jsx-development": "^7.24.7", - "@babel/plugin-transform-react-pure-annotations": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-transform-react-display-name": "^7.25.9", + "@babel/plugin-transform-react-jsx": "^7.25.9", + "@babel/plugin-transform-react-jsx-development": "^7.25.9", + "@babel/plugin-transform-react-pure-annotations": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1820,17 +1670,10 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "license": "MIT" - }, "node_modules/@babel/runtime": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", - "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.9.tgz", + "integrity": "sha512-4zpTHZ9Cm6L9L+uIqghQX8ZXg8HKFcjYO3qHoO8zTmRm6HQUJ8SSJ+KRvbMBZn0EGVlT4DRYeQ/6hjlyXBh+Kg==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1839,29 +1682,29 @@ } }, "node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", - "license": "MIT", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz", - "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", + "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.4", - "@babel/parser": "^7.25.4", - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.4", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.7", + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.7", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1879,13 +1722,13 @@ } }, "node_modules/@babel/types": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz", - "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", + "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1899,9 +1742,9 @@ "license": "MIT" }, "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.1.tgz", - "integrity": "sha512-lSquqZCHxDfuTg/Sk2hiS0mcSFCEBuj49JfzPHJogDBT0mGCyY5A1AQzBWngitrp7i1/HAZpIgzF/VjhOEIJIg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", + "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", "dev": true, "funding": [ { @@ -1913,17 +1756,18 @@ "url": "https://opencollective.com/csstools" } ], + "license": "MIT", "engines": { "node": ">=18" }, "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.1" + "@csstools/css-tokenizer": "^3.0.3" } }, "node_modules/@csstools/css-tokenizer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.1.tgz", - "integrity": "sha512-UBqaiu7kU0lfvaP982/o3khfXccVlHPWp0/vwwiIgDF0GmqqqxoiXC/6FCjlS9u92f7CoEz6nXKQnrn1kIAkOw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", + "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", "dev": true, "funding": [ { @@ -1935,6 +1779,7 @@ "url": "https://opencollective.com/csstools" } ], + "license": "MIT", "engines": { "node": ">=18" } @@ -1962,28 +1807,6 @@ "@csstools/css-tokenizer": "^3.0.1" } }, - "node_modules/@csstools/selector-specificity": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-4.0.0.tgz", - "integrity": "sha512-189nelqtPd8++phaHNwYovKZI0FOzH1vQEE3QhHHkNIGrg5fSs9CbYP3RvfEH5geztnIA9Jwq91wyOIwAW5JIQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss-selector-parser": "^6.1.0" - } - }, "node_modules/@dual-bundle/import-meta-resolve": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", @@ -2012,21 +1835,19 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, - "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/config-array": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.1.tgz", - "integrity": "sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.0.tgz", + "integrity": "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.4", "debug": "^4.3.1", @@ -2036,12 +1857,23 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "node_modules/@eslint/core": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", + "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -2065,7 +1897,6 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=18" }, @@ -2074,9 +1905,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.0.tgz", - "integrity": "sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug==", + "version": "9.19.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.19.0.tgz", + "integrity": "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==", "dev": true, "license": "MIT", "engines": { @@ -2088,15 +1919,27 @@ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", "dev": true, - "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", + "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "dev": true, + "dependencies": { + "@eslint/core": "^0.10.0", + "levn": "^0.4.1" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@googleapis/drive": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.13.0.tgz", - "integrity": "sha512-xpXzZeYtNNFLy1m2D5A8/QR2bngpjLPEvO5KZUW4Dlwi/SBHYNTjVm37IQagtQg6QUJlFb4lVLewenUdZZB1rA==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.14.0.tgz", + "integrity": "sha512-AOokfpP6pCdcJXWA8khaCEgbGpWYavWTdAAhL4idbbf2VCQcJ2f7vPalAYNu6a4Sfj0Ly4Ehnd1xw9J9TixB1A==", "dependencies": { "googleapis-common": "^7.0.0" }, @@ -2104,6 +1947,41 @@ "node": ">=12.0.0" } }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -2119,11 +1997,10 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", - "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -2267,22 +2144,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/console/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@jest/console/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2300,26 +2161,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@jest/console/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/console/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/@jest/console/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2391,22 +2232,6 @@ } } }, - "node_modules/@jest/core/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@jest/core/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2424,26 +2249,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@jest/core/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/core/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/@jest/core/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2588,22 +2393,6 @@ } } }, - "node_modules/@jest/reporters/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@jest/reporters/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2621,26 +2410,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@jest/reporters/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/@jest/reporters/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2751,22 +2520,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/transform/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@jest/transform/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2784,26 +2537,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@jest/transform/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/transform/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/@jest/transform/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2845,22 +2578,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/types/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@jest/types/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2878,26 +2595,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@jest/types/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/types/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/@jest/types/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2969,11 +2666,43 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@keyv/serialize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.0.2.tgz", + "integrity": "sha512-+E/LyaAeuABniD/RvUezWVXKpeuvwLEA9//nE9952zBaOdBd2mQ3pPoM8cUe2X6IcMByfuSLzmYqnYshG60+HQ==", + "dev": true, + "dependencies": { + "buffer": "^6.0.3" + } + }, + "node_modules/@keyv/serialize/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/@mongodb-js/saslprep": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.8.tgz", - "integrity": "sha512-qKwC/M/nNNaKUBMQ0nuzm47b7ZYWQHN3pcXq4IIcoSBc2hOIrflAxJduIvvqmhoz3gR2TacTAs8vlsCVPkiEdQ==", - "license": "MIT", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", "dependencies": { "sparse-bitfield": "^3.0.3" } @@ -3016,14 +2745,6 @@ "node": ">= 8" } }, - "node_modules/@remix-run/router": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz", - "integrity": "sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -3052,14 +2773,14 @@ } }, "node_modules/@stylistic/stylelint-plugin": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.0.1.tgz", - "integrity": "sha512-j3mH8HSw2Rob/KJFWZ627w3CQ8gQqVHtzCdPeEffUg5vOgpz4rgrR+Xw2kU0OQCDcdW8Y1nKfdXKKjM5Rn8X0g==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.1.1.tgz", + "integrity": "sha512-XagAHHIa528EvyGybv8EEYGK5zrVW74cHpsjhtovVATbhDRuJYfE+X4HCaAieW9lCkwbX6L+X0I4CiUG3w/hFw==", "dev": true, "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.0", - "@csstools/css-tokenizer": "^3.0.0", - "@csstools/media-query-list-parser": "^3.0.0", + "@csstools/css-parser-algorithms": "^3.0.1", + "@csstools/css-tokenizer": "^3.0.1", + "@csstools/media-query-list-parser": "^3.0.1", "is-plain-object": "^5.0.0", "postcss-selector-parser": "^6.1.2", "postcss-value-parser": "^4.2.0", @@ -3118,6 +2839,18 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -3155,6 +2888,12 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/json-schema": { + "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/node": { "version": "22.1.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", @@ -3172,17 +2911,21 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "optional": true + }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", - "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", - "license": "MIT" + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" }, "node_modules/@types/whatwg-url": { "version": "11.0.5", "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", - "license": "MIT", "dependencies": { "@types/webidl-conversions": "*" } @@ -3359,11 +3102,10 @@ } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, - "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -3376,7 +3118,6 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -3430,7 +3171,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3468,15 +3208,17 @@ } }, "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "license": "MIT", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/anymatch": { @@ -3526,14 +3268,13 @@ } }, "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { "node": ">= 0.4" @@ -3629,16 +3370,15 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3665,20 +3405,18 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, - "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -3734,7 +3472,6 @@ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -3789,7 +3526,6 @@ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, - "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -3822,22 +3558,6 @@ "@babel/core": "^7.8.0" } }, - "node_modules/babel-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/babel-jest/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3855,26 +3575,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/babel-jest/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/babel-jest/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/babel-jest/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3986,6 +3686,26 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/babel-plugin-transform-import-meta": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-import-meta/-/babel-plugin-transform-import-meta-2.3.2.tgz", + "integrity": "sha512-902o4GiQqI1GqAXfD5rEoz0PJamUfJ3VllpdWaNsFTwdaNjFSFHawvBO+cp5K2j+g2h3bZ4lnM1Xb6yFYGihtA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.9", + "tslib": "^2.8.1" + }, + "peerDependencies": { + "@babel/core": "^7.10.0" + } + }, + "node_modules/babel-plugin-transform-import-meta/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/babel-preset-current-node-syntax": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", @@ -4117,16 +3837,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, "node_modules/bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", @@ -4134,10 +3844,9 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "license": "MIT", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -4147,7 +3856,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -4392,9 +4101,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", - "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", "funding": [ { "type": "opencollective", @@ -4409,10 +4118,9 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", "node-releases": "^2.0.18", "update-browserslist-db": "^1.1.0" }, @@ -4434,10 +4142,9 @@ } }, "node_modules/bson": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz", - "integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==", - "license": "Apache-2.0", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.1.tgz", + "integrity": "sha512-P92xmHDQjSKPLHqFxefqMxASNq/aWJMEZugpCjf+AF/pgcUpMMQCg7t7+ewko0/u8AapvF3luf/FoehddEK+sA==", "engines": { "node": ">=16.20.1" } @@ -4505,6 +4212,25 @@ "node": ">=0.10.0" } }, + "node_modules/cacheable": { + "version": "1.8.7", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.8.7.tgz", + "integrity": "sha512-AbfG7dAuYNjYxFUtL1lAqmlWdxczCJ47w7cFjhGcnGnUdwSo6VgmSojfoW3tUI12HUkgTJ5kqj78yyq6TsFtlg==", + "dev": true, + "dependencies": { + "hookified": "^1.6.0", + "keyv": "^5.2.3" + } + }, + "node_modules/cacheable/node_modules/keyv": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.2.3.tgz", + "integrity": "sha512-AGKecUfzrowabUv0bH1RIR5Vf7w+l4S3xtQAypKaUpTdIR1EbrAcTxHCrpo9Q+IWeUlFE2palRtgIQcgm+PQJw==", + "dev": true, + "dependencies": { + "@keyv/serialize": "^1.0.2" + } + }, "node_modules/cached-path-relative": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.1.0.tgz", @@ -4512,16 +4238,41 @@ "license": "MIT" }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "license": "MIT", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -4551,9 +4302,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001647", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001647.tgz", - "integrity": "sha512-n83xdNiyeNcHpzWY+1aFbqCK7LuLfBricc4+alSQL2Xb6OR3XpnQAmlDG+pQcdTfiHRuLcQ96VOfrPSGiNJYSg==", + "version": "1.0.30001667", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz", + "integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==", "funding": [ { "type": "opencollective", @@ -4567,22 +4318,7 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } + ] }, "node_modules/char-regex": { "version": "1.0.2", @@ -4737,19 +4473,20 @@ } }, "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "color-name": "1.1.3" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/colord": { "version": "2.9.3", @@ -4866,21 +4603,19 @@ "license": "MIT" }, "node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "license": "MIT", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", - "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", - "license": "MIT", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", "dependencies": { - "cookie": "0.4.1", + "cookie": "0.7.2", "cookie-signature": "1.0.6" }, "engines": { @@ -4920,6 +4655,16 @@ "node": ">=0.10.0" } }, + "node_modules/core-js": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz", + "integrity": "sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-js-compat": { "version": "3.38.1", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", @@ -4938,6 +4683,19 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "license": "MIT" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cosmiconfig": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", @@ -5030,22 +4788,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/create-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/create-jest/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5063,26 +4805,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/create-jest/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/create-jest/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/create-jest/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -5117,11 +4839,10 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -5154,23 +4875,21 @@ } }, "node_modules/css-functions-list": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.2.tgz", - "integrity": "sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.3.tgz", + "integrity": "sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==", "dev": true, - "license": "MIT", "engines": { "node": ">=12 || >=16" } }, "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", "dev": true, - "license": "MIT", "dependencies": { - "mdn-data": "2.0.30", + "mdn-data": "2.12.2", "source-map-js": "^1.0.1" }, "engines": { @@ -5190,22 +4909,90 @@ "node": ">=4" } }, + "node_modules/cssstyle": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", + "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "rrweb-cssom": "^0.7.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/dash-ast": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", "integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==", "license": "Apache-2.0" }, - "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "call-bind": "^1.0.6", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5215,31 +5002,29 @@ } }, "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/inspect-js" } }, "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" }, @@ -5251,12 +5036,11 @@ } }, "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", - "license": "MIT", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -5267,6 +5051,14 @@ } } }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/decode-uri-component": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", @@ -5521,10 +5313,26 @@ } }, "node_modules/dompurify": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz", - "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==", - "license": "(MPL-2.0 OR Apache-2.0)" + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", + "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/duplexer2": { "version": "0.1.4", @@ -5551,15 +5359,14 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.4.tgz", - "integrity": "sha512-orzA81VqLyIGUEA77YkVA1D+N+nNfl2isJVjjmOyrlxuooZ19ynb+dOlaDTqd/idKRS9lDCSBmtzM+kyCsMnkA==", - "license": "ISC" + "version": "1.5.32", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.32.tgz", + "integrity": "sha512-M+7ph0VGBQqqpTT2YrabjNKSQ2fEl9PVx6AK3N558gDH9NO8O6XN9SXXFWRo9u9PbEg/bWq+tjXQr+eXmxubCw==" }, "node_modules/elliptic": { - "version": "6.5.7", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", - "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", + "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -5596,14 +5403,27 @@ "license": "MIT" }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { "node": ">= 0.8" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -5638,58 +5458,58 @@ } }, "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "version": "1.23.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.7.tgz", + "integrity": "sha512-OygGC8kIcDhXX+6yAZRGLqwi2CmEXCbLQixeGUgYeR+Qwlppqmo7DIDr8XibtEBZp+fJcoYpoatp5qwLMEdcqQ==", "dev": true, - "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.6", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", + "object.assign": "^4.1.7", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-regex-test": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" }, "engines": { "node": ">= 0.4" @@ -5699,13 +5519,9 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { "node": ">= 0.4" } @@ -5720,26 +5536,27 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", - "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.1.2" + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" }, "engines": { "node": ">= 0.4" @@ -5749,7 +5566,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -5784,15 +5600,14 @@ } }, "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, - "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" }, "engines": { "node": ">= 0.4" @@ -5816,38 +5631,33 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/eslint": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.0.tgz", - "integrity": "sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA==", + "version": "9.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz", + "integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.11.0", - "@eslint/config-array": "^0.17.1", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.9.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.10.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.19.0", + "@eslint/plugin-kit": "^0.2.5", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.0", - "@nodelib/fs.walk": "^1.2.8", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.0.2", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.1.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -5857,15 +5667,11 @@ "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" @@ -5886,9 +5692,9 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "28.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.8.0.tgz", - "integrity": "sha512-Tubj1hooFxCl52G4qQu0edzV/+EZzPUeN8p2NnW5uu4fbDs+Yo7+qDVDc4/oG3FbCqEBmu/OC3LSsyiU22oghw==", + "version": "28.11.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.11.0.tgz", + "integrity": "sha512-QAfipLcNCWLVocVbZW8GimKn5p5iiMcgGbRzz8z/P5q7xw+cNEpYqyzFMtIF/ZgF2HLOyy+dYBut+DoYolvqig==", "dev": true, "dependencies": { "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -5911,29 +5717,28 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.35.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz", - "integrity": "sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA==", + "version": "7.37.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", + "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", "dev": true, - "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.2", + "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.19", + "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.8", "object.fromentries": "^2.0.8", - "object.values": "^1.2.0", + "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11", + "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "engines": { @@ -5975,11 +5780,10 @@ } }, "node_modules/eslint-scope": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", - "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -6004,22 +5808,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -6037,26 +5825,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -6071,11 +5839,10 @@ } }, "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -6107,15 +5874,14 @@ } }, "node_modules/espree": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", - "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.12.0", + "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.0.0" + "eslint-visitor-keys": "^4.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6125,11 +5891,10 @@ } }, "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", - "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -6169,7 +5934,6 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -6314,37 +6078,37 @@ "license": "MIT" }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -6353,6 +6117,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express-async-handler": { @@ -6362,19 +6130,18 @@ "license": "MIT" }, "node_modules/express-static-gzip": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/express-static-gzip/-/express-static-gzip-2.1.7.tgz", - "integrity": "sha512-QOCZUC+lhPPCjIJKpQGu1Oa61Axg9Mq09Qvit8Of7kzpMuwDeMSqjjQteQS3OVw/GkENBoSBheuQDWPlngImvw==", - "license": "MIT", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/express-static-gzip/-/express-static-gzip-2.2.0.tgz", + "integrity": "sha512-4ZQ0pHX0CAauxmzry2/8XFLM6aZA4NBvg9QezSlsEO1zLnl7vMFa48/WIcjzdfOiEUS4S1npPPKP2NHHYAp6qg==", "dependencies": { - "serve-static": "^1.14.1" + "parseurl": "^1.3.3", + "serve-static": "^1.16.2" } }, "node_modules/express/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "license": "MIT", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "engines": { "node": ">= 0.6" } @@ -6464,17 +6231,16 @@ "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -6514,11 +6280,10 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", - "dev": true, - "license": "MIT" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "dev": true }, "node_modules/fastest-levenshtein": { "version": "1.0.16", @@ -6563,13 +6328,6 @@ "node": ">=16.0.0" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT", - "optional": true - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -6583,13 +6341,12 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "license": "MIT", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -6604,7 +6361,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -6612,8 +6368,7 @@ "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/find-up": { "version": "5.0.0", @@ -6647,18 +6402,16 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true, - "license": "ISC" + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, - "license": "MIT", "dependencies": { "is-callable": "^1.1.3" } @@ -6687,13 +6440,12 @@ } }, "node_modules/formidable": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.1.tgz", - "integrity": "sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og==", - "license": "MIT", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.2.tgz", + "integrity": "sha512-Jqc1btCy3QzRbJaICGwKcBfGWuLADRerLzDqi2NwSt/UkXLsHJw2TVResiaoBufHVHy9aSgClOHCeJsSsFLTbg==", "dependencies": { "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", + "hexoid": "^2.0.0", "once": "^1.4.0" }, "funding": { @@ -6731,10 +6483,9 @@ } }, "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "license": "MIT", + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -6750,20 +6501,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -6774,16 +6511,17 @@ } }, "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -6869,16 +6607,20 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "license": "MIT", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", + "integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==", "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "dunder-proto": "^1.0.0", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -6911,15 +6653,14 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -7026,11 +6767,10 @@ } }, "node_modules/globals": { - "version": "15.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz", - "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==", + "version": "15.14.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", + "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", "dev": true, - "license": "MIT", "engines": { "node": ">=18" }, @@ -7118,12 +6858,11 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7158,11 +6897,13 @@ } }, "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, - "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7189,10 +6930,13 @@ } }, "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "license": "MIT", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -7201,10 +6945,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "license": "MIT", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { "node": ">= 0.4" }, @@ -7327,10 +7070,9 @@ } }, "node_modules/hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "license": "MIT", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-2.0.0.tgz", + "integrity": "sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw==", "engines": { "node": ">=8" } @@ -7346,6 +7088,26 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "node_modules/hookified": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.6.0.tgz", + "integrity": "sha512-se7cpwTA+iA/eY548Bu03JJqBiEZAqU2jnyKdj5B5qurtBg64CZGHTgqCv4Yh7NWu6FGI09W61MCq+NoPj9GXA==", + "dev": true + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -7391,6 +7153,21 @@ "node": ">= 0.8" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -7432,6 +7209,11 @@ "node": ">=0.10.0" } }, + "node_modules/idb-keyval": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz", + "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -7603,15 +7385,14 @@ } }, "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -7639,14 +7420,14 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -7667,7 +7448,6 @@ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", "dev": true, - "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -7679,13 +7459,15 @@ } }, "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, - "license": "MIT", "dependencies": { - "has-bigints": "^1.0.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7704,14 +7486,13 @@ } }, "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7731,7 +7512,6 @@ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -7767,12 +7547,13 @@ } }, "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, - "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" }, "engines": { @@ -7783,13 +7564,13 @@ } }, "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, - "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7830,13 +7611,15 @@ } }, "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7866,7 +7649,6 @@ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "dev": true, - "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -7894,20 +7676,6 @@ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -7925,13 +7693,13 @@ } }, "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, - "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -7940,16 +7708,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-object": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", @@ -7960,15 +7718,24 @@ "node": ">=0.10.0" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true, "license": "MIT", + "peer": true + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -7982,7 +7749,6 @@ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -7991,13 +7757,12 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -8019,13 +7784,13 @@ } }, "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, - "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -8035,13 +7800,14 @@ } }, "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, - "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -8051,13 +7817,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, - "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -8071,7 +7836,6 @@ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -8080,27 +7844,28 @@ } }, "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", + "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -8128,8 +7893,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/isexe": { "version": "2.0.0", @@ -8255,17 +8019,20 @@ } }, "node_modules/iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.4.tgz", + "integrity": "sha512-x4WH0BWmrMmg4oHHl+duwubhrvczGlyuGAZu3nvrf0UXOfPu8IhZObFEr7DE/iv01YgVZrsOiRcqw2srkKEDIA==", "dev": true, - "license": "MIT", "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "reflect.getprototypeof": "^1.0.8", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/jest": { @@ -8342,22 +8109,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-circus/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-circus/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -8375,26 +8126,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-circus/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-circus/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-circus/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -8452,22 +8183,6 @@ } } }, - "node_modules/jest-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-cli/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -8485,26 +8200,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-cli/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-cli/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -8574,22 +8269,6 @@ } } }, - "node_modules/jest-config/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-config/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -8607,26 +8286,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-config/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-config/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-config/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -8666,22 +8325,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-diff/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -8699,26 +8342,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-diff/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-diff/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-diff/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -8772,22 +8395,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-each/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-each/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -8805,26 +8412,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-each/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-each/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-each/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -8939,22 +8526,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-matcher-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-matcher-utils/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -8972,26 +8543,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-matcher-utils/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-matcher-utils/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9036,22 +8587,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-message-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-message-util/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -9069,26 +8604,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-message-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-message-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-message-util/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9190,22 +8705,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-resolve/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-resolve/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -9223,26 +8722,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-resolve/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-resolve/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-resolve/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9299,22 +8778,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runner/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-runner/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -9332,26 +8795,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-runner/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runner/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-runner/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9409,22 +8852,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runtime/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-runtime/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -9442,26 +8869,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-runtime/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runtime/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-runtime/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9517,22 +8924,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-snapshot/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -9550,26 +8941,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-snapshot/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-snapshot/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9624,22 +8995,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-util/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -9657,26 +9012,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-util/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9718,22 +9053,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-validate/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-validate/node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -9764,26 +9083,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-validate/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-validate/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-validate/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9827,22 +9126,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-watcher/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-watcher/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -9860,26 +9143,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-watcher/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-watcher/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-watcher/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9963,16 +9226,130 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "node_modules/jsdom": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", + "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "dev": true, "license": "MIT", + "peer": true, + "dependencies": { + "cssstyle": "^4.1.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.12", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.7.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom-global": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsdom-global/-/jsdom-global-3.0.2.tgz", + "integrity": "sha512-t1KMcBkz/pT5JrvcJbpUR2u/w1kO9jXctaaGJ0vZDzwFnIvGWw9IDSRciT83kIs8Bnw4qpOl8bQK08V01YgMPg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "jsdom": ">=10.0.0" + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-bigint": { @@ -10002,8 +9379,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/json-stable-stringify": { "version": "0.0.1", @@ -10164,9 +9540,9 @@ } }, "node_modules/known-css-properties": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.34.0.tgz", - "integrity": "sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.35.0.tgz", + "integrity": "sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==", "dev": true, "license": "MIT" }, @@ -10329,8 +9705,7 @@ "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/loose-envify": { "version": "1.4.0", @@ -10414,9 +9789,9 @@ } }, "node_modules/marked": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-11.2.0.tgz", - "integrity": "sha512-HR0m3bvu0jAPYiIvLUUQtdg1g6D247//lvcekpHO1WMvbwDlwSkZAX9Lw4F4YHE1T0HaaNve0tuAWuV1UJ6vtw==", + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz", + "integrity": "sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -10426,41 +9801,40 @@ } }, "node_modules/marked-emoji": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/marked-emoji/-/marked-emoji-1.4.2.tgz", - "integrity": "sha512-2sP+bp2z76dwbILzQ7ijy2PyjjAJR3iAZCzaNGThD2UijFUBeidkn6MoCdX/j47tPIcWt9nwnjqRQPd01ZrfdA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/marked-emoji/-/marked-emoji-1.4.3.tgz", + "integrity": "sha512-HDZx1VOmzu7XT2QNKWfrHGbNRMTWKj9XD78yrcH1madD30HpGLMODPOmKr/e7CA7NKKXkpXXNdndQn++ysXmHg==", "peerDependencies": { - "marked": ">=4 <15" + "marked": ">=4 <16" } }, "node_modules/marked-extended-tables": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/marked-extended-tables/-/marked-extended-tables-1.0.8.tgz", - "integrity": "sha512-GcVQP7EnfQ98o09ooqM4t4M0qfpKdKuk7/z4qZfgkLyXTXsIyFS1eeBmfC36o1NbR6aSq8ynL/LeTz3w4RS27Q==", - "license": "MIT", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/marked-extended-tables/-/marked-extended-tables-1.1.0.tgz", + "integrity": "sha512-xPQlnmr/mpIJEjwg9/guSKzxoKu7hyCD/sM59mcZc+nMIh2JuVM2se+kCa1Jo1UH+BEHcNlZ221ziJ/cOxAgCA==", "peerDependencies": { - "marked": ">=3 <12" + "marked": ">=3 <16" } }, "node_modules/marked-gfm-heading-id": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/marked-gfm-heading-id/-/marked-gfm-heading-id-3.2.0.tgz", - "integrity": "sha512-Xfxpr5lXLDLY10XqzSCA9l2dDaiabQUgtYM9hw8yunyVsB/xYBRpiic6BOiY/EAJw1ik1eWr1ET1HKOAPZBhXg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/marked-gfm-heading-id/-/marked-gfm-heading-id-4.1.1.tgz", + "integrity": "sha512-EeQZieAQmsI6c2tWLx0ETd0VjPwLV8qi+HT0dIsfVMERm0rCIuXfRvZXJbo1SgUi++lmuR1LVY+QzgNiLNvVpw==", "license": "MIT", "dependencies": { "github-slugger": "^2.0.0" }, "peerDependencies": { - "marked": ">=4 <13" + "marked": ">=13 <16" } }, "node_modules/marked-smartypants-lite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/marked-smartypants-lite/-/marked-smartypants-lite-1.0.2.tgz", - "integrity": "sha512-cEANts+s3+gnTzXPvPT2z4V8NfbMEL9QooKUviug0DkaKkXQWrUwDAmFnQAkLSJCw2BQcD8YPDyxu0HJ3mg36w==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/marked-smartypants-lite/-/marked-smartypants-lite-1.0.3.tgz", + "integrity": "sha512-OOL8cjFog8KtgUFpkihRu6G42Dz2QPOU4k2xfKmMJ0CdoX+BHBDmdesHUdoQKM0mlwTsRSU3shpMeM/LWuUokg==", "license": "MIT", "peerDependencies": { - "marked": ">=4 <12" + "marked": ">=4 <16" } }, "node_modules/markedLegacy": { @@ -10476,6 +9850,14 @@ "node": ">=0.10.0" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mathml-tag-names": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", @@ -10499,11 +9881,10 @@ } }, "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true, - "license": "CC0-1.0" + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true }, "node_modules/media-typer": { "version": "0.3.0", @@ -10517,8 +9898,7 @@ "node_modules/memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", - "license": "MIT" + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" }, "node_modules/meow": { "version": "13.2.0", @@ -10534,10 +9914,12 @@ } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "license": "MIT" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge-stream": { "version": "2.0.0", @@ -10566,11 +9948,10 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -10769,7 +10150,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", - "license": "Apache-2.0", "dependencies": { "@types/whatwg-url": "^11.0.2", "whatwg-url": "^13.0.0" @@ -10779,7 +10159,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", - "license": "MIT", "dependencies": { "punycode": "^2.3.0" }, @@ -10791,7 +10170,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "license": "BSD-2-Clause", "engines": { "node": ">=12" } @@ -10800,7 +10178,6 @@ "version": "13.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", - "license": "MIT", "dependencies": { "tr46": "^4.1.1", "webidl-conversions": "^7.0.0" @@ -10810,13 +10187,14 @@ } }, "node_modules/mongoose": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.5.3.tgz", - "integrity": "sha512-OubSDbsAclDFGHjV82MsKyIGQWFc42Ot1l+0dhRS6U9xODM7rm/ES/WpOQd8Ds9j0Mx8QzxZtrSCnBh6o9wUqw==", + "version": "8.9.6", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.9.6.tgz", + "integrity": "sha512-ipLvXwNPVuuuq5H3lnSD0lpaRH3DlCoC6emnMVJvweTwxU29uxDJWxMsNpERDQt8JMvYF1HGVuTK+Id2BlQLCA==", + "license": "MIT", "dependencies": { - "bson": "^6.7.0", + "bson": "^6.10.1", "kareem": "2.6.3", - "mongodb": "6.7.0", + "mongodb": "~6.12.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", @@ -10834,7 +10212,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", "optional": true, "peer": true, "dependencies": { @@ -10848,7 +10225,6 @@ "version": "5.1.3", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz", "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==", - "license": "Apache-2.0", "optional": true, "peer": true, "dependencies": { @@ -10865,7 +10241,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz", "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==", - "license": "Apache-2.0", "optional": true, "peer": true, "dependencies": { @@ -10880,7 +10255,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", "optional": true, "peer": true, "dependencies": { @@ -10892,13 +10266,12 @@ } }, "node_modules/mongoose/node_modules/mongodb": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.7.0.tgz", - "integrity": "sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==", - "license": "Apache-2.0", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.12.0.tgz", + "integrity": "sha512-RM7AHlvYfS7jv7+BXund/kR64DryVI+cHbVAy9P61fnb1RcWZqOW1/Wj2YhqMCx+MuYhqTRGv7AwHBzmsCKBfA==", "dependencies": { - "@mongodb-js/saslprep": "^1.1.5", - "bson": "^6.7.0", + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.1", "mongodb-connection-string-url": "^3.0.0" }, "engines": { @@ -10906,7 +10279,7 @@ }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.188.0", - "@mongodb-js/zstd": "^1.1.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", "gcp-metadata": "^5.2.0", "kerberos": "^2.0.1", "mongodb-client-encryption": ">=6.0.0 <7", @@ -10937,12 +10310,6 @@ } } }, - "node_modules/mongoose/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, "node_modules/mpath": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", @@ -10965,28 +10332,26 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "license": "MIT" - }, - "node_modules/nan": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", - "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", - "license": "MIT", - "optional": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.9.tgz", + "integrity": "sha512-Aooyr6MXU6HpvvWXKoVoXwKMs/KyVakWwg7xQfv5/S/RIgJMy0Ifa45H9qqYy7pTCszrHzP21Uk4PZq2HpEM8Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", "bin": { - "nanoid": "bin/nanoid.cjs" + "nanoid": "bin/nanoid.js" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "^18 || >=20" } }, "node_modules/nanomatch": { @@ -11251,6 +10616,14 @@ "node": ">=8" } }, + "node_modules/nwsapi": { + "version": "2.2.13", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.13.tgz", + "integrity": "sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -11287,10 +10660,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "license": "MIT", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", "engines": { "node": ">= 0.4" }, @@ -11320,14 +10692,15 @@ } }, "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "license": "MIT", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -11384,13 +10757,13 @@ } }, "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, @@ -11583,6 +10956,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -11658,9 +11045,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, "node_modules/path-type": { @@ -11690,9 +11077,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, "node_modules/picomatch": { @@ -11810,15 +11197,14 @@ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/postcss": { - "version": "8.4.41", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", - "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "dev": true, "funding": [ { @@ -11834,10 +11220,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -11863,9 +11250,9 @@ "dev": true }, "node_modules/postcss-safe-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.0.tgz", - "integrity": "sha512-ovehqRNVCpuFzbXoTb4qLtyzK3xn3t/CUBxOs8LsnQjQrShaB4lKiHoVqY8ANaC0hBMHq5QVWk77rwGklFUDrg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", + "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", "dev": true, "funding": [ { @@ -11881,7 +11268,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "engines": { "node": ">=18.0" }, @@ -11920,9 +11306,9 @@ "license": "MIT" }, "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "dev": true, "funding": [ { @@ -12095,12 +11481,11 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "license": "BSD-3-Clause", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -12225,33 +11610,36 @@ "license": "MIT" }, "node_modules/react-router": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.1.tgz", - "integrity": "sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.5.tgz", + "integrity": "sha512-8BUF+hZEU4/z/JD201yK6S+UYhsf58bzYIDq2NS1iGpwxSXDu7F+DeGSkIXMFBuHZB21FSiCzEcUb18cQNdRkA==", + "license": "MIT", "dependencies": { - "@remix-run/router": "1.19.1" + "@types/cookie": "^0.6.0", + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=20.0.0" }, "peerDependencies": { - "react": ">=16.8" + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } } }, - "node_modules/react-router-dom": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.1.tgz", - "integrity": "sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==", - "dependencies": { - "@remix-run/router": "1.19.1", - "react-router": "6.26.1" - }, + "node_modules/react-router/node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" + "node": ">=18" } }, "node_modules/read-only-stream": { @@ -12312,19 +11700,19 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", - "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.9.tgz", + "integrity": "sha512-r0Ay04Snci87djAsI4U+WNRcSw5S4pOH7qFjd/veA5gC7TbqESR3tcj28ia95L/fYUDw11JKP7uqUKUAfVvV5Q==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.23.1", + "dunder-proto": "^1.0.1", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "which-builtin-type": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -12336,14 +11724,12 @@ "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "license": "MIT" + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" }, "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", - "license": "MIT", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", "dependencies": { "regenerate": "^1.4.2" }, @@ -12354,14 +11740,12 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regenerator-transform": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "license": "MIT", "dependencies": { "@babel/runtime": "^7.8.4" } @@ -12417,16 +11801,15 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -12436,15 +11819,14 @@ } }, "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", - "license": "MIT", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", + "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", "dependencies": { - "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.11.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" }, @@ -12452,26 +11834,22 @@ "node": ">=4" } }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==" + }, "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "license": "BSD-2-Clause", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.0.tgz", + "integrity": "sha512-vTbzVAjQDzwQdKuvj7qEq6OlAprCjE656khuGQ4QaBLg7abQ9I9ISpmLuc6inWe7zP75AECjqUa4g4sdQvOXhg==", "dependencies": { - "jsesc": "~0.5.0" + "jsesc": "~3.0.2" }, "bin": { "regjsparser": "bin/parser" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "bin": { - "jsesc": "bin/jsesc" - } - }, "node_modules/remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -12510,7 +11888,6 @@ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -12612,6 +11989,14 @@ "inherits": "^2.0.1" } }, + "node_modules/rrweb-cssom": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -12637,15 +12022,15 @@ } }, "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, "engines": { @@ -12685,15 +12070,14 @@ } }, "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "is-regex": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -12717,6 +12101,20 @@ "truncate-utf8-bytes": "^1.0.0" } }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -12742,10 +12140,9 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "license": "MIT", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -12769,7 +12166,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -12777,30 +12173,36 @@ "node_modules/send/node_modules/debug/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "license": "MIT", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -12932,15 +12334,65 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "license": "MIT", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -13025,7 +12477,6 @@ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -13038,42 +12489,6 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -13190,11 +12605,10 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -13235,7 +12649,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", - "license": "MIT", "dependencies": { "memory-pager": "^1.0.2" } @@ -13435,24 +12848,24 @@ } }, "node_modules/string.prototype.matchall": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", - "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -13473,16 +12886,18 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -13492,16 +12907,19 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -13577,84 +12995,9 @@ "license": "ISC" }, "node_modules/stylelint": { - "version": "16.8.2", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.8.2.tgz", - "integrity": "sha512-fInKATippQhcSm7AB+T32GpI+626yohrg33GkFT/5jzliUw5qhlwZq2UQQwgl3HsHrf09oeARi0ZwgY/UWEv9A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/stylelint" - }, - { - "type": "github", - "url": "https://github.com/sponsors/stylelint" - } - ], - "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.0", - "@csstools/css-tokenizer": "^3.0.0", - "@csstools/media-query-list-parser": "^3.0.0", - "@csstools/selector-specificity": "^4.0.0", - "@dual-bundle/import-meta-resolve": "^4.1.0", - "balanced-match": "^2.0.0", - "colord": "^2.9.3", - "cosmiconfig": "^9.0.0", - "css-functions-list": "^3.2.2", - "css-tree": "^2.3.1", - "debug": "^4.3.6", - "fast-glob": "^3.3.2", - "fastest-levenshtein": "^1.0.16", - "file-entry-cache": "^9.0.0", - "global-modules": "^2.0.0", - "globby": "^11.1.0", - "globjoin": "^0.1.4", - "html-tags": "^3.3.1", - "ignore": "^5.3.2", - "imurmurhash": "^0.1.4", - "is-plain-object": "^5.0.0", - "known-css-properties": "^0.34.0", - "mathml-tag-names": "^2.1.3", - "meow": "^13.2.0", - "micromatch": "^4.0.7", - "normalize-path": "^3.0.0", - "picocolors": "^1.0.1", - "postcss": "^8.4.41", - "postcss-resolve-nested-selector": "^0.1.6", - "postcss-safe-parser": "^7.0.0", - "postcss-selector-parser": "^6.1.2", - "postcss-value-parser": "^4.2.0", - "resolve-from": "^5.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^7.1.0", - "supports-hyperlinks": "^3.0.0", - "svg-tags": "^1.0.0", - "table": "^6.8.2", - "write-file-atomic": "^5.0.1" - }, - "bin": { - "stylelint": "bin/stylelint.mjs" - }, - "engines": { - "node": ">=18.12.0" - } - }, - "node_modules/stylelint-config-recess-order": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recess-order/-/stylelint-config-recess-order-5.1.0.tgz", - "integrity": "sha512-ddapCF6B/kEtQYIFhQFReQ0dvK1ZdgJDM/SGFtIyeooYDbqaJqcOlGkRRGaVErCQYJY/bPSPsLRS2LdQtLJUVQ==", - "dev": true, - "dependencies": { - "stylelint-order": "^6.0.4" - }, - "peerDependencies": { - "stylelint": ">=16" - } - }, - "node_modules/stylelint-config-recommended": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.1.tgz", - "integrity": "sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==", + "version": "16.14.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.14.1.tgz", + "integrity": "sha512-oqCL7AC3786oTax35T/nuLL8p2C3k/8rHKAooezrPGRvUX0wX+qqs5kMWh5YYT4PHQgVDobHT4tw55WgpYG6Sw==", "dev": true, "funding": [ { @@ -13667,11 +13010,85 @@ } ], "license": "MIT", + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "@csstools/media-query-list-parser": "^4.0.2", + "@csstools/selector-specificity": "^5.0.0", + "@dual-bundle/import-meta-resolve": "^4.1.0", + "balanced-match": "^2.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^9.0.0", + "css-functions-list": "^3.2.3", + "css-tree": "^3.1.0", + "debug": "^4.3.7", + "fast-glob": "^3.3.3", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^10.0.5", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.3.1", + "ignore": "^7.0.3", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.35.0", + "mathml-tag-names": "^2.1.3", + "meow": "^13.2.0", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.5.1", + "postcss-resolve-nested-selector": "^0.1.6", + "postcss-safe-parser": "^7.0.1", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "string-width": "^4.2.3", + "supports-hyperlinks": "^3.1.0", + "svg-tags": "^1.0.0", + "table": "^6.9.0", + "write-file-atomic": "^5.0.1" + }, + "bin": { + "stylelint": "bin/stylelint.mjs" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/stylelint-config-recess-order": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recess-order/-/stylelint-config-recess-order-6.0.0.tgz", + "integrity": "sha512-1KqrttqpIrCYFAVQ1/bbgXo7EvvcjmkxxmnzVr+U66Xr2OlrNZqQ5+44Tmct6grCWY6wGTIBh2tSANqcmwIM2g==", + "dev": true, + "dependencies": { + "stylelint-order": "^6.0.4" + }, + "peerDependencies": { + "stylelint": ">=16" + } + }, + "node_modules/stylelint-config-recommended": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-15.0.0.tgz", + "integrity": "sha512-9LejMFsat7L+NXttdHdTq94byn25TD+82bzGRiV1Pgasl99pWnwipXS5DguTpp3nP1XjvLXVnEJIuYBfsRjRkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], "engines": { "node": ">=18.12.0" }, "peerDependencies": { - "stylelint": "^16.1.0" + "stylelint": "^16.13.0" } }, "node_modules/stylelint-order": { @@ -13688,51 +13105,10 @@ "stylelint": "^14.0.0 || ^15.0.0 || ^16.0.1" } }, - "node_modules/stylelint/node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.1.tgz", - "integrity": "sha512-lSquqZCHxDfuTg/Sk2hiS0mcSFCEBuj49JfzPHJogDBT0mGCyY5A1AQzBWngitrp7i1/HAZpIgzF/VjhOEIJIg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.1" - } - }, - "node_modules/stylelint/node_modules/@csstools/css-tokenizer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.1.tgz", - "integrity": "sha512-UBqaiu7kU0lfvaP982/o3khfXccVlHPWp0/vwwiIgDF0GmqqqxoiXC/6FCjlS9u92f7CoEz6nXKQnrn1kIAkOw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "engines": { - "node": ">=18" - } - }, "node_modules/stylelint/node_modules/@csstools/media-query-list-parser": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz", - "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.2.tgz", + "integrity": "sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==", "dev": true, "funding": [ { @@ -13744,25 +13120,36 @@ "url": "https://opencollective.com/csstools" } ], - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.1", - "@csstools/css-tokenizer": "^3.0.1" - } - }, - "node_modules/stylelint/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/stylelint/node_modules/@csstools/selector-specificity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-5.0.0.tgz", + "integrity": "sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^7.0.0" } }, "node_modules/stylelint/node_modules/balanced-match": { @@ -13773,30 +13160,46 @@ "license": "MIT" }, "node_modules/stylelint/node_modules/file-entry-cache": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.0.0.tgz", - "integrity": "sha512-6MgEugi8p2tiUhqO7GnPsmbCCzj0YRCwwaTbpGRyKZesjRSzkqkAE9fPp7V2yMs5hwfgbQLgdvSSkGNg1s5Uvw==", + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.0.5.tgz", + "integrity": "sha512-umpQsJrBNsdMDgreSryMEXvJh66XeLtZUwA8Gj7rHGearGufUFv6rB/bcXRFsiGWw/VeSUgUofF4Rf2UKEOrTA==", "dev": true, - "license": "MIT", "dependencies": { - "flat-cache": "^5.0.0" - }, - "engines": { - "node": ">=18" + "flat-cache": "^6.1.5" } }, "node_modules/stylelint/node_modules/flat-cache": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", - "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.5.tgz", + "integrity": "sha512-QR+2kN38f8nMfiIQ1LHYjuDEmZNZVjxuxY+HufbS3BW0EX01Q5OnH7iduOYRutmgiXb797HAKcXUeXrvRjjgSQ==", + "dev": true, + "dependencies": { + "cacheable": "^1.8.7", + "flatted": "^3.3.2", + "hookified": "^1.6.0" + } + }, + "node_modules/stylelint/node_modules/ignore": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.3.tgz", + "integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/stylelint/node_modules/postcss-selector-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", + "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", "dev": true, "license": "MIT", "dependencies": { - "flatted": "^3.3.1", - "keyv": "^4.5.4" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, "engines": { - "node": ">=18" + "node": ">=4" } }, "node_modules/stylelint/node_modules/resolve-from": { @@ -13822,22 +13225,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/stylelint/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/stylelint/node_modules/write-file-atomic": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", @@ -13862,17 +13249,16 @@ } }, "node_modules/superagent": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", - "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==", - "license": "MIT", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.1.1.tgz", + "integrity": "sha512-9pIwrHrOj3uAnqg9gDlW7EA2xv+N5au/dSM0kM22HTqmUu8jBxNT+8uA7tA3UoCnmiqzpSbu8rasIUZvbyamMQ==", "dependencies": { "component-emitter": "^1.3.0", "cookiejar": "^2.1.4", "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.0", - "formidable": "^3.5.1", + "formidable": "^3.5.2", "methods": "^1.1.2", "mime": "2.6.0", "qs": "^6.11.0" @@ -13907,6 +13293,38 @@ "node": ">=14.18.0" } }, + "node_modules/supertest/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/supertest/node_modules/superagent": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", + "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==", + "dev": true, + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^3.5.1", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0" + }, + "engines": { + "node": ">=14.18.0" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -13920,17 +13338,19 @@ } }, "node_modules/supports-hyperlinks": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", - "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz", + "integrity": "sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" }, "engines": { "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/supports-hyperlinks/node_modules/has-flag": { @@ -13938,7 +13358,6 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -13948,7 +13367,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -13974,6 +13392,14 @@ "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", "dev": true }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/syntax-error": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", @@ -13984,11 +13410,10 @@ } }, "node_modules/table": { - "version": "6.8.2", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", - "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "ajv": "^8.0.1", "lodash.truncate": "^4.4.2", @@ -14005,7 +13430,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -14021,8 +13445,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/test-exclude": { "version": "6.0.0", @@ -14039,13 +13462,6 @@ "node": ">=8" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT" - }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -14073,6 +13489,28 @@ "node": ">=0.6.0" } }, + "node_modules/tldts": { + "version": "6.1.56", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.56.tgz", + "integrity": "sha512-2PT1oRZCxtsbLi5R2SQjE/v4vvgRggAtVcYj+3Rrcnu2nPZvu7m64+gDa/EsVSWd3QzEc0U0xN+rbEKsJC47kA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "tldts-core": "^6.1.56" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.56", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.56.tgz", + "integrity": "sha512-Ihxv/Bwiyj73icTYVgBUkQ3wstlCglLoegSgl64oSrGUBX1hc7Qmf/CnrnJLaQdZrCnTaLqMYOwKMKlkfkFrxQ==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -14080,15 +13518,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -14221,6 +13650,20 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tough-cookie": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -14261,6 +13704,12 @@ "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", "license": "MIT" }, + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", + "license": "ISC" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -14311,32 +13760,30 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -14346,18 +13793,18 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, - "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { "node": ">= 0.4" @@ -14367,18 +13814,17 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-proto": "^1.0.3", "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -14418,16 +13864,18 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -14463,10 +13911,9 @@ "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "license": "MIT", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "engines": { "node": ">=4" } @@ -14475,7 +13922,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "license": "MIT", "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -14485,10 +13931,9 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "license": "MIT", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", "engines": { "node": ">=4" } @@ -14497,7 +13942,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "license": "MIT", "engines": { "node": ">=4" } @@ -14634,7 +14078,6 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -14671,21 +14114,6 @@ "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", "license": "MIT" }, - "node_modules/url/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -14821,6 +14249,20 @@ "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", "license": "MIT" }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -14952,25 +14394,6 @@ "node": ">=0.10.0" } }, - "node_modules/watchify/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, "node_modules/watchify/node_modules/glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", @@ -15146,6 +14569,45 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause" }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -15173,41 +14635,43 @@ } }, "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, - "license": "MIT", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-builtin-type": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", - "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, - "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", + "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", + "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", - "which-typed-array": "^1.1.15" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -15221,7 +14685,6 @@ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, - "license": "MIT", "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -15236,16 +14699,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", "dev": true, - "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "for-each": "^0.3.3", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { @@ -15282,39 +14745,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -15356,6 +14786,25 @@ } } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 8226325cd..2bf3a0a93 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,21 @@ { "name": "homebrewery", "description": "Create authentic looking D&D homebrews using only markdown", - "version": "3.14.1", + "version": "3.17.0", + "type": "module", "engines": { "npm": "^10.2.x", - "node": "^20.8.x" + "node": "^20.18.x" }, "repository": { "type": "git", "url": "git://github.com/naturalcrit/homebrewery.git" }, "scripts": { - "dev": "node scripts/dev.js", - "quick": "node scripts/quick.js", - "build": "node scripts/buildHomebrew.js && node scripts/buildAdmin.js", - "builddev": "node scripts/buildHomebrew.js --dev", + "dev": "node --experimental-require-module scripts/dev.js", + "quick": "node --experimental-require-module scripts/quick.js", + "build": "node --experimental-require-module scripts/buildHomebrew.js && node --experimental-require-module scripts/buildAdmin.js", + "builddev": "node --experimental-require-module scripts/buildHomebrew.js --dev", "lint": "eslint --fix", "lint:dry": "eslint", "stylelint": "stylelint --fix **/*.{less}", @@ -24,6 +25,9 @@ "test": "jest --runInBand", "test:api-unit": "jest \"server/.*.spec.js\" --verbose", "test:api-unit:themes": "jest \"server/.*.spec.js\" -t \"theme bundle\" --verbose", + "test:api-unit:css": "jest \"server/.*.spec.js\" -t \"Get CSS\" --verbose", + "test:api-unit:notifications": "jest \"server/.*.spec.js\" -t \"Notifications\" --verbose", + "test:content-negotiation": "jest \"server/middleware/.*.spec.js\" --verbose", "test:coverage": "jest --coverage --silent --runInBand", "test:dev": "jest --verbose --watch", "test:basic": "jest tests/markdown/basic.test.js --verbose", @@ -34,12 +38,15 @@ "test:mustache-syntax:injection": "jest \".*(mustache-syntax).*\" -t '^Injection:.*' --verbose --noStackTrace", "test:definition-lists": "jest tests/markdown/definition-lists.test.js --verbose --noStackTrace", "test:hard-breaks": "jest tests/markdown/hard-breaks.test.js --verbose --noStackTrace", + "test:non-breaking-spaces": "jest tests/markdown/non-breaking-spaces.test.js --verbose --noStackTrace", + "test:paragraph-justification": "jest tests/markdown/paragraph-justification.test.js --verbose --noStackTrace", "test:emojis": "jest tests/markdown/emojis.test.js --verbose --noStackTrace", "test:route": "jest tests/routes/static-pages.test.js --verbose", - "phb": "node scripts/phb.js", + "test:safehtml": "jest tests/html/safeHTML.test.js --verbose", + "phb": "node --experimental-require-module scripts/phb.js", "prod": "set NODE_ENV=production && npm run build", "postinstall": "npm run build", - "start": "node server.js" + "start": "node --experimental-require-module server.js" }, "author": "stolksdorf", "license": "MIT", @@ -53,6 +60,9 @@ "shared", "server" ], + "transformIgnorePatterns": [ + "node_modules/(?!nanoid/).*" + ], "coveragePathIgnorePatterns": [ "build/*" ], @@ -74,67 +84,63 @@ "jest-expect-message" ] }, - "babel": { - "presets": [ - "@babel/preset-env", - "@babel/preset-react" - ], - "plugins": [ - "@babel/plugin-transform-runtime" - ] - }, "dependencies": { - "@babel/core": "^7.25.2", - "@babel/plugin-transform-runtime": "^7.25.4", - "@babel/preset-env": "^7.25.4", - "@babel/preset-react": "^7.24.7", - "@googleapis/drive": "^8.13.0", + "@babel/core": "^7.26.7", + "@babel/plugin-transform-runtime": "^7.25.9", + "@babel/preset-env": "^7.26.7", + "@babel/preset-react": "^7.26.3", + "@googleapis/drive": "^8.14.0", "body-parser": "^1.20.2", "classnames": "^2.5.1", "codemirror": "^5.65.6", - "cookie-parser": "^1.4.6", + "cookie-parser": "^1.4.7", + "core-js": "^3.40.0", + "cors": "^2.8.5", "create-react-class": "^15.7.0", "dedent-tabs": "^0.10.3", - "dompurify": "^3.1.6", + "dompurify": "^3.2.4", "expr-eval": "^2.0.2", - "express": "^4.19.2", + "express": "^4.21.2", "express-async-handler": "^1.2.0", - "express-static-gzip": "2.1.7", - "fs-extra": "11.2.0", + "express-static-gzip": "2.2.0", + "fs-extra": "11.3.0", + "idb-keyval": "^6.2.1", "js-yaml": "^4.1.0", "jwt-simple": "^0.5.6", "less": "^3.13.1", "lodash": "^4.17.21", - "marked": "11.2.0", - "marked-emoji": "^1.4.2", - "marked-extended-tables": "^1.0.8", - "marked-gfm-heading-id": "^3.2.0", - "marked-smartypants-lite": "^1.0.2", + "marked": "13.0.3", + "marked-emoji": "^1.4.3", + "marked-extended-tables": "^1.1.0", + "marked-gfm-heading-id": "^4.0.1", + "marked-smartypants-lite": "^1.0.3", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^8.5.3", - "nanoid": "3.3.4", + "mongoose": "^8.9.6", + "nanoid": "5.0.9", "nconf": "^0.12.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-frame-component": "^4.1.3", - "react-router-dom": "6.26.1", + "react-router": "^7.1.5", "sanitize-filename": "1.6.3", - "superagent": "^9.0.2", + "superagent": "^10.1.1", "vitreum": "git+https://git@github.com/calculuschild/vitreum.git" }, "devDependencies": { - "@stylistic/stylelint-plugin": "^3.0.1", - "eslint": "^9.9.0", - "eslint-plugin-jest": "^28.8.0", - "eslint-plugin-react": "^7.35.0", - "globals": "^15.9.0", + "@stylistic/stylelint-plugin": "^3.1.1", + "babel-plugin-transform-import-meta": "^2.3.2", + "eslint": "^9.19.0", + "eslint-plugin-jest": "^28.11.0", + "eslint-plugin-react": "^7.37.4", + "globals": "^15.14.0", "jest": "^29.7.0", "jest-expect-message": "^1.1.3", + "jsdom-global": "^3.0.2", "postcss-less": "^6.0.0", - "stylelint": "^16.8.2", - "stylelint-config-recess-order": "^5.1.0", - "stylelint-config-recommended": "^14.0.1", + "stylelint": "^16.14.1", + "stylelint-config-recess-order": "^6.0.0", + "stylelint-config-recommended": "^15.0.0", "supertest": "^7.0.0" } } diff --git a/scripts/buildAdmin.js b/scripts/buildAdmin.js index 1157a063b..9c77315ef 100644 --- a/scripts/buildAdmin.js +++ b/scripts/buildAdmin.js @@ -1,13 +1,14 @@ -const fs = require('fs-extra'); -const Proj = require('./project.json'); -const { pack } = require('vitreum'); +import fs from 'fs-extra'; +import Proj from './project.json' with { type: 'json' }; +import vitreum from 'vitreum'; +const { pack } = vitreum; + +import lessTransform from 'vitreum/transforms/less.js'; +import assetTransform from 'vitreum/transforms/asset.js'; + const isDev = !!process.argv.find((arg)=>arg=='--dev'); -const lessTransform = require('vitreum/transforms/less.js'); -const assetTransform = require('vitreum/transforms/asset.js'); -//const Meta = require('vitreum/headtags'); - const transforms = { '.less' : lessTransform, '*' : assetTransform('./build') @@ -17,7 +18,7 @@ const build = async ({ bundle, render, ssr })=>{ const css = await lessTransform.generate({ paths: './shared' }); await fs.outputFile('./build/admin/bundle.css', css); await fs.outputFile('./build/admin/bundle.js', bundle); - await fs.outputFile('./build/admin/ssr.js', ssr); + await fs.outputFile('./build/admin/ssr.cjs', ssr); }; fs.emptyDirSync('./build/admin'); diff --git a/scripts/buildHomebrew.js b/scripts/buildHomebrew.js index f072b0359..656714d87 100644 --- a/scripts/buildHomebrew.js +++ b/scripts/buildHomebrew.js @@ -1,16 +1,18 @@ -const fs = require('fs-extra'); -const zlib = require('zlib'); -const Proj = require('./project.json'); +import fs from 'fs-extra'; +import zlib from 'zlib'; +import Proj from './project.json' with { type: 'json' }; +import vitreum from 'vitreum'; +const { pack, watchFile, livereload } = vitreum; -const { pack, watchFile, livereload } = require('vitreum'); -const isDev = !!process.argv.find((arg)=>arg=='--dev'); +import lessTransform from 'vitreum/transforms/less.js'; +import assetTransform from 'vitreum/transforms/asset.js'; +import babel from '@babel/core'; +import babelConfig from '../babel.config.json' with { type : 'json' }; +import less from 'less'; -const lessTransform = require('vitreum/transforms/less.js'); -const assetTransform = require('vitreum/transforms/asset.js'); -const babel = require('@babel/core'); -const less = require('less'); +const isDev = !!process.argv.find((arg) => arg === '--dev'); -const babelify = async (code)=>(await babel.transformAsync(code, { presets: [['@babel/preset-env', { 'exclude': ['proposal-dynamic-import'] }], '@babel/preset-react'], plugins: ['@babel/plugin-transform-runtime'] })).code; +const babelify = async (code)=>(await babel.transformAsync(code, babelConfig)).code; const transforms = { '.js' : (code, filename, opts)=>babelify(code), @@ -24,7 +26,7 @@ const build = async ({ bundle, render, ssr })=>{ //css = `@layer bundle {\n${css}\n}`; await fs.outputFile('./build/homebrew/bundle.css', css); await fs.outputFile('./build/homebrew/bundle.js', bundle); - await fs.outputFile('./build/homebrew/ssr.js', ssr); + await fs.outputFile('./build/homebrew/ssr.cjs', ssr); await fs.copy('./client/homebrew/favicon.ico', './build/assets/favicon.ico'); @@ -51,7 +53,7 @@ fs.emptyDirSync('./build'); const themes = { Legacy: {}, V3: {} }; let themeFiles = fs.readdirSync('./themes/Legacy'); - for (dir of themeFiles) { + for (let dir of themeFiles) { const themeData = JSON.parse(fs.readFileSync(`./themes/Legacy/${dir}/settings.json`).toString()); themeData.path = dir; themes.Legacy[dir] = (themeData); @@ -68,7 +70,7 @@ fs.emptyDirSync('./build'); } themeFiles = fs.readdirSync('./themes/V3'); - for (dir of themeFiles) { + for (let dir of themeFiles) { const themeData = JSON.parse(fs.readFileSync(`./themes/V3/${dir}/settings.json`).toString()); themeData.path = dir; themes.V3[dir] = (themeData); @@ -104,14 +106,14 @@ fs.emptyDirSync('./build'); const editorThemesBuildDir = './build/homebrew/cm-themes'; await fs.copy('./node_modules/codemirror/theme', editorThemesBuildDir); await fs.copy('./themes/codeMirror/customThemes', editorThemesBuildDir); - editorThemeFiles = fs.readdirSync(editorThemesBuildDir); + const editorThemeFiles = fs.readdirSync(editorThemesBuildDir); const editorThemeFile = './themes/codeMirror/editorThemes.json'; if(fs.existsSync(editorThemeFile)) fs.rmSync(editorThemeFile); const stream = fs.createWriteStream(editorThemeFile, { flags: 'a' }); stream.write('[\n"default"'); - for (themeFile of editorThemeFiles) { + for (let themeFile of editorThemeFiles) { stream.write(`,\n"${themeFile.slice(0, -4)}"`); } stream.write('\n]\n'); diff --git a/server.js b/server.js index 6cbe07c4f..fe5a9a363 100644 --- a/server.js +++ b/server.js @@ -1,12 +1,12 @@ -const DB = require('./server/db.js'); -const server = require('./server/app.js'); -const config = require('./server/config.js'); +import DB from './server/db.js'; +import server from './server/app.js'; +import config from './server/config.js'; DB.connect(config).then(()=>{ // Ensure that we have successfully connected to the database // before launching server const PORT = process.env.PORT || config.get('web_port') || 8000; - server.app.listen(PORT, ()=>{ + server.listen(PORT, ()=>{ const reset = '\x1b[0m'; // Reset to default style const bright = '\x1b[1m'; // Bright (bold) style const cyan = '\x1b[36m'; // Cyan color diff --git a/server/admin.api.js b/server/admin.api.js index fe2def3ce..1a39f020b 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -1,9 +1,15 @@ -const HomebrewModel = require('./homebrew.model.js').model; -const router = require('express').Router(); -const Moment = require('moment'); -//const render = require('vitreum/steps/render'); -const templateFn = require('../client/template.js'); -const zlib = require('zlib'); +import { model as HomebrewModel } from './homebrew.model.js'; +import { model as NotificationModel } from './notifications.model.js'; +import express from 'express'; +import Moment from 'moment'; +import zlib from 'zlib'; +import templateFn from '../client/template.js'; + +import HomebrewAPI from './homebrew.api.js'; +import asyncHandler from 'express-async-handler'; +import { splitTextStyleAndMetadata } from '../shared/helpers.js'; + +const router = express.Router(); process.env.ADMIN_USER = process.env.ADMIN_USER || 'admin'; process.env.ADMIN_PASS = process.env.ADMIN_PASS || 'password3'; @@ -22,7 +28,7 @@ const mw = { if(process.env.ADMIN_USER === username && process.env.ADMIN_PASS === password){ return next(); } - return res.status(401).send('Access denied'); + throw { HBErrorCode: '52', code: 401, message: 'Access denied' }; } }; @@ -66,23 +72,8 @@ router.post('/admin/cleanup', mw.adminOnly, (req, res)=>{ }); /* Searches for matching edit or share id, also attempts to partial match */ -router.get('/admin/lookup/:id', mw.adminOnly, async (req, res, next)=>{ - HomebrewModel.findOne({ - $or : [ - { editId: { $regex: req.params.id, $options: 'i' } }, - { shareId: { $regex: req.params.id, $options: 'i' } }, - ] - }).exec() - .then((brew)=>{ - if(!brew) // No document found - return res.status(404).json({ error: 'Document not found' }); - else - return res.json(brew); - }) - .catch((err)=>{ - console.error(err); - return res.status(500).json({ error: 'Internal Server Error' }); - }); +router.get('/admin/lookup/:id', mw.adminOnly, asyncHandler(HomebrewAPI.getBrew('admin', false)), async (req, res, next)=>{ + return res.json(req.brew); }); /* Find 50 brews that aren't compressed yet */ @@ -100,6 +91,28 @@ router.get('/admin/finduncompressed', mw.adminOnly, (req, res)=>{ }); }); +/* Cleans `` from the "text" field of a brew */ +router.put('/admin/clean/script/:id', asyncHandler(HomebrewAPI.getBrew('admin', false)), async (req, res)=>{ + console.log(`[ADMIN] Cleaning script tags from ShareID ${req.params.id}`); + + function cleanText(text){return text.replaceAll(/(<\/?s)cript/gi, '');}; + + const brew = req.brew; + + const properties = ['text', 'description', 'title']; + properties.forEach((property)=>{ + brew[property] = cleanText(brew[property]); + }); + + splitTextStyleAndMetadata(brew); + + req.body = brew; + + // Remove Account from request to prevent Admin user from being added to brew as an Author + req.account = undefined; + + return await HomebrewAPI.updateBrew(req, res); +}); /* Compresses the "text" field of a brew to binary */ router.put('/admin/compress/:id', (req, res)=>{ @@ -138,12 +151,48 @@ router.get('/admin/stats', mw.adminOnly, async (req, res)=>{ } }); +// ####################### NOTIFICATIONS + +router.get('/admin/notification/all', async (req, res, next)=>{ + try { + const notifications = await NotificationModel.getAll(); + return res.json(notifications); + + } catch (error) { + console.log('Error getting all notifications: ', error.message); + return res.status(500).json({ message: error.message }); + } +}); + +router.post('/admin/notification/add', mw.adminOnly, async (req, res, next)=>{ + try { + const notification = await NotificationModel.addNotification(req.body); + return res.status(201).json(notification); + } catch (error) { + console.log('Error adding notification: ', error.message); + return res.status(500).json({ message: error.message }); + } +}); + +router.delete('/admin/notification/delete/:id', mw.adminOnly, async (req, res, next)=>{ + try { + const notification = await NotificationModel.deleteNotification(req.params.id); + return res.json(notification); + } catch (error) { + console.error('Error deleting notification: { key: ', req.params.id, ' error: ', error.message, ' }'); + return res.status(500).json({ message: error.message }); + } +}); + router.get('/admin', mw.adminOnly, (req, res)=>{ templateFn('admin', { url : req.originalUrl }) .then((page)=>res.send(page)) - .catch((err)=>res.sendStatus(500)); + .catch((err)=>{ + console.log(err); + res.sendStatus(500); + }); }); -module.exports = router; +export default router; diff --git a/server/admin.api.spec.js b/server/admin.api.spec.js new file mode 100644 index 000000000..6a23393b1 --- /dev/null +++ b/server/admin.api.spec.js @@ -0,0 +1,117 @@ +import supertest from 'supertest'; +import HBApp from './app.js'; +import {model as NotificationModel } from './notifications.model.js'; + + +// Mimic https responses to avoid being redirected all the time +const app = supertest.agent(HBApp).set('X-Forwarded-Proto', 'https'); + +describe('Tests for admin api', ()=>{ + afterEach(()=>{ + jest.resetAllMocks(); + }); + + describe('Notifications', ()=>{ + it('should return list of all notifications', async ()=>{ + const testNotifications = ['a', 'b']; + + jest.spyOn(NotificationModel, 'find') + .mockImplementationOnce(() => { + return { exec: jest.fn().mockResolvedValue(testNotifications) }; + }); + + const response = await app + .get('/admin/notification/all') + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`); + + expect(response.status).toBe(200); + expect(response.body).toEqual(testNotifications); + }); + + it('should add a new notification', async ()=>{ + const inputNotification = { + title : 'Test Notification', + text : 'This is a test notification', + startAt : new Date().toISOString(), + stopAt : new Date().toISOString(), + dismissKey : 'testKey' + }; + + const savedNotification = { + ...inputNotification, + _id : expect.any(String), + createdAt : expect.any(String), + startAt : inputNotification.startAt, + stopAt : inputNotification.stopAt, + }; + + jest.spyOn(NotificationModel.prototype, 'save') + .mockImplementationOnce(function() { + return Promise.resolve(this); + }); + + const response = await app + .post('/admin/notification/add') + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`) + .send(inputNotification); + + expect(response.status).toBe(201); + expect(response.body).toEqual(savedNotification); + }); + + it('should handle error adding a notification without dismissKey', async () => { + const inputNotification = { + title : 'Test Notification', + text : 'This is a test notification', + startAt : new Date().toISOString(), + stopAt : new Date().toISOString() + }; + + //Change 'save' function to just return itself instead of actually interacting with the database + jest.spyOn(NotificationModel.prototype, 'save') + .mockImplementationOnce(function() { + return Promise.resolve(this); + }); + + const response = await app + .post('/admin/notification/add') + .set('Authorization', 'Basic ' + Buffer.from('admin:password3').toString('base64')) + .send(inputNotification); + + expect(response.status).toBe(500); + expect(response.body).toEqual({ message: 'Dismiss key is required!' }); + }); + + it('should delete a notification based on its dismiss key', async ()=>{ + const dismissKey = 'testKey'; + + jest.spyOn(NotificationModel, 'findOneAndDelete') + .mockImplementationOnce((key) => { + return { exec: jest.fn().mockResolvedValue(key) }; + }); + const response = await app + .delete(`/admin/notification/delete/${dismissKey}`) + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`); + + expect(NotificationModel.findOneAndDelete).toHaveBeenCalledWith({'dismissKey': 'testKey'}); + expect(response.status).toBe(200); + expect(response.body).toEqual({ dismissKey: 'testKey' }); + }); + + it('should handle error deleting a notification that doesnt exist', async ()=>{ + const dismissKey = 'testKey'; + + jest.spyOn(NotificationModel, 'findOneAndDelete') + .mockImplementationOnce(() => { + return { exec: jest.fn().mockResolvedValue() }; + }); + const response = await app + .delete(`/admin/notification/delete/${dismissKey}`) + .set('Authorization', `Basic ${Buffer.from('admin:password3').toString('base64')}`); + + expect(NotificationModel.findOneAndDelete).toHaveBeenCalledWith({'dismissKey': 'testKey'}); + expect(response.status).toBe(500); + expect(response.body).toEqual({ message: 'Notification not found' }); + }); + }); +}); diff --git a/server/app.js b/server/app.js index 8b3e4652b..4dec6b4c4 100644 --- a/server/app.js +++ b/server/app.js @@ -1,521 +1,636 @@ -/*eslint max-lines: ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}]*/ -// Set working directory to project root -process.chdir(`${__dirname}/..`); - -const _ = require('lodash'); -const jwt = require('jwt-simple'); -const express = require('express'); -const yaml = require('js-yaml'); -const app = express(); -const config = require('./config.js'); - -const { homebrewApi, getBrew, getUsersBrewThemes } = require('./homebrew.api.js'); -const GoogleActions = require('./googleActions.js'); -const serveCompressedStaticAssets = require('./static-assets.mv.js'); -const sanitizeFilename = require('sanitize-filename'); -const asyncHandler = require('express-async-handler'); -const templateFn = require('./../client/template.js'); - -const { DEFAULT_BREW } = require('./brewDefaults.js'); - -const { splitTextStyleAndMetadata } = require('../shared/helpers.js'); - - -const sanitizeBrew = (brew, accessType)=>{ - brew._id = undefined; - brew.__v = undefined; - if(accessType !== 'edit' && accessType !== 'shareAuthor') { - brew.editId = undefined; - } - return brew; -}; - -app.use('/', serveCompressedStaticAssets(`build`)); -app.use(require('./middleware/content-negotiation.js')); -app.use(require('body-parser').json({ limit: '25mb' })); -app.use(require('cookie-parser')()); -app.use(require('./forcessl.mw.js')); - -//Account Middleware -app.use((req, res, next)=>{ - if(req.cookies && req.cookies.nc_session){ - try { - req.account = jwt.decode(req.cookies.nc_session, config.get('secret')); - //console.log("Just loaded up JWT from cookie:"); - //console.log(req.account); - } catch (e){} - } - - req.config = { - google_client_id : config.get('google_client_id'), - google_client_secret : config.get('google_client_secret') - }; - return next(); -}); - -app.use(homebrewApi); -app.use(require('./admin.api.js')); - -const HomebrewModel = require('./homebrew.model.js').model; -const welcomeText = require('fs').readFileSync('client/homebrew/pages/homePage/welcome_msg.md', 'utf8'); -const welcomeTextLegacy = require('fs').readFileSync('client/homebrew/pages/homePage/welcome_msg_legacy.md', 'utf8'); -const migrateText = require('fs').readFileSync('client/homebrew/pages/homePage/migrate.md', 'utf8'); -const changelogText = require('fs').readFileSync('changelog.md', 'utf8'); -const faqText = require('fs').readFileSync('faq.md', 'utf8'); - -String.prototype.replaceAll = function(s, r){return this.split(s).join(r);}; - -const defaultMetaTags = { - site_name : 'The Homebrewery - Make your Homebrew content look legit!', - title : 'The Homebrewery', - description : 'A NaturalCrit Tool for creating authentic Homebrews using Markdown.', - image : `${config.get('publicUrl')}/thumbnail.png`, - type : 'website' -}; - -//Robots.txt -app.get('/robots.txt', (req, res)=>{ - return res.sendFile(`robots.txt`, { root: process.cwd() }); -}); - -//Home page -app.get('/', (req, res, next)=>{ - req.brew = { - text : welcomeText, - renderer : 'V3', - theme : '5ePHB' - }, - - req.ogMeta = { ...defaultMetaTags, - title : 'Homepage', - description : 'Homepage' - }; - - splitTextStyleAndMetadata(req.brew); - return next(); -}); - -//Home page Legacy -app.get('/legacy', (req, res, next)=>{ - req.brew = { - text : welcomeTextLegacy, - renderer : 'legacy', - theme : '5ePHB' - }, - - req.ogMeta = { ...defaultMetaTags, - title : 'Homepage (Legacy)', - description : 'Homepage' - }; - - splitTextStyleAndMetadata(req.brew); - return next(); -}); - -//Legacy/Other Document -> v3 Migration Guide -app.get('/migrate', (req, res, next)=>{ - req.brew = { - text : migrateText, - renderer : 'V3', - theme : '5ePHB' - }, - - req.ogMeta = { ...defaultMetaTags, - title : 'v3 Migration Guide', - description : 'A brief guide to converting Legacy documents to the v3 renderer.' - }; - - splitTextStyleAndMetadata(req.brew); - return next(); -}); - -//Changelog page -app.get('/changelog', async (req, res, next)=>{ - req.brew = { - title : 'Changelog', - text : changelogText, - renderer : 'V3', - theme : '5ePHB' - }, - - req.ogMeta = { ...defaultMetaTags, - title : 'Changelog', - description : 'Development changelog.' - }; - - splitTextStyleAndMetadata(req.brew); - return next(); -}); - -//FAQ page -app.get('/faq', async (req, res, next)=>{ - req.brew = { - title : 'FAQ', - text : faqText, - renderer : 'V3', - theme : '5ePHB' - }, - - req.ogMeta = { ...defaultMetaTags, - title : 'FAQ', - description : 'Frequently Asked Questions' - }; - - splitTextStyleAndMetadata(req.brew); - return next(); -}); - -//Source page -app.get('/source/:id', asyncHandler(getBrew('share')), (req, res)=>{ - const { brew } = req; - - const replaceStrings = { '&': '&', '<': '<', '>': '>' }; - let text = brew.text; - for (const replaceStr in replaceStrings) { - text = text.replaceAll(replaceStr, replaceStrings[replaceStr]); - } - text = `
    ${text}
    `; - res.status(200).send(text); -}); - -//Download brew source page -app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{ - const { brew } = req; - sanitizeBrew(brew, 'share'); - const prefix = 'HB - '; - - const encodeRFC3986ValueChars = (str)=>{ - return ( - encodeURIComponent(str) - .replace(/[!'()*]/g, (char)=>{`%${char.charCodeAt(0).toString(16).toUpperCase()}`;}) - ); - }; - - let fileName = sanitizeFilename(`${prefix}${brew.title}`).replaceAll(' ', ''); - if(!fileName || !fileName.length) { fileName = `${prefix}-Untitled-Brew`; }; - res.set({ - 'Cache-Control' : 'no-cache', - 'Content-Type' : 'text/plain', - 'Content-Disposition' : `attachment; filename*=UTF-8''${encodeRFC3986ValueChars(fileName)}.txt` - }); - res.status(200).send(brew.text); -}); - -//User Page -app.get('/user/:username', async (req, res, next)=>{ - const ownAccount = req.account && (req.account.username == req.params.username); - - req.ogMeta = { ...defaultMetaTags, - title : `${req.params.username}'s Collection`, - description : 'View my collection of homebrew on the Homebrewery.' - // type : could be 'profile'? - }; - - const fields = [ - 'googleId', - 'title', - 'pageCount', - 'description', - 'authors', - 'lang', - 'published', - 'views', - 'shareId', - 'editId', - 'createdAt', - 'updatedAt', - 'lastViewed', - 'thumbnail', - 'tags' - ]; - - let brews = await HomebrewModel.getByUser(req.params.username, ownAccount, fields) - .catch((err)=>{ - console.log(err); - }); - - if(ownAccount && req?.account?.googleId){ - const auth = await GoogleActions.authCheck(req.account, res); - let googleBrews = await GoogleActions.listGoogleBrews(auth) - .catch((err)=>{ - console.error(err); - }); - - if(googleBrews && googleBrews.length > 0) { - for (const brew of brews.filter((brew)=>brew.googleId)) { - const match = googleBrews.findIndex((b)=>b.editId === brew.editId); - if(match !== -1) { - brew.googleId = googleBrews[match].googleId; - brew.stubbed = true; - brew.pageCount = googleBrews[match].pageCount; - brew.renderer = googleBrews[match].renderer; - brew.version = googleBrews[match].version; - brew.webViewLink = googleBrews[match].webViewLink; - googleBrews.splice(match, 1); - } - } - - googleBrews = googleBrews.map((brew)=>({ ...brew, authors: [req.account.username] })); - brews = _.concat(brews, googleBrews); - } - } - - req.brews = _.map(brews, (brew)=>{ - // Clean up brew data - brew.title = brew.title?.trim(); - brew.description = brew.description?.trim(); - return sanitizeBrew(brew, ownAccount ? 'edit' : 'share'); - }); - - return next(); -}); - -//Edit Page -app.get('/edit/:id', asyncHandler(getBrew('edit')), asyncHandler(async(req, res, next)=>{ - req.brew = req.brew.toObject ? req.brew.toObject() : req.brew; - - req.userThemes = await(getUsersBrewThemes(req.account?.username)); - - req.ogMeta = { ...defaultMetaTags, - title : req.brew.title || 'Untitled Brew', - description : req.brew.description || 'No description.', - image : req.brew.thumbnail || defaultMetaTags.image, - type : 'article' - }; - - sanitizeBrew(req.brew, 'edit'); - splitTextStyleAndMetadata(req.brew); - res.header('Cache-Control', 'no-cache, no-store'); //reload the latest saved brew when pressing back button, not the cached version before save. - return next(); -})); - -//New Page from ID -app.get('/new/:id', asyncHandler(getBrew('share')), asyncHandler(async(req, res, next)=>{ - sanitizeBrew(req.brew, 'share'); - splitTextStyleAndMetadata(req.brew); - const brew = { - shareId : req.brew.shareId, - title : `CLONE - ${req.brew.title}`, - text : req.brew.text, - style : req.brew.style, - renderer : req.brew.renderer, - theme : req.brew.theme, - tags : req.brew.tags, - }; - req.brew = _.defaults(brew, DEFAULT_BREW); - - req.userThemes = await(getUsersBrewThemes(req.account?.username)); - - req.ogMeta = { ...defaultMetaTags, - title : 'New', - description : 'Start crafting your homebrew on the Homebrewery!' - }; - - return next(); -})); - -//New Page -app.get('/new', asyncHandler(async(req, res, next)=>{ - req.userThemes = await(getUsersBrewThemes(req.account?.username)); - - req.ogMeta = { ...defaultMetaTags, - title : 'New', - description : 'Start crafting your homebrew on the Homebrewery!' - }; - - return next(); -})); - -//Share Page -app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{ - const { brew } = req; - req.ogMeta = { ...defaultMetaTags, - title : req.brew.title || 'Untitled Brew', - description : req.brew.description || 'No description.', - image : req.brew.thumbnail || defaultMetaTags.image, - type : 'article' - }; - - // increase visitor view count, do not include visits by author(s) - if(!brew.authors.includes(req.account?.username)){ - if(req.params.id.length > 12 && !brew._id) { - const googleId = brew.googleId; - const shareId = brew.shareId; - await GoogleActions.increaseView(googleId, shareId, 'share', brew) - .catch((err)=>{next(err);}); - } else { - await HomebrewModel.increaseView({ shareId: brew.shareId }); - } - }; - - brew.authors.includes(req.account?.username) ? sanitizeBrew(req.brew, 'shareAuthor') : sanitizeBrew(req.brew, 'share'); - splitTextStyleAndMetadata(req.brew); - return next(); -})); - -//Account Page -app.get('/account', asyncHandler(async (req, res, next)=>{ - const data = {}; - data.title = 'Account Information Page'; - - let auth; - let googleCount = []; - if(req.account) { - if(req.account.googleId) { - try { - auth = await GoogleActions.authCheck(req.account, res, false); - } catch (e) { - auth = undefined; - console.log('Google auth check failed!'); - console.log(e); - } - if(auth.credentials.access_token) { - try { - googleCount = await GoogleActions.listGoogleBrews(auth); - } catch (e) { - googleCount = undefined; - console.log('List Google files failed!'); - console.log(e); - } - } - } - - const query = { authors: req.account.username, googleId: { $exists: false } }; - const mongoCount = await HomebrewModel.countDocuments(query) - .catch((err)=>{ - mongoCount = 0; - console.log(err); - }); - - data.accountDetails = { - username : req.account.username, - issued : req.account.issued, - googleId : Boolean(req.account.googleId), - authCheck : Boolean(req.account.googleId && auth.credentials.access_token), - mongoCount : mongoCount, - googleCount : googleCount?.length - }; - } - - req.brew = data; - - req.ogMeta = { ...defaultMetaTags, - title : `Account Page`, - description : null - }; - - return next(); -})); - -const nodeEnv = config.get('node_env'); -const isLocalEnvironment = config.get('local_environments').includes(nodeEnv); -// Local only -if(isLocalEnvironment){ - // Login - app.post('/local/login', (req, res)=>{ - const username = req.body.username; - if(!username) return; - - const payload = jwt.encode({ username: username, issued: new Date }, config.get('secret')); - return res.json(payload); - }); -} - -//Send rendered page -app.use(asyncHandler(async (req, res, next)=>{ - if (!req.route) return res.redirect('/'); // Catch-all for invalid routes - - const page = await renderPage(req, res); - if(!page) return; - res.send(page); -})); - -//Render the page -const renderPage = async (req, res)=>{ - // Create configuration object - const configuration = { - local : isLocalEnvironment, - publicUrl : config.get('publicUrl') ?? '', - environment : nodeEnv - }; - const props = { - version : require('./../package.json').version, - url : req.customUrl || req.originalUrl, - brew : req.brew, - brews : req.brews, - googleBrews : req.googleBrews, - account : req.account, - enable_v3 : config.get('enable_v3'), - enable_themes : config.get('enable_themes'), - config : configuration, - ogMeta : req.ogMeta, - userThemes : req.userThemes - }; - const title = req.brew ? req.brew.title : ''; - const page = await templateFn('homebrew', title, props) - .catch((err)=>{ - console.log(err); - }); - return page; -}; - -//v=====----- Error-Handling Middleware -----=====v// -//Format Errors as plain objects so all fields will appear in the string sent -const formatErrors = (key, value)=>{ - if(value instanceof Error) { - const error = {}; - Object.getOwnPropertyNames(value).forEach(function (key) { - error[key] = value[key]; - }); - return error; - } - return value; -}; - -const getPureError = (error)=>{ - return JSON.parse(JSON.stringify(error, formatErrors)); -}; - -app.use(async (err, req, res, next)=>{ - err.originalUrl = req.originalUrl; - console.error(err); - - if(err.originalUrl?.startsWith('/api/')) { - // console.log('API error'); - res.status(err.status || err.response?.status || 500).send(err); - return; - } - - // console.log('non-API error'); - const status = err.status || err.code || 500; - - req.ogMeta = { ...defaultMetaTags, - title : 'Error Page', - description : 'Something went wrong!' - }; - req.brew = { - ...err, - title : 'Error - Something went wrong!', - text : err.errors?.map((error)=>{return error.message;}).join('\n\n') || err.message || 'Unknown error!', - status : status, - HBErrorCode : err.HBErrorCode ?? '00', - pureError : getPureError(err) - }; - req.customUrl= '/error'; - - const page = await renderPage(req, res); - if(!page) return; - res.send(page); -}); - -app.use((req, res)=>{ - if(!res.headersSent) { - console.error('Headers have not been sent, responding with a server error.', req.url); - res.status(500).send('An error occurred and the server did not send a response. The error has been logged, please note the time this occurred and report this issue.'); - } -}); -//^=====--------------------------------------=====^// - -module.exports = { - app : app -}; +/*eslint max-lines: ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}]*/ +// Set working directory to project root +import { dirname } from 'path'; +import { fileURLToPath } from 'url'; +import packageJSON from './../package.json' with { type: 'json' }; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +process.chdir(`${__dirname}/..`); +const version = packageJSON.version; + +import _ from 'lodash'; +import jwt from 'jwt-simple'; +import express from 'express'; +import yaml from 'js-yaml'; +import config from './config.js'; +import fs from 'fs-extra'; + +const app = express(); + +import api from './homebrew.api.js'; +const { homebrewApi, getBrew, getUsersBrewThemes, getCSS } = api; +import adminApi from './admin.api.js'; +import vaultApi from './vault.api.js'; +import GoogleActions from './googleActions.js'; +import serveCompressedStaticAssets from './static-assets.mv.js'; +import sanitizeFilename from 'sanitize-filename'; +import asyncHandler from 'express-async-handler'; +import templateFn from '../client/template.js'; +import { model as HomebrewModel } from './homebrew.model.js'; + +import { DEFAULT_BREW } from './brewDefaults.js'; +import { splitTextStyleAndMetadata } from '../shared/helpers.js'; + +//==== Middleware Imports ====// +import contentNegotiation from './middleware/content-negotiation.js'; +import bodyParser from 'body-parser'; +import cookieParser from 'cookie-parser'; +import forceSSL from './forcessl.mw.js'; + + +const sanitizeBrew = (brew, accessType)=>{ + brew._id = undefined; + brew.__v = undefined; + if(accessType !== 'edit' && accessType !== 'shareAuthor') { + brew.editId = undefined; + } + return brew; +}; + +app.set('trust proxy', 1 /* number of proxies between user and server */); + +app.use('/', serveCompressedStaticAssets(`build`)); +app.use(contentNegotiation); +app.use(bodyParser.json({ limit: '25mb' })); +app.use(cookieParser()); +app.use(forceSSL); + +import cors from 'cors'; + +const nodeEnv = config.get('node_env'); +const isLocalEnvironment = config.get('local_environments').includes(nodeEnv); + +const corsOptions = { + origin : (origin, callback)=>{ + + const allowedOrigins = [ + 'https://homebrewery.naturalcrit.com', + 'https://www.naturalcrit.com', + 'https://naturalcrit-stage.herokuapp.com', + 'https://homebrewery-stage.herokuapp.com', + ]; + + if(isLocalEnvironment) { + allowedOrigins.push('http://localhost:8000', 'http://localhost:8010'); + } + + const herokuRegex = /^https:\/\/(?:homebrewery-pr-\d+\.herokuapp\.com|naturalcrit-pr-\d+\.herokuapp\.com)$/; // Matches any Heroku app + + if(!origin || allowedOrigins.includes(origin) || herokuRegex.test(origin)) { + callback(null, true); + } else { + console.log(origin, 'not allowed'); + callback(new Error('Not allowed by CORS, if you think this is an error, please contact us')); + } + }, + methods : ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], + credentials : true, +}; + +app.use(cors(corsOptions)); + +//Account Middleware +app.use((req, res, next)=>{ + if(req.cookies && req.cookies.nc_session){ + try { + req.account = jwt.decode(req.cookies.nc_session, config.get('secret')); + //console.log("Just loaded up JWT from cookie:"); + //console.log(req.account); + } catch (e){ + console.log(e); + } + } + + req.config = { + google_client_id : config.get('google_client_id'), + google_client_secret : config.get('google_client_secret') + }; + return next(); +}); + +app.use(homebrewApi); +app.use(adminApi); +app.use(vaultApi); + +const welcomeText = fs.readFileSync('client/homebrew/pages/homePage/welcome_msg.md', 'utf8'); +const welcomeTextLegacy = fs.readFileSync('client/homebrew/pages/homePage/welcome_msg_legacy.md', 'utf8'); +const migrateText = fs.readFileSync('client/homebrew/pages/homePage/migrate.md', 'utf8'); +const changelogText = fs.readFileSync('changelog.md', 'utf8'); +const faqText = fs.readFileSync('faq.md', 'utf8'); + +String.prototype.replaceAll = function(s, r){return this.split(s).join(r);}; + +const defaultMetaTags = { + site_name : 'The Homebrewery - Make your Homebrew content look legit!', + title : 'The Homebrewery', + description : 'A NaturalCrit Tool for creating authentic Homebrews using Markdown.', + image : `${config.get('publicUrl')}/thumbnail.png`, + type : 'website' +}; + +//Robots.txt +app.get('/robots.txt', (req, res)=>{ + return res.sendFile(`robots.txt`, { root: process.cwd() }); +}); + +//Home page +app.get('/', (req, res, next)=>{ + req.brew = { + text : welcomeText, + renderer : 'V3', + theme : '5ePHB' + }, + + req.ogMeta = { ...defaultMetaTags, + title : 'Homepage', + description : 'Homepage' + }; + + splitTextStyleAndMetadata(req.brew); + return next(); +}); + +//Home page Legacy +app.get('/legacy', (req, res, next)=>{ + req.brew = { + text : welcomeTextLegacy, + renderer : 'legacy', + theme : '5ePHB' + }, + + req.ogMeta = { ...defaultMetaTags, + title : 'Homepage (Legacy)', + description : 'Homepage' + }; + + splitTextStyleAndMetadata(req.brew); + return next(); +}); + +//Legacy/Other Document -> v3 Migration Guide +app.get('/migrate', (req, res, next)=>{ + req.brew = { + text : migrateText, + renderer : 'V3', + theme : '5ePHB' + }, + + req.ogMeta = { ...defaultMetaTags, + title : 'v3 Migration Guide', + description : 'A brief guide to converting Legacy documents to the v3 renderer.' + }; + + splitTextStyleAndMetadata(req.brew); + return next(); +}); + +//Changelog page +app.get('/changelog', async (req, res, next)=>{ + req.brew = { + title : 'Changelog', + text : changelogText, + renderer : 'V3', + theme : '5ePHB' + }, + + req.ogMeta = { ...defaultMetaTags, + title : 'Changelog', + description : 'Development changelog.' + }; + + splitTextStyleAndMetadata(req.brew); + return next(); +}); + +//FAQ page +app.get('/faq', async (req, res, next)=>{ + req.brew = { + title : 'FAQ', + text : faqText, + renderer : 'V3', + theme : '5ePHB' + }, + + req.ogMeta = { ...defaultMetaTags, + title : 'FAQ', + description : 'Frequently Asked Questions' + }; + + splitTextStyleAndMetadata(req.brew); + return next(); +}); + +//Source page +app.get('/source/:id', asyncHandler(getBrew('share')), (req, res)=>{ + const { brew } = req; + + const replaceStrings = { '&': '&', '<': '<', '>': '>' }; + let text = brew.text; + for (const replaceStr in replaceStrings) { + text = text.replaceAll(replaceStr, replaceStrings[replaceStr]); + } + text = `
    ${text}
    `; + res.status(200).send(text); +}); + +//Download brew source page +app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{ + const { brew } = req; + sanitizeBrew(brew, 'share'); + const prefix = 'HB - '; + + const encodeRFC3986ValueChars = (str)=>{ + return ( + encodeURIComponent(str) + .replace(/[!'()*]/g, (char)=>{`%${char.charCodeAt(0).toString(16).toUpperCase()}`;}) + ); + }; + + let fileName = sanitizeFilename(`${prefix}${brew.title}`).replaceAll(' ', ''); + if(!fileName || !fileName.length) { fileName = `${prefix}-Untitled-Brew`; }; + res.set({ + 'Cache-Control' : 'no-cache', + 'Content-Type' : 'text/plain', + 'Content-Disposition' : `attachment; filename*=UTF-8''${encodeRFC3986ValueChars(fileName)}.txt` + }); + res.status(200).send(brew.text); +}); + +//Serve brew metadata +app.get('/metadata/:id', asyncHandler(getBrew('share')), (req, res)=>{ + const { brew } = req; + sanitizeBrew(brew, 'share'); + + const fields = ['title', 'pageCount', 'description', 'authors', 'lang', + 'published', 'views', 'shareId', 'createdAt', 'updatedAt', + 'lastViewed', 'thumbnail', 'tags' + ]; + + const metadata = fields.reduce((acc, field)=>{ + if(brew[field] !== undefined) acc[field] = brew[field]; + return acc; + }, {}); + res.status(200).json(metadata); +}); + +//Serve brew styling +app.get('/css/:id', asyncHandler(getBrew('share')), (req, res)=>{getCSS(req, res);}); + +//User Page +app.get('/user/:username', async (req, res, next)=>{ + const ownAccount = req.account && (req.account.username == req.params.username); + + req.ogMeta = { ...defaultMetaTags, + title : `${req.params.username}'s Collection`, + description : 'View my collection of homebrew on the Homebrewery.' + // type : could be 'profile'? + }; + + const fields = [ + 'googleId', + 'title', + 'pageCount', + 'description', + 'authors', + 'lang', + 'published', + 'views', + 'shareId', + 'editId', + 'createdAt', + 'updatedAt', + 'lastViewed', + 'thumbnail', + 'tags' + ]; + + let brews = await HomebrewModel.getByUser(req.params.username, ownAccount, fields) + .catch((err)=>{ + console.log(err); + }); + + brews.forEach((brew)=>brew.stubbed = true); //All brews from MongoDB are "stubbed" + + if(ownAccount && req?.account?.googleId){ + const auth = await GoogleActions.authCheck(req.account, res); + let googleBrews = await GoogleActions.listGoogleBrews(auth) + .catch((err)=>{ + console.error(err); + }); + + // If stub matches file from Google, use Google metadata over stub metadata + if(googleBrews && googleBrews.length > 0) { + for (const brew of brews.filter((brew)=>brew.googleId)) { + const match = googleBrews.findIndex((b)=>b.editId === brew.editId); + if(match !== -1) { + brew.googleId = googleBrews[match].googleId; + brew.pageCount = googleBrews[match].pageCount; + brew.renderer = googleBrews[match].renderer; + brew.version = googleBrews[match].version; + brew.webViewLink = googleBrews[match].webViewLink; + googleBrews.splice(match, 1); + } + } + + //Remaining unstubbed google brews display current user as author + googleBrews = googleBrews.map((brew)=>({ ...brew, authors: [req.account.username] })); + brews = _.concat(brews, googleBrews); + } + } + + req.brews = _.map(brews, (brew)=>{ + // Clean up brew data + brew.title = brew.title?.trim(); + brew.description = brew.description?.trim(); + return sanitizeBrew(brew, ownAccount ? 'edit' : 'share'); + }); + + return next(); +}); + +//Change author name on brews +app.put('/api/user/rename', async (req, res)=>{ + const { username, newUsername } = req.body; + const ownAccount = req.account && (req.account.username == newUsername); + + if(!username || !newUsername) + return res.status(400).json({ error: 'Username and newUsername are required.' }); + if(!ownAccount) + return res.status(403).json({ error: 'Must be logged in to change your username' }); + try { + const brews = await HomebrewModel.getByUser(username, true, ['authors']); + const renamePromises = brews.map(async (brew)=>{ + const updatedAuthors = brew.authors.map((author)=>author === username ? newUsername : author + ); + return HomebrewModel.updateOne( + { _id: brew._id }, + { $set: { authors: updatedAuthors } } + ); + }); + await Promise.all(renamePromises); + + return res.json({ success: true, message: `Brews for ${username} renamed to ${newUsername}.` }); + } catch (error) { + console.error('Error renaming brews:', error); + return res.status(500).json({ error: 'Failed to rename brews.' }); + } +}); + +//Edit Page +app.get('/edit/:id', asyncHandler(getBrew('edit')), asyncHandler(async(req, res, next)=>{ + req.brew = req.brew.toObject ? req.brew.toObject() : req.brew; + + req.userThemes = await(getUsersBrewThemes(req.account?.username)); + + req.ogMeta = { ...defaultMetaTags, + title : req.brew.title || 'Untitled Brew', + description : req.brew.description || 'No description.', + image : req.brew.thumbnail || defaultMetaTags.image, + type : 'article' + }; + + sanitizeBrew(req.brew, 'edit'); + splitTextStyleAndMetadata(req.brew); + res.header('Cache-Control', 'no-cache, no-store'); //reload the latest saved brew when pressing back button, not the cached version before save. + return next(); +})); + +//New Page from ID +app.get('/new/:id', asyncHandler(getBrew('share')), asyncHandler(async(req, res, next)=>{ + sanitizeBrew(req.brew, 'share'); + splitTextStyleAndMetadata(req.brew); + const brew = { + shareId : req.brew.shareId, + title : `CLONE - ${req.brew.title}`, + text : req.brew.text, + style : req.brew.style, + renderer : req.brew.renderer, + theme : req.brew.theme, + tags : req.brew.tags, + }; + req.brew = _.defaults(brew, DEFAULT_BREW); + + req.userThemes = await(getUsersBrewThemes(req.account?.username)); + + req.ogMeta = { ...defaultMetaTags, + title : 'New', + description : 'Start crafting your homebrew on the Homebrewery!' + }; + + return next(); +})); + +//New Page +app.get('/new', asyncHandler(async(req, res, next)=>{ + req.userThemes = await(getUsersBrewThemes(req.account?.username)); + + req.ogMeta = { ...defaultMetaTags, + title : 'New', + description : 'Start crafting your homebrew on the Homebrewery!' + }; + + return next(); +})); + +//Share Page +app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{ + const { brew } = req; + req.ogMeta = { ...defaultMetaTags, + title : req.brew.title || 'Untitled Brew', + description : req.brew.description || 'No description.', + image : req.brew.thumbnail || defaultMetaTags.image, + type : 'article' + }; + + // increase visitor view count, do not include visits by author(s) + if(!brew.authors.includes(req.account?.username)){ + if(req.params.id.length > 12 && !brew._id) { + const googleId = brew.googleId; + const shareId = brew.shareId; + await GoogleActions.increaseView(googleId, shareId, 'share', brew) + .catch((err)=>{next(err);}); + } else { + await HomebrewModel.increaseView({ shareId: brew.shareId }); + } + }; + + brew.authors.includes(req.account?.username) ? sanitizeBrew(req.brew, 'shareAuthor') : sanitizeBrew(req.brew, 'share'); + splitTextStyleAndMetadata(req.brew); + return next(); +})); + +//Account Page +app.get('/account', asyncHandler(async (req, res, next)=>{ + const data = {}; + data.title = 'Account Information Page'; + + if(!req.account) { + res.set('WWW-Authenticate', 'Bearer realm="Authorization Required"'); + const error = new Error('No valid account'); + error.status = 401; + error.HBErrorCode = '50'; + error.page = data.title; + return next(error); + }; + + let auth; + let googleCount = []; + if(req.account) { + if(req.account.googleId) { + auth = await GoogleActions.authCheck(req.account, res, false); + + googleCount = await GoogleActions.listGoogleBrews(auth) + .catch((err)=>{ + console.error(err); + }); + } + + const query = { authors: req.account.username, googleId: { $exists: false } }; + const mongoCount = await HomebrewModel.countDocuments(query) + .catch((err)=>{ + mongoCount = 0; + console.log(err); + }); + + data.accountDetails = { + username : req.account.username, + issued : req.account.issued, + googleId : Boolean(req.account.googleId), + authCheck : Boolean(req.account.googleId && auth?.credentials.access_token), + mongoCount : mongoCount, + googleCount : googleCount?.length + }; + } + + req.brew = data; + + req.ogMeta = { ...defaultMetaTags, + title : `Account Page`, + description : null + }; + + return next(); +})); + +// Local only +if(isLocalEnvironment){ + // Login + app.post('/local/login', (req, res)=>{ + const username = req.body.username; + if(!username) return; + + const payload = jwt.encode({ username: username, issued: new Date }, config.get('secret')); + return res.json(payload); + }); +} + +// Add Static Local Paths +app.use('/staticImages', express.static(config.get('hb_images') && fs.existsSync(config.get('hb_images')) ? config.get('hb_images') :'staticImages')); +app.use('/staticFonts', express.static(config.get('hb_fonts') && fs.existsSync(config.get('hb_fonts')) ? config.get('hb_fonts'):'staticFonts')); + +//Vault Page +app.get('/vault', asyncHandler(async(req, res, next)=>{ + req.ogMeta = { ...defaultMetaTags, + title : 'The Vault', + description : 'Search for Brews' + }; + return next(); +})); + +//Send rendered page +app.use(asyncHandler(async (req, res, next)=>{ + if(!req.route) return res.redirect('/'); // Catch-all for invalid routes + + const page = await renderPage(req, res); + if(!page) return; + res.send(page); +})); + +//Render the page +const renderPage = async (req, res)=>{ + // Create configuration object + const configuration = { + local : isLocalEnvironment, + publicUrl : config.get('publicUrl') ?? '', + environment : nodeEnv, + deployment : config.get('heroku_app_name') ?? '' + }; + const props = { + version : version, + url : req.customUrl || req.originalUrl, + brew : req.brew, + brews : req.brews, + googleBrews : req.googleBrews, + account : req.account, + enable_v3 : config.get('enable_v3'), + enable_themes : config.get('enable_themes'), + config : configuration, + ogMeta : req.ogMeta, + userThemes : req.userThemes + }; + const title = req.brew ? req.brew.title : ''; + const page = await templateFn('homebrew', title, props) + .catch((err)=>{ + console.log(err); + }); + return page; +}; + +//v=====----- Error-Handling Middleware -----=====v// +//Format Errors as plain objects so all fields will appear in the string sent +const formatErrors = (key, value)=>{ + if(value instanceof Error) { + const error = {}; + Object.getOwnPropertyNames(value).forEach(function (key) { + error[key] = value[key]; + }); + return error; + } + return value; +}; + +const getPureError = (error)=>{ + return JSON.parse(JSON.stringify(error, formatErrors)); +}; + +app.use(async (err, req, res, next)=>{ + err.originalUrl = req.originalUrl; + console.error(err); + + if(err.originalUrl?.startsWith('/api')) { + // console.log('API error'); + res.status(err.status || err.response?.status || 500).send(err); + return; + } + + // console.log('non-API error'); + const status = err.status || err.code || 500; + + req.ogMeta = { ...defaultMetaTags, + title : 'Error Page', + description : 'Something went wrong!' + }; + req.brew = { + ...err, + title : 'Error - Something went wrong!', + text : err.errors?.map((error)=>{return error.message;}).join('\n\n') || err.message || 'Unknown error!', + status : status, + HBErrorCode : err.HBErrorCode ?? '00', + pureError : getPureError(err) + }; + req.customUrl= '/error'; + + const page = await renderPage(req, res); + if(!page) return; + res.send(page); +}); + +app.use((req, res)=>{ + if(!res.headersSent) { + console.error('Headers have not been sent, responding with a server error.', req.url); + res.status(500).send('An error occurred and the server did not send a response. The error has been logged, please note the time this occurred and report this issue.'); + } +}); +//^=====--------------------------------------=====^// + +export default app; diff --git a/server/brewDefaults.js b/server/brewDefaults.js index 62fc6e671..11a84b9e9 100644 --- a/server/brewDefaults.js +++ b/server/brewDefaults.js @@ -1,4 +1,4 @@ -const _ = require('lodash'); +import _ from 'lodash'; // Default properties for newly-created brews const DEFAULT_BREW = { @@ -32,7 +32,7 @@ const DEFAULT_BREW_LOAD = _.defaults( }, DEFAULT_BREW); -module.exports = { +export { DEFAULT_BREW, DEFAULT_BREW_LOAD }; diff --git a/server/config.js b/server/config.js index fc50e68ba..69674595b 100644 --- a/server/config.js +++ b/server/config.js @@ -1,5 +1,7 @@ -module.exports = require('nconf') - .argv() - .env({ lowerCase: true }) - .file('environment', { file: `config/${process.env.NODE_ENV}.json` }) - .file('defaults', { file: 'config/default.json' }); +import nconf from 'nconf'; + +export default nconf + .argv() + .env({ lowerCase: true }) + .file('environment', { file: `config/${process.env.NODE_ENV}.json` }) + .file('defaults', { file: 'config/default.json' }); \ No newline at end of file diff --git a/server/db.js b/server/db.js index 7690927e9..97da56a08 100644 --- a/server/db.js +++ b/server/db.js @@ -5,7 +5,7 @@ // reused by both the main application and all tests which require database // connection. -const Mongoose = require('mongoose'); +import Mongoose from 'mongoose'; const getMongoDBURL = (config)=>{ return config.get('mongodb_uri') || @@ -31,7 +31,7 @@ const connect = async (config)=>{ .catch((error)=>handleConnectionError(error)); }; -module.exports = { - connect : connect, - disconnect : disconnect +export default { + connect, + disconnect }; diff --git a/server/forcessl.mw.js b/server/forcessl.mw.js index 4f03d9c32..bf9ab65d3 100644 --- a/server/forcessl.mw.js +++ b/server/forcessl.mw.js @@ -1,4 +1,4 @@ -module.exports = (req, res, next)=>{ +export default (req, res, next)=>{ if(process.env.NODE_ENV === 'local' || process.env.NODE_ENV === 'docker') return next(); if(req.header('x-forwarded-proto') !== 'https') { return res.redirect(302, `https://${req.get('Host')}${req.url}`); diff --git a/server/googleActions.js b/server/googleActions.js index 93367248e..2c2cbac73 100644 --- a/server/googleActions.js +++ b/server/googleActions.js @@ -1,8 +1,9 @@ /* eslint-disable max-lines */ -const googleDrive = require('@googleapis/drive'); -const { nanoid } = require('nanoid'); -const token = require('./token.js'); -const config = require('./config.js'); +import googleDrive from '@googleapis/drive'; +import { nanoid } from 'nanoid'; +import token from './token.js'; +import config from './config.js'; + let serviceAuth; if(!config.get('service_account')){ @@ -25,6 +26,15 @@ if(!config.get('service_account')){ const defaultAuth = serviceAuth || config.get('google_api_key'); +const retryConfig = { + retry: 3, // Number of retry attempts + retryDelay: 100, // Initial delay in milliseconds + retryDelayMultiplier: 2, // Multiplier for exponential backoff + maxRetryDelay: 32000, // Maximum delay in milliseconds + httpMethodsToRetry: ['PATCH'], // Only retry PATCH requests + statusCodesToRetry: [[429, 429]], // Only retry on 429 status code +}; + const GoogleActions = { authCheck : (account, res, updateTokens=true)=>{ @@ -50,7 +60,7 @@ const GoogleActions = { account.googleRefreshToken = tokens.refresh_token; } account.googleAccessToken = tokens.access_token; - const JWTToken = token.generateAccessToken(account); + const JWTToken = token(account); //Save updated token to cookie //res.cookie('nc_session', JWTToken, { maxAge: 1000*60*60*24*365, path: '/', sameSite: 'lax' }); @@ -63,7 +73,7 @@ const GoogleActions = { getGoogleFolder : async (auth)=>{ const drive = googleDrive.drive({ version: 'v3', auth }); - fileMetadata = { + const fileMetadata = { 'name' : 'Homebrewery', 'mimeType' : 'application/vnd.google-apps.folder' }; @@ -112,9 +122,7 @@ const GoogleActions = { }) .catch((err)=>{ console.log(`Error Listing Google Brews`); - console.error(err); throw (err); - //TODO: Should break out here, but continues on for some reason. }); fileList.push(...obj.data.files); NextPageToken = obj.data.nextPageToken; @@ -147,7 +155,7 @@ const GoogleActions = { return brews; }, - updateGoogleBrew : async (brew)=>{ + updateGoogleBrew : async (brew, userIp)=>{ const drive = googleDrive.drive({ version: 'v3', auth: defaultAuth }); await drive.files.update({ @@ -168,11 +176,14 @@ const GoogleActions = { media : { mimeType : 'text/plain', body : brew.text - } + }, + headers: { + 'X-Forwarded-For': userIp, // Set the X-Forwarded-For header + }, + retryConfig }) .catch((err)=>{ console.log('Error saving to google'); - console.error(err); throw (err); }); @@ -211,7 +222,6 @@ const GoogleActions = { }) .catch((err)=>{ console.log('Error while creating new Google brew'); - console.error(err); throw (err); }); @@ -231,8 +241,8 @@ const GoogleActions = { return obj.data.id; }, - getGoogleBrew : async (id, accessId, accessType)=>{ - const drive = googleDrive.drive({ version: 'v3', auth: defaultAuth }); + getGoogleBrew : async (auth = defaultAuth, id, accessId, accessType)=>{ + const drive = googleDrive.drive({ version: 'v3', auth: auth }); const obj = await drive.files.get({ fileId : id, @@ -335,4 +345,4 @@ const GoogleActions = { } }; -module.exports = GoogleActions; +export default GoogleActions; diff --git a/server/homebrew.api.js b/server/homebrew.api.js index c314454e2..9a479732e 100644 --- a/server/homebrew.api.js +++ b/server/homebrew.api.js @@ -1,18 +1,20 @@ /* eslint-disable max-lines */ -const _ = require('lodash'); -const HomebrewModel = require('./homebrew.model.js').model; -const router = require('express').Router(); -const zlib = require('zlib'); -const GoogleActions = require('./googleActions.js'); -const Markdown = require('../shared/naturalcrit/markdown.js'); -const yaml = require('js-yaml'); -const asyncHandler = require('express-async-handler'); -const { nanoid } = require('nanoid'); -const { splitTextStyleAndMetadata } = require('../shared/helpers.js'); +import _ from 'lodash'; +import {model as HomebrewModel} from './homebrew.model.js'; +import express from 'express'; +import zlib from 'zlib'; +import GoogleActions from './googleActions.js'; +import Markdown from '../shared/naturalcrit/markdown.js'; +import yaml from 'js-yaml'; +import asyncHandler from 'express-async-handler'; +import { nanoid } from 'nanoid'; +import { splitTextStyleAndMetadata } from '../shared/helpers.js'; +import checkClientVersion from './middleware/check-client-version.js'; -const { DEFAULT_BREW, DEFAULT_BREW_LOAD } = require('./brewDefaults.js'); +const router = express.Router(); -const Themes = require('../themes/themes.json'); +import { DEFAULT_BREW, DEFAULT_BREW_LOAD } from './brewDefaults.js'; +import Themes from '../themes/themes.json' with { type: 'json' }; const isStaticTheme = (renderer, themeName)=>{ return Themes[renderer]?.[themeName] !== undefined; @@ -85,69 +87,85 @@ const api = { // Create middleware with the accessType passed in as part of the scope return async (req, res, next)=>{ // Get relevant IDs for the brew - const { id, googleId } = api.getId(req); + let { id, googleId } = api.getId(req); + + const accessMap = { + edit : { editId: id }, + share : { shareId: id }, + admin : { $or : [{ editId: id }, { shareId: id }] } + }; // Try to find the document in the Homebrewery database -- if it doesn't exist, that's fine. - let stub = await HomebrewModel.get(accessType === 'edit' ? { editId: id } : { shareId: id }) + let stub = await HomebrewModel.get(accessMap[accessType]) .catch((err)=>{ - if(googleId) { + if(googleId) console.warn(`Unable to find document stub for ${accessType}Id ${id}`); - } else { + else console.warn(err); - } }); stub = stub?.toObject(); + googleId ??= stub?.googleId; - if(stub?.lock?.locked && accessType != 'edit') { - throw { HBErrorCode: '100', code: stub.lock.code, message: stub.lock.shareMessage, brewId: stub.shareId, brewTitle: stub.title }; + const isOwner = (accessType == 'edit' && (!stub || stub?.authors?.length === 0)) || stub?.authors?.[0] === req.account?.username; + const isAuthor = stub?.authors?.includes(req.account?.username); + const isInvited = stub?.invitedAuthors?.includes(req.account?.username); + + if(accessType === 'edit' && !(isOwner || isAuthor || isInvited)) { + const accessError = { name: 'Access Error', status: 401, authors: stub?.authors, brewTitle: stub?.title, shareId: stub?.shareId }; + if(req.account) + throw { ...accessError, message: 'User is not an Author', HBErrorCode: '03' }; + else + throw { ...accessError, message: 'User is not logged in', HBErrorCode: '04' }; } - // If there is a google id, try to find the google brew - if(!stubOnly && (googleId || stub?.googleId)) { - let googleError; - const googleBrew = await GoogleActions.getGoogleBrew(googleId || stub?.googleId, id, accessType) - .catch((err)=>{ - googleError = err; + if(stub?.lock?.locked && accessType != 'edit') { + throw { HBErrorCode: '51', code: stub?.lock.code, message: stub?.lock.shareMessage, brewId: stub?.shareId, brewTitle: stub?.title }; + } + + // If there's a google id, get it if requesting the full brew or if no stub found yet + if(googleId && (!stubOnly || !stub)) { + const oAuth2Client = isOwner ? GoogleActions.authCheck(req.account, res) : undefined; + + const googleBrew = await GoogleActions.getGoogleBrew(oAuth2Client, googleId, id, accessType) + .catch((googleError)=>{ + const reason = googleError.errors?.[0].reason; + if(reason == 'notFound') + throw { ...googleError, HBErrorCode: '02', authors: stub?.authors, account: req.account?.username }; + else + throw { ...googleError, HBErrorCode: '01' }; }); - // Throw any error caught while attempting to retrieve Google brew. - if(googleError) { - const reason = googleError.errors?.[0].reason; - if(reason == 'notFound') { - throw { ...googleError, HBErrorCode: '02', authors: stub?.authors, account: req.account?.username }; - } else { - throw { ...googleError, HBErrorCode: '01' }; - } - } + // Combine the Homebrewery stub with the google brew, or if the stub doesn't exist just use the google brew stub = stub ? _.assign({ ...api.excludeStubProps(stub), stubbed: true }, api.excludeGoogleProps(googleBrew)) : googleBrew; } - const authorsExist = stub?.authors?.length > 0; - const isAuthor = stub?.authors?.includes(req.account?.username); - const isInvited = stub?.invitedAuthors?.includes(req.account?.username); - 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 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 - if(!stub && !stubOnly) { + if(!stub) throw { name: 'BrewLoad Error', message: 'Brew not found', status: 404, HBErrorCode: '05', accessType: accessType, brewId: id }; - } // Clean up brew: fill in missing fields with defaults / fix old invalid values - if(stub) { - stub.tags = stub.tags || undefined; // Clear empty strings - stub.renderer = stub.renderer || undefined; // Clear empty strings - stub = _.defaults(stub, DEFAULT_BREW_LOAD); // Fill in blank fields - } + stub.tags = stub.tags || undefined; // Clear empty strings + stub.renderer = stub.renderer || undefined; // Clear empty strings + stub = _.defaults(stub, DEFAULT_BREW_LOAD); // Fill in blank fields - req.brew = stub ?? {}; + req.brew = stub; next(); }; }, + + getCSS : async (req, res)=>{ + const { brew } = req; + if(!brew) return res.status(404).send(''); + splitTextStyleAndMetadata(brew); + if(!brew.style) return res.status(404).send(''); + + res.set({ + 'Cache-Control' : 'no-cache', + 'Content-Type' : 'text/css' + }); + return res.status(200).send(brew.style); + }, + mergeBrewText : (brew)=>{ let text = brew.text; if(brew.style !== undefined) { @@ -228,11 +246,8 @@ const api = { let googleId, saved; if(saveToGoogle) { - googleId = await api.newGoogleBrew(req.account, newHomebrew, res) - .catch((err)=>{ - console.error(err); - res.status(err?.status || err?.response?.status || 500).send(err?.message || err); - }); + googleId = await api.newGoogleBrew(req.account, newHomebrew, res); + if(!googleId) return; api.excludeStubProps(newHomebrew); newHomebrew.googleId = googleId; @@ -284,9 +299,8 @@ const api = { req.params.id = currentTheme.theme; req.params.renderer = currentTheme.renderer; - } + } else { //=== Static Themes ===// - else { const localSnippets = `${req.params.renderer}_${req.params.id}`; // Just log the name for loading on client const localStyle = `@import url(\"/themes/${req.params.renderer}/${req.params.id}/style.css\");`; completeSnippets.push(localSnippets); @@ -337,19 +351,13 @@ const api = { brew.googleId = undefined; } else if(!brew.googleId && saveToGoogle) { // If we don't have a google id and the user wants to save to google, create the google brew and set the google id on the brew - brew.googleId = await api.newGoogleBrew(req.account, api.excludeGoogleProps(brew), res) - .catch((err)=>{ - console.error(err); - res.status(err.status || err.response.status).send(err.message || err); - }); + brew.googleId = await api.newGoogleBrew(req.account, api.excludeGoogleProps(brew), res); + if(!brew.googleId) return; } else if(brew.googleId) { // If the google id exists and no other actions are being performed, update the google brew - const updated = await GoogleActions.updateGoogleBrew(api.excludeGoogleProps(brew)) - .catch((err)=>{ - console.error(err); - res.status(err?.response?.status || 500).send(err); - }); + const updated = await GoogleActions.updateGoogleBrew(api.excludeGoogleProps(brew), req.ip); + if(!updated) return; } @@ -459,12 +467,11 @@ const api = { } }; -router.use('/api', require('./middleware/check-client-version.js')); -router.post('/api', asyncHandler(api.newBrew)); -router.put('/api/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew)); -router.put('/api/update/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew)); -router.delete('/api/:id', asyncHandler(api.deleteBrew)); -router.get('/api/remove/:id', asyncHandler(api.deleteBrew)); +router.post('/api', checkClientVersion, asyncHandler(api.newBrew)); +router.put('/api/:id', checkClientVersion, asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew)); +router.put('/api/update/:id', checkClientVersion, asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew)); +router.delete('/api/:id', checkClientVersion, asyncHandler(api.deleteBrew)); +router.get('/api/remove/:id', checkClientVersion, asyncHandler(api.deleteBrew)); router.get('/api/theme/:renderer/:id', asyncHandler(api.getThemeBundle)); -module.exports = api; +export default api; \ No newline at end of file diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js index 5f1739b97..8270b1568 100644 --- a/server/homebrew.api.spec.js +++ b/server/homebrew.api.spec.js @@ -1,5 +1,7 @@ /* eslint-disable max-lines */ +import { splitTextStyleAndMetadata } from '../shared/helpers.js'; + describe('Tests for api', ()=>{ let api; let google; @@ -36,8 +38,9 @@ describe('Tests for api', ()=>{ } }); - google = require('./googleActions.js'); - model = require('./homebrew.model.js').model; + google = require('./googleActions.js').default; + model = require('./homebrew.model.js').model; + api = require('./homebrew.api').default; jest.mock('./googleActions.js'); google.authCheck = jest.fn(()=>'client'); @@ -50,11 +53,10 @@ describe('Tests for api', ()=>{ res = { status : jest.fn(()=>res), send : jest.fn(()=>{}), + set : jest.fn(()=>{}), setHeader : jest.fn(()=>{}) }; - api = require('./homebrew.api'); - hbBrew = { text : `brew text`, style : 'hello yes i am css', @@ -296,7 +298,7 @@ describe('Tests for api', ()=>{ expect(next).toHaveBeenCalled(); expect(api.getId).toHaveBeenCalledWith(req); expect(model.get).toHaveBeenCalledWith({ shareId: '1' }); - expect(google.getGoogleBrew).toHaveBeenCalledWith('2', '1', 'share'); + expect(google.getGoogleBrew).toHaveBeenCalledWith(undefined, '2', '1', 'share'); }); it('access is denied to a locked brew', async()=>{ @@ -308,7 +310,7 @@ describe('Tests for api', ()=>{ const req = { brew: {} }; const next = jest.fn(); - await expect(fn(req, null, next)).rejects.toEqual({ 'HBErrorCode': '100', 'brewId': '1', 'brewTitle': 'test brew', 'code': 404, 'message': 'brew locked' }); + await expect(fn(req, null, next)).rejects.toEqual({ 'HBErrorCode': '51', 'brewId': '1', 'brewTitle': 'test brew', 'code': 404, 'message': 'brew locked' }); }); }); @@ -559,16 +561,6 @@ brew`); views : 0 }); }); - - it('should handle google error', async()=>{ - google.newGoogleBrew = jest.fn(()=>{ - throw 'err'; - }); - await api.newBrew({ body: { text: 'asdf', title: '' }, query: { saveToGoogle: true }, account: { username: 'test user' } }, res); - - expect(res.status).toHaveBeenCalledWith(500); - expect(res.send).toHaveBeenCalledWith('err'); - }); }); describe('deleteGoogleBrew', ()=>{ @@ -916,4 +908,119 @@ brew`); expect(saved.googleId).toEqual(brew.googleId); }); }); + describe('Get CSS', ()=>{ + it('should return brew style content as CSS text', async ()=>{ + const testBrew = { title: 'test brew', text: '```css\n\nI Have a style!\n````\n\n' }; + + const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew })); + api.getId = jest.fn(()=>({ id: '1', googleId: undefined })); + model.get = jest.fn(()=>toBrewPromise(testBrew)); + + const fn = api.getBrew('share', true); + const req = { brew: {} }; + const next = jest.fn(); + await fn(req, null, next); + await api.getCSS(req, res); + + expect(req.brew).toEqual(testBrew); + expect(req.brew).toHaveProperty('style', '\nI Have a style!\n'); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.send).toHaveBeenCalledWith('\nI Have a style!\n'); + expect(res.set).toHaveBeenCalledWith({ + 'Cache-Control' : 'no-cache', + 'Content-Type' : 'text/css' + }); + }); + + it('should return 404 when brew has no style content', async ()=>{ + const testBrew = { title: 'test brew', text: 'I don\'t have a style!' }; + + const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew })); + api.getId = jest.fn(()=>({ id: '1', googleId: undefined })); + model.get = jest.fn(()=>toBrewPromise(testBrew)); + + const fn = api.getBrew('share', true); + const req = { brew: {} }; + const next = jest.fn(); + await fn(req, null, next); + await api.getCSS(req, res); + + expect(req.brew).toEqual(testBrew); + expect(req.brew).toHaveProperty('style'); + expect(res.status).toHaveBeenCalledWith(404); + expect(res.send).toHaveBeenCalledWith(''); + }); + + it('should return 404 when brew does not exist', async ()=>{ + const testBrew = { }; + + const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew })); + api.getId = jest.fn(()=>({ id: '1', googleId: undefined })); + model.get = jest.fn(()=>toBrewPromise(testBrew)); + + const fn = api.getBrew('share', true); + const req = { brew: {} }; + const next = jest.fn(); + await fn(req, null, next); + await api.getCSS(req, res); + + expect(req.brew).toEqual(testBrew); + expect(req.brew).toHaveProperty('style'); + expect(res.status).toHaveBeenCalledWith(404); + expect(res.send).toHaveBeenCalledWith(''); + }); + }); + describe('Split Text, Style, and Metadata', ()=>{ + + it('basic splitting', async ()=>{ + const testBrew = { + text : '```metadata\n' + + 'title: title\n' + + 'description: description\n' + + 'tags: [ \'tag a\' , \'tag b\' ]\n' + + 'systems: [ test system ]\n' + + 'renderer: legacy\n' + + 'theme: 5ePHB\n' + + 'lang: en\n' + + '\n' + + '```\n' + + '\n' + + '```css\n' + + 'style\n' + + 'style\n' + + 'style\n' + + '```\n' + + '\n' + + 'text\n' + }; + + splitTextStyleAndMetadata(testBrew); + + // Metadata + expect(testBrew.title).toEqual('title'); + expect(testBrew.description).toEqual('description'); + expect(testBrew.tags).toEqual(['tag a', 'tag b']); + expect(testBrew.systems).toEqual(['test system']); + expect(testBrew.renderer).toEqual('legacy'); + expect(testBrew.theme).toEqual('5ePHB'); + expect(testBrew.lang).toEqual('en'); + // Style + expect(testBrew.style).toEqual('style\nstyle\nstyle'); + // Text + expect(testBrew.text).toEqual('text\n'); + }); + + it('convert tags string to array', async ()=>{ + const testBrew = { + text : '```metadata\n' + + 'tags: tag a\n' + + '```\n\n' + }; + + splitTextStyleAndMetadata(testBrew); + + // Metadata + expect(testBrew.tags).toEqual(['tag a']); + }); + }); }); diff --git a/server/homebrew.model.js b/server/homebrew.model.js index c8db8fdcc..adeac0d5d 100644 --- a/server/homebrew.model.js +++ b/server/homebrew.model.js @@ -1,7 +1,8 @@ -const mongoose = require('mongoose'); -const { nanoid } = require('nanoid'); -const _ = require('lodash'); -const zlib = require('zlib'); +import mongoose from 'mongoose'; +import { nanoid } from 'nanoid'; +import _ from 'lodash'; +import zlib from 'zlib'; + const HomebrewSchema = mongoose.Schema({ shareId : { type: String, default: ()=>{return nanoid(12);}, index: { unique: true } }, @@ -44,7 +45,7 @@ HomebrewSchema.statics.get = async function(query, fields=null){ const brew = await Homebrew.findOne(query, fields).orFail() .catch((error)=>{throw 'Can not find brew';}); if(!_.isNil(brew.textBin)) { // Uncompress zipped text field - unzipped = zlib.inflateRawSync(brew.textBin); + const unzipped = zlib.inflateRawSync(brew.textBin); brew.text = unzipped.toString(); } return brew; @@ -62,7 +63,7 @@ HomebrewSchema.statics.getByUser = async function(username, allowAccess=false, f const Homebrew = mongoose.model('Homebrew', HomebrewSchema); -module.exports = { - schema : HomebrewSchema, - model : Homebrew, +export { + HomebrewSchema as schema, + Homebrew as model }; diff --git a/server/middleware/check-client-version.js b/server/middleware/check-client-version.js index e9caf6eff..45d868a5a 100644 --- a/server/middleware/check-client-version.js +++ b/server/middleware/check-client-version.js @@ -1,8 +1,10 @@ -module.exports = (req, res, next)=>{ - const userVersion = req.get('Homebrewery-Version'); - const version = require('../../package.json').version; +import packageJSON from '../../package.json' with { type: 'json' }; - if(userVersion != version) { +export default (req, res, next)=>{ + const userVersion = req.get('Homebrewery-Version'); + const version = packageJSON.version; + + if(userVersion !== version) { return res.status(412).send({ message : `Client version ${userVersion} is out of date. Please save your changes elsewhere and refresh to pick up client version ${version}.` }); @@ -10,3 +12,4 @@ module.exports = (req, res, next)=>{ next(); }; + diff --git a/server/middleware/content-negotiation.js b/server/middleware/content-negotiation.js index 201e64a25..9625a5826 100644 --- a/server/middleware/content-negotiation.js +++ b/server/middleware/content-negotiation.js @@ -1,12 +1,16 @@ -module.exports = (req, res, next)=>{ +import config from '../config.js'; +const nodeEnv = config.get('node_env'); +const isLocalEnvironment = config.get('local_environments').includes(nodeEnv); + +export default (req, res, next)=>{ const isImageRequest = req.get('Accept')?.split(',') ?.filter((h)=>!h.includes('q=')) ?.every((h)=>/image\/.*/.test(h)); - if(isImageRequest) { + if(isImageRequest && !(isLocalEnvironment && req.url?.startsWith('/staticImages'))) { return res.status(406).send({ message : 'Request for image at this URL is not supported' }); } next(); -}; \ No newline at end of file +}; diff --git a/server/middleware/content-negotiation.spec.js b/server/middleware/content-negotiation.spec.js index 68f22eb1c..80b9e515d 100644 --- a/server/middleware/content-negotiation.spec.js +++ b/server/middleware/content-negotiation.spec.js @@ -1,41 +1,41 @@ -const contentNegotiationMiddleware = require('./content-negotiation.js'); - -describe('content-negotiation-middleware', ()=>{ - let request; - let response; - let next; - - beforeEach(()=>{ - request = { - get : function(key) { - return this[key]; - } - }; - response = { - status : jest.fn(()=>response), - send : jest.fn(()=>{}) - }; - next = jest.fn(); - }); - - it('should return 406 on image request', ()=>{ - contentNegotiationMiddleware({ - Accept : 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8', - ...request - }, response); - - expect(response.status).toHaveBeenLastCalledWith(406); - expect(response.send).toHaveBeenCalledWith({ - message : 'Request for image at this URL is not supported' - }); - }); - - it('should call next on non-image request', ()=>{ - contentNegotiationMiddleware({ - Accept : 'text,image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8', - ...request - }, response, next); - - expect(next).toHaveBeenCalled(); - }); -}); \ No newline at end of file +import contentNegotiationMiddleware from './content-negotiation.js'; + +describe('content-negotiation-middleware', ()=>{ + let request; + let response; + let next; + + beforeEach(()=>{ + request = { + get : function(key) { + return this[key]; + } + }; + response = { + status : jest.fn(()=>response), + send : jest.fn(()=>{}) + }; + next = jest.fn(); + }); + + it('should return 406 on image request', ()=>{ + contentNegotiationMiddleware({ + Accept : 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8', + ...request + }, response); + + expect(response.status).toHaveBeenLastCalledWith(406); + expect(response.send).toHaveBeenCalledWith({ + message : 'Request for image at this URL is not supported' + }); + }); + + it('should call next on non-image request', ()=>{ + contentNegotiationMiddleware({ + Accept : 'text,image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8', + ...request + }, response, next); + + expect(next).toHaveBeenCalled(); + }); +}); diff --git a/server/notifications.model.js b/server/notifications.model.js new file mode 100644 index 000000000..723609bb5 --- /dev/null +++ b/server/notifications.model.js @@ -0,0 +1,62 @@ +import mongoose from 'mongoose'; +import _ from 'lodash'; + +const NotificationSchema = new mongoose.Schema({ + dismissKey : { type: String, unique: true, required: true }, + title : { type: String, default: '' }, + text : { type: String, default: '' }, + createdAt : { type: Date, default: Date.now }, + startAt : { type: Date, default: Date.now }, + stopAt : { type: Date, default: Date.now }, +}, { versionKey: false }); + +NotificationSchema.statics.addNotification = async function(data) { + if(!data.dismissKey) throw { message: 'Dismiss key is required!' }; + + const defaults = { + title : '', + text : '', + startAt : new Date(), + stopAt : new Date(), + }; + + const notificationData = _.defaults(data, defaults); + + try { + const newNotification = new this(notificationData); + const savedNotification = await newNotification.save(); + return savedNotification; + } catch (err) { + throw { message: err.message || 'Error saving notification' }; + } +}; + +NotificationSchema.statics.deleteNotification = async function(dismissKey) { + if(!dismissKey) throw { message: 'Dismiss key is required!' }; + + try { + const deletedNotification = await this.findOneAndDelete({ dismissKey }).exec(); + if(!deletedNotification) { + throw { message: 'Notification not found' }; + } + return deletedNotification; + } catch (err) { + throw { message: err.message || 'Error deleting notification' }; + } +}; + +NotificationSchema.statics.getAll = async function() { + try { + const notifications = await this.find().exec(); + return notifications; + } catch (err) { + throw { message: err.message || 'Error retrieving notifications' }; + } +}; + +const Notification = mongoose.model('Notification', NotificationSchema); + +export { + NotificationSchema as schema, + Notification as model +}; diff --git a/server/static-assets.mv.js b/server/static-assets.mv.js index 732420d46..adf606689 100644 --- a/server/static-assets.mv.js +++ b/server/static-assets.mv.js @@ -1,4 +1,4 @@ -const expressStaticGzip = require('express-static-gzip'); +import expressStaticGzip from 'express-static-gzip'; // Serve brotli-compressed static files if available const customCacheControlHandler=(response, path)=>{ @@ -28,4 +28,4 @@ const init=(pathToAssets)=>{ } }); }; -module.exports = init; +export default init; diff --git a/server/token.js b/server/token.js index 70d6e01c5..7a23dff4b 100644 --- a/server/token.js +++ b/server/token.js @@ -1,7 +1,5 @@ -const jwt = require('jwt-simple'); - -// Load configuration values -const config = require('./config.js'); +import jwt from 'jwt-simple'; +import config from './config.js'; // Generate an Access Token for the given User ID const generateAccessToken = (account)=>{ @@ -24,6 +22,4 @@ const generateAccessToken = (account)=>{ return token; }; -module.exports = { - generateAccessToken : generateAccessToken -}; +export default generateAccessToken; \ No newline at end of file diff --git a/server/vault.api.js b/server/vault.api.js new file mode 100644 index 000000000..6a7b9fb91 --- /dev/null +++ b/server/vault.api.js @@ -0,0 +1,109 @@ +import express from 'express'; +import asyncHandler from 'express-async-handler'; +import {model as HomebrewModel } from './homebrew.model.js'; + +const router = express.Router(); + +const titleConditions = (title)=>{ + if(!title) return {}; + return { + $text : { + $search : title, + $caseSensitive : false, + }, + }; +}; + +const authorConditions = (author)=>{ + if(!author) return {}; + return { authors: author }; +}; + +const rendererConditions = (legacy, v3)=>{ + if(legacy === 'true' && v3 !== 'true') + return { renderer: 'legacy' }; + + if(v3 === 'true' && legacy !== 'true') + return { renderer: 'V3' }; + + return {}; // If all renderers selected, renderer field not needed in query for speed +}; + +const sortConditions = (sort, dir) => { + return { [sort]: dir === 'asc' ? 1 : -1 }; +}; + +const findBrews = async (req, res)=>{ + const title = req.query.title || ''; + const author = req.query.author || ''; + const page = Math.max(parseInt(req.query.page) || 1, 1); + const count = Math.max(parseInt(req.query.count) || 20, 10); + const skip = (page - 1) * count; + const sort = req.query.sort || 'title'; + const dir = req.query.dir || 'asc'; + + const combinedQuery = { + $and : [ + { published: true }, + rendererConditions(req.query.legacy, req.query.v3), + titleConditions(title), + authorConditions(author) + ], + }; + + const projection = { + editId : 0, + googleId : 0, + text : 0, + textBin : 0, + version : 0 + }; + + await HomebrewModel.find(combinedQuery, projection) + .sort(sortConditions(sort, dir)) + .skip(skip) + .limit(count) + .maxTimeMS(5000) + .exec() + .then((brews)=>{ + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + + const processedBrews = brews.map((brew)=>{ + brew.authors = brew.authors.map((author)=>emailRegex.test(author) ? 'hidden' : author + ); + return brew; + }); + res.json({ brews: processedBrews, page }); + }) + .catch((error)=>{ + throw { ...error, message: 'Error finding brews in Vault search', HBErrorCode: 90 }; + }); +}; + +const findTotal = async (req, res)=>{ + const title = req.query.title || ''; + const author = req.query.author || ''; + + const combinedQuery = { + $and : [ + { published: true }, + rendererConditions(req.query.legacy, req.query.v3), + titleConditions(title), + authorConditions(author) + ], + }; + + await HomebrewModel.countDocuments(combinedQuery) + .then((totalBrews)=>{ + console.log(`when returning, the total of brews is ${totalBrews} for the query ${JSON.stringify(combinedQuery)}`); + res.json({ totalBrews }); + }) + .catch((error)=>{ + throw { ...error, message: 'Error finding brews in Vault search findTotal function', HBErrorCode: 91 }; + }); +}; + +router.get('/api/vault/total', asyncHandler(findTotal)); +router.get('/api/vault', asyncHandler(findBrews)); + +export default router; diff --git a/shared/helpers.js b/shared/helpers.js index ac684b06f..b2190cdcd 100644 --- a/shared/helpers.js +++ b/shared/helpers.js @@ -1,6 +1,6 @@ -const _ = require('lodash'); -const yaml = require('js-yaml'); -const request = require('../client/homebrew/utils/request-middleware.js'); +import _ from 'lodash'; +import yaml from 'js-yaml'; +import request from '../client/homebrew/utils/request-middleware.js'; const splitTextStyleAndMetadata = (brew)=>{ brew.text = brew.text.replaceAll('\r\n', '\n'); @@ -21,6 +21,9 @@ const splitTextStyleAndMetadata = (brew)=>{ brew.snippets = brew.text.slice(11, index - 1); brew.text = brew.text.slice(index + 5); } + + // Handle old brews that still have empty strings in the tags metadata + if(typeof brew.tags === 'string') brew.tags = brew.tags ? [brew.tags] : []; }; const printCurrentBrew = ()=>{ @@ -51,7 +54,7 @@ const fetchThemeBundle = async (obj, renderer, theme)=>{ })); }; -module.exports = { +export { splitTextStyleAndMetadata, printCurrentBrew, fetchThemeBundle, diff --git a/shared/naturalcrit/codeEditor/autocompleteEmoji.js b/shared/naturalcrit/codeEditor/autocompleteEmoji.js index fae373115..c7efa172b 100644 --- a/shared/naturalcrit/codeEditor/autocompleteEmoji.js +++ b/shared/naturalcrit/codeEditor/autocompleteEmoji.js @@ -1,7 +1,7 @@ -const diceFont = require('../../../themes/fonts/iconFonts/diceFont.js'); -const elderberryInn = require('../../../themes/fonts/iconFonts/elderberryInn.js'); -const fontAwesome = require('../../../themes/fonts/iconFonts/fontAwesome.js'); -const gameIcons = require('../../../themes/fonts/iconFonts/gameIcons.js'); +import diceFont from '../../../themes/fonts/iconFonts/diceFont.js'; +import elderberryInn from '../../../themes/fonts/iconFonts/elderberryInn.js'; +import fontAwesome from '../../../themes/fonts/iconFonts/fontAwesome.js'; +import gameIcons from '../../../themes/fonts/iconFonts/gameIcons.js'; const emojis = { ...diceFont, diff --git a/shared/naturalcrit/codeEditor/codeEditor.jsx b/shared/naturalcrit/codeEditor/codeEditor.jsx index 3186e39f1..fb69b6dcf 100644 --- a/shared/naturalcrit/codeEditor/codeEditor.jsx +++ b/shared/naturalcrit/codeEditor/codeEditor.jsx @@ -397,6 +397,11 @@ const CodeEditor = createClass({ getCursorPosition : function(){ return this.codeMirror.getCursor(); }, + getTopVisibleLine : function(){ + const rect = this.codeMirror.getWrapperElement().getBoundingClientRect(); + const topVisibleLine = this.codeMirror.lineAtHeight(rect.top, 'window'); + return topVisibleLine; + }, updateSize : function(){ this.codeMirror.refresh(); }, diff --git a/shared/naturalcrit/codeEditor/codeEditor.less b/shared/naturalcrit/codeEditor/codeEditor.less index cb73b0a88..84a5c63f1 100644 --- a/shared/naturalcrit/codeEditor/codeEditor.less +++ b/shared/naturalcrit/codeEditor/codeEditor.less @@ -11,49 +11,54 @@ @import (less) './themes/fonts/iconFonts/fontAwesome.less'; @keyframes sourceMoveAnimation { - 50% {background-color: red; color: white;} - 100% {background-color: unset; color: unset;} + 50% { color : white;background-color : red;} + 100% { color : unset;background-color : unset;} } -.codeEditor{ - @media screen and (pointer : coarse) { - font-size : 16px; - } - .CodeMirror-foldmarker { - font-family: inherit; - text-shadow: none; - font-weight: 600; - color: grey; - } +.codeEditor { + @media screen and (pointer : coarse) { + font-size : 16px; + } + .CodeMirror-foldmarker { + font-family : inherit; + font-weight : 600; + color : grey; + text-shadow : none; + } - .sourceMoveFlash .CodeMirror-line{ - animation-name: sourceMoveAnimation; - animation-duration: 0.4s; - } + .CodeMirror-foldgutter { + cursor : pointer; + border-left : 1px solid #EEEEEE; + transition : background 0.1s; + &:hover { background : #DDDDDD; } + } - .CodeMirror-vscrollbar { - &::-webkit-scrollbar { - width: 20px; - } - &::-webkit-scrollbar-thumb { - width: 20px; - background: linear-gradient(90deg, #858585 15px, #808080 15px); - } - } + .sourceMoveFlash .CodeMirror-line { + animation-name : sourceMoveAnimation; + animation-duration : 0.4s; + } + + .CodeMirror-vscrollbar { + &::-webkit-scrollbar { width : 20px; } + &::-webkit-scrollbar-thumb { + width : 20px; + background : linear-gradient(90deg, #858585 15px, #808080 15px); + } + } - //.cm-tab { - // background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAQAAACOs/baAAAARUlEQVR4nGJgIAG8JkXxUAcCtDWemcGR1lY4MvgzCEKY7jSBjgxBDAG09UEQzAe0AMwMHrSOAwEGRtpaMIwAAAAA//8DAG4ID9EKs6YqAAAAAElFTkSuQmCC) no-repeat right; - //} + //.cm-tab { + // background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAQAAACOs/baAAAARUlEQVR4nGJgIAG8JkXxUAcCtDWemcGR1lY4MvgzCEKY7jSBjgxBDAG09UEQzAe0AMwMHrSOAwEGRtpaMIwAAAAA//8DAG4ID9EKs6YqAAAAAElFTkSuQmCC) no-repeat right; + //} - //.cm-trailingspace { - // .cm-space { - // background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQAgMAAABW5NbuAAAACVBMVEVHcEwAAAAAAAAWawmTAAAAA3RSTlMAPBJ6PMxpAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAFUlEQVQI12NgwACcCQysASAEZGAAACMuAX06aCQUAAAAAElFTkSuQmCC) no-repeat right; - // } - //} + //.cm-trailingspace { + // .cm-space { + // background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAQAgMAAABW5NbuAAAACVBMVEVHcEwAAAAAAAAWawmTAAAAA3RSTlMAPBJ6PMxpAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAFUlEQVQI12NgwACcCQysASAEZGAAACMuAX06aCQUAAAAAElFTkSuQmCC) no-repeat right; + // } + //} } .emojiPreview { - font-size: 1.5em; - line-height: 1.2em; + font-size : 1.5em; + line-height : 1.2em; } \ No newline at end of file diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js index 2ee99f72b..99766b536 100644 --- a/shared/naturalcrit/markdown.js +++ b/shared/naturalcrit/markdown.js @@ -1,19 +1,20 @@ +/* eslint-disable max-depth */ /* eslint-disable max-lines */ -const _ = require('lodash'); -const Marked = require('marked'); -const MarkedExtendedTables = require('marked-extended-tables'); -const { markedSmartypantsLite: MarkedSmartypantsLite } = require('marked-smartypants-lite'); -const { gfmHeadingId: MarkedGFMHeadingId } = require('marked-gfm-heading-id'); -const { markedEmoji: MarkedEmojis } = require('marked-emoji'); +import _ from 'lodash'; +import { Parser as MathParser } from 'expr-eval'; +import { marked as Marked } from 'marked'; +import MarkedExtendedTables from 'marked-extended-tables'; +import { markedSmartypantsLite as MarkedSmartypantsLite } from 'marked-smartypants-lite'; +import { gfmHeadingId as MarkedGFMHeadingId, resetHeadings as MarkedGFMResetHeadingIDs } from 'marked-gfm-heading-id'; +import { markedEmoji as MarkedEmojis } from 'marked-emoji'; //Icon fonts included so they can appear in emoji autosuggest dropdown -const diceFont = require('../../themes/fonts/iconFonts/diceFont.js'); -const elderberryInn = require('../../themes/fonts/iconFonts/elderberryInn.js'); -const fontAwesome = require('../../themes/fonts/iconFonts/fontAwesome.js'); -const gameIcons = require('../../themes/fonts/iconFonts/gameIcons.js'); +import diceFont from '../../themes/fonts/iconFonts/diceFont.js'; +import elderberryInn from '../../themes/fonts/iconFonts/elderberryInn.js'; +import gameIcons from '../../themes/fonts/iconFonts/gameIcons.js'; +import fontAwesome from '../../themes/fonts/iconFonts/fontAwesome.js'; -const MathParser = require('expr-eval').Parser; -const renderer = new Marked.Renderer(); +const renderer = new Marked.Renderer(); const tokenizer = new Marked.Tokenizer(); //Limit math features to simple items @@ -102,6 +103,20 @@ renderer.link = function (href, title, text) { return out; }; +// Expose `src` attribute as `--HB_src` to make the URL accessible via CSS +renderer.image = function (href, title, text) { + href = cleanUrl(href); + if(href === null) + return text; + + let out = `${text}`${key}="${value}"`).join(' ')}` : ''}` + + `${tags.styles ? ` style="${Object.entries(tags.styles).map(([key, value])=>`${key}:${value};`).join(' ')}"` : ''}` + + `${tags.attributes ? ` ${Object.entries(tags.attributes).map(([key, value])=>`${key}="${value}"`).join(' ')}` : ''}` + `>${this.parser.parseInline(token.tokens)}`; // parseInline to turn child tokens into HTML } }; @@ -214,7 +229,7 @@ const mustacheDivs = { return ``${key}="${value}"`).join(' ')}` : ''}` + `>${this.parser.parse(token.tokens)}
    `; // parse to turn child tokens into HTML } @@ -251,18 +266,13 @@ const mustacheInjectInline = { const text = this.parser.parseInline([token]); 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 tags = mergeHTMLTags(originalTags, injectedTags); const openingTag = /(<[^\s<>]+)[^\n<>]*(>.*)/s.exec(text); if(openingTag) { return `${openingTag[1]}` + `${tags.classes ? ` class="${tags.classes}"` : ''}` + `${tags.id ? ` id="${tags.id}"` : ''}` + - `${tags.styles ? ` style="${tags.styles}"` : ''}` + + `${!_.isEmpty(tags.styles) ? ` style="${Object.entries(tags.styles).map(([key, value])=>`${key}:${value};`).join(' ')}"` : ''}` + `${!_.isEmpty(tags.attributes) ? ` ${Object.entries(tags.attributes).map(([key, value])=>`${key}="${value}"`).join(' ')}` : ''}` + `${openingTag[2]}`; // parse to turn child tokens into HTML } @@ -300,18 +310,13 @@ const mustacheInjectBlock = { const text = this.parser.parse([token]); 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 tags = mergeHTMLTags(originalTags, injectedTags); const openingTag = /(<[^\s<>]+)[^\n<>]*(>.*)/s.exec(text); if(openingTag) { return `${openingTag[1]}` + `${tags.classes ? ` class="${tags.classes}"` : ''}` + `${tags.id ? ` id="${tags.id}"` : ''}` + - `${tags.styles ? ` style="${tags.styles}"` : ''}` + + `${!_.isEmpty(tags.styles) ? ` style="${Object.entries(tags.styles).map(([key, value])=>`${key}:${value};`).join(' ')}"` : ''}` + `${!_.isEmpty(tags.attributes) ? ` ${Object.entries(tags.attributes).map(([key, value])=>`${key}="${value}"`).join(' ')}` : ''}` + `${openingTag[2]}`; // parse to turn child tokens into HTML } @@ -356,6 +361,43 @@ const superSubScripts = { } }; + +const justifiedParagraphClasses = []; +justifiedParagraphClasses[2] = 'Left'; +justifiedParagraphClasses[4] = 'Right'; +justifiedParagraphClasses[6] = 'Center'; + +const justifiedParagraphs = { + name : 'justifiedParagraphs', + level : 'block', + start(src) { + return src.match(/\n(?:-:|:-|-:) {1}/m)?.index; + }, // Hint to Marked.js to stop and check for a match + tokenizer(src, tokens) { + const regex = /^(((:-))|((-:))|((:-:))) .+(\n(([^\n].*\n)*(\n|$))|$)/ygm; + const match = regex.exec(src); + if(match?.length) { + let whichJustify; + if(match[2]?.length) whichJustify = 2; + if(match[4]?.length) whichJustify = 4; + if(match[6]?.length) whichJustify = 6; + return { + type : 'justifiedParagraphs', // Should match "name" above + raw : match[0], // Text to consume from the source + length : match[whichJustify].length, + text : match[0].slice(match[whichJustify].length), + class : justifiedParagraphClasses[whichJustify], + tokens : this.lexer.inlineTokens(match[0].slice(match[whichJustify].length + 1)) + }; + } + }, + renderer(token) { + return `

    ${this.parser.parseInline(token.tokens)}

    `; + } + +}; + + const forcedParagraphBreaks = { name : 'hardBreaks', level : 'block', @@ -377,10 +419,31 @@ const forcedParagraphBreaks = { } }; +const nonbreakingSpaces = { + name : 'nonbreakingSpaces', + level : 'inline', + start(src) { return src.match(/:>+/m)?.index; }, // Hint to Marked.js to stop and check for a match + tokenizer(src, tokens) { + const regex = /:(>+)/ym; + const match = regex.exec(src); + if(match?.length) { + return { + type : 'nonbreakingSpaces', // Should match "name" above + raw : match[0], // Text to consume from the source + length : match[1].length, + text : '' + }; + } + }, + renderer(token) { + return ` `.repeat(token.length).concat(''); + } +}; + const definitionListsSingleLine = { name : 'definitionListsSingleLine', level : 'block', - start(src) { return src.match(/\n[^\n]*?::[^\n]*/m)?.index; }, // Hint to Marked.js to stop and check for a match + start(src) { return src.match(/\n[^\n]*?::[^\n]*/m)?.index; }, // Hint to Marked.js to stop and check for a match tokenizer(src, tokens) { const regex = /^([^\n]*?)::([^\n]*)(?:\n|$)/ym; let match; @@ -645,7 +708,7 @@ function MarkedVariables() { } if(match[8]) { // Inline Definition const label = match[10] ? match[10].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space - let content = match[11] ? match[11].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space + let content = match[11] || null; // In case of nested (), find the correct matching end ) let level = 0; @@ -661,10 +724,8 @@ function MarkedVariables() { break; } } - if(i > -1) { - combinedRegex.lastIndex = combinedRegex.lastIndex - (content.length - i); - content = content.slice(0, i).trim().replace(/\s+/g, ' '); - } + combinedRegex.lastIndex = combinedRegex.lastIndex - (content.length - i); + content = content.slice(0, i).trim().replace(/\s+/g, ' '); varsQueue.push( { type : 'varDefBlock', @@ -727,20 +788,27 @@ const MarkedEmojiOptions = { renderer : (token)=>`` }; +const tableTerminators = [ + `:+\\n`, // hardBreak + ` *{[^\n]+}`, // blockInjector + ` *{{[^{\n]*\n.*?\n}}` // mustacheDiv +]; + Marked.use(MarkedVariables()); -Marked.use({ extensions : [definitionListsMultiLine, definitionListsSingleLine, forcedParagraphBreaks, superSubScripts, - mustacheSpans, mustacheDivs, mustacheInjectInline] }); +Marked.use({ extensions : [justifiedParagraphs, definitionListsMultiLine, definitionListsSingleLine, forcedParagraphBreaks, + nonbreakingSpaces, superSubScripts, mustacheSpans, mustacheDivs, mustacheInjectInline] }); Marked.use(mustacheInjectBlock); Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false }); -Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions)); +Marked.use(MarkedExtendedTables(tableTerminators), MarkedGFMHeadingId({ globalSlugs: true }), + MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions)); function cleanUrl(href) { - try { - href = encodeURI(href).replace(/%25/g, '%'); - } catch { - return null; - } - return href; + try { + href = encodeURI(href).replace(/%25/g, '%'); + } catch { + return null; + } + return href; } const escapeTest = /[&<>"']/; @@ -793,15 +861,20 @@ const processStyleTags = (string)=>{ const index = attr.indexOf('='); let [key, value] = [attr.substring(0, index), attr.substring(index + 1)]; value = value.replace(/"/g, ''); - obj[key] = value; + obj[key.trim()] = value.trim(); return obj; }, {}) || null; - const styles = tags?.length ? tags.map((tag)=>tag.replace(/:"?([^"]*)"?/g, ':$1;').trim()).join(' ') : null; + const styles = tags?.length ? tags.reduce((styleObj, style) => { + const index = style.indexOf(':'); + const [key, value] = [style.substring(0, index), style.substring(index + 1)]; + styleObj[key.trim()] = value.replace(/"?([^"]*)"?/g, '$1').trim(); + return styleObj; + }, {}) : null; return { id : id, classes : classes, - styles : styles, + styles : _.isEmpty(styles) ? null : styles, attributes : _.isEmpty(attributes) ? null : attributes }; }; @@ -811,37 +884,56 @@ const extractHTMLStyleTags = (htmlString)=>{ const firstElementOnly = htmlString.split('>')[0]; const id = firstElementOnly.match(/id="([^"]*)"/)?.[1] || null; const classes = firstElementOnly.match(/class="([^"]*)"/)?.[1] || null; - const styles = firstElementOnly.match(/style="([^"]*)"/)?.[1] || null; + const styles = firstElementOnly.match(/style="([^"]*)"/)?.[1] + ?.split(';').reduce((styleObj, style) => { + if (style.trim() === '') return styleObj; + const index = style.indexOf(':'); + const [key, value] = [style.substring(0, index), style.substring(index + 1)]; + styleObj[key.trim()] = value.trim(); + return styleObj; + }, {}) || null; const attributes = firstElementOnly.match(/[a-zA-Z]+="[^"]*"/g) ?.filter((attr)=>!attr.startsWith('class="') && !attr.startsWith('style="') && !attr.startsWith('id="')) .reduce((obj, attr)=>{ const index = attr.indexOf('='); let [key, value] = [attr.substring(0, index), attr.substring(index + 1)]; - value = value.replace(/"/g, ''); - obj[key] = value; + obj[key.trim()] = value.replace(/"/g, ''); return obj; }, {}) || null; return { id : id, classes : classes, - styles : styles, + styles : _.isEmpty(styles) ? null : styles, attributes : _.isEmpty(attributes) ? null : attributes }; }; +const mergeHTMLTags = (originalTags, newTags) => { + return { + id : newTags.id || originalTags.id || null, + classes : [originalTags.classes, newTags.classes].join(' ').trim() || null, + styles : Object.assign(originalTags.styles ?? {}, newTags.styles ?? {}), + attributes : Object.assign(originalTags.attributes ?? {}, newTags.attributes ?? {}) + }; +}; + const globalVarsList = {}; let varsQueue = []; let globalPageNumber = 0; -module.exports = { +const Markdown = { marked : Marked, - render : (rawBrewText, pageNumber=1)=>{ - globalVarsList[pageNumber] = {}; //Reset global links for current page, to ensure values are parsed in order - varsQueue = []; //Could move into MarkedVariables() - globalPageNumber = pageNumber; + render : (rawBrewText, pageNumber=0)=>{ + globalVarsList[pageNumber] = {}; //Reset global links for current page, to ensure values are parsed in order + varsQueue = []; //Could move into MarkedVariables() + globalPageNumber = pageNumber; + if(pageNumber==0) { + MarkedGFMResetHeadingIDs(); + } rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n
    \n`); + const opts = Marked.defaults; rawBrewText = opts.hooks.preprocess(rawBrewText); @@ -912,3 +1004,6 @@ module.exports = { return errors; }, }; + +export default Markdown; + diff --git a/shared/naturalcrit/nav/nav.jsx b/shared/naturalcrit/nav/nav.jsx index a4682aeab..d9b403239 100644 --- a/shared/naturalcrit/nav/nav.jsx +++ b/shared/naturalcrit/nav/nav.jsx @@ -12,10 +12,8 @@ const Nav = { displayName : 'Nav.base', render : function(){ return ; + ; } }), logo : function(){ diff --git a/shared/naturalcrit/splitPane/splitPane.jsx b/shared/naturalcrit/splitPane/splitPane.jsx index 55af5e386..1500c759f 100644 --- a/shared/naturalcrit/splitPane/splitPane.jsx +++ b/shared/naturalcrit/splitPane/splitPane.jsx @@ -1,182 +1,110 @@ require('./splitPane.less'); const React = require('react'); -const createClass = require('create-react-class'); -const cx = require('classnames'); +const { useState, useEffect } = React; -const SplitPane = createClass({ - displayName : 'SplitPane', - getDefaultProps : function() { - return { - storageKey : 'naturalcrit-pane-split', - onDragFinish : function(){} //fires when dragging - }; - }, +const storageKey = 'naturalcrit-pane-split'; - getInitialState : function() { - return { - currentDividerPos : null, - windowWidth : 0, - isDragging : false, - moveSource : false, - moveBrew : false, - showMoveArrows : true - }; - }, +const SplitPane = (props)=>{ + const { + onDragFinish = ()=>{}, + showDividerButtons = true + } = props; - pane1 : React.createRef(null), - pane2 : React.createRef(null), + const [isDragging, setIsDragging] = useState(false); + const [dividerPos, setDividerPos] = useState(null); + const [moveSource, setMoveSource] = useState(false); + const [moveBrew, setMoveBrew] = useState(false); + const [showMoveArrows, setShowMoveArrows] = useState(true); + const [liveScroll, setLiveScroll] = useState(false); - componentDidMount : function() { - const dividerPos = window.localStorage.getItem(this.props.storageKey); - if(dividerPos){ - this.setState({ - currentDividerPos : this.limitPosition(dividerPos, 0.1*(window.innerWidth-13), 0.9*(window.innerWidth-13)), - userSetDividerPos : dividerPos, - windowWidth : window.innerWidth - }); - } else { - this.setState({ - currentDividerPos : window.innerWidth / 2, - userSetDividerPos : window.innerWidth / 2 - }); - } - window.addEventListener('resize', this.handleWindowResize); - }, + useEffect(()=>{ + const savedPos = window.localStorage.getItem(storageKey); + setDividerPos(savedPos ? limitPosition(savedPos, 0.1 * (window.innerWidth - 13), 0.9 * (window.innerWidth - 13)) : window.innerWidth / 2); + setLiveScroll(window.localStorage.getItem('liveScroll') === 'true'); - componentWillUnmount : function() { - window.removeEventListener('resize', this.handleWindowResize); - }, + window.addEventListener('resize', handleResize); + return ()=>window.removeEventListener('resize', handleResize); + }, []); - handleWindowResize : function() { - // Allow divider to increase in size to last user-set position - // Limit current position to between 10% and 90% of visible space - const newLoc = this.limitPosition(this.state.userSetDividerPos, 0.1*(window.innerWidth-13), 0.9*(window.innerWidth-13)); + const limitPosition = (x, min = 1, max = window.innerWidth - 13)=>Math.round(Math.min(max, Math.max(min, x))); - this.setState({ - currentDividerPos : newLoc, - windowWidth : window.innerWidth - }); - }, - - limitPosition : function(x, min = 1, max = window.innerWidth - 13) { - const result = Math.round(Math.min(max, Math.max(min, x))); - return result; - }, - - handleUp : function(e){ + //when resizing, the divider should grow smaller if less space is given, then grow back if the space is restored, to the original position + const handleResize = () =>setDividerPos(limitPosition(window.localStorage.getItem(storageKey), 0.1 * (window.innerWidth - 13), 0.9 * (window.innerWidth - 13))); + + const handleUp =(e)=>{ e.preventDefault(); - if(this.state.isDragging){ - this.props.onDragFinish(this.state.currentDividerPos); - window.localStorage.setItem(this.props.storageKey, this.state.currentDividerPos); + if(isDragging) { + onDragFinish(dividerPos); + window.localStorage.setItem(storageKey, dividerPos); } - this.setState({ isDragging: false }); - }, + setIsDragging(false); + }; - handleDown : function(e){ + const handleDown = (e)=>{ e.preventDefault(); - this.setState({ isDragging: true }); - //this.unFocus() - }, - - handleMove : function(e){ - if(!this.state.isDragging) return; + setIsDragging(true); + }; + const handleMove = (e)=>{ + if(!isDragging) return; e.preventDefault(); - const newSize = this.limitPosition(e.pageX); - this.setState({ - currentDividerPos : newSize, - userSetDividerPos : newSize - }); - }, - /* - unFocus : function() { - if(document.selection){ - document.selection.empty(); - }else{ - window.getSelection().removeAllRanges(); - } - }, - */ + setDividerPos(limitPosition(e.pageX)); + }; - setMoveArrows : function(newState) { - if(this.state.showMoveArrows != newState){ - this.setState({ - showMoveArrows : newState - }); - } - }, + const liveScrollToggle = ()=>{ + window.localStorage.setItem('liveScroll', String(!liveScroll)); + setLiveScroll(!liveScroll); + }; - renderMoveArrows : function(){ - if(this.state.showMoveArrows) { - return <> -
    this.setState({ moveSource: !this.state.moveSource })} > - -
    -
    this.setState({ moveBrew: !this.state.moveBrew })} > - -
    - ; - } - }, - - renderDivider : function(){ - return <> - {this.renderMoveArrows()} -
    -
    - - - -
    + const renderMoveArrows = (showMoveArrows && + <> +
    setMoveSource(!moveSource)} > +
    - ; - }, +
    setMoveBrew(!moveBrew)} > + +
    +
    + +
    + + ); - render : function(){ - return
    - - {React.cloneElement(this.props.children[0], { - moveBrew : this.state.moveBrew, - moveSource : this.state.moveSource, - setMoveArrows : this.setMoveArrows - })} + const renderDivider = ( +
    + {showDividerButtons && renderMoveArrows} +
    + + + +
    +
    + ); + + return ( +
    + + {props.children[0]} - {this.renderDivider()} - {this.props.children[1]} -
    ; - } -}); + {renderDivider} + {props.children[1]} +
    + ); +}; -const Pane = createClass({ - displayName : 'Pane', - getDefaultProps : function() { - return { - width : null - }; - }, - render : function(){ - let styles = {}; - if(this.props.width){ - styles = { - flex : 'none', - width : `${this.props.width}px` - }; - } else { - styles = { - pointerEvents : this.props.isDragging ? 'none' : 'auto' //Disable mouse capture in the rightmost pane; dragging into the iframe drops the divider otherwise - }; - } +const Pane = ({ width, children, isDragging, moveBrew, moveSource, liveScroll, setMoveArrows })=>{ + const styles = width + ? { flex: 'none', width: `${width}px` } + : { pointerEvents: isDragging ? 'none' : 'auto' }; //Disable mouse capture in the right pane; else dragging into the iframe drops the divider - return
    - {this.props.children} -
    ; - } -}); + return ( +
    + {React.cloneElement(children, { moveBrew, moveSource, liveScroll, setMoveArrows })} +
    + ); +}; module.exports = SplitPane; diff --git a/shared/naturalcrit/splitPane/splitPane.less b/shared/naturalcrit/splitPane/splitPane.less index 831b5ce47..8b61097be 100644 --- a/shared/naturalcrit/splitPane/splitPane.less +++ b/shared/naturalcrit/splitPane/splitPane.less @@ -1,60 +1,68 @@ -.splitPane{ +.splitPane { position : relative; display : flex; + flex-direction : row; height : 100%; outline : none; - flex-direction : row; - .pane{ + .pane { + flex : 1; overflow-x : hidden; overflow-y : hidden; - flex : 1; } - .divider{ - touch-action : none; + .divider { + position : relative; display : table; - height : 100%; width : 15px; - cursor : ew-resize; - background-color : #bbb; + height : 100%; text-align : center; - .dots{ + touch-action : none; + cursor : ew-resize; + background-color : #BBBBBB; + .dots { display : table-cell; - vertical-align : middle; text-align : center; - i{ + vertical-align : middle; + i { display : block !important; margin : 10px 0px; font-size : 6px; - color : #666; + color : #666666; } } - &:hover{ - background-color: #999; - } + &:hover,&.dragging { background-color : #999999; } } - .arrow{ + .arrow { position : absolute; + left : 50%; + z-index : 999; width : 25px; height : 25px; - border : 2px solid #bbb; - border-radius : 15px; - text-align : center; font-size : 1.2em; + text-align : center; cursor : pointer; - background-color : #ddd; - z-index : 999; - box-shadow : 0 4px 5px #0000007f; - &.left{ + background-color : #DDDDDD; + border : 2px solid #BBBBBB; + border-radius : 15px; + box-shadow : 0 4px 5px #0000007F; + translate : -50%; + &.left { .tooltipLeft('Jump to location in Editor'); top : 30px; } - &.right{ + &.right { .tooltipRight('Jump to location in Preview'); top : 60px; } - &:hover{ - background-color: #666; + &.lock { + .tooltipRight('De-sync Editor and Preview locations.'); + top : 90px; + background : #666666; } + &.unlock { + .tooltipRight('Sync Editor and Preview locations'); + top : 90px; + } + &:hover { background-color : #666666; } } } diff --git a/shared/naturalcrit/styles/core.less b/shared/naturalcrit/styles/core.less index c742ad7b1..3248269c5 100644 --- a/shared/naturalcrit/styles/core.less +++ b/shared/naturalcrit/styles/core.less @@ -21,10 +21,7 @@ html,body, #reactRoot{ *{ box-sizing : border-box; } -button{ - .button(); -} -.button(@backgroundColor : @green){ +.colorButton(@backgroundColor : @green){ .animate(background-color); display : inline-block; padding : 0.6em 1.2em; @@ -46,5 +43,6 @@ button{ } &:disabled{ background-color : @silver !important; + cursor:not-allowed; } } diff --git a/shared/naturalcrit/styles/reset.less b/shared/naturalcrit/styles/reset.less index be5bffc7e..df5564a21 100644 --- a/shared/naturalcrit/styles/reset.less +++ b/shared/naturalcrit/styles/reset.less @@ -1,4 +1,4 @@ -:where(html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video){ +:where(html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,button,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video){ border:0;font-size:100%;font:inherit;vertical-align:baseline;margin:0;padding:0 } @@ -25,3 +25,9 @@ :where(table){ border-collapse:collapse;border-spacing:0 } + +:where(button) { + background-color: unset; + text-transform: unset; + color: unset; +} diff --git a/stylelint_plugins/declaration-block-multi-line-min-declarations.js b/stylelint_plugins/declaration-block-multi-line-min-declarations.js index ba40ab5f6..517fd27ec 100644 --- a/stylelint_plugins/declaration-block-multi-line-min-declarations.js +++ b/stylelint_plugins/declaration-block-multi-line-min-declarations.js @@ -1,5 +1,5 @@ -const stylelint = require('stylelint'); -const { isNumber } = require('stylelint/lib/utils/validateTypes.cjs'); +import stylelint from 'stylelint'; +import { isNumber } from 'stylelint/lib/utils/validateTypes.mjs'; const { report, ruleMessages, validateOptions } = stylelint.utils; const ruleName = 'naturalcrit/declaration-block-multi-line-min-declarations'; @@ -7,9 +7,8 @@ const messages = ruleMessages(ruleName, { expected : (decls)=>`Rule with ${decls} declaration${decls == 1 ? '' : 's'} should be single line`, }); - -module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOption, secondaryOptionObject, context) { - return function lint(postcssRoot, postcssResult) { +const ruleFunction = (primaryOption, secondaryOptionObject, context)=>{ + return (postcssRoot, postcssResult)=>{ const validOptions = validateOptions( postcssResult, @@ -20,26 +19,23 @@ module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOpti } ); - if(!validOptions) { //If the options are invalid, don't lint + if(!validOptions) //If the options are invalid, don't lint return; - } + const isAutoFixing = Boolean(context.fix); postcssRoot.walkRules((rule)=>{ //Iterate CSS rules //Apply rule only if all children are decls (no further nested rules) - if(rule.nodes.length > primaryOption || !rule.nodes.every((node)=>node.type === 'decl')) { + if(rule.nodes.length > primaryOption || !rule.nodes.every((node)=>node.type === 'decl')) return; - } //Ignore if already one line if(!rule.nodes.some((node)=>node.raws.before.includes('\n')) && !rule.raws.after.includes('\n')) return; if(isAutoFixing) { //We are in β€œfix” mode - rule.each((decl)=>{ - decl.raws.before = ' '; - }); + rule.each((decl)=>decl.raws.before = ' '); rule.raws.after = ' '; } else { report({ @@ -52,7 +48,9 @@ module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOpti } }); }; -}); +}; -module.exports.ruleName = ruleName; -module.exports.messages = messages; +ruleFunction.ruleName = ruleName; +ruleFunction.messages = messages; + +export default stylelint.createPlugin(ruleName, ruleFunction); diff --git a/stylelint_plugins/declaration-colon-align.js b/stylelint_plugins/declaration-colon-align.js index f1f5269d3..aeb92fcd8 100644 --- a/stylelint_plugins/declaration-colon-align.js +++ b/stylelint_plugins/declaration-colon-align.js @@ -1,32 +1,29 @@ -const stylelint = require('stylelint'); - +import stylelint from 'stylelint'; const { report, ruleMessages, validateOptions } = stylelint.utils; + const ruleName = 'naturalcrit/declaration-colon-align'; const messages = ruleMessages(ruleName, { expected : (rule)=>`Expected colons aligned within rule "${rule}"`, }); - -module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOption, secondaryOptionObject, context) { - return function lint(postcssRoot, postcssResult) { +const ruleFunction = (primaryOption, secondaryOptionObject, context)=>{ + return (postcssRoot, postcssResult)=>{ const validOptions = validateOptions( postcssResult, ruleName, { actual : primaryOption, - possible : [ - true, - false - ] + possible : [true, false] } ); - if(!validOptions) { //If the options are invalid, don't lint + if(!validOptions) // If the options are invalid, don't lint return; - } + const isAutoFixing = Boolean(context.fix); - postcssRoot.walkRules((rule)=>{ //Iterate CSS rules + + postcssRoot.walkRules((rule)=>{ // Iterate CSS rules let maxColonPos = 0; let misaligned = false; @@ -36,33 +33,37 @@ module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOpti return; const colonPos = declaration.prop.length + declaration.raws.between.indexOf(':'); - if(maxColonPos > 0 && colonPos != maxColonPos) { + + if(maxColonPos > 0 && colonPos != maxColonPos) misaligned = true; - } + maxColonPos = Math.max(maxColonPos, colonPos); }); - if(misaligned) { - if(isAutoFixing) { //We are in β€œfix” mode - rule.each((declaration)=>{ - if(declaration.type != 'decl') - return; + if(!misaligned) + return; - declaration.raws.between = `${' '.repeat(maxColonPos - declaration.prop.length)}:${declaration.raws.between.split(':')[1]}`; - }); - } else { //We are in β€œreport only” mode - report({ - ruleName, - result : postcssResult, - message : messages.expected(rule.selector), // Build the reported message - node : rule, // Specify the reported node - word : rule.selector, // Which exact word caused the error? This positions the error properly - }); - } + if(isAutoFixing) { // We are in β€œfix” mode + rule.each((declaration)=>{ + if(declaration.type != 'decl') + return; + + declaration.raws.between = `${' '.repeat(maxColonPos - declaration.prop.length)}:${declaration.raws.between.split(':')[1]}`; + }); + } else { // We are in β€œreport only” mode + report({ + ruleName, + result : postcssResult, + message : messages.expected(rule.selector), // Build the reported message + node : rule, // Specify the reported node + word : rule.selector, // Which exact word caused the error? This positions the error properly + }); } }); }; -}); +}; -module.exports.ruleName = ruleName; -module.exports.messages = messages; +ruleFunction.ruleName = ruleName; +ruleFunction.messages = messages; + +export default stylelint.createPlugin(ruleName, ruleFunction); \ No newline at end of file diff --git a/stylelint_plugins/declaration-colon-min-space-before.js b/stylelint_plugins/declaration-colon-min-space-before.js index e75c035a6..7776c6fef 100644 --- a/stylelint_plugins/declaration-colon-min-space-before.js +++ b/stylelint_plugins/declaration-colon-min-space-before.js @@ -1,5 +1,5 @@ -const stylelint = require('stylelint'); -const { isNumber } = require('stylelint/lib/utils/validateTypes.cjs'); +import stylelint from 'stylelint'; +import { isNumber } from 'stylelint/lib/utils/validateTypes.mjs'; const { report, ruleMessages, validateOptions } = stylelint.utils; const ruleName = 'naturalcrit/declaration-colon-min-space-before'; @@ -7,9 +7,8 @@ const messages = ruleMessages(ruleName, { expected : (num)=>`Expected at least ${num} space${num == 1 ? '' : 's'} before ":"` }); - -module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOption, secondaryOptionObject, context) { - return function lint(postcssRoot, postcssResult) { +const ruleFunction = (primaryOption, secondaryOptionObject, context)=>{ + return (postcssRoot, postcssResult)=>{ const validOptions = validateOptions( postcssResult, @@ -30,9 +29,9 @@ module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOpti const between = decl.raws.between; const colonIndex = between.indexOf(':'); - if(between.slice(0, colonIndex).length >= primaryOption) { + if(between.slice(0, colonIndex).length >= primaryOption) return; - } + if(isAutoFixing) { //We are in β€œfix” mode decl.raws.between = between.slice(0, colonIndex).replace(/\s*$/, ' '.repeat(primaryOption)) + between.slice(colonIndex); } else { @@ -46,7 +45,9 @@ module.exports = stylelint.createPlugin(ruleName, function getPlugin(primaryOpti } }); }; -}); +}; -module.exports.ruleName = ruleName; -module.exports.messages = messages; +ruleFunction.ruleName = ruleName; +ruleFunction.messages = messages; + +export default stylelint.createPlugin(ruleName, ruleFunction); \ No newline at end of file diff --git a/tests/html/safeHTML.test.js b/tests/html/safeHTML.test.js new file mode 100644 index 000000000..51fa1e995 --- /dev/null +++ b/tests/html/safeHTML.test.js @@ -0,0 +1,50 @@ + + +require('jsdom-global')(); + +import { safeHTML } from '../../client/homebrew/brewRenderer/safeHTML'; + +test('Javascript via href', function() { + const source = `Click me`; + const rendered = safeHTML(source); + expect(rendered).toBe('Click me'); +}); + +test('Javascript via src', function() { + const source = ``; + const rendered = safeHTML(source); + expect(rendered).toBe(''); +}); + +test('Javascript via form submit action', function() { + const source = `
    \n\n
    `; + const rendered = safeHTML(source); + expect(rendered).toBe('
    \n\n
    '); +}); + +test('Javascript via inline event handler - onClick', function() { + const source = `
    \nClick me\n
    `; + const rendered = safeHTML(source); + expect(rendered).toBe('
    \nClick me\n
    '); +}); + +test('Javascript via inline event handler - onMouseOver', function() { + const source = `
    Hover over me
    `; + const rendered = safeHTML(source); + expect(rendered).toBe('
    Hover over me
    '); +}); + +test('Javascript via data attribute', function() { + const source = `
    Test
    `; + const rendered = safeHTML(source); + expect(rendered).toBe('
    Test
    '); +}); + +test('Javascript via event delegation', function() { + const source = `
    `; + const rendered = safeHTML(source); + expect(rendered).toBe('
    '); +}); + + + diff --git a/tests/markdown/basic.test.js b/tests/markdown/basic.test.js index 80f5520e7..e5feec0b3 100644 --- a/tests/markdown/basic.test.js +++ b/tests/markdown/basic.test.js @@ -1,6 +1,6 @@ /* eslint-disable max-lines */ -const Markdown = require('naturalcrit/markdown.js'); +import Markdown from 'naturalcrit/markdown.js'; test('Processes the markdown within an HTML block if its just a class wrapper', function() { const source = '
    *Bold text*
    '; diff --git a/tests/markdown/definition-lists.test.js b/tests/markdown/definition-lists.test.js index 9c0bdf6b0..039754ca1 100644 --- a/tests/markdown/definition-lists.test.js +++ b/tests/markdown/definition-lists.test.js @@ -1,6 +1,6 @@ /* eslint-disable max-lines */ -const Markdown = require('naturalcrit/markdown.js'); +import Markdown from 'naturalcrit/markdown.js'; describe('Inline Definition Lists', ()=>{ test('No Term 1 Definition', function() { diff --git a/tests/markdown/emojis.test.js b/tests/markdown/emojis.test.js index a4abbc6a4..e70ebb673 100644 --- a/tests/markdown/emojis.test.js +++ b/tests/markdown/emojis.test.js @@ -1,4 +1,4 @@ -const Markdown = require('naturalcrit/markdown.js'); +import Markdown from 'naturalcrit/markdown.js'; const dedent = require('dedent-tabs').default; // Marked.js adds line returns after closing tags on some default tokens. diff --git a/tests/markdown/hard-breaks.test.js b/tests/markdown/hard-breaks.test.js index 3d0f59a41..8af102716 100644 --- a/tests/markdown/hard-breaks.test.js +++ b/tests/markdown/hard-breaks.test.js @@ -1,6 +1,6 @@ /* eslint-disable max-lines */ -const Markdown = require('naturalcrit/markdown.js'); +import Markdown from 'naturalcrit/markdown.js'; describe('Hard Breaks', ()=>{ test('Single Break', function() { diff --git a/tests/markdown/mustache-syntax.test.js b/tests/markdown/mustache-syntax.test.js index 3f7f2529b..d17518411 100644 --- a/tests/markdown/mustache-syntax.test.js +++ b/tests/markdown/mustache-syntax.test.js @@ -1,7 +1,7 @@ /* eslint-disable max-lines */ const dedent = require('dedent-tabs').default; -const Markdown = require('naturalcrit/markdown.js'); +import Markdown from 'naturalcrit/markdown.js'; // Marked.js adds line returns after closing tags on some default tokens. // This removes those line returns for comparison sake. @@ -300,7 +300,7 @@ describe('Injection: When an injection tag follows an element', ()=>{ 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'); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('text'); }); it('Renders a span "text" with its own classes, appended with injected classes', function() { @@ -322,9 +322,9 @@ describe('Injection: When an injection tag follows an element', ()=>{ }); it('Renders an image element with injected style', function() { - const source = '![alt text](http://i.imgur.com/hMna6G0.png){position:absolute}'; + const source = '![alt text](https://i.imgur.com/hMna6G0.png){position:absolute}'; const rendered = Markdown.render(source).trimReturns(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('

    alt text

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

    alt text

    '); }); it('Renders an element modified by only the first of two consecutive injections', function() { @@ -343,19 +343,19 @@ describe('Injection: When an injection tag follows an element', ()=>{ it('Renders an image with added attributes', function() { const source = `![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

    `); }); it('Renders an image with "=" in the url, and added attributes', function() { const source = `![homebrew mug](https://i.imgur.com/hMna6G0.png?auth=12345&height=1024) {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

    `); }); it('Renders an image and added attributes with "=" in the value, ', 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,otherUrl="url?auth=12345"}`; 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

    `); }); }); @@ -429,7 +429,7 @@ describe('Injection: When an injection tag follows an element', ()=>{ }} {width:10px,color:red}`; 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('Renders a span "text" with its own classes, appended with injected classes', function() { diff --git a/tests/markdown/non-breaking-spaces.test.js b/tests/markdown/non-breaking-spaces.test.js new file mode 100644 index 000000000..9dad4eb0f --- /dev/null +++ b/tests/markdown/non-breaking-spaces.test.js @@ -0,0 +1,72 @@ +/* eslint-disable max-lines */ + +import Markdown from 'naturalcrit/markdown.js'; + +describe('Non-Breaking Spaces', ()=>{ + test('Single Space', function() { + const source = ':>\n\n'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

     

    `); + }); + + test('Double Space', function() { + const source = ':>>\n\n'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

      

    `); + }); + + test('Triple Space', function() { + const source = ':>>>\n\n'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

       

    `); + }); + + test('Many Space', function() { + const source = ':>>>>>>>>>>\n\n'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

              

    `); + }); + + test('Multiple sets of Spaces', function() { + const source = ':>>>\n:>>>\n:>>>'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

       \n   \n   

    `); + }); + + test('Pair of inline Spaces', function() { + const source = ':>>:>>'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

        

    `); + }); + + test('Space directly between two paragraphs', function() { + const source = 'Line 1\n:>>\nLine 2'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

    Line 1\n  \nLine 2

    `); + }); + + test('Ignored inside a code block', function() { + const source = '```\n\n:>\n\n```\n'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
    \n:>\n
    `); + }); + + test('I am actually a single-line definition list!', function() { + const source = 'Term ::> Definition 1\n'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
    Term
    > Definition 1
    \n
    `); + }); + + test('I am actually a definition list!', function() { + const source = 'Term\n::> Definition 1\n'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
    Term
    \n
    > Definition 1
    `); + }); + + test('I am actually a two-term definition list!', function() { + const source = 'Term\n::> Definition 1\n::>> Definition 2'; + const rendered = Markdown.render(source).trim(); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
    Term
    \n
    > Definition 1
    \n
    >> Definition 2
    `); + }); +}); + diff --git a/tests/markdown/paragraph-justification.test.js b/tests/markdown/paragraph-justification.test.js new file mode 100644 index 000000000..48b311e85 --- /dev/null +++ b/tests/markdown/paragraph-justification.test.js @@ -0,0 +1,27 @@ +/* eslint-disable max-lines */ + +import Markdown from 'naturalcrit/markdown.js'; + +describe('Justification', ()=>{ + test('Left Justify', function() { + const source = ':- Hello'; + const rendered = Markdown.render(source); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

    Hello

    `); + }); + test('Right Justify', function() { + const source = '-: Hello'; + const rendered = Markdown.render(source); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

    Hello

    `); + }); + test('Center Justify', function() { + const source = ':-: Hello'; + const rendered = Markdown.render(source); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`

    Hello

    `); + }); + + test('Ignored inside a code block', function() { + const source = '```\n\n:- Hello\n\n```\n'; + const rendered = Markdown.render(source); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
    \n:- Hello\n
    \n`); + }); +}); diff --git a/tests/markdown/variables.test.js b/tests/markdown/variables.test.js index 2c8db375e..41259da7e 100644 --- a/tests/markdown/variables.test.js +++ b/tests/markdown/variables.test.js @@ -1,7 +1,7 @@ /* eslint-disable max-lines */ const dedent = require('dedent-tabs').default; -const Markdown = require('naturalcrit/markdown.js'); +import Markdown from 'naturalcrit/markdown.js'; // Marked.js adds line returns after closing tags on some default tokens. // This removes those line returns for comparison sake. @@ -315,21 +315,21 @@ describe('Normal Links and Images', ()=>{ const source = `![alt text](url)`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent` -

    alt text

    `.trimReturns()); +

    alt text

    `.trimReturns()); }); it('Renders normal images with a title', function() { const source = 'An image ![alt text](url "and title")!'; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent` -

    An image alt text!

    `.trimReturns()); +

    An image alt text!

    `.trimReturns()); }); it('Applies curly injectors to images', function() { 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() { @@ -402,4 +402,12 @@ describe('Variable names that are subsets of other names', ()=>{ const rendered = Markdown.render(source).trimReturns(); expect(rendered).toBe('

    14

    '); }); +}); + +describe('Regression Tests', ()=>{ + it('Don\'t Eat all the parentheticals!', function() { + const source='\n| title 1 | title 2 | title 3 | title 4|\n|-----------|---------|---------|--------|\n|[foo](bar) | Ipsum | ) | ) |\n'; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('
    title 1title 2title 3title 4
    fooIpsum))
    '); + }); }); \ No newline at end of file diff --git a/tests/routes/static-pages.test.js b/tests/routes/static-pages.test.js index c5578dec9..ebfa48dd4 100644 --- a/tests/routes/static-pages.test.js +++ b/tests/routes/static-pages.test.js @@ -1,8 +1,8 @@ -const supertest = require('supertest'); +import supertest from 'supertest'; +import HBApp from 'app.js'; // Mimic https responses to avoid being redirected all the time -const app = supertest.agent(require('app.js').app) - .set('X-Forwarded-Proto', 'https'); +const app = supertest.agent(HBApp).set('X-Forwarded-Proto', 'https'); describe('Tests for static pages', ()=>{ it('Home page works', ()=>{ diff --git a/themes/Legacy/5ePHB/style.less b/themes/Legacy/5ePHB/style.less index 09eb2eec7..4ebfbf840 100644 --- a/themes/Legacy/5ePHB/style.less +++ b/themes/Legacy/5ePHB/style.less @@ -42,23 +42,25 @@ body { } .phb, .page{ .useColumns(); - counter-increment : phb-page-numbers; - position : relative; - z-index : 15; - box-sizing : border-box; - overflow : hidden; - height : 279.4mm; - width : 215.9mm; - padding : 1.0cm 1.7cm; - padding-bottom : 1.5cm; - background-color : @background; - background-image : @backgroundImage; - font-family : BookSanity; - font-size : 0.317cm; - text-rendering : optimizeLegibility; - page-break-before : always; - page-break-after : always; - contain : size; + counter-increment : phb-page-numbers; + position : relative; + z-index : 15; + box-sizing : border-box; + overflow : hidden; + height : 279.4mm; + width : 215.9mm; + padding : 1.0cm 1.7cm; + padding-bottom : 1.5cm; + background-color : @background; + background-image : @backgroundImage; + font-family : BookSanity; + font-size : 0.317cm; + text-rendering : optimizeLegibility; + page-break-before : always; + page-break-after : always; + contain : strict; + content-visibility : auto; + contain-intrinsic-size : auto none; } .phb{ diff --git a/themes/V3/5ePHB/style.less b/themes/V3/5ePHB/style.less index e4bbb3514..46d7e3239 100644 --- a/themes/V3/5ePHB/style.less +++ b/themes/V3/5ePHB/style.less @@ -11,6 +11,7 @@ --HB_Color_CaptionText : #766649; // Brown --HB_Color_WatercolorStain : #BBAD82; // Light brown --HB_Color_Footnotes : #C9AD6A; // Gold + --TOC : 'include'; } .useSansSerif() { diff --git a/themes/V3/Blank/snippets.js b/themes/V3/Blank/snippets.js index b30412bd0..08e378490 100644 --- a/themes/V3/Blank/snippets.js +++ b/themes/V3/Blank/snippets.js @@ -25,14 +25,30 @@ module.exports = [ gen : '\n\\page\n' }, { - name : 'Page Number', - icon : 'fas fa-bookmark', - gen : '{{pageNumber 1}}\n' - }, - { - name : 'Auto-incrementing Page Number', - icon : 'fas fa-sort-numeric-down', - gen : '{{pageNumber,auto}}\n' + name : 'Page Numbering', + icon : 'fas fa-bookmark', + subsnippets : [ + { + name : 'Page Number', + icon : 'fas fa-bookmark', + gen : '{{pageNumber 1}}\n' + }, + { + name : 'Auto-incrementing Page Number', + icon : 'fas fa-sort-numeric-down', + gen : '{{pageNumber,auto}}\n' + }, + { + name : 'Skip Page Number Increment this Page', + icon : 'fas fa-xmark', + gen : '{{skipCounting}}\n' + }, + { + name : 'Restart Numbering', + icon : 'fas fa-arrow-rotate-left', + gen : '{{resetCounting}}\n' + }, + ] }, { name : 'Footer', @@ -224,6 +240,18 @@ module.exports = [ gen : dedent` ![cat warrior](https://s-media-cache-ak0.pinimg.com/736x/4a/81/79/4a8179462cfdf39054a418efd4cb743e.jpg) {width:325px,mix-blend-mode:multiply}` }, + { + name : 'Image Wrap Left', + icon : 'fac image-wrap-left', + gen : dedent` + ![homebrewery_mug](http://i.imgur.com/hMna6G0.png) {width:280px,margin-right:-3cm,wrapLeft}` + }, + { + name : 'Image Wrap Right', + icon : 'fac image-wrap-right', + gen : dedent` + ![homebrewery_mug](http://i.imgur.com/hMna6G0.png) {width:280px,margin-left:-3cm,wrapRight}` + }, { name : 'Background Image', icon : 'fas fa-tree', @@ -411,6 +439,11 @@ module.exports = [ icon : 'font MrEavesRemake', gen : dedent`{{font-family:MrEavesRemake Dummy Text}}` }, + { + name : 'Pagella', + icon : 'font Pagella', + gen : dedent`{{font-family:Pagella Dummy Text}}` + }, { name : 'Solbera Imitation', icon : 'font SolberaImitationRemake', @@ -469,22 +502,40 @@ module.exports = [ ] }, - /**************** PAGE *************/ + /**************** LAYOUT *************/ { groupName : 'Print', icon : 'fas fa-print', view : 'style', snippets : [ + { + name : 'A3 Page Size', + icon : 'far fa-file', + gen : dedent`/* A3 Page Size */ + .page { + width : 297mm; + height : 420mm; + }\n\n`, + }, { name : 'A4 Page Size', icon : 'far fa-file', gen : dedent`/* A4 Page Size */ - .page{ + .page { width : 210mm; height : 296.8mm; }\n\n` }, + { + name : 'A5 Page Size', + icon : 'far fa-file', + gen : dedent`/* A5 Page Size */ + .page { + width : 148mm; + height : 210mm; + }\n\n`, + }, { name : 'Square Page Size', icon : 'far fa-file', @@ -496,6 +547,17 @@ module.exports = [ columns : unset; }\n\n` }, + { + name : 'Card Page Size', + icon : 'far fa-file', + gen : dedent`/* Card Size */ + .page { + width : 63.5mm; + height : 88.9mm; + padding : 5mm; + columns : unset; + }\n\n` + }, { name : 'Ink Friendly', icon : 'fas fa-tint', @@ -511,5 +573,5 @@ module.exports = [ }\n\n` }, ] - } + }, ]; diff --git a/themes/V3/Blank/snippets/footer.gen.js b/themes/V3/Blank/snippets/footer.gen.js index 6583cd06e..9384baed7 100644 --- a/themes/V3/Blank/snippets/footer.gen.js +++ b/themes/V3/Blank/snippets/footer.gen.js @@ -1,4 +1,4 @@ -const Markdown = require('../../../../shared/naturalcrit/markdown.js'); +import Markdown from '../../../../shared/naturalcrit/markdown.js'; module.exports = { createFooterFunc : function(headerSize=1){ diff --git a/themes/V3/Blank/snippets/tableOfContents.gen.js b/themes/V3/Blank/snippets/tableOfContents.gen.js index 3aea01735..44c400762 100644 --- a/themes/V3/Blank/snippets/tableOfContents.gen.js +++ b/themes/V3/Blank/snippets/tableOfContents.gen.js @@ -1,77 +1,78 @@ -const _ = require('lodash'); const dedent = require('dedent-tabs').default; -const getTOC = (pages)=>{ +// Map each actual page to its footer label, accounting for skips or numbering resets +const mapPages = (pages)=>{ + let actualPage = 0; + let mappedPage = 0; // Number displayed in footer + const pageMap = []; - const recursiveAdd = (title, page, targetDepth, child, curDepth=0)=>{ - if(curDepth > 5) return; // Something went wrong. - if(curDepth == targetDepth) { - child.push({ - title : title, - page : page, - children : [] - }); - } else { - if(child.length == 0) { - child.push({ - title : null, - page : page, - children : [] - }); + pages.forEach((page)=>{ + actualPage++; + const doSkip = page.querySelector('.skipCounting'); + const doReset = page.querySelector('.resetCounting'); + + if(doReset) + mappedPage = 1; + if(!doSkip && !doReset) + mappedPage++; + + pageMap[actualPage] = { + mappedPage : mappedPage, + showPage : !doSkip + }; + }); + return pageMap; +}; + +const getMarkdown = (headings, pageMap)=>{ + const levelPad = ['- ###', ' - ####', ' -', ' -', ' -', ' -']; + + const allMarkdown = []; + const depthChain = [0]; + + headings.forEach((heading)=>{ + const page = parseInt(heading.closest('.page').id?.replace(/^p/, '')); + const mappedPage = pageMap[page].mappedPage; + const showPage = pageMap[page].showPage; + const title = heading.textContent.trim(); + const ToCExclude = getComputedStyle(heading).getPropertyValue('--TOC'); + const depth = parseInt(heading.tagName.substring(1)); + + if(!title || !showPage || ToCExclude == 'exclude') + return; + + //If different header depth than last, remove indents until nearest higher-level header, then indent once + if(depth !== depthChain[depthChain.length -1]) { + while (depth <= depthChain[depthChain.length - 1]) { + depthChain.pop(); } - recursiveAdd(title, page, targetDepth, _.last(child).children, curDepth+1,); + depthChain.push(depth); } - }; - const res = []; + const markdown = `${levelPad[depthChain.length - 2]} [{{ ${title}}}{{ ${mappedPage}}}](#p${page})`; + allMarkdown.push(markdown); + }); + return allMarkdown.join('\n'); +}; +const getTOC = ()=>{ const iframe = document.getElementById('BrewRenderer'); const iframeDocument = iframe.contentDocument || iframe.contentWindow.document; const headings = iframeDocument.querySelectorAll('h1, h2, h3, h4, h5, h6'); - const headerDepth = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6']; + const pages = iframeDocument.querySelectorAll('.page'); - _.each(headings, (heading)=>{ - const onPage = parseInt(heading.closest('.page').id?.replace(/^p/, '')); - const ToCExclude = getComputedStyle(heading).getPropertyValue('--TOC'); - - if(ToCExclude != 'exclude') { - recursiveAdd(heading.textContent.trim(), onPage, headerDepth.indexOf(heading.tagName), res); - } - }); - return res; -}; - - -const ToCIterate = (entries, curDepth=0)=>{ - const levelPad = ['- ###', ' - ####', ' - ', ' - ', ' - ', ' - ']; - const toc = []; - if(entries.title !== null){ - toc.push(`${levelPad[curDepth]} [{{ ${entries.title}}}{{ ${entries.page}}}](#p${entries.page})`); - } - if(entries.children.length) { - _.each(entries.children, (entry, idx)=>{ - const children = ToCIterate(entry, entry.title == null ? curDepth : curDepth+1); - if(children.length) { - toc.push(...children); - } - }); - } - return toc; + const pageMap = mapPages(pages); + return getMarkdown(headings, pageMap); }; module.exports = function(props){ - const pages = props.brew.text.split('\\page'); - const TOC = getTOC(pages); - const markdown = _.reduce(TOC, (r, g1, idx1)=>{ - r.push(ToCIterate(g1).join('\n')); - return r; - }, []).join('\n'); + const TOC = getTOC(); return dedent` {{toc,wide # Contents - ${markdown} + ${TOC} }} \n`; -}; +}; \ No newline at end of file diff --git a/themes/V3/Blank/style.less b/themes/V3/Blank/style.less index 4b0653f4b..82a101d09 100644 --- a/themes/V3/Blank/style.less +++ b/themes/V3/Blank/style.less @@ -1,3 +1,4 @@ +@import (less) './themes/fonts/Blank/fonts.less'; @import (less) './themes/fonts/5e/fonts.less'; @import (less) './themes/assets/assets.less'; @import (less) './themes/fonts/iconFonts/elderberryInn.less'; @@ -12,7 +13,7 @@ } @page { margin : 0; } -body { counter-reset : page-numbers; } +body { counter-reset : page-numbers 0; } * { -webkit-print-color-adjust : exact; } //***************************** @@ -44,20 +45,21 @@ body { counter-reset : page-numbers; } } .page { .useColumns(); - position : relative; - z-index : 15; - box-sizing : border-box; - width : 215.9mm; - height : 279.4mm; - padding : 1.4cm 1.9cm 1.7cm; - overflow : hidden; - counter-increment : page-numbers; - background-color : var(--HB_Color_Background); - text-rendering : optimizeLegibility; - contain : size; + position : relative; + z-index : 15; + box-sizing : border-box; + width : 215.9mm; + height : 279.4mm; + padding : 1.4cm 1.9cm 1.7cm; + overflow : hidden; + background-color : var(--HB_Color_Background); + text-rendering : optimizeLegibility; + contain : strict; + content-visibility : auto; + contain-intrinsic-size : auto none; } -//***************************** -// * BASE + //***************************** + // * BASE // *****************************/ .page { p { @@ -156,6 +158,19 @@ body { counter-reset : page-numbers; } break-inside : avoid; } + /* Wrap Text */ + .wrapLeft { + shape-outside : var(--HB_src); + float : right; + shape-margin : 0.2cm; + } + + .wrapRight { + shape-outside : var(--HB_src); + float : left; + shape-margin : 0.2cm; + } + /* Watermark */ .watermark { position : absolute; @@ -481,6 +496,15 @@ body { counter-reset : page-numbers; } &:nth-child(even) { .pageNumber { left : 30px; } } + + .resetCounting { + counter-set : page-numbers 1; + } + + &:not(:has(.skipCounting)) { + counter-increment : page-numbers; + } + } // ***************************** diff --git a/themes/fonts/Blank/fonts.less b/themes/fonts/Blank/fonts.less new file mode 100644 index 000000000..4a3d2d1e8 --- /dev/null +++ b/themes/fonts/Blank/fonts.less @@ -0,0 +1,46 @@ +/* +TeX Gyre Pagella +License: + % Copyright 2007--2018 for TeX Gyre extensions by B. Jackowski, + % J.M. Nowacki et al. (on behalf of TeX Users Groups). Vietnamese + % characters were added by Han The Thanh. + % + % This work can be freely used and distributed under + % the GUST Font License (GFL -- see GUST-FONT-LICENSE.txt) + % which is actually an instance of the LaTeX Project Public License + % (LPPL -- see http://www.latex-project.org/lppl.txt ). + % + % This work has the maintenance status "maintained". The Current Maintainer + % of this work is Bogus\l{}aw Jackowski and Janusz M. Nowacki. + % + % This work consists of the files listed + % in the MANIFEST-TeX-Gyre-Pagella.txt file. +*/ + +@font-face { + font-family: Pagella; + src: url('../../../fonts/Blank/texgyrepagella-regular.woff2'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: Pagella; + src: url('../../../fonts/Blank/texgyrepagella-bold.woff2'); + font-weight: bold; + font-style: normal; +} + +@font-face { + font-family: Pagella; + src: url('../../../fonts/Blank/texgyrepagella-italic.woff2'); + font-weight: normal; + font-style: italic; +} + +@font-face { + font-family: Pagella; + src: url('../../../fonts/Blank/texgyrepagella-bolditalic.woff2'); + font-weight: bold; + font-style: italic; +} diff --git a/themes/fonts/Blank/texgyrepagella-bold.woff2 b/themes/fonts/Blank/texgyrepagella-bold.woff2 new file mode 100644 index 000000000..5256e2d9e Binary files /dev/null and b/themes/fonts/Blank/texgyrepagella-bold.woff2 differ diff --git a/themes/fonts/Blank/texgyrepagella-bolditalic.woff2 b/themes/fonts/Blank/texgyrepagella-bolditalic.woff2 new file mode 100644 index 000000000..7384fcb1a Binary files /dev/null and b/themes/fonts/Blank/texgyrepagella-bolditalic.woff2 differ diff --git a/themes/fonts/Blank/texgyrepagella-italic.woff2 b/themes/fonts/Blank/texgyrepagella-italic.woff2 new file mode 100644 index 000000000..a6d15f26e Binary files /dev/null and b/themes/fonts/Blank/texgyrepagella-italic.woff2 differ diff --git a/themes/fonts/Blank/texgyrepagella-regular.woff2 b/themes/fonts/Blank/texgyrepagella-regular.woff2 new file mode 100644 index 000000000..c37301bca Binary files /dev/null and b/themes/fonts/Blank/texgyrepagella-regular.woff2 differ diff --git a/themes/fonts/iconFonts/diceFont.js b/themes/fonts/iconFonts/diceFont.js index 6ac75ad26..a349d7b0a 100644 --- a/themes/fonts/iconFonts/diceFont.js +++ b/themes/fonts/iconFonts/diceFont.js @@ -93,4 +93,4 @@ const diceFont = { 'df_solid_small_dot_d6_6' : 'df solid-small-dot-d6-6' }; -module.exports = diceFont; \ No newline at end of file +export default diceFont; \ No newline at end of file diff --git a/themes/fonts/iconFonts/elderberryInn.js b/themes/fonts/iconFonts/elderberryInn.js index 042648e4d..6fd933f78 100644 --- a/themes/fonts/iconFonts/elderberryInn.js +++ b/themes/fonts/iconFonts/elderberryInn.js @@ -206,4 +206,4 @@ const elderberryInn = { 'ei_wish' : 'ei wish' }; -module.exports = elderberryInn; \ No newline at end of file +export default elderberryInn; \ No newline at end of file diff --git a/themes/fonts/iconFonts/fontAwesome.js b/themes/fonts/iconFonts/fontAwesome.js index 764bd5bc5..f5f89e3aa 100644 --- a/themes/fonts/iconFonts/fontAwesome.js +++ b/themes/fonts/iconFonts/fontAwesome.js @@ -2051,4 +2051,4 @@ const fontAwesome = { 'fab_zhihu' : 'fab fa-zhihu' }; -module.exports = fontAwesome; \ No newline at end of file +export default fontAwesome; \ No newline at end of file diff --git a/themes/fonts/iconFonts/gameIcons.js b/themes/fonts/iconFonts/gameIcons.js index d92591cef..346e83027 100644 --- a/themes/fonts/iconFonts/gameIcons.js +++ b/themes/fonts/iconFonts/gameIcons.js @@ -506,4 +506,4 @@ const gameIcons = { 'gi_acid' : 'gi acid' }; -module.exports = gameIcons; \ No newline at end of file +export default gameIcons; \ No newline at end of file