diff --git a/.circleci/config.yml b/.circleci/config.yml index 62b5e0a3aa..be2fe2cae4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -76,8 +76,8 @@ run_save_node_bin: &run_save_node_bin fi # This environment is set to every job (and the initial build). -build_machine_environment: &build_machine_environment - # Specify that we want an actual machine (ala Circle 1.0), not a Docker image. +build_machine_environment: + &build_machine_environment # Specify that we want an actual machine (ala Circle 1.0), not a Docker image. docker: - image: meteor/circleci:2024.09.11-android-34-node-20 resource_class: large @@ -104,8 +104,8 @@ build_machine_environment: &build_machine_environment # These will be evaled before each command. PRE_TEST_COMMANDS: |- - ulimit -c unlimited; # Set core dump size as Ubuntu 14.04 lacks prlimit. - ulimit -a # Display all ulimit settings for transparency. + ulimit -c unlimited; # Set core dump size as Ubuntu 14.04 lacks prlimit. + ulimit -a # Display all ulimit settings for transparency. # This is only to make Meteor self-test not remind us that we can set # this argument for self-tests. @@ -178,7 +178,7 @@ jobs: command: | eval $PRE_TEST_COMMANDS; cd dev_bundle/lib - ../../meteor npm install @types/node@20.10.5 --save-dev + ../../meteor npm install @types/node@22.7.4 --save-dev # Ensure that meteor/tools has no TypeScript errors. ../../meteor npm install -g typescript cd ../../ @@ -765,7 +765,9 @@ jobs: if [[ -n "$CIRCLE_PULL_REQUEST" ]]; then PR_NUMBER=$(echo $CIRCLE_PULL_REQUEST | sed 's|.*/pull/\([0-9]*\)|\1|') PR_BRANCH=$(curl -s https://api.github.com/repos/meteor/meteor/pulls/$PR_NUMBER | jq -r .head.ref) - git clone --branch $PR_BRANCH https://github.com/meteor/meteor.git ${CHECKOUT_METEOR_DOCS} + git clone https://github.com/meteor/meteor.git ${CHECKOUT_METEOR_DOCS} + cd ${CHECKOUT_METEOR_DOCS} + git fetch origin pull/$PR_NUMBER/head:$PR_BRANCH else git clone --branch $CIRCLE_BRANCH https://github.com/meteor/meteor.git ${CHECKOUT_METEOR_DOCS} fi diff --git a/.envrc b/.envrc index a60b67f960..e023176dc5 100644 --- a/.envrc +++ b/.envrc @@ -27,6 +27,10 @@ function @test-self { @meteor self-test "$@" } +function @test-in-console { + "$ROOT_DIR/packages/test-in-console/run.sh" "$@" +} + function @check-syntax { node "$ROOT_DIR/scripts/admin/check-legacy-syntax/check-syntax.js" } @@ -50,4 +54,60 @@ function @docs-start { function @docs-migration-start { npm run docs:dev --prefix "$ROOT_DIR/v3-docs/v3-migration-docs" -} \ No newline at end of file +} + +function @get-changes { + git diff --numstat HEAD~1 HEAD | awk '($1 + $2) <= 5000 {print $3}' +} + +function @summarize-changes { + changes=$(@get-changes) + + if [ -n "$changes" ]; then + changes=$(git diff HEAD~1 HEAD -- $(echo "$changes" | tr '\n' ' ')) + else + changes=$(git diff HEAD~1 HEAD) + fi + + echo "$changes" | llm -s "Summarize the following changes in a few sentences:" +} + +function @packages-bumped { + git diff --name-only devel...$(git branch --show-current) | grep "packages/.*/package.js$" | while IFS= read -r file; do + if ! git show devel:$file > /dev/null 2>&1; then + continue + fi + + old=$(git show devel:$file | grep -o "version: *['\"][^'\"]*['\"]" | sed "s/version: *.['\"]//;s/['\"].*//") + version=$(grep -o "version: *['\"][^'\"]*['\"]" "$file" | sed "s/version: *.['\"]//;s/['\"].*//") + name=$(grep -o "name: *['\"][^'\"]*['\"]" "$file" | sed "s/name: *.['\"]//;s/['\"].*//") + + pkg_name=$(echo "$file" | sed -E 's|packages/([^/]*/)?([^/]*)/package\.js|\2|') + + version_in_red=$(tput setaf 1)$version$(tput sgr0) + + if [[ "$version" != "$old" ]]; then + echo "- $pkg_name@$version_in_red" + fi + done +} + +function @packages-bumped-npm { + git diff --name-only devel...$(git branch --show-current) | grep "npm-packages/.*/package.json$" | while IFS= read -r file; do + if ! git show devel:$file > /dev/null 2>&1; then + continue + fi + + old=$(git show devel:$file | grep -o "version: *['\"][^'\"]*['\"]" | sed "s/version: *.['\"]//;s/['\"].*//") + version=$(grep -o "\"version\": *['\"][^'\"]*['\"]" "$file" | sed "s/\"version\": *.['\"]//;s/['\"].*//") + name=$(grep -o "\"name\": *['\"][^'\"]*['\"]" "$file" | sed "s/\"name\": *.['\"]//;s/['\"].*//") + + pkg_name=$(echo "$file" | sed -E 's|npm-packages/([^/]*/)?([^/]*)/package\.json|\2|') + + version_in_red=$(tput setaf 1)$version$(tput sgr0) + + if [[ "$version" != "$old" ]]; then + echo "- $pkg_name@$version_in_red" + fi + done +} diff --git a/.github/workflows/check-code-style.yml b/.github/workflows/check-code-style.yml index 5b7dbbac4c..8b24944b3f 100644 --- a/.github/workflows/check-code-style.yml +++ b/.github/workflows/check-code-style.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - run: npm ci - name: Run ESLint@8 run: npx eslint@8 "./npm-packages/meteor-installer/**/*.js" diff --git a/.github/workflows/check-syntax.yml b/.github/workflows/check-syntax.yml index 1f9f151c94..a20bb011ca 100644 --- a/.github/workflows/check-syntax.yml +++ b/.github/workflows/check-syntax.yml @@ -1,6 +1,5 @@ name: Check legacy syntax on: - - push - pull_request jobs: check-code-style: @@ -9,7 +8,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x - run: cd scripts/admin/check-legacy-syntax && npm ci - name: Check syntax run: cd scripts/admin/check-legacy-syntax && node check-syntax.js diff --git a/.github/workflows/guide.yml b/.github/workflows/guide.yml index f485145f18..bd3d801966 100644 --- a/.github/workflows/guide.yml +++ b/.github/workflows/guide.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 12.x + node-version: 22.x - name: Build the Guide run: npm ci && npm run build - name: Deploy to Netlify for preview diff --git a/.github/workflows/meteor-selftest-windows.yml b/.github/workflows/meteor-selftest-windows.yml index d78f633ec3..c6831b7a6f 100644 --- a/.github/workflows/meteor-selftest-windows.yml +++ b/.github/workflows/meteor-selftest-windows.yml @@ -22,15 +22,22 @@ env: jobs: test: runs-on: windows-2019-meteor + concurrency: + group: ${{ github.head_ref }}-meteor-selftest-windows + cancel-in-progress: true steps: + - name: cleanup + shell: powershell + run: Remove-Item -Recurse -Force ${{ github.workspace }}\* + - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v2 with: - node-version: 20.x + node-version: 22.x - name: Install dependencies shell: pwsh @@ -45,7 +52,7 @@ jobs: .\scripts\windows\ci\test.ps1 - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: | .\dev_bundle diff --git a/.github/workflows/npm-eslint-plugin-meteor.yml b/.github/workflows/npm-eslint-plugin-meteor.yml index 29a72f0005..ff51a2e847 100644 --- a/.github/workflows/npm-eslint-plugin-meteor.yml +++ b/.github/workflows/npm-eslint-plugin-meteor.yml @@ -21,7 +21,7 @@ jobs: - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 22.x cache: npm - run: npm ci - run: npm test diff --git a/.github/workflows/test-deprecated-packages.yml b/.github/workflows/test-deprecated-packages.yml new file mode 100644 index 0000000000..e4ed12fe92 --- /dev/null +++ b/.github/workflows/test-deprecated-packages.yml @@ -0,0 +1,52 @@ +name: Test Deprecated Packages + +# Disabled until we figure out how to fix the error from puppeteer +# Runs on Travis CI for now +# +#on: +# push: +# branches: +# - main +# pull_request: + +jobs: + build: + runs-on: ubuntu-latest + concurrency: + group: ${{ github.head_ref }}-test-deprecated-packages + cancel-in-progress: true + timeout-minutes: 60 + + env: + PUPPETEER_DOWNLOAD_PATH: /home/runner/.npm/chromium + + steps: + - name: Update and install dependencies + run: sudo apt-get update && sudo apt-get install -y libnss3 g++-12 + + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: 20.15.1 + + - name: Cache Node.js modules + uses: actions/cache@v3 + with: + path: | + ~/.npm + .meteor + .babel-cache + dev_bundle + /home/runner/.npm/chromium + key: ${{ runner.os }}-node-${{ hashFiles('meteor', '**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Install dependencies + run: npm install + + - name: Run tests + run: ./packages/test-in-console/run.sh \ No newline at end of file diff --git a/.gitignore b/.gitignore index fe19b9b3e9..4742e13056 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,7 @@ packages/**/.npm # doc files should not be committed packages/**/*.docs.js + +#cursor +.cursorignore +.cursorrules diff --git a/.travis.yml b/.travis.yml index 4d9997a4d7..5d5a1e7852 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ dist: jammy sudo: required services: xvfb node_js: - - "20.15.1" + - "22.14.0" cache: directories: - ".meteor" @@ -16,6 +16,7 @@ env: - CXX=g++-12 - phantom=false - PUPPETEER_DOWNLOAD_PATH=~/.npm/chromium + - TEST_PACKAGES_EXCLUDE=stylus addons: apt: sources: diff --git a/README.md b/README.md index a6249e4157..149e1c4771 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@
- + + + + Meteor logo +
-
-
[![Travis CI Status](https://api.travis-ci.com/meteor/meteor.svg?branch=devel)](https://app.travis-ci.com/github/meteor/meteor) @@ -54,23 +56,20 @@ How about trying a tutorial to get started with your favorite technology? | [ React](https://docs.meteor.com/tutorials/react/) | | - | | [ Blaze](https://blaze-tutorial.meteor.com/) | -| [ Vue](https://vue-tutorial.meteor.com/) | -| [ Svelte](https://svelte-tutorial.meteor.com/) | - -Next, read the [documentation](https://docs.meteor.com/) and get some [examples](https://github.com/meteor/examples). +| [ Vue](https://docs.meteor.com/tutorials/vue/meteorjs3-vue3-vue-meteor-tracker.html) | # πŸš€ Quick Start On your platform, use this line: ```shell -> npm install -g meteor +> npx meteor ``` πŸš€ To create a project: ```shell -> meteor create my-app +> meteor create ``` β˜„οΈ Run it: @@ -84,10 +83,9 @@ meteor **Building an application with Meteor?** -* Deploy on [Meteor Cloud](https://www.meteor.com/cloud) +* Deploy on [Galaxy](https://www.meteor.com/cloud) * Discuss on [Forums](https://forums.meteor.com/) * Join the Meteor Discord by clicking this [invite link](https://discord.gg/hZkTCaVjmT). -* Announcement list. Subscribe in the [footer](https://www.meteor.com/). Interested in helping or contributing to Meteor? These resources will help: @@ -96,15 +94,3 @@ Interested in helping or contributing to Meteor? These resources will help: * [Contribution guidelines](CONTRIBUTING.md) * [Feature requests](https://github.com/meteor/meteor/discussions/) * [Issue tracker](https://github.com/meteor/meteor/issues) - -To uninstall Meteor: - - If installed via npm, run: - ```shell - meteor-installer uninstall - ``` - - If installed via curl, run: - ```shell - rm -rf ~/.meteor - sudo rm /usr/local/bin/meteor - ``` -To find more information about installation, [read here](https://docs.meteor.com/about/install.html#uninstall). diff --git a/SECURITY.md b/SECURITY.md index 278969b992..aaf7b92c93 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,7 +4,8 @@ | Version | Support Status | ------- | -------------- -| 2.x.y | βœ… all security issues +| 3.x.y | βœ… all security issues +| 2.x.y | ⚠️ only major security issues (Until 2025-07) | <= 1.12.x | ❌ no longer supported ## Reporting a Vulnerability diff --git a/docs/_config.yml b/docs/_config.yml index 0556242aac..6834fe8ced 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -88,7 +88,6 @@ sidebar_categories: - packages/server-render - packages/spacebars - packages/standard-minifier-css - - packages/underscore - packages/url - packages/webapp - packages/packages-listing @@ -393,7 +392,6 @@ redirects: /#/full/oauth-encryption: 'packages/oauth-encryption.html' /#/full/random: 'packages/random.html' /#/full/spiderable: 'packages/spiderable.html' - /#/full/underscore: 'packages/underscore.html' /#/full/webapp: 'packages/webapp.html' '#meteor_isclient': 'api/core.html#Meteor-isClient' '#meteor_isserver': 'api/core.html#Meteor-isServer' @@ -669,6 +667,5 @@ redirects: '#oauth-encryption': 'packages/oauth-encryption.html' '#random': 'packages/random.html' '#spiderable': 'packages/spiderable.html' - '#underscore': 'packages/underscore.html' '#webapp': 'packages/webapp.html' '#pkg_spacebars': 'packages/spacebars.html' diff --git a/docs/generators/changelog/versions/0-before-2.10.md b/docs/generators/changelog/versions/0-before-2.10.md index 1774e87739..54c856eaa8 100644 --- a/docs/generators/changelog/versions/0-before-2.10.md +++ b/docs/generators/changelog/versions/0-before-2.10.md @@ -2935,6 +2935,7 @@ N/A setMinimumBrowserVersions({ chrome: 49, firefox: 45, + firefoxIOS: 100, edge: 12, ie: Infinity, // Sorry, IE11. mobile_safari: [9, 2], // 9.2.0+ diff --git a/docs/history.md b/docs/history.md index 5f9df5f8d7..e206711c49 100644 --- a/docs/history.md +++ b/docs/history.md @@ -4651,6 +4651,7 @@ N/A setMinimumBrowserVersions({ chrome: 49, firefox: 45, + firefoxIOS: 100, edge: 12, ie: Infinity, // Sorry, IE11. mobile_safari: [9, 2], // 9.2.0+ diff --git a/docs/jsdoc/jsdoc-conf.json b/docs/jsdoc/jsdoc-conf.json index 82ed30dd10..4fd2956fc1 100644 --- a/docs/jsdoc/jsdoc-conf.json +++ b/docs/jsdoc/jsdoc-conf.json @@ -4,7 +4,6 @@ "packages/ddp/sockjs-0.3.4.js", "packages/test-in-browser/diff_match_patch_uncompressed.js", "packages/jquery/jquery.js", - "packages/underscore/underscore.js", "packages/json/json2.js", "packages/minimongo/minimongo_tests.js", "tools/node_modules", diff --git a/docs/source/index.md b/docs/source/index.md index 441b4dbe2b..208309701d 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -2,7 +2,7 @@ title: Docs --- -> Meteor 2.x uses the deprecated Node.js 14. Meteor 3.0 has been released and runs on Node.js 20. Check out our [v3 docs](https://v3-docs.meteor.com) and [migration guide](https://v3-migration-docs.meteor.com/). +> Meteor 2.x runs on a deprecated Node.js version (14). Meteor 3 has been released with support for the latest Node.js LTS version. For more information, please consult our [migration guide](https://v3-migration-docs.meteor.com/) and the [latest docs](https://v3-docs.meteor.com).

What is Meteor?

diff --git a/docs/source/install.md b/docs/source/install.md index 1db8b8f1c6..6a58d65cfd 100644 --- a/docs/source/install.md +++ b/docs/source/install.md @@ -8,7 +8,7 @@ You need to install the Meteor command line tool to create, run, and manage your

Node.js version

-> Meteor 2.x uses the deprecated Node.js 14. Meteor 3.0 has been released and runs on Node.js 20. Check out our [v3 docs](https://v3-docs.meteor.com) and [migration guide](https://v3-migration-docs.meteor.com/). +> Meteor 2.x runs on a deprecated Node.js version (14). Meteor 3 has been released with support for the latest Node.js LTS version. For more information, please consult our [migration guide](https://v3-migration-docs.meteor.com/) and the [latest docs](https://v3-docs.meteor.com). - Node.js version >= 10 and <= 14 is required. - We recommend you using [nvm](https://github.com/nvm-sh/nvm) or [Volta](https://volta.sh/) for managing Node.js versions. @@ -30,7 +30,8 @@ You need to install the Meteor command line tool to create, run, and manage your Install the latest official version of Meteor.js from your terminal by running one of the commands below. You can check our [changelog](https://docs.meteor.com/changelog.html) for the release notes. -> Run `node -v` to ensure you are using Node.js 14. Meteor 3.0, currently in its Release Candidate version, runs on Node.js v20. +> Meteor 2.x runs on a deprecated Node.js version (14). Meteor 3 is released with support for the latest Node.js LTS version. +> For more information, please consult our [migration guide](https://guide.meteor.com/3.0-migration.html) and the [new docs](https://docs.meteor.com/). For Windows, Linux and OS X, you can run the following command: diff --git a/guide/source/index.md b/guide/source/index.md index 427c62ae52..7541dc2fff 100644 --- a/guide/source/index.md +++ b/guide/source/index.md @@ -3,7 +3,7 @@ title: Introduction description: This is the guide for using Meteor, a full-stack JavaScript platform for developing modern web and mobile applications. --- -> Meteor 2.x uses the deprecated Node.js 14. Meteor 3.0 has been released and runs on Node.js 20. Check out our [v3 docs](https://v3-docs.meteor.com) and [migration guide](https://v3-migration-docs.meteor.com/). +> Meteor 2.x runs on a deprecated Node.js version (14). Meteor 3 has been released with support for the latest Node.js LTS version. For more information, please consult our [migration guide](https://v3-migration-docs.meteor.com/) and the [latest docs](https://v3-docs.meteor.com).

What is Meteor?

diff --git a/guide/source/testing.md b/guide/source/testing.md index f7500c2db4..7dfffb4a51 100644 --- a/guide/source/testing.md +++ b/guide/source/testing.md @@ -238,9 +238,11 @@ import { Tracker } from 'meteor/tracker'; const withDiv = function withDiv(callback) { const el = document.createElement('div'); document.body.appendChild(el); + let view = null try { - callback(el); + view = callback(el); } finally { + if (view) Blaze.remove(view) document.body.removeChild(el); } }; @@ -248,9 +250,10 @@ const withDiv = function withDiv(callback) { export const withRenderedTemplate = function withRenderedTemplate(template, data, callback) { withDiv((el) => { const ourTemplate = isString(template) ? Template[template] : template; - Blaze.renderWithData(ourTemplate, data, el); + const view = Blaze.renderWithData(ourTemplate, data, el); Tracker.flush(); callback(el); + return view }); }; ``` diff --git a/meteor b/meteor index 38b7a223d0..745c3affbf 100755 --- a/meteor +++ b/meteor @@ -1,6 +1,6 @@ #!/usr/bin/env bash -BUNDLE_VERSION=20.18.0.1 +BUNDLE_VERSION=22.14.0.4 # OS Check. Put here because here is where we download the precompiled @@ -123,6 +123,7 @@ fi DEV_BUNDLE="$SCRIPT_DIR/dev_bundle" METEOR="$SCRIPT_DIR/tools/index.js" +PROCESS_REQUIRES="$SCRIPT_DIR/tools/node-process-warnings.js" # Set the nofile ulimit as high as permitted by the hard-limit/kernel if [ "$(ulimit -Sn)" != "unlimited" ]; then @@ -148,5 +149,6 @@ fi exec "$DEV_BUNDLE/bin/node" \ --max-old-space-size=4096 \ --no-wasm-code-gc \ + --require="$PROCESS_REQUIRES"\ ${TOOL_NODE_FLAGS} \ "$METEOR" "$@" diff --git a/meteor.bat b/meteor.bat index aa6b05d750..1f0f75d081 100644 --- a/meteor.bat +++ b/meteor.bat @@ -48,6 +48,7 @@ SET BABEL_CACHE_DIR=%~dp0\.babel-cache "%~dp0\dev_bundle\bin\node.exe" ^ --no-wasm-code-gc ^ + --require="%~dp0\tools\node-process-warnings.js" ^ %TOOL_NODE_FLAGS% ^ "%~dp0\tools\index.js" %* diff --git a/npm-packages/eslint-plugin-meteor/scripts/dev-bundle-tool-package.js b/npm-packages/eslint-plugin-meteor/scripts/dev-bundle-tool-package.js index 943c277f8e..0ce217a8f2 100644 --- a/npm-packages/eslint-plugin-meteor/scripts/dev-bundle-tool-package.js +++ b/npm-packages/eslint-plugin-meteor/scripts/dev-bundle-tool-package.js @@ -10,11 +10,11 @@ var packageJson = { dependencies: { // Explicit dependency because we are replacing it with a bundled version // and we want to make sure there are no dependencies on a higher version - npm: "10.8.2", + npm: "10.9.2", pacote: "https://github.com/meteor/pacote/tarball/a81b0324686e85d22c7688c47629d4009000e8b8", "node-gyp": "9.4.0", "@mapbox/node-pre-gyp": "1.0.11", - typescript: "5.6.2", + typescript: "5.6.3", "@meteorjs/babel": "7.20.0", "@meteorjs/reify": "0.25.3", // So that Babel can emit require("@babel/runtime/helpers/...") calls. diff --git a/npm-packages/meteor-babel/package-lock.json b/npm-packages/meteor-babel/package-lock.json index 692c92bdac..3ded2f7c42 100644 --- a/npm-packages/meteor-babel/package-lock.json +++ b/npm-packages/meteor-babel/package-lock.json @@ -1,6 +1,6 @@ { "name": "@meteorjs/babel", - "version": "7.20.0", + "version": "7.20.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -786,9 +786,9 @@ } }, "@meteorjs/reify": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/@meteorjs/reify/-/reify-0.25.3.tgz", - "integrity": "sha512-OVtWOLNvonGwA9Uowzp18q6L2Z3V/kPItS1bNyJMryfXFnosM2O0Hm3pYcxRfP36/0tc1BCiV3dA8yrr8RgMUA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@meteorjs/reify/-/reify-0.25.4.tgz", + "integrity": "sha512-/HwynJK85QtS2Rm26M9TS8aEMnqVJ2TIzJNJTGAQz+G6cTYmJGWaU4nFH96oxiDIBbnT6Y3TfX92HDuS9TtNRg==", "requires": { "acorn": "^8.8.1", "magic-string": "^0.25.3", @@ -804,14 +804,14 @@ } }, "@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" }, "acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==" + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", + "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==" }, "ansi-colors": { "version": "3.2.3", diff --git a/npm-packages/meteor-babel/package.json b/npm-packages/meteor-babel/package.json index ec06ff484c..24771f96c0 100644 --- a/npm-packages/meteor-babel/package.json +++ b/npm-packages/meteor-babel/package.json @@ -1,7 +1,7 @@ { "name": "@meteorjs/babel", "author": "Meteor ", - "version": "7.20.0", + "version": "7.20.1", "license": "MIT", "type": "commonjs", "description": "Babel wrapper package for use with Meteor", @@ -42,7 +42,7 @@ "@babel/template": "^7.16.7", "@babel/traverse": "^7.17.0", "@babel/types": "^7.17.0", - "@meteorjs/reify": "0.25.3", + "@meteorjs/reify": "0.25.4", "babel-preset-meteor": "^7.10.0", "babel-preset-minify": "^0.5.1", "convert-source-map": "^1.6.0", diff --git a/npm-packages/meteor-installer/config.js b/npm-packages/meteor-installer/config.js index 7c2a0a6bd2..41c524300f 100644 --- a/npm-packages/meteor-installer/config.js +++ b/npm-packages/meteor-installer/config.js @@ -1,7 +1,7 @@ const os = require('os'); const path = require('path'); -const METEOR_LATEST_VERSION = '3.0.4'; +const METEOR_LATEST_VERSION = '3.2'; const sudoUser = process.env.SUDO_USER || ''; function isRoot() { return process.getuid && process.getuid() === 0; diff --git a/npm-packages/meteor-installer/package-lock.json b/npm-packages/meteor-installer/package-lock.json index dabafd2c0b..40e12504fa 100644 --- a/npm-packages/meteor-installer/package-lock.json +++ b/npm-packages/meteor-installer/package-lock.json @@ -1,792 +1,793 @@ -{ - "name": "meteor", - "version": "3.0.4", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "meteor", - "version": "3.0.4", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "7zip-bin": "^5.2.0", - "cli-progress": "^3.11.1", - "https-proxy-agent": "^5.0.1", - "node-7z": "^2.1.2", - "node-downloader-helper": "^2.1.9", - "rimraf": "^6.0.1", - "semver": "^7.3.7", - "tar": "^6.1.11", - "tmp": "^0.2.1" - }, - "bin": { - "meteor-installer": "cli.js" - }, - "engines": { - "node": ">=20.x", - "npm": ">=10.x" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/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==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/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==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/7zip-bin": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", - "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==" - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/cli-progress": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", - "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", - "dependencies": { - "string-width": "^4.2.3" - }, - "engines": { - "node": ">=4" - } - }, - "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/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/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/glob": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", - "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/https-proxy-agent": { - "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==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/jackspeak": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz", - "integrity": "sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/lodash.defaultsdeep": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz", - "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==" - }, - "node_modules/lodash.defaultto": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/lodash.defaultto/-/lodash.defaultto-4.14.0.tgz", - "integrity": "sha512-G6tizqH6rg4P5j32Wy4Z3ZIip7OfG8YWWlPFzUFGcYStH1Ld0l1tWs6NevEQNEDnO1M3NZYjuHuraaFSN5WqeQ==" - }, - "node_modules/lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==" - }, - "node_modules/lodash.isempty": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", - "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==" - }, - "node_modules/lodash.negate": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/lodash.negate/-/lodash.negate-3.0.2.tgz", - "integrity": "sha512-JGJYYVslKYC0tRMm/7igfdHulCjoXjoganRNWM8AgS+RXfOvFnPkOveDhPI65F9aAypCX9QEEQoBqWf7Q6uAeA==" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "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==" - }, - "node_modules/node-7z": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/node-7z/-/node-7z-2.1.2.tgz", - "integrity": "sha512-mSmn90OIYKYIkuRwH1YRJl2sMwB9OlYhCQS4SPTOfxlzWwomoC1G9j4tsvAsv7vJPwvK7B76Z0a2dH5Mvwo91Q==", - "dependencies": { - "cross-spawn": "^7.0.2", - "debug": "^4.1.1", - "lodash.defaultsdeep": "^4.6.1", - "lodash.defaultto": "^4.14.0", - "lodash.flattendeep": "^4.4.0", - "lodash.isempty": "^4.4.0", - "lodash.negate": "^3.0.2", - "normalize-path": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-downloader-helper": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/node-downloader-helper/-/node-downloader-helper-2.1.9.tgz", - "integrity": "sha512-FSvAol2Z8UP191sZtsUZwHIN0eGoGue3uEXGdWIH5228e9KH1YHXT7fN8Oa33UGf+FbqGTQg3sJfrRGzmVCaJA==", - "bin": { - "ndh": "bin/ndh" - }, - "engines": { - "node": ">=14.18" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "license": "BlueOak-1.0.0" - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz", - "integrity": "sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==", - "license": "ISC", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/path-scurry/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/rimraf": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", - "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", - "license": "ISC", - "dependencies": { - "glob": "^11.0.0", - "package-json-from-dist": "^1.0.0" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", - "engines": { - "node": ">=14.14" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/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/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wrap-ansi/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==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } -} +{ + "name": "meteor", + "version": "3.2.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "meteor", + "version": "3.2.0", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "7zip-bin": "^5.2.0", + "cli-progress": "^3.11.1", + "https-proxy-agent": "^5.0.1", + "node-7z": "^2.1.2", + "node-downloader-helper": "^2.1.9", + "rimraf": "^6.0.1", + "semver": "^7.3.7", + "tar": "^6.1.11", + "tmp": "^0.2.1" + }, + "bin": { + "meteor-installer": "cli.js" + }, + "engines": { + "node": ">=20.x", + "npm": ">=10.x" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/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==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/7zip-bin": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", + "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==" + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-progress": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", + "dependencies": { + "string-width": "^4.2.3" + }, + "engines": { + "node": ">=4" + } + }, + "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/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/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/glob": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/https-proxy-agent": { + "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==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/jackspeak": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz", + "integrity": "sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/lodash.defaultsdeep": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz", + "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==" + }, + "node_modules/lodash.defaultto": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/lodash.defaultto/-/lodash.defaultto-4.14.0.tgz", + "integrity": "sha512-G6tizqH6rg4P5j32Wy4Z3ZIip7OfG8YWWlPFzUFGcYStH1Ld0l1tWs6NevEQNEDnO1M3NZYjuHuraaFSN5WqeQ==" + }, + "node_modules/lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==" + }, + "node_modules/lodash.isempty": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==" + }, + "node_modules/lodash.negate": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.negate/-/lodash.negate-3.0.2.tgz", + "integrity": "sha512-JGJYYVslKYC0tRMm/7igfdHulCjoXjoganRNWM8AgS+RXfOvFnPkOveDhPI65F9aAypCX9QEEQoBqWf7Q6uAeA==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "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==" + }, + "node_modules/node-7z": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-7z/-/node-7z-2.1.2.tgz", + "integrity": "sha512-mSmn90OIYKYIkuRwH1YRJl2sMwB9OlYhCQS4SPTOfxlzWwomoC1G9j4tsvAsv7vJPwvK7B76Z0a2dH5Mvwo91Q==", + "dependencies": { + "cross-spawn": "^7.0.2", + "debug": "^4.1.1", + "lodash.defaultsdeep": "^4.6.1", + "lodash.defaultto": "^4.14.0", + "lodash.flattendeep": "^4.4.0", + "lodash.isempty": "^4.4.0", + "lodash.negate": "^3.0.2", + "normalize-path": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-downloader-helper": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/node-downloader-helper/-/node-downloader-helper-2.1.9.tgz", + "integrity": "sha512-FSvAol2Z8UP191sZtsUZwHIN0eGoGue3uEXGdWIH5228e9KH1YHXT7fN8Oa33UGf+FbqGTQg3sJfrRGzmVCaJA==", + "bin": { + "ndh": "bin/ndh" + }, + "engines": { + "node": ">=14.18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz", + "integrity": "sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "license": "ISC", + "dependencies": { + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/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/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi/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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/npm-packages/meteor-installer/package.json b/npm-packages/meteor-installer/package.json index f385aaf156..4f6ee73b6e 100644 --- a/npm-packages/meteor-installer/package.json +++ b/npm-packages/meteor-installer/package.json @@ -1,6 +1,6 @@ { "name": "meteor", - "version": "3.0.4", + "version": "3.2.0", "description": "Install Meteor", "main": "install.js", "scripts": { diff --git a/npm-packages/meteor-node-stubs/CHANGELOG.md b/npm-packages/meteor-node-stubs/CHANGELOG.md index 775ab2be53..c510d86ea7 100644 --- a/npm-packages/meteor-node-stubs/CHANGELOG.md +++ b/npm-packages/meteor-node-stubs/CHANGELOG.md @@ -1,3 +1,15 @@ +v1.2.13 - 2025-02-27 + +* Update `elliptic` to v6.6.1 to address a security vulnerability. + +v1.2.12 - 2024-10-31 + +* Update `elliptic` to v6.6.0 to address a security vulnerability. + +v1.2.11 - 2024-10-25 + +* Update `rimraf` to v5 to remove vulnerable `inflight` dependency. + v1.2.8 - 2024-04-01 * Add new dependency `@meteorjs/crypto-browserify` to replace `crypto-browserify` as it had unsafe dependencies. diff --git a/npm-packages/meteor-node-stubs/package-lock.json b/npm-packages/meteor-node-stubs/package-lock.json index 4fba62f619..30579b7814 100644 --- a/npm-packages/meteor-node-stubs/package-lock.json +++ b/npm-packages/meteor-node-stubs/package-lock.json @@ -1,12 +1,12 @@ { "name": "meteor-node-stubs", - "version": "1.2.10", + "version": "1.2.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "meteor-node-stubs", - "version": "1.2.10", + "version": "1.2.12", "bundleDependencies": [ "@meteorjs/crypto-browserify", "assert", @@ -41,7 +41,7 @@ "console-browserify": "^1.2.0", "constants-browserify": "^1.0.0", "domain-browser": "^4.23.0", - "elliptic": "^6.5.7", + "elliptic": "^6.6.1", "events": "^3.3.0", "https-browserify": "^1.0.0", "os-browserify": "^0.3.0", @@ -60,7 +60,24 @@ "vm-browserify": "^1.1.2" }, "devDependencies": { - "rimraf": "^2.7.1" + "rimraf": "^5.0.10" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/@meteorjs/crypto-browserify": { @@ -102,6 +119,40 @@ "node": ">=4" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/asn1.js": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", @@ -177,13 +228,12 @@ "inBundle": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/brorand": { @@ -390,10 +440,22 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "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 }, "node_modules/console-browserify": { @@ -457,6 +519,21 @@ "sha.js": "^2.4.8" } }, + "node_modules/cross-spawn": { + "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", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -531,10 +608,16 @@ "url": "https://bevry.me/fund" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "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.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", "inBundle": true, "license": "MIT", "dependencies": { @@ -553,6 +636,12 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "inBundle": true }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -604,11 +693,21 @@ "is-callable": "^1.1.3" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/function-bind": { "version": "1.1.2", @@ -640,20 +739,20 @@ } }, "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": "*" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -796,16 +895,6 @@ ], "inBundle": true }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -840,6 +929,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-generator-function": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", @@ -892,6 +990,33 @@ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "inBundle": true }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -935,15 +1060,27 @@ "inBundle": true }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" } }, "node_modules/object-inspect": { @@ -1002,21 +1139,18 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, "node_modules/os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", "inBundle": true }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -1059,13 +1193,29 @@ "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", "inBundle": true }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/pbkdf2": { @@ -1184,15 +1334,18 @@ } }, "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", "dev": true, "dependencies": { - "glob": "^7.1.3" + "glob": "^10.3.7" }, "bin": { - "rimraf": "bin.js" + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/ripemd160": { @@ -1262,6 +1415,27 @@ "sha.js": "bin.js" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -1281,6 +1455,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/stream-browserify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", @@ -1312,6 +1498,102 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/timers-browserify": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", @@ -1369,6 +1651,21 @@ "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", "inBundle": true }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/which-typed-array": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", @@ -1388,12 +1685,97 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/npm-packages/meteor-node-stubs/package.json b/npm-packages/meteor-node-stubs/package.json index 2068c222c4..2c2caa5dbb 100644 --- a/npm-packages/meteor-node-stubs/package.json +++ b/npm-packages/meteor-node-stubs/package.json @@ -2,7 +2,7 @@ "name": "meteor-node-stubs", "author": "Ben Newman ", "description": "Stub implementations of Node built-in modules, a la Browserify", - "version": "1.2.10", + "version": "1.2.13", "main": "index.js", "license": "MIT", "homepage": "https://github.com/meteor/meteor/blob/devel/npm-packages/meteor-node-stubs/README.md", @@ -18,7 +18,7 @@ "console-browserify": "^1.2.0", "constants-browserify": "^1.0.0", "domain-browser": "^4.23.0", - "elliptic": "^6.5.7", + "elliptic": "^6.6.1", "events": "^3.3.0", "https-browserify": "^1.0.0", "os-browserify": "^0.3.0", @@ -62,7 +62,7 @@ "vm-browserify" ], "devDependencies": { - "rimraf": "^2.7.1" + "rimraf": "^5.0.10" }, "repository": { "type": "git", diff --git a/npm-packages/meteor-node-stubs/scripts/build-deps.js b/npm-packages/meteor-node-stubs/scripts/build-deps.js index e85d8d7c1f..7c6808f6ec 100644 --- a/npm-packages/meteor-node-stubs/scripts/build-deps.js +++ b/npm-packages/meteor-node-stubs/scripts/build-deps.js @@ -2,6 +2,7 @@ var fs = require("fs"); var path = require("path"); var depsDir = path.join(__dirname, "..", "deps"); var map = require("../map.json"); +var rr = require("rimraf"); // Each file in the `deps` directory expresses the dependencies of a stub. // For example, `deps/http.js` calls `require("http-browserify")` to @@ -14,16 +15,15 @@ var map = require("../map.json"); // bundled. Note that these modules should not be `require`d at runtime, // but merely scanned at bundling time. -fs.mkdir(depsDir, function () { - require("rimraf")("deps/*.js", function (error) { - if (error) throw error; - Object.keys(map).forEach(function (id) { - fs.writeFileSync( - path.join(depsDir, id + ".js"), - typeof map[id] === "string" - ? "require(" + JSON.stringify(map[id]) + ");\n" - : "" - ); - }); - }); +rr.rimrafSync(depsDir); + +fs.mkdirSync(depsDir); + +Object.keys(map).forEach(function (id) { + fs.writeFileSync( + path.join(depsDir, id + ".js"), + typeof map[id] === "string" + ? "require(" + JSON.stringify(map[id]) + ");\n" + : "" + ); }); diff --git a/package-lock.json b/package-lock.json index 1ca8bd21d9..ea3a5d33e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,10 @@ "@babel/eslint-parser": "^7.21.3", "@babel/eslint-plugin": "^7.19.1", "@babel/preset-react": "^7.18.6", + "@types/lodash.isempty": "^4.4.9", "@types/node": "^18.16.18", + "@types/sockjs": "^0.3.36", + "@types/sockjs-client": "^1.5.4", "@typescript-eslint/eslint-plugin": "^5.56.0", "@typescript-eslint/parser": "^5.56.0", "eslint": "^8.36.0", @@ -25,7 +28,7 @@ "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", - "prettier": "^2.8.6", + "prettier": "^2.8.8", "typescript": "^5.4.5" } }, @@ -1096,6 +1099,21 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==", + "dev": true + }, + "node_modules/@types/lodash.isempty": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@types/lodash.isempty/-/lodash.isempty-4.4.9.tgz", + "integrity": "sha512-DPSFfnT2JmZiAWNWOU8IRZws/Ha6zyGF5m06TydfsY+0dVoQqby2J61Na2QU4YtwiZ+moC6cJS6zWYBJq4wBVw==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/node": { "version": "18.19.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.3.tgz", @@ -1111,6 +1129,21 @@ "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", "dev": true }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/sockjs-client": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/sockjs-client/-/sockjs-client-1.5.4.tgz", + "integrity": "sha512-zk+uFZeWyvJ5ZFkLIwoGA/DfJ+pYzcZ8eH4H/EILCm2OBZyHH6Hkdna1/UWL/CFruh5wj6ES7g75SvUB0VsH5w==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", @@ -1817,6 +1850,19 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/browserslist": { "version": "4.22.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", @@ -2992,39 +3038,6 @@ "node": ">=8.6.0" } }, - "node_modules/fast-glob/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fast-glob/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fast-glob/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/fast-glob/node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -3038,18 +3051,6 @@ "node": ">=8.6" } }, - "node_modules/fast-glob/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -3083,6 +3084,19 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -3626,6 +3640,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-number-object": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", @@ -4236,6 +4260,7 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin-prettier.js" }, @@ -4695,6 +4720,19 @@ "node": ">=4" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", diff --git a/package.json b/package.json index 16de72f280..28c1cf54f4 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,10 @@ "@babel/eslint-parser": "^7.21.3", "@babel/eslint-plugin": "^7.19.1", "@babel/preset-react": "^7.18.6", + "@types/lodash.isempty": "^4.4.9", "@types/node": "^18.16.18", + "@types/sockjs": "^0.3.36", + "@types/sockjs-client": "^1.5.4", "@typescript-eslint/eslint-plugin": "^5.56.0", "@typescript-eslint/parser": "^5.56.0", "eslint": "^8.36.0", @@ -29,7 +32,7 @@ "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", - "prettier": "^2.8.6", + "prettier": "^2.8.8", "typescript": "^5.4.5" }, "jshintConfig": { diff --git a/packages/accounts-2fa/.npm/package/npm-shrinkwrap.json b/packages/accounts-2fa/.npm/package/npm-shrinkwrap.json deleted file mode 100644 index ebacac28ad..0000000000 --- a/packages/accounts-2fa/.npm/package/npm-shrinkwrap.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "lockfileVersion": 4, - "dependencies": { - "@types/node": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", - "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==" - }, - "@types/notp": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/notp/-/notp-2.0.5.tgz", - "integrity": "sha512-ZsZS0PYUa6ZE4K3yOGerBvaxCp4ePf6ZmkFbPeilcqz2Ui/lmXox7KlRt7XZkXzqUgXhFLkc09ixyVmFLCU3gQ==" - }, - "node-2fa": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/node-2fa/-/node-2fa-2.0.3.tgz", - "integrity": "sha512-PQldrOhjuoZyoydMvMSctllPN1ZPZ1/NwkEcgYwY9faVqE/OymxR+3awPpbWZxm6acLKqvmNqQmdqTsqYyflFw==" - }, - "notp": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/notp/-/notp-2.0.3.tgz", - "integrity": "sha512-oBig/2uqkjQ5AkBuw4QJYwkEWa/q+zHxI5/I5z6IeP2NT0alpJFsP/trrfCC+9xOAgQSZXssNi962kp5KBmypQ==" - }, - "qrcode-svg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/qrcode-svg/-/qrcode-svg-1.1.0.tgz", - "integrity": "sha512-XyQCIXux1zEIA3NPb0AeR8UMYvXZzWEhgdBgBjH9gO7M48H9uoHzviNz8pXw3UzrAcxRRRn9gxHewAVK7bn9qw==" - }, - "thirty-two": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz", - "integrity": "sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==" - }, - "tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" - } - } -} diff --git a/packages/accounts-base/accounts-base.d.ts b/packages/accounts-base/accounts-base.d.ts index 0ab68de576..7fbbfde0a6 100644 --- a/packages/accounts-base/accounts-base.d.ts +++ b/packages/accounts-base/accounts-base.d.ts @@ -82,6 +82,11 @@ export namespace Accounts { passwordEnrollTokenExpirationInDays?: number | undefined; ambiguousErrorMessages?: boolean | undefined; bcryptRounds?: number | undefined; + argon2Enabled?: string | false; + argon2Type?: string | undefined; + argon2TimeCost: number | undefined; + argon2MemoryCost: number | undefined; + argon2Parallelism: number | undefined; defaultFieldSelector?: { [key: string]: 0 | 1 } | undefined; collection?: string | undefined; loginTokenExpirationHours?: number | undefined; @@ -353,10 +358,10 @@ export namespace Accounts { /** * - * Check whether the provided password matches the bcrypt'ed password in + * Check whether the provided password matches the encrypted password in * the database user record. `password` can be a string (in which case - * it will be run through SHA256 before bcrypt) or an object with - * properties `digest` and `algorithm` (in which case we bcrypt + * it will be run through SHA256 before bcrypt or argon2) or an object with + * properties `digest` and `algorithm` (in which case we bcrypt/argon2 * `password.digest`). */ function _checkPasswordAsync( diff --git a/packages/accounts-base/accounts_common.js b/packages/accounts-base/accounts_common.js index fdd45a2890..c910659684 100644 --- a/packages/accounts-base/accounts_common.js +++ b/packages/accounts-base/accounts_common.js @@ -14,6 +14,11 @@ const VALID_CONFIG_KEYS = [ 'passwordEnrollTokenExpiration', 'ambiguousErrorMessages', 'bcryptRounds', + 'argon2Enabled', + 'argon2Type', + 'argon2TimeCost', + 'argon2MemoryCost', + 'argon2Parallelism', 'defaultFieldSelector', 'collection', 'loginTokenExpirationHours', @@ -194,41 +199,6 @@ export class AccountsCommon { ? this.users.findOneAsync(userId, this._addDefaultFieldSelector(options)) : null; } - // Set up config for the accounts system. Call this on both the client - // and the server. - // - // Note that this method gets overridden on AccountsServer.prototype, but - // the overriding method calls the overridden method. - // - // XXX we should add some enforcement that this is called on both the - // client and the server. Otherwise, a user can - // 'forbidClientAccountCreation' only on the client and while it looks - // like their app is secure, the server will still accept createUser - // calls. https://github.com/meteor/meteor/issues/828 - // - // @param options {Object} an object with fields: - // - sendVerificationEmail {Boolean} - // Send email address verification emails to new users created from - // client signups. - // - forbidClientAccountCreation {Boolean} - // Do not allow clients to create accounts directly. - // - restrictCreationByEmailDomain {Function or String} - // Require created users to have an email matching the function or - // having the string as domain. - // - loginExpirationInDays {Number} - // Number of days since login until a user is logged out (login token - // expires). - // - collection {String|Mongo.Collection} - // A collection name or a Mongo.Collection object to hold the users. - // - passwordResetTokenExpirationInDays {Number} - // Number of days since password reset token creation until the - // token can't be used any longer (password reset token expires). - // - ambiguousErrorMessages {Boolean} - // Return ambiguous error messages from login failures to prevent - // user enumeration. - // - bcryptRounds {Number} - // Allows override of number of bcrypt rounds (aka work factor) used - // to store passwords. /** * @summary Set global accounts options. You can also set these in `Meteor.settings.packages.accounts` without the need to call this function. @@ -244,8 +214,13 @@ export class AccountsCommon { * @param {Number} options.passwordResetTokenExpiration The number of milliseconds from when a link to reset password is sent until token expires and user can't reset password with the link anymore. If `passwordResetTokenExpirationInDays` is set, it takes precedent. * @param {Number} options.passwordEnrollTokenExpirationInDays The number of days from when a link to set initial password is sent until token expires and user can't set password with the link anymore. Defaults to 30. * @param {Number} options.passwordEnrollTokenExpiration The number of milliseconds from when a link to set initial password is sent until token expires and user can't set password with the link anymore. If `passwordEnrollTokenExpirationInDays` is set, it takes precedent. - * @param {Boolean} options.ambiguousErrorMessages Return ambiguous error messages from login failures to prevent user enumeration. Defaults to `false`, but in production environments it is recommended it defaults to `true`. + * @param {Boolean} options.ambiguousErrorMessages Return ambiguous error messages from login failures to prevent user enumeration. Defaults to `true`. * @param {Number} options.bcryptRounds Allows override of number of bcrypt rounds (aka work factor) used to store passwords. The default is 10. + * @param {Boolean} options.argon2Enabled Enable argon2 algorithm usage in replacement for bcrypt. The default is `false`. + * @param {'argon2id' | 'argon2i' | 'argon2d'} options.argon2Type Allows override of the argon2 algorithm type. The default is `argon2id`. + * @param {Number} options.argon2TimeCost Allows override of number of argon2 iterations (aka time cost) used to store passwords. The default is 2. + * @param {Number} options.argon2MemoryCost Allows override of the amount of memory (in KiB) used by the argon2 algorithm. The default is 19456 (19MB). + * @param {Number} options.argon2Parallelism Allows override of the number of threads used by the argon2 algorithm. The default is 1. * @param {MongoFieldSpecifier} options.defaultFieldSelector To exclude by default large custom fields from `Meteor.user()` and `Meteor.findUserBy...()` functions when called without a field selector, and all `onLogin`, `onLoginFailure` and `onLogout` callbacks. Example: `Accounts.config({ defaultFieldSelector: { myBigArray: 0 }})`. Beware when using this. If, for instance, you do not include `email` when excluding the fields, you can have problems with functions like `forgotPassword` that will break because they won't have the required data available. It's recommend that you always keep the fields `_id`, `username`, and `email`. * @param {String|Mongo.Collection} options.collection A collection name or a Mongo.Collection object to hold the users. * @param {Number} options.loginTokenExpirationHours When using the package `accounts-2fa`, use this to set the amount of time a token sent is valid. As it's just a number, you can use, for example, 0.5 to make the token valid for just half hour. The default is 1 hour. diff --git a/packages/accounts-base/accounts_server.js b/packages/accounts-base/accounts_server.js index cf8f573b1d..ed45af5a0f 100644 --- a/packages/accounts-base/accounts_server.js +++ b/packages/accounts-base/accounts_server.js @@ -1806,21 +1806,6 @@ const setupUsersCollection = async users => { return true; }, - updateAsync: (userId, user, fields, modifier) => { - // make sure it is our record - if (user._id !== userId) { - return false; - } - - // user can only modify the 'profile' field. sets to multiple - // sub-keys (eg profile.foo and profile.bar) are merged into entry - // in the fields list. - if (fields.length !== 1 || fields[0] !== 'profile') { - return false; - } - - return true; - }, fetch: ['_id'] // we only look at _id. }); @@ -1861,4 +1846,3 @@ const generateCasePermutationsForString = string => { } return permutations; } - diff --git a/packages/accounts-base/package.js b/packages/accounts-base/package.js index d0dad77c8d..6af3172a4e 100644 --- a/packages/accounts-base/package.js +++ b/packages/accounts-base/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "A user account system", - version: "3.0.3", + version: "3.1.0", }); Package.onUse((api) => { diff --git a/packages/accounts-oauth/oauth_client.js b/packages/accounts-oauth/oauth_client.js index d47736cc84..ad28401437 100644 --- a/packages/accounts-oauth/oauth_client.js +++ b/packages/accounts-oauth/oauth_client.js @@ -74,34 +74,38 @@ Meteor.startup(() => { Accounts.oauth.tryLoginAfterPopupClosed = ( credentialToken, callback, - shouldRetry = true + timeout = 1000 ) => { - const credentialSecret = - OAuth._retrieveCredentialSecret(credentialToken); + let startTime = Date.now(); + let calledOnce = false; + let intervalId; + const checkForCredentialSecret = (clearInterval = false) => { + const credentialSecret = OAuth._retrieveCredentialSecret(credentialToken); + if (!calledOnce && (credentialSecret || clearInterval)) { + calledOnce = true; + Meteor.clearInterval(intervalId); + Accounts.callLoginMethod({ + methodArguments: [{ oauth: { credentialToken, credentialSecret } }], + userCallback: callback ? err => callback(convertError(err)) : () => {}, + }); + } else if (clearInterval) { + Meteor.clearInterval(intervalId); + } + }; + // Check immediately + checkForCredentialSecret(); + + // Then check on an interval // In some case the function OAuth._retrieveCredentialSecret() can return null, because the local storage might not // be ready. So we retry after a timeout. - - if (!credentialSecret) { - if (!shouldRetry) { - return; + intervalId = Meteor.setInterval(() => { + if (Date.now() - startTime > timeout) { + checkForCredentialSecret(true); + } else { + checkForCredentialSecret(); } - Meteor.setTimeout( - () => - Accounts.oauth.tryLoginAfterPopupClosed( - credentialToken, - callback, - false - ), - 500 - ); - return; - } - // continue with the rest of the function - Accounts.callLoginMethod({ - methodArguments: [{ oauth: { credentialToken, credentialSecret } }], - userCallback: callback && (err => callback(convertError(err))), - }); + }, 250); }; Accounts.oauth.credentialRequestCompleteHandler = callback => @@ -112,4 +116,3 @@ Accounts.oauth.credentialRequestCompleteHandler = callback => Accounts.oauth.tryLoginAfterPopupClosed(credentialTokenOrError, callback); } } - diff --git a/packages/accounts-oauth/package.js b/packages/accounts-oauth/package.js index e8d1716706..890762bd9b 100644 --- a/packages/accounts-oauth/package.js +++ b/packages/accounts-oauth/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Common code for OAuth-based login services", - version: '1.4.5', + version: '1.4.6', }); Package.onUse(api => { diff --git a/packages/accounts-password/.npm/package/npm-shrinkwrap.json b/packages/accounts-password/.npm/package/npm-shrinkwrap.json deleted file mode 100644 index 4960480477..0000000000 --- a/packages/accounts-password/.npm/package/npm-shrinkwrap.json +++ /dev/null @@ -1,306 +0,0 @@ -{ - "lockfileVersion": 4, - "dependencies": { - "@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==" - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==" - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==" - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "bcrypt": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz", - "integrity": "sha512-9BTgmrhZM2t1bNuDtrtIMVSmmxZBrJ71n8Wg+YgdjHuIWYF7SjjmCPZFB+/5i/o/PIeRpwVJR3P+NrpIItUjqw==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==" - }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" - }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, - "debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, - "detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==" - } - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==" - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==" - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, - "https-proxy-agent": { - "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==" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==" - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==" - }, - "minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==" - } - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" - }, - "node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==" - }, - "nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==" - }, - "npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" - }, - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==" - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==" - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==" - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==" - }, - "tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==" - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==" - }, - "wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==" - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } -} diff --git a/packages/accounts-password/package.js b/packages/accounts-password/package.js index eb63b6182c..31786541ba 100644 --- a/packages/accounts-password/package.js +++ b/packages/accounts-password/package.js @@ -1,49 +1,51 @@ -Package.describe({ - summary: "Password support for accounts", - // Note: 2.2.0-beta.3 was published during the Meteor 1.6 prerelease - // process, so it might be best to skip to 2.3.x instead of reusing - // 2.2.x in the future. The version was also bumped to 2.0.0 temporarily - // during the Meteor 1.5.1 release process, so versions 2.0.0-beta.2 - // through -beta.5 and -rc.0 have already been published. - version: "3.0.2", -}); - -Npm.depends({ - bcrypt: "5.0.1", -}); - -Package.onUse((api) => { - api.use(["accounts-base", "sha", "ejson", "ddp"], ["client", "server"]); - - // Export Accounts (etc) to packages using this one. - api.imply("accounts-base", ["client", "server"]); - - api.use("email", "server"); - api.use("random", "server"); - api.use("check", "server"); - api.use("ecmascript"); - - api.addFiles("email_templates.js", "server"); - api.addFiles("password_server.js", "server"); - api.addFiles("password_client.js", "client"); -}); - -Package.onTest((api) => { - api.use([ - "accounts-password", - "sha", - "tinytest", - "test-helpers", - "tracker", - "accounts-base", - "random", - "email", - "check", - "ddp", - "ecmascript", - ]); - api.addFiles("password_tests_setup.js", "server"); - api.addFiles("password_tests.js", ["client", "server"]); - api.addFiles("email_tests_setup.js", "server"); - api.addFiles("email_tests.js", "client"); -}); +Package.describe({ + summary: "Password support for accounts", + // Note: 2.2.0-beta.3 was published during the Meteor 1.6 prerelease + // process, so it might be best to skip to 2.3.x instead of reusing + // 2.2.x in the future. The version was also bumped to 2.0.0 temporarily + // during the Meteor 1.5.1 release process, so versions 2.0.0-beta.2 + // through -beta.5 and -rc.0 have already been published. + version: "3.1.0", +}); + +Npm.depends({ + bcrypt: "5.0.1", + argon2: "0.41.1", +}); + +Package.onUse((api) => { + api.use(["accounts-base", "sha", "ejson", "ddp"], ["client", "server"]); + + // Export Accounts (etc) to packages using this one. + api.imply("accounts-base", ["client", "server"]); + + api.use("email", "server"); + api.use("random", "server"); + api.use("check", "server"); + api.use("ecmascript"); + + api.addFiles("email_templates.js", "server"); + api.addFiles("password_server.js", "server"); + api.addFiles("password_client.js", "client"); +}); + +Package.onTest((api) => { + api.use([ + "accounts-password", + "sha", + "tinytest", + "test-helpers", + "tracker", + "accounts-base", + "random", + "email", + "check", + "ddp", + "ecmascript" + ]); + api.addFiles("password_tests_setup.js", "server"); + api.addFiles("password_tests.js", ["client", "server"]); + api.addFiles("email_tests_setup.js", "server"); + api.addFiles("email_tests.js", "client"); + api.addFiles("password_argon_tests.js", ["client", "server"]); +}); diff --git a/packages/accounts-password/password_argon_tests.js b/packages/accounts-password/password_argon_tests.js new file mode 100644 index 0000000000..c1fb806d9a --- /dev/null +++ b/packages/accounts-password/password_argon_tests.js @@ -0,0 +1,221 @@ +if (Meteor.isServer) { + Tinytest.addAsync("passwords Argon - migration from bcrypt encryption to argon2", async (test) => { + Accounts._options.argon2Enabled = false; + const username = Random.id(); + const email = `${username}@bcrypt.com`; + const password = "password"; + const userId = await Accounts.createUser( + { + username: username, + email: email, + password: password + } + ); + Accounts._options.argon2Enabled = true; + let user = await Meteor.users.findOneAsync(userId); + const isValid = await Accounts._checkPasswordAsync(user, password); + test.equal(isValid.userId, userId, "checkPassword with bcrypt - User ID should be returned"); + test.equal(typeof isValid.error, "undefined", "checkPassword with bcrypt - No error should be returned"); + + // wait for the migration to happen + await waitUntil( + async () => { + user = await Meteor.users.findOneAsync(userId); + return ( + typeof user.services.password.bcrypt === "undefined" && + typeof user.services.password.argon2 === "string" + ); + }, + { description: "bcrypt should be unset and argon2 should be set" } + ); + + // password is still valid using argon2 + const isValidArgon = await Accounts._checkPasswordAsync(user, password); + test.equal(isValidArgon.userId, userId, "checkPassword with argon2 - User ID should be returned"); + test.equal(typeof isValidArgon.error, "undefined", "checkPassword with argon2 - No error should be returned"); + + // cleanup + Accounts._options.argon2Enabled = false; + await Meteor.users.removeAsync(userId); + }); + + + Tinytest.addAsync("passwords Argon - setPassword", async (test) => { + Accounts._options.argon2Enabled = true; + const username = Random.id(); + const email = `${username}-intercept@example.com`; + + const userId = await Accounts.createUser({ username: username, email: email }); + + let user = await Meteor.users.findOneAsync(userId); + // no services yet. + test.equal(user.services.password, undefined); + + // set a new password. + await Accounts.setPasswordAsync(userId, "new password"); + user = await Meteor.users.findOneAsync(userId); + const oldSaltedHash = user.services.password.argon2; + test.isTrue(oldSaltedHash); + // Send a reset password email (setting a reset token) and insert a login + // token. + await Accounts.sendResetPasswordEmail(userId, email); + await Accounts._insertLoginToken(userId, Accounts._generateStampedLoginToken()); + const user2 = await Meteor.users.findOneAsync(userId); + test.isTrue(user2.services.password.reset); + test.isTrue(user2.services.resume.loginTokens); + + // reset with the same password, see we get a different salted hash + await Accounts.setPasswordAsync(userId, "new password", { logout: false }); + user = await Meteor.users.findOneAsync(userId); + const newSaltedHash = user.services.password.argon2; + test.isTrue(newSaltedHash); + test.notEqual(oldSaltedHash, newSaltedHash); + // No more reset token. + const user3 = await Meteor.users.findOneAsync(userId); + test.isFalse(user3.services.password.reset); + // But loginTokens are still here since we did logout: false. + test.isTrue(user3.services.resume.loginTokens); + + // reset again, see that the login tokens are gone. + await Accounts.setPasswordAsync(userId, "new password"); + user = await Meteor.users.findOneAsync(userId); + const newerSaltedHash = user.services.password.argon2; + test.isTrue(newerSaltedHash); + test.notEqual(oldSaltedHash, newerSaltedHash); + test.notEqual(newSaltedHash, newerSaltedHash); + // No more tokens. + const user4 = await Meteor.users.findOneAsync(userId); + test.isFalse(user4.services.password.reset); + test.isFalse(user4.services.resume.loginTokens); + + // cleanup + Accounts._options.argon2Enabled = false; + await Meteor.users.removeAsync(userId); + }); + + Tinytest.addAsync("passwords Argon - migration from argon2 encryption to bcrypt", async (test) => { + Accounts._options.argon2Enabled = true; + const username = Random.id(); + const email = `${username}@bcrypt.com`; + const password = "password"; + const userId = await Accounts.createUser( + { + username: username, + email: email, + password: password + } + ); + Accounts._options.argon2Enabled = false; + let user = await Meteor.users.findOneAsync(userId); + const isValidArgon = await Accounts._checkPasswordAsync(user, password); + test.equal(isValidArgon.userId, userId, "checkPassword with argon2 - User ID should be returned"); + test.equal(typeof isValidArgon.error, "undefined", "checkPassword with argon2 - No error should be returned"); + + // wait for the migration to happen + await waitUntil( + async () => { + user = await Meteor.users.findOneAsync(userId); + return ( + typeof user.services.password.bcrypt === "string" && + typeof user.services.password.argon2 === "undefined" + ); + }, + { description: "bcrypt should be string and argon2 should be undefined" } + ); + + // password is still valid using bcrypt + const isValidBcrypt = await Accounts._checkPasswordAsync(user, password); + test.equal(isValidBcrypt.userId, userId, "checkPassword with argon2 - User ID should be returned"); + test.equal(typeof isValidBcrypt.error, "undefined", "checkPassword with argon2 - No error should be returned"); + + // cleanup + await Meteor.users.removeAsync(userId); + }); + + const getUserHashArgon2Params = function (user) { + const hash = user?.services?.password?.argon2; + return Accounts._getArgon2Params(hash); + } + const hashPasswordWithSha = function (password) { + return { + digest: SHA256(password), + algorithm: "sha-256" + }; + } + + testAsyncMulti("passwords Argon - allow custom argon2 Params and ensure migration if changed", [ + async function(test) { + Accounts._options.argon2Enabled = true; + // Verify that a argon2 hash generated for a new account uses the + // default params. + let username = Random.id(); + this.password = hashPasswordWithSha("abc123"); + this.userId1 = await Accounts.createUserAsync({ username, password: this.password }); + this.user1 = await Meteor.users.findOneAsync(this.userId1); + let argon2Params = getUserHashArgon2Params(this.user1); + test.equal(argon2Params.type, Accounts._argon2Type()); + test.equal(argon2Params.memoryCost, Accounts._argon2MemoryCost()); + test.equal(argon2Params.timeCost, Accounts._argon2TimeCost()); + test.equal(argon2Params.parallelism, Accounts._argon2Parallelism()); + + + // When a custom number of argon2 TimeCost is set via Accounts.config, + // and an account was already created using the default number of TimeCost, + // make sure that a new hash is created (and stored) using the new number + // of TimeCost, the next time the password is checked. + this.customType = "argon2d"; // argon2.argon2d = 2 + this.customTimeCost = 4; + this.customMemoryCost = 32768; + this.customParallelism = 1; + Accounts._options.argon2Type = this.customType; + Accounts._options.argon2TimeCost = this.customTimeCost; + Accounts._options.argon2MemoryCost = this.customMemoryCost; + Accounts._options.argon2Parallelism = this.customParallelism; + + await Accounts._checkPasswordAsync(this.user1, this.password); + }, + async function(test) { + const defaultType = Accounts._argon2Type(); + const defaultTimeCost = Accounts._argon2TimeCost(); + const defaultMemoryCost = Accounts._argon2MemoryCost(); + const defaultParallelism = Accounts._argon2Parallelism(); + let params; + let username; + + let resolve; + const promise = new Promise(res => resolve = res); + + Meteor.setTimeout(async () => { + this.user1 = await Meteor.users.findOneAsync(this.userId1); + params = getUserHashArgon2Params(this.user1); + test.equal(params.type, 2); + test.equal(params.timeCost, this.customTimeCost); + test.equal(params.memoryCost, this.customMemoryCost); + test.equal(params.parallelism, this.customParallelism); + + // When a custom number of argon2 TimeCost is set, make sure it's + // used for new argon2 password hashes. + username = Random.id(); + const userId2 = await Accounts.createUser({ username, password: this.password }); + const user2 = await Meteor.users.findOneAsync(userId2); + params = getUserHashArgon2Params(user2); + test.equal(params.type, 2); + test.equal(params.timeCost, this.customTimeCost); + test.equal(params.memoryCost, this.customMemoryCost); + test.equal(params.parallelism, this.customParallelism); + + // Cleanup + Accounts._options.argon2Enabled = false; + Accounts._options.argon2Type = defaultType; + Accounts._options.argon2TimeCost = defaultTimeCost; + Accounts._options.argon2MemoryCost = defaultMemoryCost; + Accounts._options.argon2Parallelism = defaultParallelism; + await Meteor.users.removeAsync(this.userId1); + await Meteor.users.removeAsync(userId2); + resolve(); + }, 1000); + + return promise; + } + ]); +} diff --git a/packages/accounts-password/password_server.js b/packages/accounts-password/password_server.js index e2537f2c5c..455c071cc3 100644 --- a/packages/accounts-password/password_server.js +++ b/packages/accounts-password/password_server.js @@ -1,4 +1,5 @@ -import { hash as bcryptHash, compare as bcryptCompare } from 'bcrypt'; +import argon2 from "argon2"; +import { hash as bcryptHash, compare as bcryptCompare } from "bcrypt"; import { Accounts } from "meteor/accounts-base"; // Utility for grabbing user @@ -6,8 +7,9 @@ const getUserById = async (id, options) => await Meteor.users.findOneAsync(id, Accounts._addDefaultFieldSelector(options)); -// User records have a 'services.password.bcrypt' field on them to hold -// their hashed passwords. +// User records have two fields that are used for password-based login: +// - 'services.password.bcrypt', which stores the bcrypt password, which will be deprecated +// - 'services.password.argon2', which stores the argon2 password // // When the client sends a password to the server, it can either be a // string (the plaintext password) or an object with keys 'digest' and @@ -17,90 +19,268 @@ const getUserById = // strings. // // When the server receives a plaintext password as a string, it always -// hashes it with SHA256 before passing it into bcrypt. When the server +// hashes it with SHA256 before passing it into bcrypt / argon2. When the server // receives a password as an object, it asserts that the algorithm is -// "sha-256" and then passes the digest to bcrypt. - +// "sha-256" and then passes the digest to bcrypt / argon2. Accounts._bcryptRounds = () => Accounts._options.bcryptRounds || 10; -// Given a 'password' from the client, extract the string that we should -// bcrypt. 'password' can be one of: -// - String (the plaintext password) -// - Object with 'digest' and 'algorithm' keys. 'algorithm' must be "sha-256". -// +Accounts._argon2Enabled = () => Accounts._options.argon2Enabled || false; + +const ARGON2_TYPES = { + argon2i: argon2.argon2i, + argon2d: argon2.argon2d, + argon2id: argon2.argon2id +}; + +Accounts._argon2Type = () => ARGON2_TYPES[Accounts._options.argon2Type] || argon2.argon2id; +Accounts._argon2TimeCost = () => Accounts._options.argon2TimeCost || 2; +Accounts._argon2MemoryCost = () => Accounts._options.argon2MemoryCost || 19456; +Accounts._argon2Parallelism = () => Accounts._options.argon2Parallelism || 1; + +/** + * Extracts the string to be encrypted using bcrypt or Argon2 from the given `password`. + * + * @param {string|Object} password - The password provided by the client. It can be: + * - A plaintext string password. + * - An object with the following properties: + * @property {string} digest - The hashed password. + * @property {string} algorithm - The hashing algorithm used. Must be "sha-256". + * + * @returns {string} - The resulting password string to encrypt. + * + * @throws {Error} - If the `algorithm` in the password object is not "sha-256". + */ const getPasswordString = password => { if (typeof password === "string") { password = SHA256(password); - } else { // 'password' is an object + } + else { // 'password' is an object if (password.algorithm !== "sha-256") { throw new Error("Invalid password hash algorithm. " + - "Only 'sha-256' is allowed."); + "Only 'sha-256' is allowed."); } password = password.digest; } return password; }; -// Use bcrypt to hash the password for storage in the database. -// `password` can be a string (in which case it will be run through -// SHA256 before bcrypt) or an object with properties `digest` and -// `algorithm` (in which case we bcrypt `password.digest`). -// -const hashPassword = async password => { +/** + * Encrypt the given `password` using either bcrypt or Argon2. + * @param password can be a string (in which case it will be run through SHA256 before encryption) or an object with properties `digest` and `algorithm` (in which case we bcrypt or Argon2 `password.digest`). + * @returns {Promise} The encrypted password. + */ +const hashPassword = async (password) => { password = getPasswordString(password); - return await bcryptHash(password, Accounts._bcryptRounds()); + if (Accounts._argon2Enabled() === true) { + return await argon2.hash(password, { + type: Accounts._argon2Type(), + timeCost: Accounts._argon2TimeCost(), + memoryCost: Accounts._argon2MemoryCost(), + parallelism: Accounts._argon2Parallelism() + }); + } + else { + return await bcryptHash(password, Accounts._bcryptRounds()); + } }; // Extract the number of rounds used in the specified bcrypt hash. -const getRoundsFromBcryptHash = hash => { +const getRoundsFromBcryptHash = (hash) => { let rounds; if (hash) { - const hashSegments = hash.split('$'); + const hashSegments = hash.split("$"); if (hashSegments.length > 2) { rounds = parseInt(hashSegments[2], 10); } } return rounds; }; +Accounts._getRoundsFromBcryptHash = getRoundsFromBcryptHash; -// Check whether the provided password matches the bcrypt'ed password in -// the database user record. `password` can be a string (in which case -// it will be run through SHA256 before bcrypt) or an object with -// properties `digest` and `algorithm` (in which case we bcrypt -// `password.digest`). -// -// The user parameter needs at least user._id and user.services -Accounts._checkPasswordUserFields = {_id: 1, services: 1}; -// + +/** + * Extract readable parameters from an Argon2 hash string. + * @param {string} hash - The Argon2 hash string. + * @returns {object} An object containing the parsed parameters. + * @throws {Error} If the hash format is invalid. + */ +function getArgon2Params(hash) { + const regex = /^\$(argon2(?:i|d|id))\$v=\d+\$m=(\d+),t=(\d+),p=(\d+)/; + + const match = hash.match(regex); + + if (!match) { + throw new Error("Invalid Argon2 hash format."); + } + + const [, type, memoryCost, timeCost, parallelism] = match; + + return { + type: ARGON2_TYPES[type], + timeCost: parseInt(timeCost, 10), + memoryCost: parseInt(memoryCost, 10), + parallelism: parseInt(parallelism, 10) + }; +} + +Accounts._getArgon2Params = getArgon2Params; + +const getUserPasswordHash = user => { + return user.services?.password?.argon2 || user.services?.password?.bcrypt; +}; + +Accounts._checkPasswordUserFields = { _id: 1, services: 1 }; + +const isBcrypt = (hash) => { + // bcrypt hashes start with $2a$ or $2b$ + return hash.startsWith("$2"); +}; + +const isArgon = (hash) => { + // argon2 hashes start with $argon2i$, $argon2d$ or $argon2id$ + return hash.startsWith("$argon2"); +} + +const updateUserPasswordDefered = (user, formattedPassword) => { + Meteor.defer(async () => { + await updateUserPassword(user, formattedPassword); + }); +}; + +/** + * Hashes the provided password and returns an object that can be used to update the user's password. + * @param formattedPassword + * @returns {Promise<{$set: {"services.password.bcrypt": string}}|{$unset: {"services.password.bcrypt": number}, $set: {"services.password.argon2": string}}>} + */ +const getUpdatorForUserPassword = async (formattedPassword) => { + const encryptedPassword = await hashPassword(formattedPassword); + if (Accounts._argon2Enabled() === false) { + return { + $set: { + "services.password.bcrypt": encryptedPassword + }, + $unset: { + "services.password.argon2": 1 + } + }; + } + else if (Accounts._argon2Enabled() === true) { + return { + $set: { + "services.password.argon2": encryptedPassword + }, + $unset: { + "services.password.bcrypt": 1 + } + }; + } +}; + +const updateUserPassword = async (user, formattedPassword) => { + const updator = await getUpdatorForUserPassword(formattedPassword); + await Meteor.users.updateAsync({ _id: user._id }, updator); +}; + +/** + * Checks whether the provided password matches the hashed password stored in the user's database record. + * + * @param {Object} user - The user object containing at least: + * @property {string} _id - The user's unique identifier. + * @property {Object} services - The user's services data. + * @property {Object} services.password - The user's password object. + * @property {string} [services.password.argon2] - The Argon2 hashed password. + * @property {string} [services.password.bcrypt] - The bcrypt hashed password, deprecated + * + * @param {string|Object} password - The password provided by the client. It can be: + * - A plaintext string password. + * - An object with the following properties: + * @property {string} digest - The hashed password. + * @property {string} algorithm - The hashing algorithm used. Must be "sha-256". + * + * @returns {Promise} - A result object with the following properties: + * @property {string} userId - The user's unique identifier. + * @property {Object} [error] - An error object if the password does not match or an error occurs. + * + * @throws {Error} - If an unexpected error occurs during the process. + */ const checkPasswordAsync = async (user, password) => { const result = { userId: user._id }; const formattedPassword = getPasswordString(password); - const hash = user.services.password.bcrypt; - const hashRounds = getRoundsFromBcryptHash(hash); + const hash = getUserPasswordHash(user); - if (! await bcryptCompare(formattedPassword, hash)) { - result.error = Accounts._handleError("Incorrect password", false); - } else if (hash && Accounts._bcryptRounds() != hashRounds) { - // The password checks out, but the user's bcrypt hash needs to be updated. - Meteor.defer(async () => { - await Meteor.users.updateAsync({ _id: user._id }, { - $set: { - 'services.password.bcrypt': - await bcryptHash(formattedPassword, Accounts._bcryptRounds()) + const argon2Enabled = Accounts._argon2Enabled(); + if (argon2Enabled === false) { + if (isArgon(hash)) { + // this is a rollback feature, enabling to switch back from argon2 to bcrypt if needed + // TODO : deprecate this + console.warn("User has an argon2 password and argon2 is not enabled, rolling back to bcrypt encryption"); + const match = await argon2.verify(hash, formattedPassword); + if (!match) { + result.error = Accounts._handleError("Incorrect password", false); + } + else{ + // The password checks out, but the user's stored password needs to be updated to argon2 + updateUserPasswordDefered(user, { digest: formattedPassword, algorithm: "sha-256" }); + } + } + else { + const hashRounds = getRoundsFromBcryptHash(hash); + const match = await bcryptCompare(formattedPassword, hash); + if (!match) { + result.error = Accounts._handleError("Incorrect password", false); + } + else if (hash) { + const paramsChanged = hashRounds !== Accounts._bcryptRounds(); + // The password checks out, but the user's bcrypt hash needs to be updated + // to match current bcrypt settings + if (paramsChanged === true) { + updateUserPasswordDefered(user, { digest: formattedPassword, algorithm: "sha-256" }); } - }); - }); + } + } } + else if (argon2Enabled === true) { + if (isBcrypt(hash)) { + // migration code from bcrypt to argon2 + const match = await bcryptCompare(formattedPassword, hash); + if (!match) { + result.error = Accounts._handleError("Incorrect password", false); + } + else { + // The password checks out, but the user's stored password needs to be updated to argon2 + updateUserPasswordDefered(user, { digest: formattedPassword, algorithm: "sha-256" }); + } + } + else { + // argon2 password + const argon2Params = getArgon2Params(hash); + const match = await argon2.verify(hash, formattedPassword); + if (!match) { + result.error = Accounts._handleError("Incorrect password", false); + } + else if (hash) { + const paramsChanged = argon2Params.memoryCost !== Accounts._argon2MemoryCost() || + argon2Params.timeCost !== Accounts._argon2TimeCost() || + argon2Params.parallelism !== Accounts._argon2Parallelism() || + argon2Params.type !== Accounts._argon2Type(); + if (paramsChanged === true) { + // The password checks out, but the user's argon2 hash needs to be updated with the right params + updateUserPasswordDefered(user, { digest: formattedPassword, algorithm: "sha-256" }); + } + } + } + } + return result; }; -Accounts._checkPasswordAsync = checkPasswordAsync; +Accounts._checkPasswordAsync = checkPasswordAsync; /// /// LOGIN @@ -185,9 +365,7 @@ Accounts.registerLoginHandler("password", async options => { Accounts._handleError("User not found"); } - - if (!user.services || !user.services.password || - !user.services.password.bcrypt) { + if (!getUserPasswordHash(user)) { Accounts._handleError("User has no password set"); } @@ -267,51 +445,54 @@ Accounts.setUsername = // `digest` and `algorithm` (representing the SHA256 of the password). Meteor.methods( { - changePassword: async function (oldPassword, newPassword) { - check(oldPassword, passwordValidator); - check(newPassword, passwordValidator); + changePassword: async function(oldPassword, newPassword) { + check(oldPassword, passwordValidator); + check(newPassword, passwordValidator); - if (!this.userId) { - throw new Meteor.Error(401, "Must be logged in"); - } + if (!this.userId) { + throw new Meteor.Error(401, "Must be logged in"); + } - const user = await getUserById(this.userId, {fields: { - services: 1, - ...Accounts._checkPasswordUserFields, - }}); - if (!user) { - Accounts._handleError("User not found"); - } + const user = await getUserById(this.userId, { + fields: { + services: 1, + ...Accounts._checkPasswordUserFields + } + }); + if (!user) { + Accounts._handleError("User not found"); + } - if (!user.services || !user.services.password || !user.services.password.bcrypt) { - Accounts._handleError("User has no password set"); - } + if (!getUserPasswordHash(user)) { + Accounts._handleError("User has no password set"); + } - const result = await checkPasswordAsync(user, oldPassword); - if (result.error) { - throw result.error; - } + const result = await checkPasswordAsync(user, oldPassword); + if (result.error) { + throw result.error; + } - const hashed = await hashPassword(newPassword); + // It would be better if this removed ALL existing tokens and replaced + // the token for the current connection with a new one, but that would + // be tricky, so we'll settle for just replacing all tokens other than + // the one for the current connection. + const currentToken = Accounts._getLoginToken(this.connection.id); + const updator = await getUpdatorForUserPassword(newPassword); - // It would be better if this removed ALL existing tokens and replaced - // the token for the current connection with a new one, but that would - // be tricky, so we'll settle for just replacing all tokens other than - // the one for the current connection. - const currentToken = Accounts._getLoginToken(this.connection.id); - await Meteor.users.updateAsync( - { _id: this.userId }, - { - $set: { 'services.password.bcrypt': hashed }, - $pull: { - 'services.resume.loginTokens': { hashedToken: { $ne: currentToken } } - }, - $unset: { 'services.password.reset': 1 } + await Meteor.users.updateAsync( + { _id: this.userId }, + { + $set: updator.$set, + $pull: { + "services.resume.loginTokens": { hashedToken: { $ne: currentToken } } + }, + $unset: { "services.password.reset": 1, ...updator.$unset } + } + ); + + return { passwordChanged: true }; } - ); - - return {passwordChanged: true}; -}}); + }); // Force change the users password. @@ -320,37 +501,34 @@ Meteor.methods( * @summary Forcibly change the password for a user. * @locus Server * @param {String} userId The id of the user to update. - * @param {String} newPassword A new password for the user. + * @param {String} newPlaintextPassword A new password for the user. * @param {Object} [options] * @param {Object} options.logout Logout all current connections with this userId (default: true) * @importFromPackage accounts-base */ Accounts.setPasswordAsync = async (userId, newPlaintextPassword, options) => { - check(userId, String); - check(newPlaintextPassword, Match.Where(str => Match.test(str, String) && str.length <= Meteor.settings?.packages?.accounts?.passwordMaxLength || 256)); - check(options, Match.Maybe({ logout: Boolean })); - options = { logout: true , ...options }; + check(userId, String); + check(newPlaintextPassword, Match.Where(str => Match.test(str, String) && str.length <= Meteor.settings?.packages?.accounts?.passwordMaxLength || 256)); + check(options, Match.Maybe({ logout: Boolean })); + options = { logout: true, ...options }; - const user = await getUserById(userId, { fields: { _id: 1 } }); - if (!user) { - throw new Meteor.Error(403, "User not found"); - } + const user = await getUserById(userId, { fields: { _id: 1 } }); + if (!user) { + throw new Meteor.Error(403, "User not found"); + } - const update = { - $unset: { - 'services.password.reset': 1 - }, - $set: {'services.password.bcrypt': await hashPassword(newPlaintextPassword)} + let updator = await getUpdatorForUserPassword(newPlaintextPassword); + updator.$unset = updator.$unset || {}; + updator.$unset["services.password.reset"] = 1; + + if (options.logout) { + updator.$unset["services.resume.loginTokens"] = 1; + } + + await Meteor.users.updateAsync({ _id: user._id }, updator); }; - if (options.logout) { - update.$unset['services.resume.loginTokens'] = 1; - } - - await Meteor.users.updateAsync({_id: user._id}, update); -}; - /// /// RESETTING VIA EMAIL /// @@ -430,25 +608,32 @@ Accounts.generateResetToken = // if this method is called from the enroll account work-flow then // store the token record in 'services.password.enroll' db field // else store the token record in in 'services.password.reset' db field - if(reason === 'enrollAccount') { - await Meteor.users.updateAsync({_id: user._id}, { - $set : { - 'services.password.enroll': tokenRecord + if (reason === "enrollAccount") { + await Meteor.users.updateAsync( + { _id: user._id }, + { + $set: { + "services.password.enroll": tokenRecord + } } - }); + ); // before passing to template, update user object with new token - Meteor._ensure(user, 'services', 'password').enroll = tokenRecord; - } else { - await Meteor.users.updateAsync({_id: user._id}, { - $set : { - 'services.password.reset': tokenRecord + Meteor._ensure(user, "services", "password").enroll = tokenRecord; + } + else { + await Meteor.users.updateAsync( + { _id: user._id }, + { + $set: { + "services.password.reset": tokenRecord + } } - }); + ); // before passing to template, update user object with new token - Meteor._ensure(user, 'services', 'password').reset = tokenRecord; + Meteor._ensure(user, "services", "password").reset = tokenRecord; } - return {email, user, token}; + return { email, user, token }; }; /** @@ -534,7 +719,7 @@ Accounts.sendResetPasswordEmail = const options = await Accounts.generateOptionsForEmail(realEmail, user, url, 'resetPassword'); await Email.sendAsync(options); - if (Meteor.isDevelopment) { + if (Meteor.isDevelopment && !Meteor.isPackageTest) { console.log(`\nReset password URL: ${ url }`); } return { email: realEmail, user, token, url, options }; @@ -570,7 +755,7 @@ Accounts.sendEnrollmentEmail = await Accounts.generateOptionsForEmail(realEmail, user, url, 'enrollAccount'); await Email.sendAsync(options); - if (Meteor.isDevelopment) { + if (Meteor.isDevelopment && !Meteor.isPackageTest) { console.log(`\nEnrollment email URL: ${ url }`); } return { email: realEmail, user, token, url, options }; @@ -642,8 +827,6 @@ Meteor.methods( error: new Meteor.Error(403, "Token has invalid email address") }; - const hashed = await hashPassword(newPassword); - // NOTE: We're about to invalidate tokens on the user, who we might be // logged in as. Make sure to avoid logging ourselves out if this // happens. But also make sure not to leave the connection in a state @@ -653,6 +836,8 @@ Meteor.methods( const resetToOldToken = () => Accounts._setLoginToken(user._id, this.connection, oldToken); + const updator = await getUpdatorForUserPassword(newPassword); + try { // Update the user record by: // - Changing the password to the new one @@ -664,29 +849,36 @@ Meteor.methods( affectedRecords = await Meteor.users.updateAsync( { _id: user._id, - 'emails.address': email, - 'services.password.enroll.token': token + "emails.address": email, + "services.password.enroll.token": token }, { $set: { - 'services.password.bcrypt': hashed, - 'emails.$.verified': true + "emails.$.verified": true, + ...updator.$set }, - $unset: { 'services.password.enroll': 1 } + $unset: { + "services.password.enroll": 1, + ...updator.$unset + } }); - } else { + } + else { affectedRecords = await Meteor.users.updateAsync( { _id: user._id, - 'emails.address': email, - 'services.password.reset.token': token + "emails.address": email, + "services.password.reset.token": token }, { $set: { - 'services.password.bcrypt': hashed, - 'emails.$.verified': true + "emails.$.verified": true, + ...updator.$set }, - $unset: { 'services.password.reset': 1 } + $unset: { + "services.password.reset": 1, + ...updator.$unset + } }); } if (affectedRecords !== 1) @@ -704,15 +896,16 @@ Meteor.methods( await Accounts._clearAllLoginTokens(user._id); if (Accounts._check2faEnabled?.(user)) { - return { - userId: user._id, - error: Accounts._handleError( - 'Changed password, but user not logged in because 2FA is enabled', - false, - '2fa-enabled' - ), - }; - }return { userId: user._id }; + return { + userId: user._id, + error: Accounts._handleError( + 'Changed password, but user not logged in because 2FA is enabled', + false, + '2fa-enabled' + ), + }; + } + return { userId: user._id }; } ); } @@ -748,7 +941,7 @@ Accounts.sendVerificationEmail = const url = Accounts.urls.verifyEmail(token, extraParams); const options = await Accounts.generateOptionsForEmail(realEmail, user, url, 'verifyEmail'); await Email.sendAsync(options); - if (Meteor.isDevelopment) { + if (Meteor.isDevelopment && !Meteor.isPackageTest) { console.log(`\nVerification email URL: ${ url }`); } return { email: realEmail, user, token, url, options }; @@ -990,7 +1183,13 @@ const createUser = const user = { services: {} }; if (password) { const hashed = await hashPassword(password); - user.services.password = { bcrypt: hashed }; + const argon2Enabled = Accounts._argon2Enabled(); + if (argon2Enabled === false) { + user.services.password = { bcrypt: hashed }; + } + else { + user.services.password = { argon2: hashed }; + } } return await Accounts._createUserCheckingDuplicates({ user, email, username, options }); @@ -1074,17 +1273,7 @@ Accounts.createUserVerifyingEmail = // method calling Accounts.createUser could set? // -Accounts.createUserAsync = - async (options, callback) => { - options = { ...options }; - - // XXX allow an optional callback? - if (callback) { - throw new Error("Accounts.createUser with callback not supported on the server yet."); - } - - return createUser(options); - }; +Accounts.createUserAsync = createUser // Create user directly on the server. // diff --git a/packages/accounts-password/password_tests.js b/packages/accounts-password/password_tests.js index 88461e7d91..e488c9a89e 100644 --- a/packages/accounts-password/password_tests.js +++ b/packages/accounts-password/password_tests.js @@ -10,7 +10,7 @@ const makeTestConnAsync = }) const simplePollAsync = (fn) => new Promise((resolve, reject) => simplePoll(fn,resolve,reject)) -function hashPassword(password) { +function hashPasswordWithSha(password) { return { digest: SHA256(password), algorithm: "sha-256" @@ -486,7 +486,7 @@ if (Meteor.isClient) (() => { function (test, expect) { this.secondConn = DDP.connect(Meteor.absoluteUrl()); this.secondConn.call('login', - { user: { username: this.username }, password: hashPassword(this.password) }, + { user: { username: this.username }, password: hashPasswordWithSha(this.password) }, expect((err, result) => { test.isFalse(err); this.secondConn.setUserId(result.id); @@ -802,7 +802,7 @@ if (Meteor.isClient) (() => { // Can update own profile using ID. await Meteor.users.updateAsync( this.userId, { $set: { 'profile.updated': 42 } }, - ); + ); test.equal(42, Meteor.user().profile.updated); }, logoutStep @@ -1212,10 +1212,10 @@ if (Meteor.isServer) (() => { // This test properly belongs in accounts-base/accounts_tests.js, but // this is where the tests that actually log in are. - Tinytest.addAsync('accounts - user() out of context', async test => { + Tinytest.addAsync('accounts - userAsync() out of context', async test => { await test.throwsAsync( async () => - await Meteor.user() + await Meteor.userAsync() ); await Meteor.users.removeAsync({}); }); @@ -1230,7 +1230,7 @@ if (Meteor.isServer) (() => { const username = Random.id(); const id = await Accounts.createUser({ username: username, - password: hashPassword('password') + password: hashPasswordWithSha('password') }); const { @@ -1245,7 +1245,7 @@ if (Meteor.isServer) (() => { const result = await clientConn.callAsync('login', { user: { username: username }, - password: hashPassword('password') + password: hashPasswordWithSha('password') }); test.isTrue(result); @@ -1278,7 +1278,7 @@ if (Meteor.isServer) (() => { const userId = await Accounts.createUser({ username: username, email: email, - password: hashPassword("old-password") + password: hashPasswordWithSha("old-password") }); const user = await Meteor.users.findOneAsync(userId); @@ -1297,7 +1297,7 @@ if (Meteor.isServer) (() => { await test.throwsAsync( async () => - await Meteor.callAsync("resetPassword", resetPasswordToken, hashPassword("new-password")), + await Meteor.callAsync("resetPassword", resetPasswordToken, hashPasswordWithSha("new-password")), /Token has invalid email address/ ); await test.throwsAsync( @@ -1306,7 +1306,7 @@ if (Meteor.isServer) (() => { "login", { user: { username: username }, - password: hashPassword("new-password") + password: hashPasswordWithSha("new-password") } ), /Something went wrong. Please check your credentials./); @@ -1321,7 +1321,7 @@ if (Meteor.isServer) (() => { const userId = await Accounts.createUser({ username: username, email: email, - password: hashPassword("old-password") + password: hashPasswordWithSha("old-password") }); const user = await Meteor.users.findOneAsync(userId); @@ -1338,11 +1338,11 @@ if (Meteor.isServer) (() => { test.isTrue(await clientConn.callAsync( "resetPassword", resetPasswordToken, - hashPassword("new-password") + hashPasswordWithSha("new-password") )); test.isTrue(await clientConn.callAsync("login", { user: { username }, - password: hashPassword("new-password") + password: hashPasswordWithSha("new-password") })); }); @@ -1355,7 +1355,7 @@ if (Meteor.isServer) (() => { const userId = await Accounts.createUser({ username: username, email: email, - password: hashPassword("old-password") + password: hashPasswordWithSha("old-password") }); const user = await Meteor.users.findOneAsync(userId); @@ -1373,7 +1373,7 @@ if (Meteor.isServer) (() => { await Meteor.users.updateAsync(userId, { $set: { "services.password.reset.when": new Date(Date.now() + -5 * 24 * 3600 * 1000) } }); try { - await Meteor.callAsync("resetPassword", resetPasswordToken, hashPassword("new-password")) + await Meteor.callAsync("resetPassword", resetPasswordToken, hashPasswordWithSha("new-password")) } catch (e) { test.throws(() => { throw e; @@ -1385,7 +1385,7 @@ if (Meteor.isServer) (() => { "login", { user: { username: username }, - password: hashPassword("new-password") + password: hashPasswordWithSha("new-password") } ), /Something went wrong. Please check your credentials./); @@ -1405,7 +1405,7 @@ if (Meteor.isServer) (() => { { username: username, email: email, - password: hashPassword(password) + password: hashPasswordWithSha(password) }, ); @@ -1432,7 +1432,7 @@ if (Meteor.isServer) (() => { await Accounts.createUser( { email: email, - password: hashPassword('password') + password: hashPasswordWithSha('password') } ); await Accounts.sendResetPasswordEmail(userId, email); @@ -1452,7 +1452,7 @@ if (Meteor.isServer) (() => { await Accounts.createUser( { email: email, - password: hashPassword('password') + password: hashPasswordWithSha('password') } ); await Accounts.sendResetPasswordEmail(userId, email); @@ -1498,12 +1498,12 @@ if (Meteor.isServer) (() => { await clientConn.callAsync( "resetPassword", enrollPasswordToken, - hashPassword("new-password")) + hashPasswordWithSha("new-password")) ); test.isTrue( await clientConn.callAsync("login", { user: { username }, - password: hashPassword("new-password") + password: hashPasswordWithSha("new-password") }) ); @@ -1535,7 +1535,7 @@ if (Meteor.isServer) (() => { await Meteor.users.updateAsync(userId, { $set: { "services.password.enroll.when": new Date(Date.now() + -35 * 24 * 3600 * 1000) } }); await test.throwsAsync( - async () => await Meteor.callAsync("resetPassword", enrollPasswordToken, hashPassword("new-password")), + async () => await Meteor.callAsync("resetPassword", enrollPasswordToken, hashPasswordWithSha("new-password")), /Token expired/ ); }); @@ -1544,7 +1544,7 @@ if (Meteor.isServer) (() => { async test => { const email = `${ test.id }-intercept@example.com`; const userId = - await Accounts.createUser({ email: email, password: hashPassword('password') }); + await Accounts.createUser({ email: email, password: hashPasswordWithSha('password') }); await Accounts.sendEnrollmentEmail(userId, email); const user1 = await Meteor.users.findOneAsync(userId); @@ -1561,7 +1561,7 @@ if (Meteor.isServer) (() => { const userId = await Accounts.createUser({ email: email, - password: hashPassword('password') + password: hashPasswordWithSha('password') }); await Accounts.sendEnrollmentEmail(userId, email); @@ -1580,7 +1580,7 @@ if (Meteor.isServer) (() => { async test => { const email = `${ test.id }-intercept@example.com`; const userId = - await Accounts.createUser({ email: email, password: hashPassword('password') }); + await Accounts.createUser({ email: email, password: hashPasswordWithSha('password') }); await Accounts.sendResetPasswordEmail(userId, email); const user1 = await Meteor.users.findOneAsync(userId); @@ -1727,108 +1727,108 @@ if (Meteor.isServer) (() => { }); Tinytest.addAsync("passwords - add email when user has not an existing email", - async test => { - const userId = await Accounts.createUser({ - username: `user${ Random.id() }` - }); + async test => { + const userId = await Accounts.createUser({ + username: `user${ Random.id() }` + }); - const newEmail = `${ Random.id() }@turing.com`; - await Accounts.addEmailAsync(userId, newEmail); - const u1 = await Accounts._findUserByQuery({ id: userId }) - test.equal(u1.emails, [ - { address: newEmail, verified: false }, - ]); - }); + const newEmail = `${ Random.id() }@turing.com`; + await Accounts.addEmailAsync(userId, newEmail); + const u1 = await Accounts._findUserByQuery({ id: userId }) + test.equal(u1.emails, [ + { address: newEmail, verified: false }, + ]); + }); Tinytest.addAsync("passwords - add email when the user has an existing email " + "only differing in case", async test => { - const origEmail = `${ Random.id() }@turing.com`; - const userId = await Accounts.createUser({ - email: origEmail + const origEmail = `${ Random.id() }@turing.com`; + const userId = await Accounts.createUser({ + email: origEmail + }); + + const newEmail = `${ Random.id() }@turing.com`; + await Accounts.addEmailAsync(userId, newEmail); + + const thirdEmail = origEmail.toUpperCase(); + await Accounts.addEmailAsync(userId, thirdEmail, true); + const u1 = await Accounts._findUserByQuery({ id: userId }) + test.equal(u1.emails, [ + { address: thirdEmail, verified: true }, + { address: newEmail, verified: false } + ]); }); - const newEmail = `${ Random.id() }@turing.com`; - await Accounts.addEmailAsync(userId, newEmail); - - const thirdEmail = origEmail.toUpperCase(); - await Accounts.addEmailAsync(userId, thirdEmail, true); - const u1 = await Accounts._findUserByQuery({ id: userId }) - test.equal(u1.emails, [ - { address: thirdEmail, verified: true }, - { address: newEmail, verified: false } - ]); - }); - Tinytest.addAsync("passwords - add email should fail when there is an existing " + "user with an email only differing in case", async test => { - const user1Email = `${ Random.id() }@turing.com`; - const userId1 = await Accounts.createUser({ - email: user1Email + const user1Email = `${ Random.id() }@turing.com`; + const userId1 = await Accounts.createUser({ + email: user1Email + }); + + const user2Email = `${ Random.id() }@turing.com`; + const userId2 = await Accounts.createUser({ + email: user2Email + }); + + const dupEmail = user1Email.toUpperCase(); + await test.throwsAsync( + async () => await Accounts.addEmailAsync(userId2, dupEmail), + /Email already exists/ + ); + + const u1 = await Accounts._findUserByQuery({ id: userId1 }) + test.equal(u1.emails, [ + { address: user1Email, verified: false } + ]); + const u2 = await Accounts._findUserByQuery({ id: userId2 }) + test.equal(u2.emails, [ + { address: user2Email, verified: false } + ]); }); - const user2Email = `${ Random.id() }@turing.com`; - const userId2 = await Accounts.createUser({ - email: user2Email - }); - - const dupEmail = user1Email.toUpperCase(); - await test.throwsAsync( - async () => await Accounts.addEmailAsync(userId2, dupEmail), - /Email already exists/ - ); - - const u1 = await Accounts._findUserByQuery({ id: userId1 }) - test.equal(u1.emails, [ - { address: user1Email, verified: false } - ]); - const u2 = await Accounts._findUserByQuery({ id: userId2 }) - test.equal(u2.emails, [ - { address: user2Email, verified: false } - ]); - }); - Tinytest.addAsync("passwords - remove email", async test => { - const origEmail = `${ Random.id() }@turing.com`; - const userId = await Accounts.createUser({ - email: origEmail + const origEmail = `${ Random.id() }@turing.com`; + const userId = await Accounts.createUser({ + email: origEmail + }); + + const newEmail = `${ Random.id() }@turing.com`; + await Accounts.addEmailAsync(userId, newEmail); + + const thirdEmail = `${ Random.id() }@turing.com`; + await Accounts.addEmailAsync(userId, thirdEmail, true); + const u1 = await Accounts._findUserByQuery({ id: userId }) + test.equal(u1.emails, [ + { address: origEmail, verified: false }, + { address: newEmail, verified: false }, + { address: thirdEmail, verified: true } + ]); + + await Accounts.removeEmail(userId, newEmail); + const u2 = await Accounts._findUserByQuery({ id: userId }) + test.equal(u2.emails, [ + { address: origEmail, verified: false }, + { address: thirdEmail, verified: true } + ]); + + await Accounts.removeEmail(userId, origEmail); + const u3 = await Accounts._findUserByQuery({ id: userId }) + test.equal(u3.emails, [ + { address: thirdEmail, verified: true } + ]); }); - const newEmail = `${ Random.id() }@turing.com`; - await Accounts.addEmailAsync(userId, newEmail); - - const thirdEmail = `${ Random.id() }@turing.com`; - await Accounts.addEmailAsync(userId, thirdEmail, true); - const u1 = await Accounts._findUserByQuery({ id: userId }) - test.equal(u1.emails, [ - { address: origEmail, verified: false }, - { address: newEmail, verified: false }, - { address: thirdEmail, verified: true } - ]); - - await Accounts.removeEmail(userId, newEmail); - const u2 = await Accounts._findUserByQuery({ id: userId }) - test.equal(u2.emails, [ - { address: origEmail, verified: false }, - { address: thirdEmail, verified: true } - ]); - - await Accounts.removeEmail(userId, origEmail); - const u3 = await Accounts._findUserByQuery({ id: userId }) - test.equal(u3.emails, [ - { address: thirdEmail, verified: true } - ]); - }); - const getUserHashRounds = user => Number(user.services.password.bcrypt.substring(4, 6)); testAsyncMulti("passwords - allow custom bcrypt rounds",[ async function (test) { // Verify that a bcrypt hash generated for a new account uses the let username = Random.id(); - this.password = hashPassword('abc123'); + this.password = hashPasswordWithSha('abc123'); this.userId1 = await Accounts.createUser({ username, password: this.password }); this.user1 = await Meteor.users.findOneAsync(this.userId1); let rounds = getUserHashRounds(this.user1); @@ -1876,24 +1876,52 @@ if (Meteor.isServer) (() => { Tinytest.addAsync('passwords - extra params in email urls', async (test) => { - const username = Random.id(); - const email = `${ username }-intercept@example.com`; + const username = Random.id(); + const email = `${ username }-intercept@example.com`; - const userId = await Accounts.createUser({ - username: username, - email: email + const userId = await Accounts.createUser({ + username: username, + email: email + }); + + const extraParams = { test: 'success' }; + await Accounts.sendEnrollmentEmail(userId, email, null, extraParams); + + const [enrollPasswordEmailOptions] = + await Meteor.callAsync("getInterceptedEmails", email); + + const re = new RegExp(`${Meteor.absoluteUrl()}(\\S*)`); + const match = enrollPasswordEmailOptions.text.match(re); + const url = new URL(match) + test.equal(url.searchParams.get('test'), extraParams.test); }); - const extraParams = { test: 'success' }; - await Accounts.sendEnrollmentEmail(userId, email, null, extraParams); + Tinytest.addAsync('passwords - createUserAsync', async test => { + const username = Random.id(); + const email = `${username}-intercept@example.com`; + const password = 'password'; - const [enrollPasswordEmailOptions] = - await Meteor.callAsync("getInterceptedEmails", email); + const userId = await Accounts.createUserAsync({ + username: username, + email: email, + password: password + }); - const re = new RegExp(`${Meteor.absoluteUrl()}(\\S*)`); - const match = enrollPasswordEmailOptions.text.match(re); - const url = new URL(match) - test.equal(url.searchParams.get('test'), extraParams.test); + test.isTrue(userId, 'User ID should be returned'); + const user = await Meteor.users.findOneAsync(userId); + test.equal(user.username, username, 'Username should match'); + test.equal(user.emails[0].address, email, 'Email should match'); + + Accounts.config({ + ambiguousErrorMessages: false, + }) + + await test.throwsAsync(async () => { + await Accounts.createUserAsync({ + username: username, + email: email, + password: password + }); + }, 'already exists'); }); - })(); diff --git a/packages/accounts-password/password_tests_setup.js b/packages/accounts-password/password_tests_setup.js index 48a34c8766..50a1e95862 100644 --- a/packages/accounts-password/password_tests_setup.js +++ b/packages/accounts-password/password_tests_setup.js @@ -121,7 +121,7 @@ Accounts.config({ Meteor.methods( { testMeteorUser: - async () => await Meteor.user(), + async () => await Meteor.userAsync(), clearUsernameAndProfile: async function () { diff --git a/packages/accounts-passwordless/package.js b/packages/accounts-passwordless/package.js index 902897a01d..c9da46ee47 100644 --- a/packages/accounts-passwordless/package.js +++ b/packages/accounts-passwordless/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: 'No-password login/sign-up support for accounts', - version: '3.0.0', + version: '3.0.1', }); Package.onUse(api => { diff --git a/packages/accounts-passwordless/passwordless_server.js b/packages/accounts-passwordless/passwordless_server.js index 33925be23a..1ef908612c 100644 --- a/packages/accounts-passwordless/passwordless_server.js +++ b/packages/accounts-passwordless/passwordless_server.js @@ -220,7 +220,7 @@ Meteor.methods({ */ Accounts.sendLoginTokenEmail = async ({ userId, sequence, email, extra = {} }) => { const user = await getUserById(userId); - const url = Accounts.urls.loginToken(email, sequence); + const url = Accounts.urls.loginToken(email, sequence, extra); const options = await Accounts.generateOptionsForEmail( email, user, diff --git a/packages/allow-deny/allow-deny.js b/packages/allow-deny/allow-deny.js index faf7f774c9..802cd467dd 100644 --- a/packages/allow-deny/allow-deny.js +++ b/packages/allow-deny/allow-deny.js @@ -49,7 +49,7 @@ const CollectionPrototype = AllowDeny.CollectionPrototype; * @memberOf Mongo.Collection * @instance * @param {Object} options - * @param {Function} options.insertAsync,updateAsync,removeAsync Functions that look at a proposed modification to the database and return true if it should be allowed. + * @param {Function} options.insert,update,remove Functions that look at a proposed modification to the database and return true if it should be allowed. * @param {String[]} options.fetch Optional performance enhancement. Limits the fields that will be fetched from the database for inspection by your `update` and `remove` functions. * @param {Function} options.transform Overrides `transform` on the [`Collection`](#collections). Pass `null` to disable transformation. */ @@ -64,7 +64,7 @@ CollectionPrototype.allow = function(options) { * @memberOf Mongo.Collection * @instance * @param {Object} options - * @param {Function} options.insertAsync,updateAsync,removeAsync Functions that look at a proposed modification to the database and return true if it should be denied, even if an [allow](#allow) rule says otherwise. + * @param {Function} options.insert,update,remove Functions that look at a proposed modification to the database and return true if it should be denied, even if an [allow](#allow) rule says otherwise. * @param {String[]} options.fetch Optional performance enhancement. Limits the fields that will be fetched from the database for inspection by your `update` and `remove` functions. * @param {Function} options.transform Overrides `transform` on the [`Collection`](#collections). Pass `null` to disable transformation. */ @@ -174,9 +174,14 @@ CollectionPrototype._defineMutationMethods = function(options) { // single-ID selectors. if (!isInsert(method)) throwIfSelectorIsNotId(args[0], method); + const syncMethodName = method.replace('Async', ''); + const syncValidatedMethodName = '_validated' + method.charAt(0).toUpperCase() + syncMethodName.slice(1); + // it forces to use async validated behavior + const validatedMethodName = syncValidatedMethodName + 'Async'; + if (self._restricted) { // short circuit if there is no way it will pass. - if (self._validators[method].allow.length === 0) { + if (self._validators[syncMethodName].allow.length === 0) { throw new Meteor.Error( 403, 'Access denied. No allow validators set on restricted ' + @@ -186,11 +191,6 @@ CollectionPrototype._defineMutationMethods = function(options) { ); } - const syncMethodName = method.replace('Async', ''); - const syncValidatedMethodName = '_validated' + method.charAt(0).toUpperCase() + syncMethodName.slice(1); - // it forces to use async validated behavior on the server - const validatedMethodName = Meteor.isServer ? syncValidatedMethodName + 'Async' : syncValidatedMethodName; - args.unshift(this.userId); isInsert(method) && args.push(generatedId); return self[validatedMethodName].apply(self, args); @@ -292,7 +292,7 @@ CollectionPrototype._validatedInsertAsync = async function(userId, doc, const self = this; // call user validators. // Any deny returns true means denied. - if (await asyncSome(self._validators.insertAsync.deny, async (validator) => { + if (await asyncSome(self._validators.insert.deny, async (validator) => { const result = validator(userId, docToValidate(validator, doc, generatedId)); return Meteor._isPromise(result) ? await result : result; })) { @@ -300,7 +300,7 @@ CollectionPrototype._validatedInsertAsync = async function(userId, doc, } // Any allow returns true means proceed. Throw error if they all fail. - if (await asyncEvery(self._validators.insertAsync.allow, async (validator) => { + if (await asyncEvery(self._validators.insert.allow, async (validator) => { const result = validator(userId, docToValidate(validator, doc, generatedId)); return !(Meteor._isPromise(result) ? await result : result); })) { @@ -315,36 +315,6 @@ CollectionPrototype._validatedInsertAsync = async function(userId, doc, return self._collection.insertAsync.call(self._collection, doc); }; -CollectionPrototype._validatedInsert = function (userId, doc, - generatedId) { - const self = this; - - // call user validators. - // Any deny returns true means denied. - if (self._validators.insert.deny.some((validator) => { - return validator(userId, docToValidate(validator, doc, generatedId)); - })) { - throw new Meteor.Error(403, "Access denied"); - } - // Any allow returns true means proceed. Throw error if they all fail. - - if (self._validators.insert.allow.every((validator) => { - return !validator(userId, docToValidate(validator, doc, generatedId)); - })) { - throw new Meteor.Error(403, "Access denied"); - } - - // If we generated an ID above, insert it now: after the validation, but - // before actually inserting. - if (generatedId !== null) - doc._id = generatedId; - - return (Meteor.isServer - ? self._collection.insertAsync - : self._collection.insert - ).call(self._collection, doc); -}; - // Simulate a mongo `update` operation while validating that the access // control rules set by calls to `allow/deny` are satisfied. If all // pass, rewrite the mongo operation to use $in to set the list of @@ -414,7 +384,7 @@ CollectionPrototype._validatedUpdateAsync = async function( // call user validators. // Any deny returns true means denied. - if (await asyncSome(self._validators.updateAsync.deny, async (validator) => { + if (await asyncSome(self._validators.update.deny, async (validator) => { const factoriedDoc = transformDoc(validator, doc); const result = validator(userId, factoriedDoc, @@ -424,8 +394,9 @@ CollectionPrototype._validatedUpdateAsync = async function( })) { throw new Meteor.Error(403, "Access denied"); } + // Any allow returns true means proceed. Throw error if they all fail. - if (await asyncEvery(self._validators.updateAsync.allow, async (validator) => { + if (await asyncEvery(self._validators.update.allow, async (validator) => { const factoriedDoc = transformDoc(validator, doc); const result = validator(userId, factoriedDoc, @@ -447,102 +418,6 @@ CollectionPrototype._validatedUpdateAsync = async function( self._collection, selector, mutator, options); }; -CollectionPrototype._validatedUpdate = function( - userId, selector, mutator, options) { - const self = this; - - check(mutator, Object); - - options = Object.assign(Object.create(null), options); - - if (!LocalCollection._selectorIsIdPerhapsAsObject(selector)) - throw new Error("validated update should be of a single ID"); - - // We don't support upserts because they don't fit nicely into allow/deny - // rules. - if (options.upsert) - throw new Meteor.Error(403, "Access denied. Upserts not " + - "allowed in a restricted collection."); - - const noReplaceError = "Access denied. In a restricted collection you can only" + - " update documents, not replace them. Use a Mongo update operator, such " + - "as '$set'."; - - const mutatorKeys = Object.keys(mutator); - - // compute modified fields - const modifiedFields = {}; - - if (mutatorKeys.length === 0) { - throw new Meteor.Error(403, noReplaceError); - } - mutatorKeys.forEach((op) => { - const params = mutator[op]; - if (op.charAt(0) !== '$') { - throw new Meteor.Error(403, noReplaceError); - } else if (!hasOwn.call(ALLOWED_UPDATE_OPERATIONS, op)) { - throw new Meteor.Error( - 403, "Access denied. Operator " + op + " not allowed in a restricted collection."); - } else { - Object.keys(params).forEach((field) => { - // treat dotted fields as if they are replacing their - // top-level part - if (field.indexOf('.') !== -1) - field = field.substring(0, field.indexOf('.')); - - // record the field we are trying to change - modifiedFields[field] = true; - }); - } - }); - - const fields = Object.keys(modifiedFields); - - const findOptions = {transform: null}; - if (!self._validators.fetchAllFields) { - findOptions.fields = {}; - self._validators.fetch.forEach((fieldName) => { - findOptions.fields[fieldName] = 1; - }); - } - - const doc = self._collection.findOne(selector, findOptions); - if (!doc) // none satisfied! - return 0; - - // call user validators. - // Any deny returns true means denied. - if (self._validators.update.deny.some((validator) => { - const factoriedDoc = transformDoc(validator, doc); - return validator(userId, - factoriedDoc, - fields, - mutator); - })) { - throw new Meteor.Error(403, "Access denied"); - } - // Any allow returns true means proceed. Throw error if they all fail. - if (self._validators.update.allow.every((validator) => { - const factoriedDoc = transformDoc(validator, doc); - return !validator(userId, - factoriedDoc, - fields, - mutator); - })) { - throw new Meteor.Error(403, "Access denied"); - } - - options._forbidReplace = true; - - // Back when we supported arbitrary client-provided selectors, we actually - // rewrote the selector to include an _id clause before passing to Mongo to - // avoid races, but since selector is guaranteed to already just be an ID, we - // don't have to any more. - - return self._collection.update.call( - self._collection, selector, mutator, options); -}; - // Only allow these operations in validated updates. Specifically // whitelist operations, rather than blacklist, so new complex // operations that are added aren't automatically allowed. A complex @@ -573,14 +448,14 @@ CollectionPrototype._validatedRemoveAsync = async function(userId, selector) { // call user validators. // Any deny returns true means denied. - if (await asyncSome(self._validators.removeAsync.deny, async (validator) => { + if (await asyncSome(self._validators.remove.deny, async (validator) => { const result = validator(userId, transformDoc(validator, doc)); return Meteor._isPromise(result) ? await result : result; })) { throw new Meteor.Error(403, "Access denied"); } // Any allow returns true means proceed. Throw error if they all fail. - if (await asyncEvery(self._validators.removeAsync.allow, async (validator) => { + if (await asyncEvery(self._validators.remove.allow, async (validator) => { const result = validator(userId, transformDoc(validator, doc)); return !(Meteor._isPromise(result) ? await result : result); })) { @@ -595,43 +470,6 @@ CollectionPrototype._validatedRemoveAsync = async function(userId, selector) { return self._collection.removeAsync.call(self._collection, selector); }; -CollectionPrototype._validatedRemove = function(userId, selector) { - const self = this; - - const findOptions = {transform: null}; - if (!self._validators.fetchAllFields) { - findOptions.fields = {}; - self._validators.fetch.forEach((fieldName) => { - findOptions.fields[fieldName] = 1; - }); - } - - const doc = self._collection.findOne(selector, findOptions); - if (!doc) - return 0; - - // call user validators. - // Any deny returns true means denied. - if (self._validators.remove.deny.some((validator) => { - return validator(userId, transformDoc(validator, doc)); - })) { - throw new Meteor.Error(403, "Access denied"); - } - // Any allow returns true means proceed. Throw error if they all fail. - if (self._validators.remove.allow.every((validator) => { - return !validator(userId, transformDoc(validator, doc)); - })) { - throw new Meteor.Error(403, "Access denied"); - } - - // Back when we supported arbitrary client-provided selectors, we actually - // rewrote the selector to {_id: {$in: [ids that we found]}} before passing to - // Mongo to avoid races, but since selector is guaranteed to already just be - // an ID, we don't have to any more. - - return self._collection.remove.call(self._collection, selector); -}; - CollectionPrototype._callMutatorMethodAsync = function _callMutatorMethodAsync(name, args, options = {}) { // For two out of three mutator methods, the first argument is a selector @@ -711,6 +549,13 @@ function addValidator(collection, allowOrDeny, options) { Object.keys(options).forEach((key) => { if (!validKeysRegEx.test(key)) throw new Error(allowOrDeny + ": Invalid key: " + key); + + // TODO deprecated async config on future versions + const isAsyncKey = key.includes('Async'); + if (isAsyncKey) { + const syncKey = key.replace('Async', ''); + Meteor.deprecate(allowOrDeny + `: The "${key}" key is deprecated. Use "${syncKey}" instead.`); + } }); collection._restricted = true; @@ -740,7 +585,9 @@ function addValidator(collection, allowOrDeny, options) { options.transform ); } - collection._validators[name][allowOrDeny].push(options[name]); + const isAsyncName = name.includes('Async'); + const validatorSyncName = isAsyncName ? name.replace('Async', '') : name; + collection._validators[validatorSyncName][allowOrDeny].push(options[name]); } }); diff --git a/packages/allow-deny/package.js b/packages/allow-deny/package.js index 98a3d427a9..c4dc9d3e78 100644 --- a/packages/allow-deny/package.js +++ b/packages/allow-deny/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'allow-deny', - version: '2.0.0', + version: '2.1.0', // Brief, one-line summary of the package. summary: 'Implements functionality for allow/deny and client-side db operations', // URL to the Git repository containing the source code for this package. diff --git a/packages/babel-compiler/package.js b/packages/babel-compiler/package.js index 54c1ca804e..03822c789b 100644 --- a/packages/babel-compiler/package.js +++ b/packages/babel-compiler/package.js @@ -1,13 +1,13 @@ Package.describe({ name: "babel-compiler", summary: "Parser/transpiler for ECMAScript 2015+ syntax", - version: '7.11.1', + version: '7.11.3', }); Npm.depends({ - '@meteorjs/babel': '7.20.0', - 'json5': '2.1.1', - 'semver': '7.3.8' + '@meteorjs/babel': '7.20.1', + 'json5': '2.2.3', + 'semver': '7.6.3' }); Package.onUse(function (api) { diff --git a/packages/boilerplate-generator-tests/.npm/package/npm-shrinkwrap.json b/packages/boilerplate-generator-tests/.npm/package/npm-shrinkwrap.json deleted file mode 100644 index d749aa7131..0000000000 --- a/packages/boilerplate-generator-tests/.npm/package/npm-shrinkwrap.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "lockfileVersion": 4, - "dependencies": { - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" - } - } -} diff --git a/packages/boilerplate-generator-tests/package.js b/packages/boilerplate-generator-tests/package.js index 98a6db887d..8c95f5f034 100644 --- a/packages/boilerplate-generator-tests/package.js +++ b/packages/boilerplate-generator-tests/package.js @@ -2,7 +2,7 @@ Package.describe({ // These tests are in a separate package so that we can Npm.depend on // parse5, a html parsing library. summary: "Tests for the boilerplate-generator package", - version: '1.5.2', + version: '1.5.3', documentation: null }); @@ -13,7 +13,6 @@ Npm.depends({ Package.onTest(function (api) { api.use('ecmascript'); api.use([ - 'underscore', 'tinytest', 'boilerplate-generator' ], 'server'); diff --git a/packages/boilerplate-generator-tests/web.browser-tests.js b/packages/boilerplate-generator-tests/web.browser-tests.js index 915e2f6ebf..48c0d06fd8 100644 --- a/packages/boilerplate-generator-tests/web.browser-tests.js +++ b/packages/boilerplate-generator-tests/web.browser-tests.js @@ -1,6 +1,5 @@ import { parse, serialize } from 'parse5'; import { generateHTMLForArch } from './test-lib'; -import { _ } from 'meteor/underscore'; Tinytest.addAsync( "boilerplate-generator-tests - web.browser - basic output", @@ -66,10 +65,6 @@ Tinytest.addAsync( async function (test) { const newHtml = await generateHTMLForArch("web.browser", false); - _.templateSettings = { - interpolate: /\{\{(.+?)\}\}/g - }; - test.matches(newHtml, /foo="foobar"/); test.matches(newHtml, /]*href="[^<>]*bootstrap[^<>]*">/); test.matches(newHtml, /]*src="[^<>]*templating[^<>]*">/); diff --git a/packages/boilerplate-generator-tests/web.cordova-tests.js b/packages/boilerplate-generator-tests/web.cordova-tests.js index 04eb249ac5..ffe954105e 100644 --- a/packages/boilerplate-generator-tests/web.cordova-tests.js +++ b/packages/boilerplate-generator-tests/web.cordova-tests.js @@ -1,6 +1,5 @@ import { parse, serialize } from 'parse5'; import { generateHTMLForArch } from './test-lib'; -import { _ } from 'meteor/underscore'; Tinytest.addAsync( "boilerplate-generator-tests - web.cordova - basic output", @@ -60,9 +59,7 @@ Tinytest.addAsync( async function (test) { const newHtml = await generateHTMLForArch('web.cordova', false); - _.templateSettings = { - interpolate: /\{\{(.+?)\}\}/g - }; + test.matches(newHtml, /]*href="[^<>]*bootstrap[^<>]*">/); test.matches(newHtml, /]*src="[^<>]*templating[^<>]*">/); test.matches(newHtml, /'; Tinytest.add("webapp - npm modules", function (test) { // Make sure the version number looks like a version number. - test.matches(WebAppInternals.NpmModules.express.version, /^4\.(\d+)\.(\d+)/); + test.matches(WebAppInternals.NpmModules.express.version, /^5\.(\d+)\.(\d+)/); test.equal(typeof(WebAppInternals.NpmModules.express.module), 'function'); }); @@ -364,3 +363,53 @@ Tinytest.addAsync( test.isTrue(/__meteor_runtime_config__ = (.*customKey[^"].*customValue.*)/.test(html)); } ); + +Tinytest.addAsync("webapp - parse url queries", async function (test) { + WebApp.handlers.get("/queries", async (req, res) => { + res.json(req.query); + }); + + const queriesTestCases = [ + 'planet=Mars', + 'galaxy=Andromeda&star=Betelgeuse', + 'spacecraft=Voyager%202', + 'meteor=Perseid&meteor=Leonid', + 'astronaut[name]=Neil&astronaut[mission]=Apollo%2011', + 'galaxy[name]=Milky%20Way&galaxy[diameter]=105700', + 'constellation[name]=Orion&constellation[stars][]=Betelgeuse&constellation[stars][]=Rigel', + 'galaxy[name]=Andromeda&galaxy[age]=10&meteors[]=Perseid&meteors[]=Geminid', + 'astronaut[name]=Buzz&astronaut[missions][first]=Apollo%2011&astronaut[missions][second]=Apollo%2022', + 'spacecraft[]=Voyager&spacecraft[]=Pioneer&spacecraft[0][type]=orbiter', + 'comet=Halley&status=active%20comet', + 'planet=&galaxy=' + ]; + const queryResults = [ + { planet: 'Mars' }, + { galaxy: 'Andromeda', star: 'Betelgeuse' }, + { spacecraft: 'Voyager 2' }, + { meteor: ['Perseid', 'Leonid'] }, + { astronaut: { name: 'Neil', mission: 'Apollo 11' } }, + { galaxy: { name: 'Milky Way', diameter: '105700' } }, + { constellation: { name: 'Orion', stars: ['Betelgeuse', 'Rigel'] } }, + { + galaxy: { name: 'Andromeda', age: '10' }, + meteors: ['Perseid', 'Geminid'] + }, + { + astronaut: { + name: 'Buzz', + missions: { first: 'Apollo 11', second: 'Apollo 22' } + } + }, + { spacecraft: ['Voyager', 'Pioneer', { type: 'orbiter' }] }, + { comet: 'Halley', status: 'active comet' }, + { planet: '', galaxy: '' } + ]; + let i = 0; + for await (const queriesTestCase of queriesTestCases) { + const resp = await asyncGet(`${Meteor.absoluteUrl()}/queries?${queriesTestCase}`); + const queryParsed = JSON.parse(resp.content); + test.equal(queryParsed, queryResults[i]); + i++; + } +}); diff --git a/scripts/admin/bump-all-version-numbers.js b/scripts/admin/bump-all-version-numbers.js index 915c999296..a4e45f0d12 100644 --- a/scripts/admin/bump-all-version-numbers.js +++ b/scripts/admin/bump-all-version-numbers.js @@ -1,21 +1,20 @@ // run as node scripts/admin/bump-all-version-numbers.js -var fs = require("fs"); -var _ = require("../../packages/underscore/underscore.js")._; +const fs = require("fs"); -var packageNames = _.rest(process.argv, 2); +const packageNames = process.argv.slice(2); -_.each(packageNames, function (name) { +packageNames.forEach(name => { // name = "packages/" + name + "/package.js"; - var content = fs.readFileSync(name, {encoding: "utf-8"}); + const content = fs.readFileSync(name, {encoding: "utf-8"}); - var match = content.match(/\d+\.\d+\.\d+-winr.\d+/); + const match = content.match(/\d+\.\d+\.\d+-winr.\d+/); if (match) { - var versionNumber = match[0]; - var s = versionNumber.split("."); - s[3] = (parseInt(s[3], 10) + 1); - var incremented = s.join("."); + const versionNumber = match[0]; + const s = versionNumber.split("."); + s[3] = (parseInt(s[3], 10) + 1).toString(); + const incremented = s.join("."); content = content.replace(versionNumber, incremented); console.log(match[0], match[1], incremented); diff --git a/scripts/admin/check-legacy-syntax/check-syntax.js b/scripts/admin/check-legacy-syntax/check-syntax.js index 09fd3acd3a..d5a1db490f 100644 --- a/scripts/admin/check-legacy-syntax/check-syntax.js +++ b/scripts/admin/check-legacy-syntax/check-syntax.js @@ -34,6 +34,7 @@ const packages = { // Ignored server files that has a features > 2016 ignoredFiles: [ "async_helpers.js", + "asynchronous_queue.js", ] }, "accounts-ui": {}, @@ -83,7 +84,7 @@ const packages = { "logic-solver": { // TODO: Fibers - Legacy // Revisit when we remove fibers, this may break for legacy - ignoredFiles: ["logic.js", "optimize.js"], + ignoredFiles: ["logic.js", "optimize.js", "minisat.js"], }, "meteor-base": {}, "mobile-experience": {}, @@ -118,7 +119,6 @@ const packages = { typescript: { serverFiles: ["plugin.js"], }, - underscore: {}, url: {}, }; diff --git a/scripts/admin/meteor-release-experimental.json b/scripts/admin/meteor-release-experimental.json index 6c5da70cb0..da73ff6743 100644 --- a/scripts/admin/meteor-release-experimental.json +++ b/scripts/admin/meteor-release-experimental.json @@ -1,6 +1,6 @@ { "track": "METEOR", - "version": "3.0.4-rc.0", + "version": "3.2-beta.2", "recommended": false, "official": false, "description": "Meteor experimental release" diff --git a/scripts/admin/meteor-release-official.json b/scripts/admin/meteor-release-official.json index db3db5140f..5a1a6ed980 100644 --- a/scripts/admin/meteor-release-official.json +++ b/scripts/admin/meteor-release-official.json @@ -1,6 +1,6 @@ { "track": "METEOR", - "version": "3.0.4", + "version": "3.2", "recommended": false, "official": true, "description": "The Official Meteor Distribution" diff --git a/scripts/admin/update-semver/package-lock.json b/scripts/admin/update-semver/package-lock.json index 80901d6328..10b77a0c9f 100644 --- a/scripts/admin/update-semver/package-lock.json +++ b/scripts/admin/update-semver/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "semver": "^7.3.8" + "semver": "^7.5.2" } }, "node_modules/lru-cache": { @@ -24,9 +24,10 @@ } }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -53,9 +54,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", + "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", "requires": { "lru-cache": "^6.0.0" } diff --git a/scripts/admin/update-semver/package.json b/scripts/admin/update-semver/package.json index 3ba0bf697b..4f875a48cf 100644 --- a/scripts/admin/update-semver/package.json +++ b/scripts/admin/update-semver/package.json @@ -11,6 +11,6 @@ "author": "grubba27", "license": "MIT", "dependencies": { - "semver": "^7.3.8" + "semver": "^7.5.2" } } diff --git a/scripts/build-dev-bundle-common.sh b/scripts/build-dev-bundle-common.sh index 502bfc0a10..f60e62fbee 100644 --- a/scripts/build-dev-bundle-common.sh +++ b/scripts/build-dev-bundle-common.sh @@ -5,10 +5,10 @@ set -u UNAME=$(uname) ARCH=$(uname -m) -NODE_VERSION=20.18.0 -MONGO_VERSION_64BIT=7.0.5 +NODE_VERSION=22.14.0 +MONGO_VERSION_64BIT=7.0.16 MONGO_VERSION_32BIT=3.2.22 -NPM_VERSION=10.8.2 +NPM_VERSION=10.9.2 if [ "$UNAME" == "Linux" ] ; then diff --git a/scripts/dev-bundle-server-package.js b/scripts/dev-bundle-server-package.js index f7a4cc12f3..ef92f51625 100644 --- a/scripts/dev-bundle-server-package.js +++ b/scripts/dev-bundle-server-package.js @@ -11,7 +11,7 @@ var packageJson = { // Keep the versions of these packages consistent with the versions // found in dev-bundle-tool-package.js. promise: "8.3.0", - "@meteorjs/reify": "0.25.3", + "@meteorjs/reify": "0.25.4", "@babel/parser": "7.25.0", "lru-cache": "6.0.0", underscore: "1.13.7", diff --git a/scripts/dev-bundle-tool-package.js b/scripts/dev-bundle-tool-package.js index 2faa32bcdf..04aaa1029a 100644 --- a/scripts/dev-bundle-tool-package.js +++ b/scripts/dev-bundle-tool-package.js @@ -10,14 +10,14 @@ var packageJson = { dependencies: { // Explicit dependency because we are replacing it with a bundled version // and we want to make sure there are no dependencies on a higher version - npm: "10.8.2", - "node-gyp": "9.4.0", + npm: "10.9.2", + "node-gyp": "10.2.0", "@mapbox/node-pre-gyp": "1.0.11", - typescript: "5.6.2", + typescript: "5.6.3", "@meteorjs/babel": "7.20.0", // Keep the versions of these packages consistent with the versions // found in dev-bundle-server-package.js. - "@meteorjs/reify": "0.25.3", + "@meteorjs/reify": "0.25.4", // So that Babel can emit require("@babel/runtime/helpers/...") calls. "@babel/runtime": "7.25.0", // For backwards compatibility with isopackets that still depend on diff --git a/scripts/generate-dev-bundle.ps1 b/scripts/generate-dev-bundle.ps1 index d86e6ddf7b..f96509b205 100644 --- a/scripts/generate-dev-bundle.ps1 +++ b/scripts/generate-dev-bundle.ps1 @@ -185,9 +185,8 @@ Function Add-NodeAndNpm { # Let's install the npm version we really want. Write-Host "Installing npm@${NPM_VERSION}..." -ForegroundColor Magenta - & "$tempNpmCmd" install --prefix="$dirLib" --no-bin-links --save ` - --cache="$dirNpmCache" --nodedir="$dirTempNode" npm@${NPM_VERSION} | - Write-Debug + Write-Host (& "$tempNpmCmd" install --prefix="$dirLib" --no-bin-links --save ` + --cache="$dirNpmCache" --nodedir="$dirTempNode" npm@${NPM_VERSION} 2>&1) if ($LASTEXITCODE -ne 0) { throw "Couldn't install npm@${NPM_VERSION}." diff --git a/scripts/generate-dev-bundle.sh b/scripts/generate-dev-bundle.sh index 8e0de641e8..792539fe66 100755 --- a/scripts/generate-dev-bundle.sh +++ b/scripts/generate-dev-bundle.sh @@ -70,9 +70,8 @@ case $OS in ;; esac - if [ $OS = "macos" ] && [ "$(uname -m)" = "arm64" ] ; then - MONGO_NAME="mongodb-${OS}-x86_64-${MONGO_VERSION}" + MONGO_NAME="mongodb-${OS}-arm64-${MONGO_VERSION}" elif [ $OS = "linux" ] && [ "$ARCH" = "aarch64" ] ; then MONGO_NAME="mongodb-linux-aarch64-ubuntu2204-${MONGO_VERSION}" else @@ -84,6 +83,13 @@ MONGO_URL="${MONGO_BASE_URL}/${MONGO_TGZ}" echo "Downloading Mongo from ${MONGO_URL}" curl -L "${MONGO_URL}" | tar zx +# The tarball outputs as folder name "mongodb-macos-aarch64-X.X.X" even though the URL and the tarball name suggest "mongodb-macos-arm64-X.X.X" +# So we need to rename the folder to match the expected folder name +# Watch out for newer versions of the tarball that might already be named correctly +if [ $OS = "macos" ] && [ "$(uname -m)" = "arm64" ] ; then + MONGO_NAME=$(echo "$MONGO_NAME" | sed 's/arm64/aarch64/g') +fi + # Put Mongo binaries in the right spot (mongodb/bin) mkdir -p "mongodb/bin" mv "${MONGO_NAME}/bin/mongod" "mongodb/bin" diff --git a/tools/PERFORMANCE.md b/tools/PERFORMANCE.md index 7a8864bd84..635f84f78e 100644 --- a/tools/PERFORMANCE.md +++ b/tools/PERFORMANCE.md @@ -167,6 +167,94 @@ The Profiler is activated using `Profile.run(.)`, which will cause a report to b In addition to tool code, you can also use `Profile` in compiler plugins. If you want to use `Profile` in packages that are loaded into the tool (e.g. packages depended on by compiler plugins, or specially loaded into the tool as isopackets), you should always test `(typeof Profile !== 'undefined')` before accessing `Profile`, or pass it in from the tool as an option. +## Inspector Profiling + +In addition to the standard profiler, Meteor includes a more advanced profiling system based on Node.js's `inspector` module. This system generates `.cpuprofile` files that provide much more detailed performance data. + +### How to Use Inspector Profiling + +To enable inspector profiling, you need to specify which functions you want to profile through the `METEOR_INSPECT` environment variable: + +```bash +# Profile multiple functions +METEOR_INSPECT=bundler.bundle,compiler.compile meteor build ./output-build +``` + +Complete list for METEOR_INSPECT: +- bundler.bundle +- compiler.compile +- Babel.compile +- _readProjectMetadata +- initializeCatalog +- _downloadMissingPackages +- _saveChangeMetadata +- _realpath +- package-client + + +### Additional Configuration Options + +You can customize the inspector profiling behavior with the following environment variables: + +```bash +# Identifier for profile files +METEOR_INSPECT_CONTEXT=context_name + +# Directory where .cpuprofile files will be saved (default: ./profiling) +METEOR_INSPECT_OUTPUT=/path/to/directory + +# Sampling interval in ms - lower values = more details but uses more memory +METEOR_INSPECT_INTERVAL=500 + +# Maximum profile size in MB (default: 2000) +METEOR_INSPECT_MAX_SIZE=1000 +``` + +### Viewing the Results + +#### Chrome DevTools +The generated `.cpuprofile` files can be visualized in Chrome DevTools: + +1. Open Chrome DevTools +2. Go to the "Performance" or "Profiler" tab +3. Click "Load Profile" and select the generated .cpuprofile file + +#### Discoveryjs cpupro + +For a opensource interactive analysis of your `.cpuprofile` files, you can use [cpupro](https://discoveryjs.github.io/cpupro/), an open-source CPU profile viewer: + +1. Visit https://discoveryjs.github.io/cpupro/ in your browser +2. Drag and drop your `.cpuprofile` file onto the interface +3. Use the interactive visualization to explore your profile data + +cpupro offers several advantages over Chrome DevTools: +- Better handling of large profiles +- More flexible filtering options +- Advanced search capabilities +- Multiple visualization modes +- Ability to compare different profiles + +You can also run cpupro locally by installing it via npm: + + +### Important Considerations + +- Inspector profiling consumes more memory than the standard profiler +- To avoid out-of-memory (OOM) errors, consider increasing Node's memory limit: + ```bash + NODE_OPTIONS="--max-old-space-size=4096" METEOR_INSPECT=bundler.bundle meteor ... + ``` +- Very large profiles (>2GB) will be automatically truncated to avoid OOM errors + +### When to Use Inspector Profiling + +Use inspector profiling when: +- You need more detailed analysis than the standard profiler provides +- You're investigating complex performance issues +- You want to identify specific bottlenecks in heavy functions like bundler or compiler + +The standard profiler (`METEOR_PROFILE`) remains the best option for quick and general analyses, while inspector profiling is more suitable for detailed diagnostics. + ## Known Areas for Improvement These are areas to improve or investigate, along with what's known, in no diff --git a/tools/README.md b/tools/README.md index c8c68fdb8c..d41096b175 100644 --- a/tools/README.md +++ b/tools/README.md @@ -105,6 +105,34 @@ Internally, every profiled function should be wrapped into a `Profile(fn)` call. The entry point should be started explicitly with the `Profile.run()` call. Otherwise, it won't start measuring anything. +### Inspector Profiling + +In addition to the standard profiler, you can use the more advanced inspector profiling: + +```bash +# Profile a specific function (e.g. bundler.bundle) +METEOR_INSPECT=bundler.bundle meteor run + +# Profile multiple functions +METEOR_INSPECT=bundler.bundle,compiler.compile meteor build +``` + +Additional options: +```bash +# Output directory for .cpuprofile files +METEOR_INSPECT_OUTPUT=/path/to/directory + +# Name to identify profile files +METEOR_INSPECT_CONTEXT=project_xyz + +# Sampling interval (lower = more details, more memory) +METEOR_INSPECT_INTERVAL=500 +``` + +The generated `.cpuprofile` files can be opened in Chrome DevTools through the "Performance" or "Profiler" tab. + +For more details, see the `PERFORMANCE.md` file. + ## Debugging Currently, to debug the tool with `node-inspector`, you can set the ` @@ -164,4 +192,4 @@ a custom try/catch/finally system with recovery. See ## More information For more information about a particular part of Meteor Tool, see subdirectories' -README.md files and the top-level intro comments in the bigger files. +README.md files and the top-level intro comments in the bigger files. \ No newline at end of file diff --git a/tools/cli/commands.js b/tools/cli/commands.js index cf4606fdfd..7b458f9b90 100644 --- a/tools/cli/commands.js +++ b/tools/cli/commands.js @@ -20,6 +20,7 @@ const { yellow } = require('../console/console.js').colors; const inquirer = require('inquirer'); +const semver = require("semver"); var projectContextModule = require('../project-context.js'); var release = require('../packaging/release.js'); @@ -27,7 +28,7 @@ var release = require('../packaging/release.js'); const { Profile } = require("../tool-env/profile"); const open = require('open') -const { exec } = require("child_process"); +const { exec, spawn } = require("child_process"); /** * Run a command in the shell. * @param command @@ -64,6 +65,59 @@ const bash = (text, ...values) => tryRun(() => runCommand(String.raw({ raw: text }, ...values))); +/** + * Run a command in the shell and stream output in real-time. + * @param {string} command The command to execute. + * @param {string[]} args Arguments for the command. + * @return {Promise} Resolves with the exit code. + */ +const runLiveCommand = (command, args = []) => { + return new Promise((resolve, reject) => { + const childProcess = spawn(command, args, { + shell: true, + env: { ...process.env, FORCE_COLOR: "1", TERM: "xterm-256color" }, + stdio: "inherit", + }); + + const cleanup = () => { + childProcess.removeAllListeners(); + }; + + childProcess.on("close", (code) => { + cleanup(); + resolve(code); + }); + + childProcess.on("error", (error) => { + Console.error(error.message); + cleanup(); + reject(error); + }); + }); +}; + +/** + * Executes an async function and captures success or error. + * @param {() => Promise} fn The async function to execute. + * @returns {Promise<[T, null] | [null, Error]>} Result or Error tuple. + */ +const tryRunLive = async (fn) => { + try { + return [await fn(), null]; + } catch (e) { + return [null, e]; + } +}; + +/** + * Runs a Bash command with live logging. + * @param {string} text The bash command to execute. + * @param {...string} values Additional arguments. + * @returns {Promise<[number, null] | [null, Error]>} Exit code or Error. + */ +const bashLive = (text, ...values) => + tryRunLive(() => runLiveCommand(String.raw({ raw: text }, ...values))); + import { ensureDevBundleDependencies } from '../cordova/index.js'; import { CordovaRunner } from '../cordova/runner.js'; import { iOSRunTarget, AndroidRunTarget } from '../cordova/run-targets.js'; @@ -1058,7 +1112,7 @@ main.registerCommand({ process.env.GIT_TERMINAL_PROMPT = 0; const gitCommand = isWindows - ? `git clone --progress ${url} ${files.convertToOSPath(appPath)}` + ? `git clone --progress ${url} "${files.convertToOSPath(appPath)}"` : `git clone --progress ${url} ${appPath}`; const [okClone, errClone] = await bash`${gitCommand}`; const errorMessage = errClone && typeof errClone === "string" ? errClone : errClone?.message; @@ -1374,7 +1428,7 @@ ${Console.command("meteor build ../output")}`, files.pathJoin(outputPath, 'bundle')) : files.pathJoin(buildDir, 'bundle'); - await stats.recordPackages({ + stats.recordPackages({ what: "sdk.bundle", projectContext: projectContext }); @@ -1713,6 +1767,7 @@ main.registerCommand({ maxArgs: 1, options: { db: { type: Boolean }, + 'skip-cache': { type: Boolean }, }, requiresApp: true, catalogRefresh: new catalog.Refresh.Never() @@ -1739,6 +1794,10 @@ main.registerCommand({ "MONGO_URL will NOT be reset."); } + const resetMeteorNmCachePromise = options['skip-cache'] ? Promise.resolve() : files.rm_recursive_async( + files.pathJoin(options.appDir, "node_modules", ".cache", "meteor") + ); + if (options.db) { // XXX detect the case where Meteor is running the app, but // MONGO_URL was set, so we don't see a Mongo process @@ -1753,9 +1812,13 @@ main.registerCommand({ return 1; } - await files.rm_recursive_async( - files.pathJoin(options.appDir, '.meteor', 'local') - ); + await Promise.all([ + files.rm_recursive_async( + files.pathJoin(options.appDir, ".meteor", "local") + ), + resetMeteorNmCachePromise, + ]); + Console.info("Project reset."); return; } @@ -1767,9 +1830,12 @@ main.registerCommand({ return !path.includes('.meteor/local/db'); }); - var allRemovePromises = allExceptDb.map(_path => files.rm_recursive_async( - files.pathJoin(options.appDir, _path) - )); + var allRemovePromises = [ + ...allExceptDb.map((_path) => + files.rm_recursive_async(files.pathJoin(options.appDir, _path)) + ), + resetMeteorNmCachePromise + ]; await Promise.all(allRemovePromises); Console.info("Project reset."); }); @@ -3272,3 +3338,84 @@ main.registerCommand({ }, function () { throw new Error("testing stack traces!"); // #StackTraceTest this line is found in tests/source-maps.js }); + +const setupBenchmarkSuite = async (profilingPath) => { + if (await files.exists(profilingPath)) { + return; + } + const [okGitVersion, errGitVersion] = await bash`git --version`; + if (errGitVersion) throw new Error("git is not installed"); + + const parsedGitVersion = semver.coerce(okGitVersion.match(/\d+\.\d+\.\d+/)[0] || '')?.version; + const checkInvalidGitVersion = parsedGitVersion == null || semver.lt(parsedGitVersion, '2.25.0'); + if (checkInvalidGitVersion) { + throw new Error("git version is too old. Please upgrade to at least 2.25"); + } + + // Set GIT_TERMINAL_PROMPT=0 to disable prompting + process.env.GIT_TERMINAL_PROMPT = 0; + + const repoUrl = "https://github.com/meteor/performance"; + const branch = "v3.2.0"; + const gitCommand = [ + `mkdir -p ${profilingPath}`, + `git clone --no-checkout --depth 1 --filter=tree:0 --sparse --progress --branch ${branch} --single-branch ${repoUrl} ${profilingPath}`, + `cd ${profilingPath}`, + `git sparse-checkout init --cone`, + `git sparse-checkout set scripts`, + `git checkout ${branch}`, + `find ${profilingPath} -maxdepth 1 -type f -delete`, + ].join(" && "); + const [, errClone] = await bash`${gitCommand}`; + const errorMessage = errClone && typeof errClone === "string" ? errClone : errClone?.message; + if (errorMessage && errorMessage.includes("Cloning into")) { + throw new Error("error cloning benchmark"); + } + // remove .git folder from the example + await files.rm_recursive_async(files.pathJoin(profilingPath, ".git")); + Console.info( + "Meteor profiling suite cloned to: " + Console.path(profilingPath), + ); +}; + +async function doBenchmarkCommand(options) { + const isWindows = process.platform === "win32"; + if (isWindows) { + throw new Error('Profiling is not supported on Windows'); + } + + const args = process.argv.slice(3); + var projectContext = new projectContextModule.ProjectContext({ + projectDir: options.appDir, + allowIncompatibleUpdate: options['allow-incompatible-update'], + lintAppAndLocalPackages: !options['no-lint'], + }); + const profilingPath = `${projectContext.projectDir}/node_modules/.cache/meteor/performance`; + await setupBenchmarkSuite(profilingPath); + + const meteorSizeEnvs = [ + !!options['size-only'] && 'METEOR_BUNDLE_SIZE_ONLY=true', + !!options['size'] && 'METEOR_BUNDLE_SIZE=true' + ].filter(Boolean); + const meteorOptions = args.filter(arg => !['--size-only', '--size'].includes(arg)); + + const profilingCommand = [ + `${meteorSizeEnvs.join(' ')} ${profilingPath}/scripts/monitor-bundler.sh ${projectContext.projectDir} ${new Date().getTime()} ${meteorOptions.join(' ')}`.trim(), + ].join(" && "); + const [, errBenchmark] = await bashLive`${profilingCommand}`; + if (errBenchmark) { + throw new Error(errBenchmark); + } +} + +main.registerCommand( +{ + name: 'profile', + maxArgs: Infinity, + options: { + ...runCommandOptions.options || {}, + 'size': { type: Boolean }, + 'size-only': { type: Boolean }, + }, + catalogRefresh: new catalog.Refresh.Never(), +}, doBenchmarkCommand); diff --git a/tools/cli/help.txt b/tools/cli/help.txt index 20d5dc5f84..4694b83b50 100644 --- a/tools/cli/help.txt +++ b/tools/cli/help.txt @@ -1151,3 +1151,29 @@ To see the requirements for this compilation step, consult the platform requirements for 'node-gyp': https://github.com/nodejs/node-gyp + +>>> profile +Run performance profiling for the Meteor application. +Usage: meteor profile [...] + +This command runs a performance profile for the Meteor application, monitoring the +bundler process and tracking key performance metrics to help analyze the build and +bundling performance. + +Use METEOR_IDLE_TIMEOUT= to set a timeout for profiling. The default time (90s) +is usually enough for each build step to complete. If you encounter errors due to +early exits, adjust the environment variable accordingly. + +Use METEOR_CLIENT_ENTRYPOINT= to set a custom client entrypoint, and +METEOR_SERVER_ENTRYPOINT= to set a custom server entrypoint. By default, +it uses the server and client entrypoints specified in your package.json. + +Use METEOR_LOG_DIR= to set a custom log directory. + +Options: + --size monitor both bundle runtime and size + --size-only monitor only the bundle size + +The rest of options for this command are the same as those for the meteor run command. +You can pass typical runtime options (such as --settings, --exclude-archs, etc.) +to customize the profiling process. diff --git a/tools/cordova/project.js b/tools/cordova/project.js index a5abc55a52..27a6891bfd 100644 --- a/tools/cordova/project.js +++ b/tools/cordova/project.js @@ -11,6 +11,7 @@ import { Profile } from '../tool-env/profile'; import buildmessage from '../utils/buildmessage.js'; import main from '../cli/main.js'; import { execFileAsync } from '../utils/processes'; +var meteorNpm = require('../isobuild/meteor-npm'); import { cordova as cordova_lib, events as cordova_events, CordovaError } from 'cordova-lib'; @@ -462,6 +463,8 @@ to Cordova project`, async () => { let platformSpec = version ? `${platform}@${version}` : platform; await cordova_lib.platform('add', platformSpec, this.defaultOptions); + const installedPlugins = this.listInstalledPluginVersions(); + // As per Npm 8, we need now do inject a package.json file // with the dependencies so that when running any npm command // it keeps the dependencies installed. @@ -481,10 +484,11 @@ to Cordova project`, async () => { const packageJsonObj = Object.entries(packages).reduce((acc, [key, value]) => { const name = getPackageName(key); + const originalPluginVersion = installedPlugins[name]; return ({ dependencies: { ...acc.dependencies, - [name]: value.version, + [name]: originalPluginVersion || value.version, } }); }, { dependencies: { [`cordova-${platform}`]: version } }); @@ -492,6 +496,8 @@ to Cordova project`, async () => { files.pathJoin(self.projectRoot, "package.json"), JSON.stringify(packageJsonObj, null, 2) + "\n" ); + + await meteorNpm.runNpmCommand(["install"], self.projectRoot); }); } diff --git a/tools/index.js b/tools/index.js index cbb86ac7d1..82876cae67 100644 --- a/tools/index.js +++ b/tools/index.js @@ -1,23 +1,25 @@ -const { getChildProcess } = require('./cli/dev-bundle-bin-commands') +const { getChildProcess } = require("./cli/dev-bundle-bin-commands"); -getChildProcess({ isFirstTry: true }).then((child) => { - if (! child) { - // Use process.nextTick here to prevent the Promise from swallowing - // errors from the rest of the setup code. - process.nextTick(continueSetup); +getChildProcess({ isFirstTry: true }).then( + (child) => { + if (!child) { + // Use process.nextTick here to prevent the Promise from swallowing + // errors from the rest of the setup code. + process.nextTick(continueSetup); + } + // If we spawned a process to handle a dev_bundle/bin command like + // `meteor npm` or `meteor node`, then don't run any other tool code. + }, + (error) => { + process.nextTick(function () { + throw error; + }); } - // If we spawned a process to handle a dev_bundle/bin command like - // `meteor npm` or `meteor node`, then don't run any other tool code. -}, (error) => { - process.nextTick(function () { - throw error; - }); -}); +); function continueSetup() { // Set up the Babel transpiler - require('./tool-env/install-babel'); + require("./tool-env/install-babel"); // Run the Meteor command line tool - require('./cli/main'); + require("./cli/main"); } - diff --git a/tools/isobuild/bundler.js b/tools/isobuild/bundler.js index d70f31c6a5..4f1723e5e1 100644 --- a/tools/isobuild/bundler.js +++ b/tools/isobuild/bundler.js @@ -1223,13 +1223,16 @@ class Target { continue; } + var data = await resource.data; + var hash = await resource.hash; + const fileOptions = { info: 'unbuild ' + resource, arch: this.arch, - data: await resource.data, + data, cacheable: false, - hash: await resource.hash, - skipSri: !!await resource.hash + hash, + skipSri: !!hash }; const file = new File(fileOptions); @@ -1290,15 +1293,17 @@ class Target { continue; } + var data = await resource.data; + var hash = await resource.hash; let sourcePath; - if ((await resource.data) && resource.sourceRoot && resource.sourcePath) { + if (data && resource.sourceRoot && resource.sourcePath) { sourcePath = files.pathJoin(resource.sourceRoot, resource.sourcePath); } const f = new File({ info: 'resource ' + resource.servePath, arch: this.arch, - data: await resource.data, - hash: await resource.hash, + data, + hash, cacheable: false, replaceable: resource.type === 'js' && sourceBatch.hmrAvailable, sourcePath @@ -1323,14 +1328,15 @@ class Target { }); } + var sourceMap = await resource.sourceMap; // Both CSS and JS files can have source maps - if (await resource.sourceMap) { + if (sourceMap) { // XXX we used to set sourceMapRoot to // files.pathDirname(relPath) but it's unclear why. With the // currently generated source map file names, it works without it // and doesn't work well with it... maybe? we were getting // 'packages/packages/foo/bar.js' - f.setSourceMap(await resource.sourceMap, null); + f.setSourceMap(sourceMap, null); } this[resource.type].push(f); diff --git a/tools/isobuild/compiler-plugin.js b/tools/isobuild/compiler-plugin.js index 6db7ef7062..f2a52e6373 100644 --- a/tools/isobuild/compiler-plugin.js +++ b/tools/isobuild/compiler-plugin.js @@ -1720,21 +1720,21 @@ export class PackageSourceBatch { const fileHashes = []; const cacheKeyPrefix = sha1(JSON.stringify({ linkerOptions, - files: await jsResources.reduce(async (acc, inputFile) => { - const resolvedAcc = await acc; - - fileHashes.push(await inputFile.hash); - return [...resolvedAcc, { - meteorInstallOptions: inputFile.meteorInstallOptions, - absModuleId: inputFile.absModuleId, - sourceMap: !! await inputFile.sourceMap, - mainModule: inputFile.mainModule, - imported: inputFile.imported, - alias: inputFile.alias, - lazy: inputFile.lazy, - bare: inputFile.bare, - }]; - }, Promise.resolve([])) + files: await Promise.all( + jsResources.map(async (inputFile) => { + fileHashes.push(await inputFile.hash); + return { + meteorInstallOptions: inputFile.meteorInstallOptions, + absModuleId: inputFile.absModuleId, + sourceMap: !!(await inputFile.sourceMap), + mainModule: inputFile.mainModule, + imported: inputFile.imported, + alias: inputFile.alias, + lazy: inputFile.lazy, + bare: inputFile.bare, + }; + }) + ) })); const cacheKeySuffix = sha1(JSON.stringify({ LINKER_CACHE_SALT, diff --git a/tools/node-process-warnings.js b/tools/node-process-warnings.js new file mode 100644 index 0000000000..9dbc7919dd --- /dev/null +++ b/tools/node-process-warnings.js @@ -0,0 +1,22 @@ +const originalEmitWarning = process.emitWarning; + +process.emitWarning = function (message) { + /* + * A warning was introduced in Node 22: + * + * "The `punycode` module is deprecated. Please use a userland alternative instead." + * + * The problem is that punycode is deeply integrated in the Node system. It's not a + * simple direct dependency. + * + * Check these issues for more details: + * https://github.com/mathiasbynens/punycode.js/issues/137 + * https://stackoverflow.com/questions/68774489/punycode-is-deprecated-in-npm-what-should-i-replace-it-with/78946745 + * + * This warning was, besides being annoying, breaking our tests. + */ + if (message.includes("punycode")) { + return; + } + return originalEmitWarning(message); +}; diff --git a/tools/packaging/package-client.js b/tools/packaging/package-client.js index 0a10007e06..c79a2463df 100644 --- a/tools/packaging/package-client.js +++ b/tools/packaging/package-client.js @@ -466,7 +466,7 @@ exports.handlePackageServerConnectionError = function (error) { }; -// Update the package metdata in the server catalog. Chane the docs, +// Update the package metadata in the server catalog. Change the docs, // descriptions and the Git URL to new values. // // options: @@ -537,7 +537,7 @@ exports.updatePackageMetadata = async function (options) { // Upload the new Readme. await buildmessage.enterJob('uploading documentation', async function () { - var readmePath = saveReadmeToTmp(readmeInfo); + var readmePath = await saveReadmeToTmp(readmeInfo); var uploadInfo = await callPackageServerBM(conn, "createReadme", versionIdentifier); if (!uploadInfo) { diff --git a/tools/project-context.js b/tools/project-context.js index 60f5ae794a..192b3558db 100644 --- a/tools/project-context.js +++ b/tools/project-context.js @@ -318,7 +318,7 @@ Object.assign(ProjectContext.prototype, { * * @return {Promise<*|undefined>} */ - readProjectMetadata: function () { + readProjectMetadata: async function () { // don't generate a profiling report for this stage (Profile.run), // because all we do here is read a handful of files. return this._completeStagesThrough(STAGE.READ_PROJECT_METADATA); @@ -328,7 +328,7 @@ Object.assign(ProjectContext.prototype, { * @return {Promise<*|undefined>} */ initializeCatalog: function () { - return Profile.run('ProjectContext initializeCatalog', () => { + return Profile.run('ProjectContext initializeCatalog', async () => { return this._completeStagesThrough(STAGE.INITIALIZE_CATALOG); }); }, @@ -337,7 +337,7 @@ Object.assign(ProjectContext.prototype, { * @return {Promise<*|undefined>} */ resolveConstraints: function () { - return Profile.run('ProjectContext resolveConstraints', () => { + return Profile.run('ProjectContext resolveConstraints', async () => { return this._completeStagesThrough(STAGE.RESOLVE_CONSTRAINTS); }); }, @@ -347,7 +347,7 @@ Object.assign(ProjectContext.prototype, { * @return {Promise<*|undefined>} */ downloadMissingPackages: function () { - return Profile.run('ProjectContext downloadMissingPackages', () => { + return Profile.run('ProjectContext downloadMissingPackages', async () => { return this._completeStagesThrough(STAGE.DOWNLOAD_MISSING_PACKAGES); }); }, @@ -356,7 +356,7 @@ Object.assign(ProjectContext.prototype, { * @return {Promise<*|undefined>} */ buildLocalPackages: function () { - return Profile.run('ProjectContext buildLocalPackages', () => { + return Profile.run('ProjectContext buildLocalPackages', async () => { return this._completeStagesThrough(STAGE.BUILD_LOCAL_PACKAGES); }); }, @@ -365,7 +365,7 @@ Object.assign(ProjectContext.prototype, { * @return {Promise<*|undefined>} */ saveChangedMetadata: function () { - return Profile.run('ProjectContext saveChangedMetadata', () => { + return Profile.run('ProjectContext saveChangedMetadata', async () => { return this._completeStagesThrough(STAGE.SAVE_CHANGED_METADATA); }); }, @@ -376,7 +376,7 @@ Object.assign(ProjectContext.prototype, { prepareProjectForBuild: function () { // This is the same as saveChangedMetadata, but if we insert stages after // that one it will continue to mean "fully finished". - return Profile.run('ProjectContext prepareProjectForBuild', () => { + return Profile.run('ProjectContext prepareProjectForBuild', async () => { return this._completeStagesThrough(STAGE.SAVE_CHANGED_METADATA); }); }, diff --git a/tools/runners/run-app.js b/tools/runners/run-app.js index 9f5bb79ecd..9d92b57780 100644 --- a/tools/runners/run-app.js +++ b/tools/runners/run-app.js @@ -1,4 +1,3 @@ -var _ = require('underscore'); var files = require('../fs/files'); var watch = require('../fs/watch'); var bundler = require('../isobuild/bundler.js'); @@ -11,15 +10,15 @@ var Profile = require('../tool-env/profile').Profile; var release = require('../packaging/release.js'); import { pluginVersionsFromStarManifest } from '../cordova/index.js'; import { closeAllWatchers } from "../fs/safe-watcher"; -import { eachline } from "../utils/eachline"; import { loadIsopackage } from '../tool-env/isopackets.js'; +import { eachline } from "../utils/eachline"; // Parse out s as if it were a bash command line. var bashParse = function (s) { if (s.search("\"") !== -1 || s.search("'") !== -1) { throw new Error("Meteor cannot currently handle quoted SERVER_NODE_OPTIONS"); } - return _.without(s.split(/\s+/), ''); + return s.split(/\s+/).filter(Boolean); }; var getNodeOptionsFromEnvironment = function () { @@ -238,7 +237,7 @@ Object.assign(AppProcess.prototype, { files.pathJoin(self.bundlePath, 'main.js')); // Setting options - var opts = _.clone(self.nodeOptions); + var opts = JSON.parse(JSON.stringify(self.nodeOptions)); if (self.inspect) { // Always use --inspect rather than --inspect-brk, even when @@ -251,6 +250,8 @@ Object.assign(AppProcess.prototype, { opts.push("--inspect=" + self.inspect.port); } + opts.push(`--require=${files.convertToOSPath(files.pathJoin(__dirname, '../node-process-warnings.js'))}`) + opts.push(entryPoint); // Call node @@ -575,7 +576,7 @@ Object.assign(AppRunner.prototype, { if (self.recordPackageUsage) { // Maybe this doesn't need to be awaited for? - await stats.recordPackages({ + stats.recordPackages({ what: "sdk.run", projectContext: self.projectContext }); @@ -912,7 +913,7 @@ Object.assign(AppRunner.prototype, { var oldPromise = self.runPromise = self._makePromise("run"); - await refreshClient(); + refreshClient(); // Establish a watcher on the new files. setupClientWatcher(); diff --git a/tools/runners/run-mongo.js b/tools/runners/run-mongo.js index ea319eb0de..3b8b8bf603 100644 --- a/tools/runners/run-mongo.js +++ b/tools/runners/run-mongo.js @@ -1,3 +1,4 @@ +import { loadIsopackage } from '../tool-env/isopackets.js'; import { MongoExitCodes } from '../utils/mongo-exit-codes'; var files = require('../fs/files'); var utils = require('../utils/utils.js'); @@ -5,7 +6,6 @@ var fiberHelpers = require('../utils/fiber-helpers.js'); var runLog = require('./run-log.js'); var child_process = require('child_process'); var _ = require('underscore'); -import { loadIsopackage } from '../tool-env/isopackets.js'; var Console = require('../console/console.js').Console; // Given a Mongo URL, open an interactive Mongo shell on this terminal @@ -28,6 +28,17 @@ function spawnMongod(mongodPath, port, dbPath, replSetName) { mongodPath = files.convertToOSPath(mongodPath); dbPath = files.convertToOSPath(dbPath); + // Because we're spawning the process with shell: true on win32, the + // command string and args array are effectively concatenated to + // a single string and passed into the shell. If any of those paths + // contain spaces, these will get broken up on white-spaces and treated + // as separate tokens. If they are quoted, they will be parsed + // correctly by the shell. + if (process.platform === 'win32') { + mongodPath = '"' + mongodPath + '"' + dbPath = '"' + dbPath + '"' + } + let args = [ // nb: cli-test.sh and findMongoPids make strong assumptions about the // order of the arguments! Check them before changing any arguments. @@ -51,11 +62,6 @@ function spawnMongod(mongodPath, port, dbPath, replSetName) { args.push('--storageEngine', 'mmapv1', '--smallfiles'); } - // run with rosetta on mac m1 - if (process.platform === 'darwin' && process.arch === 'arm64') { - args = ['-x86_64', mongodPath, ...args]; - mongodPath = 'arch'; - } return child_process.spawn(mongodPath, args, { // Apparently in some contexts, Mongo crashes if your locale isn't set up // right. I wasn't able to reproduce it, but many people on #4019 @@ -448,11 +454,7 @@ var launchMongo = async function(options) { var yieldingMethod = async function(object, methodName, ...args) { return await Promise.race([ stopPromise, - new Promise((resolve, reject) => { - object[methodName](...args, (err, res) => { - err ? reject(err) : resolve(res); - }); - }), + object[methodName](...args), ]); }; diff --git a/tools/static-assets/server/boot.js b/tools/static-assets/server/boot.js index e6d0a7eb89..43c2668bde 100644 --- a/tools/static-assets/server/boot.js +++ b/tools/static-assets/server/boot.js @@ -498,12 +498,10 @@ var runMain = Profile("Run main()", async function () { global.__METEOR_ASYNC_LOCAL_STORAGE = new AsyncLocalStorage(); } - await Profile.run('Server startup', function() { - return global.__METEOR_ASYNC_LOCAL_STORAGE.run({}, async () => { - await loadServerBundles(); - await callStartupHooks(); - await runMain(); - }); + await Profile.run('Server startup', async function() { + await loadServerBundles(); + await callStartupHooks(); + await runMain(); }); })().catch(e => { console.log('error on boot.js', e ) diff --git a/tools/static-assets/server/npm-rebuild.js b/tools/static-assets/server/npm-rebuild.js index e0a5624d3c..ca1a3be7bf 100644 --- a/tools/static-assets/server/npm-rebuild.js +++ b/tools/static-assets/server/npm-rebuild.js @@ -28,11 +28,10 @@ var binDir = path.dirname(process.execPath); process.env.PATH = binDir + path.delimiter + process.env.PATH; var npmCmd = "npm"; +var shell = false; if (process.platform === "win32") { - var npmCmdPath = path.join(binDir, "npm.cmd"); - if (fs.existsSync(npmCmdPath)) { - npmCmd = npmCmdPath; - } + npmCmd = "npm.cmd"; + shell = true; } function rebuild(i) { @@ -41,7 +40,8 @@ function rebuild(i) { if (! dir) { // Print Node/V8/etc. versions for diagnostic purposes. spawn(npmCmd, ["version", "--json"], { - stdio: "inherit" + stdio: "inherit", + shell, }); return; @@ -49,7 +49,8 @@ function rebuild(i) { spawn(npmCmd, rebuildArgs, { cwd: path.join(__dirname, dir), - stdio: "inherit" + stdio: "inherit", + shell, }).on("exit", function (code) { if (code !== 0) { process.exit(code); diff --git a/tools/static-assets/skel-apollo/package.json b/tools/static-assets/skel-apollo/package.json index 9bf478f474..51ca845233 100644 --- a/tools/static-assets/skel-apollo/package.json +++ b/tools/static-assets/skel-apollo/package.json @@ -12,7 +12,7 @@ "@apollo/server": "^4.10.0", "@babel/runtime": "^7.23.9", "graphql": "^16.8.1", - "meteor-node-stubs": "^1.2.10", + "meteor-node-stubs": "^1.2.12", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/tools/static-assets/skel-bare/package.json b/tools/static-assets/skel-bare/package.json index 7db49f3411..f2cb0b71a7 100644 --- a/tools/static-assets/skel-bare/package.json +++ b/tools/static-assets/skel-bare/package.json @@ -6,6 +6,6 @@ }, "dependencies": { "@babel/runtime": "^7.23.5", - "meteor-node-stubs": "^1.2.10" + "meteor-node-stubs": "^1.2.12" } } diff --git a/tools/static-assets/skel-blaze/package.json b/tools/static-assets/skel-blaze/package.json index 2761af2093..59e15689c0 100644 --- a/tools/static-assets/skel-blaze/package.json +++ b/tools/static-assets/skel-blaze/package.json @@ -10,7 +10,7 @@ "dependencies": { "@babel/runtime": "^7.23.5", "jquery": "^3.7.1", - "meteor-node-stubs": "^1.2.10" + "meteor-node-stubs": "^1.2.12" }, "meteor": { "mainModule": { diff --git a/tools/static-assets/skel-chakra-ui/package.json b/tools/static-assets/skel-chakra-ui/package.json index ce630e2918..690f492c11 100644 --- a/tools/static-assets/skel-chakra-ui/package.json +++ b/tools/static-assets/skel-chakra-ui/package.json @@ -15,7 +15,7 @@ "@emotion/styled": "^11.9.3", "@react-icons/all-files": "^4.1.0", "framer-motion": "^6.4.2", - "meteor-node-stubs": "^1.2.10", + "meteor-node-stubs": "^1.2.12", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/tools/static-assets/skel-full/package.json b/tools/static-assets/skel-full/package.json index 28d1d84d2e..953198e35f 100644 --- a/tools/static-assets/skel-full/package.json +++ b/tools/static-assets/skel-full/package.json @@ -8,7 +8,7 @@ "dependencies": { "@babel/runtime": "^7.23.5", "jquery": "^3.7.1", - "meteor-node-stubs": "^1.2.10" + "meteor-node-stubs": "^1.2.12" }, "devDependencies": { "chai": "^4.2.0" diff --git a/tools/static-assets/skel-minimal/package.json b/tools/static-assets/skel-minimal/package.json index 187111707d..3ade9d3a74 100644 --- a/tools/static-assets/skel-minimal/package.json +++ b/tools/static-assets/skel-minimal/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "@babel/runtime": "^7.23.5", - "meteor-node-stubs": "^1.2.10" + "meteor-node-stubs": "^1.2.12" }, "meteor": { "mainModule": { diff --git a/tools/static-assets/skel-react/package.json b/tools/static-assets/skel-react/package.json index 9f5e7f9bc8..0c0eb6e4a5 100644 --- a/tools/static-assets/skel-react/package.json +++ b/tools/static-assets/skel-react/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "@babel/runtime": "^7.23.5", - "meteor-node-stubs": "^1.2.10", + "meteor-node-stubs": "^1.2.12", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/tools/static-assets/skel-solid/.meteor/packages b/tools/static-assets/skel-solid/.meteor/packages index f00629b1a1..665679a90c 100644 --- a/tools/static-assets/skel-solid/.meteor/packages +++ b/tools/static-assets/skel-solid/.meteor/packages @@ -19,4 +19,4 @@ hot-module-replacement # Update client in development without reloading the pag ~prototype~ static-html # Define static page content in .html files -jorgenvatle:vite-bundler +jorgenvatle:vite diff --git a/tools/static-assets/skel-solid/client/entry-meteor.js b/tools/static-assets/skel-solid/client/entry-meteor.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/static-assets/skel-solid/package.json b/tools/static-assets/skel-solid/package.json index 7b575928e5..d096a76612 100644 --- a/tools/static-assets/skel-solid/package.json +++ b/tools/static-assets/skel-solid/package.json @@ -9,21 +9,22 @@ }, "dependencies": { "@babel/runtime": "^7.23.9", - "meteor-node-stubs": "^1.2.10", - "solid-js": "^1.8.15" + "meteor-node-stubs": "^1.2.12", + "picocolors": "^1.1.1", + "solid-js": "^1.9.4" }, "meteor": { "mainModule": { - "client": "client/main.jsx", - "server": "server/main.js" + "client": "client/entry-meteor.js", + "server": "server/entry-meteor.js" }, "testModule": "tests/main.js" }, "devDependencies": { "babel-preset-solid": "^1.8.15", - "meteor-vite": "^1.10.2", - "vite": "^4.5.2", - "vite-plugin-solid": "^2.10.1", - "vite-plugin-solid-svg": "^0.8.0" + "meteor-vite": "^3.2.1", + "vite": "^6.0.11", + "vite-plugin-solid": "^2.11.0", + "vite-plugin-solid-svg": "^0.8.1" } } diff --git a/tools/static-assets/skel-solid/server/entry-meteor.js b/tools/static-assets/skel-solid/server/entry-meteor.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/static-assets/skel-solid/vite.config.js b/tools/static-assets/skel-solid/vite.config.js index c49fa70b16..5752984348 100644 --- a/tools/static-assets/skel-solid/vite.config.js +++ b/tools/static-assets/skel-solid/vite.config.js @@ -1,12 +1,19 @@ import { defineConfig } from 'vite'; import solidPlugin from 'vite-plugin-solid'; import solidSvg from "vite-plugin-solid-svg"; +import { meteor } from 'meteor-vite/plugin'; export default defineConfig({ - plugins: [solidPlugin(), solidSvg({ - defaultExport: 'component', - })], - meteor: { - clientEntry: 'imports/ui/main.jsx', - }, + plugins: [ + solidPlugin(), + solidSvg({ defaultExport: 'component' }), + meteor({ + clientEntry: 'imports/ui/main.jsx', + serverEntry: 'server/main.js', + enableExperimentalFeatures: true, + stubValidation: { + ignorePackages: ['meteor/mongo'], + }, + }), + ], }); diff --git a/tools/static-assets/skel-svelte/package.json b/tools/static-assets/skel-svelte/package.json index 1be6779d00..68e6f37acf 100644 --- a/tools/static-assets/skel-svelte/package.json +++ b/tools/static-assets/skel-svelte/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "@babel/runtime": "^7.23.5", - "meteor-node-stubs": "^1.2.10", + "meteor-node-stubs": "^1.2.12", "svelte": "^3.59.2" }, "devDependencies": { diff --git a/tools/static-assets/skel-tailwind/package.json b/tools/static-assets/skel-tailwind/package.json index e738792743..c02e2d027c 100644 --- a/tools/static-assets/skel-tailwind/package.json +++ b/tools/static-assets/skel-tailwind/package.json @@ -10,7 +10,7 @@ "dependencies": { "@babel/runtime": "^7.23.5", "autoprefixer": "^10.4.4", - "meteor-node-stubs": "^1.2.10", + "meteor-node-stubs": "^1.2.12", "postcss": "^8.4.12", "postcss-load-config": "^3.1.4", "react": "^17.0.2", diff --git a/tools/static-assets/skel-typescript/package.json b/tools/static-assets/skel-typescript/package.json index 0746e8368a..1bd98a2050 100644 --- a/tools/static-assets/skel-typescript/package.json +++ b/tools/static-assets/skel-typescript/package.json @@ -9,13 +9,13 @@ }, "dependencies": { "@babel/runtime": "^7.23.5", - "meteor-node-stubs": "^1.2.10", + "meteor-node-stubs": "^1.2.12", "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { "@types/mocha": "^8.2.3", - "@types/node": "^18.16.5", + "@types/node": "^22.10.6", "@types/react": "^18.2.5", "@types/react-dom": "^18.2.4", "typescript": "^5.4.5" diff --git a/tools/static-assets/skel-vue/.meteor/packages b/tools/static-assets/skel-vue/.meteor/packages index 3ae6a18b5f..b5e90db564 100644 --- a/tools/static-assets/skel-vue/.meteor/packages +++ b/tools/static-assets/skel-vue/.meteor/packages @@ -18,6 +18,5 @@ shell-server # Server-side component of the `meteor shell` com hot-module-replacement # Update client in development without reloading the page static-html # Define static page content in .html files -jorgenvatle:vite-bundler +jorgenvatle:vite ~prototype~ - diff --git a/tools/static-assets/skel-vue/client/entry-meteor.js b/tools/static-assets/skel-vue/client/entry-meteor.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/static-assets/skel-vue/package.json b/tools/static-assets/skel-vue/package.json index 3c1bedf120..560c8a23a5 100644 --- a/tools/static-assets/skel-vue/package.json +++ b/tools/static-assets/skel-vue/package.json @@ -10,25 +10,25 @@ }, "dependencies": { "@babel/runtime": "^7.23.5", - "meteor-node-stubs": "^1.2.10", + "meteor-node-stubs": "^1.2.12", "vue": "^3.3.9", "vue-meteor-tracker": "^3.0.0-beta.7", "vue-router": "^4.2.5" }, "meteor": { "mainModule": { - "client": "client/main.js", - "server": "server/main.js" + "client": "client/entry-meteor.js", + "server": "server/entry-meteor.js" }, "testModule": "tests/main.js" }, "devDependencies": { "@types/meteor": "^2.9.7", - "@vitejs/plugin-vue": "^3.2.0", + "@vitejs/plugin-vue": "^5.2.1", "autoprefixer": "^10.4.16", - "meteor-vite": "^1.10.3", + "meteor-vite": "^3.2.1", "postcss": "^8.4.31", "tailwindcss": "^3.3.5", - "vite": "^3.2.7" + "vite": "^6.0.11" } } diff --git a/tools/static-assets/skel-vue/server/entry-meteor.js b/tools/static-assets/skel-vue/server/entry-meteor.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/static-assets/skel-vue/vite.config.js b/tools/static-assets/skel-vue/vite.config.js index d3aeaa9aba..dd08ccf69d 100644 --- a/tools/static-assets/skel-vue/vite.config.js +++ b/tools/static-assets/skel-vue/vite.config.js @@ -1,12 +1,20 @@ -import { defineConfig } from 'vite' -import vue from '@vitejs/plugin-vue' +import { defineConfig } from 'vite'; +import vue from '@vitejs/plugin-vue'; +import { meteor } from 'meteor-vite/plugin'; export default defineConfig({ - plugins: [vue()], - meteor: { - clientEntry: 'imports/ui/main.js', - }, + plugins: [ + vue(), + meteor({ + clientEntry: 'imports/ui/main.js', + serverEntry: 'server/main.js', + enableExperimentalFeatures: true, + stubValidation: { + ignorePackages: ['meteor/mongo'], + }, + }), + ], optimizeDeps: { exclude: ['vue-meteor-tracker'], }, -}) +}); diff --git a/tools/tests/apps/app-config/package.json b/tools/tests/apps/app-config/package.json index dc5c3aaf75..65c8babb5e 100644 --- a/tools/tests/apps/app-config/package.json +++ b/tools/tests/apps/app-config/package.json @@ -6,7 +6,7 @@ }, "dependencies": { "@babel/runtime": "^7.23.5", - "meteor-node-stubs": "^1.2.10", + "meteor-node-stubs": "^1.2.12", "puppeteer": "^2.1.1" }, "meteor": { diff --git a/tools/tests/apps/app-prints-pid/package.json b/tools/tests/apps/app-prints-pid/package.json index c3c3ba64b1..1a2249fa4a 100644 --- a/tools/tests/apps/app-prints-pid/package.json +++ b/tools/tests/apps/app-prints-pid/package.json @@ -3,7 +3,7 @@ "private": true, "dependencies": { "@babel/runtime": "^7.23.5", - "meteor-node-stubs": "^1.2.10" + "meteor-node-stubs": "^1.2.12" }, "meteor": { "mainModule": { diff --git a/tools/tests/apps/client-refresh/package.json b/tools/tests/apps/client-refresh/package.json index 98588b9b2f..d79be809f3 100644 --- a/tools/tests/apps/client-refresh/package.json +++ b/tools/tests/apps/client-refresh/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "@babel/runtime": "^7.23.5", - "meteor-node-stubs": "^1.2.10" + "meteor-node-stubs": "^1.2.12" }, "meteor": { "mainModule": { diff --git a/tools/tests/apps/compiler-plugin-static-html-error/.meteor/packages b/tools/tests/apps/compiler-plugin-static-html-error/.meteor/packages index a049f97cef..aa8a211d5f 100644 --- a/tools/tests/apps/compiler-plugin-static-html-error/.meteor/packages +++ b/tools/tests/apps/compiler-plugin-static-html-error/.meteor/packages @@ -8,4 +8,3 @@ standard-minifier-css standard-minifier-js shell-server dynamic-import -underscore diff --git a/tools/tests/apps/compiler-plugin-static-html/.meteor/packages b/tools/tests/apps/compiler-plugin-static-html/.meteor/packages index a049f97cef..aa8a211d5f 100644 --- a/tools/tests/apps/compiler-plugin-static-html/.meteor/packages +++ b/tools/tests/apps/compiler-plugin-static-html/.meteor/packages @@ -8,4 +8,3 @@ standard-minifier-css standard-minifier-js shell-server dynamic-import -underscore diff --git a/tools/tests/apps/css-injection-test/package.json b/tools/tests/apps/css-injection-test/package.json index c9acaecfb9..7778b8ccd5 100644 --- a/tools/tests/apps/css-injection-test/package.json +++ b/tools/tests/apps/css-injection-test/package.json @@ -10,7 +10,7 @@ "dependencies": { "@babel/runtime": "^7.23.5", "jquery": "^3.7.1", - "meteor-node-stubs": "^1.2.10" + "meteor-node-stubs": "^1.2.12" }, "meteor": { "mainModule": "css-injection-test.js" diff --git a/tools/tests/apps/custom-minifier/package.json b/tools/tests/apps/custom-minifier/package.json index bc4cc26b3a..1bf5b6c401 100644 --- a/tools/tests/apps/custom-minifier/package.json +++ b/tools/tests/apps/custom-minifier/package.json @@ -10,7 +10,7 @@ "dependencies": { "@babel/runtime": "^7.23.5", "jquery": "^3.7.1", - "meteor-node-stubs": "^1.2.10" + "meteor-node-stubs": "^1.2.12" }, "meteor": { "mainModule": "code.js" diff --git a/tools/tests/apps/dev-bundle-bin-commands/package.json b/tools/tests/apps/dev-bundle-bin-commands/package.json index 427753ffe6..6e4a415585 100644 --- a/tools/tests/apps/dev-bundle-bin-commands/package.json +++ b/tools/tests/apps/dev-bundle-bin-commands/package.json @@ -8,6 +8,6 @@ }, "dependencies": { "@babel/runtime": "^7.23.5", - "meteor-node-stubs": "^1.2.10" + "meteor-node-stubs": "^1.2.12" } } diff --git a/tools/tests/apps/dynamic-import/package.json b/tools/tests/apps/dynamic-import/package.json index 679219d5ed..853e7e6133 100644 --- a/tools/tests/apps/dynamic-import/package.json +++ b/tools/tests/apps/dynamic-import/package.json @@ -10,7 +10,7 @@ "acorn": "^7.4.1", "arson": "^0.2.6", "jquery": "^3.7.1", - "meteor-node-stubs": "^1.2.10", + "meteor-node-stubs": "^1.2.12", "moment": "^2.29.4", "optimism": "^0.11.5", "private": "^0.1.8", diff --git a/tools/tests/apps/ecmascript-regression/package.json b/tools/tests/apps/ecmascript-regression/package.json index 59c305eeda..bc67fd9207 100644 --- a/tools/tests/apps/ecmascript-regression/package.json +++ b/tools/tests/apps/ecmascript-regression/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "@babel/runtime": "^7.23.5", - "meteor-node-stubs": "^1.2.10", + "meteor-node-stubs": "^1.2.12", "puppeteer": "^10.4.0", "react": "^17.0.2", "react-dom": "^17.0.2" diff --git a/tools/tests/apps/extra-packages-option/.meteor/packages b/tools/tests/apps/extra-packages-option/.meteor/packages index eafeae86c3..556e8e155a 100644 --- a/tools/tests/apps/extra-packages-option/.meteor/packages +++ b/tools/tests/apps/extra-packages-option/.meteor/packages @@ -4,5 +4,4 @@ # but you can also edit it by hand. meteor-base -underscore extra-package-2@=0.0.1 diff --git a/tools/tests/apps/git-commit-hash/package.json b/tools/tests/apps/git-commit-hash/package.json index a08e85969b..dfd798be69 100644 --- a/tools/tests/apps/git-commit-hash/package.json +++ b/tools/tests/apps/git-commit-hash/package.json @@ -7,7 +7,7 @@ }, "dependencies": { "@babel/runtime": "^7.23.5", - "meteor-node-stubs": "^1.2.10", + "meteor-node-stubs": "^1.2.12", "puppeteer": "^2.1.1" }, "meteor": { diff --git a/tools/tests/apps/link-config-npm-package/package.json b/tools/tests/apps/link-config-npm-package/package.json index f174884643..7f00cdf583 100644 --- a/tools/tests/apps/link-config-npm-package/package.json +++ b/tools/tests/apps/link-config-npm-package/package.json @@ -10,7 +10,7 @@ "dependencies": { "@babel/runtime": "^7.23.5", "config": "file:../config-package", - "meteor-node-stubs": "^1.2.10" + "meteor-node-stubs": "^1.2.12" }, "meteor": { "mainModule": { diff --git a/tools/tests/apps/linked-external-npm-package/package.json b/tools/tests/apps/linked-external-npm-package/package.json index 96ae9fcaa4..7735413880 100644 --- a/tools/tests/apps/linked-external-npm-package/package.json +++ b/tools/tests/apps/linked-external-npm-package/package.json @@ -10,7 +10,7 @@ "dependencies": { "@babel/runtime": "^7.23.5", "external-package": "file:../external-package", - "meteor-node-stubs": "^1.2.10" + "meteor-node-stubs": "^1.2.12" }, "meteor": { "mainModule": { diff --git a/tools/tests/apps/meteor-ignore/package.json b/tools/tests/apps/meteor-ignore/package.json index c5723f52b3..8ab2a13628 100644 --- a/tools/tests/apps/meteor-ignore/package.json +++ b/tools/tests/apps/meteor-ignore/package.json @@ -6,6 +6,6 @@ }, "dependencies": { "@babel/runtime": "^7.23.5", - "meteor-node-stubs": "^1.2.10" + "meteor-node-stubs": "^1.2.12" } } diff --git a/tools/tests/apps/modules/.meteor/packages b/tools/tests/apps/modules/.meteor/packages index 7612fb6ccc..d2ef381859 100644 --- a/tools/tests/apps/modules/.meteor/packages +++ b/tools/tests/apps/modules/.meteor/packages @@ -23,7 +23,6 @@ client-only-ecmascript modules-test-plugin shell-server@0.6.0-beta300.6 dynamic-import@0.7.4-beta300.6 -underscore@1.0.14-beta300.6 import-local-json-module dummy-compiler typescript@5.3.3-beta300.6 diff --git a/tools/tests/apps/modules/package.json b/tools/tests/apps/modules/package.json index 08d98ce964..ca41575ebe 100644 --- a/tools/tests/apps/modules/package.json +++ b/tools/tests/apps/modules/package.json @@ -19,7 +19,7 @@ "jsx-import-test": "file:imports/links/jsx-import-test", "lodash-es": "^4.17.21", "markdown-to-jsx": "4.0.3", - "meteor-node-stubs": "^1.2.10", + "meteor-node-stubs": "^1.2.12", "mobx": "5.8.0", "moment": "^2.30.1", "mssql": "^3.1.1", diff --git a/tools/tests/apps/modules/tests.js b/tools/tests/apps/modules/tests.js index e3230b7a8f..0e2635d3ee 100644 --- a/tools/tests/apps/modules/tests.js +++ b/tools/tests/apps/modules/tests.js @@ -436,7 +436,6 @@ describe("Meteor packages", () => { }); it("should be importable", () => { - assert.strictEqual(require("meteor/underscore")._, _); const Blaze = require("meteor/blaze").Blaze; assert.strictEqual(typeof Blaze, "object"); diff --git a/tools/tests/apps/shell/package.json b/tools/tests/apps/shell/package.json index 98ebe3218e..df535c6f42 100644 --- a/tools/tests/apps/shell/package.json +++ b/tools/tests/apps/shell/package.json @@ -6,6 +6,6 @@ }, "dependencies": { "@babel/runtime": "^7.23.5", - "meteor-node-stubs": "^1.2.10" + "meteor-node-stubs": "^1.2.12" } } diff --git a/tools/tests/apps/standard-app/package.json b/tools/tests/apps/standard-app/package.json index 19b6b4faf4..8d4fb76da0 100644 --- a/tools/tests/apps/standard-app/package.json +++ b/tools/tests/apps/standard-app/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "@babel/runtime": "^7.23.5", - "meteor-node-stubs": "^1.2.10" + "meteor-node-stubs": "^1.2.12" }, "meteor": { "mainModule": false, diff --git a/tools/tool-env/profile.ts b/tools/tool-env/profile.ts index 9bbef2cbe4..79c49db6e2 100644 --- a/tools/tool-env/profile.ts +++ b/tools/tool-env/profile.ts @@ -154,13 +154,69 @@ // other A: 350.0 // B: 100.0 // -// and in the leaf time report: +// and in the leaf report: // // other A: 350.0 // B: 250.0 // // In both reports the grand total is 600ms. +// Profiler Usage Documentation +/** +* To use the Meteor profiler: +* +* 1. For basic profiling: +* METEOR_PROFILE=1 meteor +* +* 2. For profiling with inspector (generating .cpuprofile files): +* METEOR_INSPECT=bundler.bundle, meteor +* +* 3. Additional settings: +* METEOR_INSPECT_CONTEXT=context_name (identification for files) +* METEOR_INSPECT_OUTPUT=path/to/directory (location where to save files) +* +* 4. To view .cpuprofile files: +* - Open Chrome DevTools +* - Go to the "Performance" or "Profiler" tab +* - Click "Load Profile" and select the .cpuprofile file +*/ + +import * as inspector from 'inspector'; +import * as fs from 'fs'; +import * as path from 'path'; + +interface MeteorAsyncLocalStorage { + getStore: () => ProfileStore | undefined; + run: (store: ProfileStore, fn: () => T) => T; +} + +interface ProfileStore { + currentEntry: string[]; + [key: string]: any; +} + +declare global { + var __METEOR_ASYNC_LOCAL_STORAGE: MeteorAsyncLocalStorage; +} + +interface InspectorConfigType { + enabled: boolean; + filter: string[]; + context: string; + outputDir: string; + samplingInterval: number | undefined; + maxProfileSize: number; +} + +const INSPECTOR_CONFIG: InspectorConfigType = { + enabled: !!process.env.METEOR_INSPECT, + filter: process.env.METEOR_INSPECT ? process.env.METEOR_INSPECT.split(',') : [], + context: process.env.METEOR_INSPECT_CONTEXT || '', + outputDir: process.env.METEOR_INSPECT_OUTPUT || path.join(process.cwd(), 'profiling'), + // Interval in ms (smaller = more details, but more memory) + samplingInterval: process.env.METEOR_INSPECT_INTERVAL ? parseInt(process.env.METEOR_INSPECT_INTERVAL || '1000', 10) : undefined, + maxProfileSize: parseInt(process.env.METEOR_INSPECT_MAX_SIZE || '2000', 10) +}; const filter = parseFloat(process.env.METEOR_PROFILE || "100"); // ms @@ -230,7 +286,6 @@ function decodeEntryKey(key: string) { return key.split('\t'); } -const globalEntry: string[] = []; let running = false; export function Profile< @@ -238,49 +293,330 @@ export function Profile< TResult, >( bucketName: string | ((...args: TArgs) => string), - f: (...args: TArgs) => TResult, + f: (...args: TArgs) => TResult | Promise, ): typeof f { - if (! Profile.enabled) { + if (!Profile.enabled) { return f; } return Object.assign(function profileWrapper(this: any) { - if (! running) { - return f.apply(this, arguments as any); + const args = Array.from(arguments) as TArgs; + + if (!running) { + return f.apply(this, args); } - const name = typeof bucketName === "function" - ? bucketName.apply(this, arguments as any) - : bucketName; + const asyncLocalStorage = global.__METEOR_ASYNC_LOCAL_STORAGE; + let store = asyncLocalStorage.getStore() || { currentEntry: [] }; - // TODO Test with Profile / use __METEOR_ASYNC_LOCAL_STORAGE - //const currentStore = asyncLo - // const currentEntry = Fiber.current - // ? Fiber.current.profilerEntry || (Fiber.current.profilerEntry = []) - // : globalEntry; - const currentEntry = globalEntry; + const name = typeof bucketName === 'function' ? bucketName.apply(this, args) : bucketName; + + // callbacks with observer to track when the function finishes + const profileInfo = { + name, + isActive: false, + isCompleted: false, + startTime: Date.now() + }; + + if (shouldRunInspectorProfiling(name)) { + profileInfo.isActive = startInspectorProfiling(name); + + if (profileInfo.isActive) { + const handleTermination = (context: string) => { + if (profileInfo.isActive && !profileInfo.isCompleted) { + return stopInspectorProfiling(name, true).catch(err => { + process.stdout.write(`[PROFILING_${context}] Error stopping profiling: ${err}\n`); + }); + } + return Promise.resolve(); + }; + + process.on('exit', () => { handleTermination('EXIT'); }); + + const signals = ['SIGINT', 'SIGTERM', 'SIGHUP']; + signals.forEach(signal => { + process.once(signal, () => { + handleTermination('SIGNAL').finally(() => { + process.exit(130); + }); + }); + }); + } + } + + const completeProfiler = () => { + if (profileInfo.isActive && !profileInfo.isCompleted) { + profileInfo.isCompleted = true; + return stopInspectorProfiling(name, true).catch(err => { + process.stdout.write(`[PROFILING_COMPLETE] Error stopping profiling: ${err}`); + }); + } + return Promise.resolve(); + }; + + function completeIfSync(result: TResult | Promise){ + if (!(result instanceof Promise)) { + completeProfiler(); + } + } - currentEntry.push(name); - const key = encodeEntryKey(currentEntry); - const start = process.hrtime(); try { - return f.apply(this, arguments as any); - } finally { - const elapsed = process.hrtime(start); - const stats = (bucketStats[key] || (bucketStats[key] = { - time: 0.0, - count: 0, - isOther: false, - })); - stats.time += (elapsed[0] * 1000 + elapsed[1] / 1000000); - stats.count++; - currentEntry.pop(); + if (!asyncLocalStorage.getStore()) { + const result = asyncLocalStorage.run(store, () => + runWithContext(name, store, f, this, args, completeProfiler)); + + // For sync results, complete profiling here + completeIfSync(result) + return result; + } + + // if there is already a store, use the current context + const result = runWithContext(name, store, f, this, args, completeProfiler); + + // For sync results, complete profiling here + completeIfSync(result) + return result; + } catch (error) { + completeProfiler(); + throw error; } }, f) as typeof f; } +// ================================ +// Inspector Profiling +// ================================ +let inspectorActive = false; +let rootSession: inspector.Session | null = null; +let rootProfileName: string | null = null; +let profileStartTime: number | null = null; + +function shouldRunInspectorProfiling(name: string): boolean { + if (!INSPECTOR_CONFIG.enabled) return false; + return INSPECTOR_CONFIG.filter.includes(name); +} + +function startInspectorProfiling(name: string): boolean { + if (!shouldRunInspectorProfiling(name)) { + return false; + } + + try { + if (rootSession) { + return false; + } + + profileStartTime = Date.now(); + + // Open the inspector only if it's not active + if (!inspectorActive) { + inspector.open(); + inspectorActive = true; + } + + // Create a single session for the duration of profiling + const session = new inspector.Session(); + session.connect(); + session.post('Profiler.enable'); + session.post('Profiler.start', { + samplingInterval: INSPECTOR_CONFIG.samplingInterval + }); + + // Store the root session for later use + rootSession = session; + rootProfileName = name; + + return true; + } catch (err) { + process.stdout.write(`[PROFILING_START] Error starting profiling for ${name}: ${err}\n`); + return false; + } +} + +function stopInspectorProfiling(name: string, isActive: boolean): Promise { + if (!isActive || !rootSession || name !== rootProfileName) { + return Promise.resolve(); + } + + return new Promise((resolve, reject) => { + try { + const duration = profileStartTime ? Date.now() - profileStartTime : 0; + const session = rootSession; + if (!session) { + return resolve(); + } + + session.post('Profiler.stop', (err: Error | null, result: any) => { + if (err) { + cleanupAndResolve(resolve); + reject(err); + return; + } + + try { + // check if we have data in the profile + if (!result || !result.profile) { + console.error(`[PROFILING_STOP] Empty profile for ${name}`); + cleanupAndResolve(resolve); + return; + } + + // check the approximate size of the profile + const profileStr = JSON.stringify(result.profile); + const profileSize = profileStr.length / (1024 * 1024); // in MB + + process.stdout.write(`[PROFILING_STOP] Profile captured successfully for ${name}: ${JSON.stringify({ + nodes: result.profile.nodes?.length || 0, + samples: result.profile.samples?.length || 0, + timeDeltas: result.profile.timeDeltas?.length || 0, + duration: duration, + size: profileSize.toFixed(2) + " MB" + })}`); + + if (profileSize > INSPECTOR_CONFIG.maxProfileSize) { + process.stdout.write(`[PROFILING_STOP] Profile too large (${profileSize.toFixed(2)}MB > ${INSPECTOR_CONFIG.maxProfileSize}MB)`); + process.stdout.write('[PROFILING_STOP] To avoid OOM, a reduced profile will be saved'); + process.stdout.write('[PROFILING_STOP] Increase METEOR_INSPECT_MAX_SIZE or METEOR_INSPECT_INTERVAL to adjust'); + + // Try to save a reduced profile + try { + // Simplify the profile to reduce size + const reducedProfile = { + nodes: result.profile.nodes?.slice(0, 10000) || [], + samples: result.profile.samples?.slice(0, 10000) || [], + timeDeltas: result.profile.timeDeltas?.slice(0, 10000) || [], + startTime: result.profile.startTime, + endTime: result.profile.endTime, + _warning: "profile truncated to avoid OOM. Use a larger interval." + }; + + saveProfile(reducedProfile, name, `${name}_reduced`, duration); + } catch (reduceErr) { + process.stdout.write(`[PROFILING_STOP] Error saving reduced profile: ${reduceErr}`); + } + + cleanupAndResolve(resolve); + return; + } + + try { + saveProfile(result.profile, name, name, duration); + } catch (saveErr) { + process.stdout.write(`[PROFILING_STOP] Error saving profile: ${saveErr}`); + } + + cleanupAndResolve(resolve); + } catch (processErr) { + process.stdout.write(`[PROFILING_STOP] Error processing profile for ${name}: ${processErr}`); + cleanupAndResolve(resolve); + reject(processErr); + } + }); + } catch (err) { + process.stdout.write(`[PROFILING_STOP] Error in stopInspectorProfiling for ${name}: ${err}`); + cleanupAndResolve(resolve); + reject(err); + } + }); + + function cleanupAndResolve(resolve: (value?: void | PromiseLike) => void) { + try { + if (rootSession) { + rootSession.post('Profiler.disable'); + rootSession.disconnect(); + } + + if (inspectorActive) { + inspector.close(); + inspectorActive = false; + } + + rootSession = null; + rootProfileName = null; + profileStartTime = null; + + // Force GC if available + if (typeof global.gc === 'function') { + try { + global.gc(); + process.stdout.write('[PROFILING_STOP] Garbage collector executed successfully'); + } catch (gcErr) { + process.stdout.write(`[PROFILING_STOP] Error executing garbage collector: ${gcErr}`); + } + } + + return resolve(); + } catch (cleanupErr) { + process.stdout.write(`[PROFILING_STOP] Error during cleanup: ${cleanupErr}`); + return resolve(); + } + } +} + +function saveProfile(profile: any, name: string, filename: string, duration: number): void { + if (!fs.existsSync(INSPECTOR_CONFIG.outputDir)) { + fs.mkdirSync(INSPECTOR_CONFIG.outputDir, { recursive: true }); + } + + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + const safeFilename = filename.replace(/[\/\\:]/g, '_'); + const filepath = path.join(INSPECTOR_CONFIG.outputDir, `${safeFilename}-${INSPECTOR_CONFIG.context}-${timestamp}.cpuprofile`); + + fs.writeFileSync(filepath, JSON.stringify(profile)); + + const profileSize = JSON.stringify(profile).length / (1024 * 1024); + + process.stdout.write(`[PROFILING_SAVE] Profile for ${name} saved in: ${filepath}`); + process.stdout.write(`[PROFILING_SAVE] Duration: ${duration}ms, size: ${profileSize.toFixed(2)}MB`); +} + +function runWithContext( + bucketName: string | ((...args: TArgs) => string), + store: { currentEntry: string[]; [key: string]: any }, + f: (...args: TArgs) => TResult | Promise, + context: any, + args: any[], + completeProfiler: () => Promise +): TResult | Promise { + const name = typeof bucketName === "function" ? bucketName.apply(context, args as TArgs) : bucketName; + store.currentEntry = [...store.currentEntry || [], name]; + const key = encodeEntryKey(store.currentEntry); + const start = process.hrtime(); + + let result: TResult | Promise; + try { + result = f.apply(context, args as TArgs); + + if (result instanceof Promise) { + // Return a promise if async + return result.finally(() => finalizeProfiling(key, start, store.currentEntry, completeProfiler)); + } + + // Return directly if sync + return result; + } finally { + if (!(result! instanceof Promise)) { + finalizeProfiling(key, start, store.currentEntry, completeProfiler); + } + } +} + +function finalizeProfiling(key: string, start: [number, number], currentEntry: string[], completeProfiler: () => Promise) { + const elapsed = process.hrtime(start); + const stats = (bucketStats[key] || (bucketStats[key] = { + time: 0.0, + count: 0, + isOther: false, + })); + stats.time += elapsed[0] * 1000 + elapsed[1] / 1_000_000; + stats.count++; + currentEntry.pop(); + completeProfiler(); +} + export namespace Profile { - export let enabled = !! process.env.METEOR_PROFILE; + export let enabled = !! process.env.METEOR_PROFILE || !! process.env.METEOR_INSPECT; async function _runAsync(bucket: string, f: () => TResult) { runningName = bucket; @@ -511,3 +847,4 @@ function setupReport() { injectOtherTime(parent); }); } + diff --git a/tools/utils/utils.js b/tools/utils/utils.js index 8ccd4f5b5a..54bbfe0b31 100644 --- a/tools/utils/utils.js +++ b/tools/utils/utils.js @@ -202,7 +202,7 @@ exports.sleepMs = function (ms) { // Return a short, high entropy string without too many funny // characters in it. exports.randomToken = function () { - return (Math.random() * 0x100000000 + 1).toString(36); + return (Math.random() * 0x100000000 + 1).toString(36).replace('.', ''); }; // Like utils.randomToken, except a legal variable name, i.e. the first diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000..62b3d0ced7 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "es2020", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "outDir": "./dist", + "rootDir": "./src", + "typeRoots": [ + "./node_modules/@types", + "./types" + ] + }, + "include": [ + "src/**/*.ts", + "src/**/*.d.ts", + "types/**/*.d.ts" + ], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/v3-docs/docs/.vitepress/config.mts b/v3-docs/docs/.vitepress/config.mts index 653f1b3b21..83add47b6a 100644 --- a/v3-docs/docs/.vitepress/config.mts +++ b/v3-docs/docs/.vitepress/config.mts @@ -1,29 +1,18 @@ import { defineConfig } from "vitepress"; +import metadata from "../generators/meteor-versions/metadata.generated"; // https://vitepress.dev/reference/site-config export default defineConfig({ title: "Docs", description: "Meteor.js Docs", - head: [ - ["link", { rel: "icon", href: "/logo.png" }], - [ - "script", - { - async: "", - src: "https://widget.kapa.ai/kapa-widget.bundle.js", - "data-website-id": "64051b0e-d79f-4fe7-b3ca-ff5c84075693", - "data-project-name": "Meteor", - "data-project-color": "#36436b", - "data-project-logo": "https://v3-docs.meteor.com/logo.png", - "data-modal-disclaimer": - "This is a custom LLM for answering questions about Meteor. Answers are based on the contents of the docs, answered forum posts, YouTube videos and GitHub issues. Please note that answers are generated by AI and may not be fully accurate, so please use your best judgement.", - }, - ], - ], + head: [["link", { rel: "icon", href: "/logo.png" }]], lastUpdated: true, sitemap: { hostname: "https://v3-docs.meteor.com", }, + ignoreDeadLinks: [ + /^http:\/\/localhost/ + ], themeConfig: { // https://vitepress.dev/reference/default-theme-config nav: [ @@ -127,6 +116,22 @@ export default defineConfig({ }, { text: "API", link: "/api/" }, { text: "Galaxy Cloud", link: "https://www.meteor.com/cloud" }, + { + text: metadata.currentVersion, + items: metadata.versions.reverse().map((v) => { + if (v.isCurrent) { + return { + text: `${v.version} (Current)`, + link: "/", + activeMatch: "/", + }; + } + return { + text: v.version, + link: v.url, + }; + }), + }, ], sidebar: [ { @@ -305,6 +310,10 @@ export default defineConfig({ text: "hot-module-replacement", link: "/packages/hot-module-replacement", }, + { + text: "roles", + link: "/packages/roles", + }, { text: "less", link: "/packages/less", @@ -317,6 +326,10 @@ export default defineConfig({ text: "markdown", link: "/packages/markdown", }, + { + text: "modern-browsers", + link: "/packages/modern-browsers", + }, { text: "modules", link: "/packages/modules", @@ -329,6 +342,10 @@ export default defineConfig({ text: "random", link: "/packages/random", }, + { + text: "react-meteor-data", + link: "/packages/react-meteor-data", + }, { text: "server-render", link: "/packages/server-render", @@ -360,6 +377,17 @@ export default defineConfig({ ], collapsed: true, }, + { + text: "Community Packages", + link: "/community-packages/index", + items: [ + { + text: "Meteor RPC", + link: "/community-packages/meteor-rpc", + }, + ], + collapsed: true, + }, { text: "Troubleshooting", items: [ @@ -369,8 +397,8 @@ export default defineConfig({ }, { text: "Windows", link: "/troubleshooting/windows" }, { - text: "Known issues in 2.13", - link: "/troubleshooting/known-issues", + text: "MongoDB Connection", + link: "/troubleshooting/mongodb-connection", }, ], collapsed: true, @@ -414,6 +442,15 @@ export default defineConfig({ ], collapsed: true, }, + { + text: "Performance", + items: [ + { + text: "WebSocket Compression", + link: "/performance/websocket-compression", + }, + ], + }, ], socialLinks: [ diff --git a/v3-docs/docs/.vitepress/theme/GoToLatest.vue b/v3-docs/docs/.vitepress/theme/GoToLatest.vue new file mode 100644 index 0000000000..cf67d76863 --- /dev/null +++ b/v3-docs/docs/.vitepress/theme/GoToLatest.vue @@ -0,0 +1,19 @@ + + + \ No newline at end of file diff --git a/v3-docs/docs/.vitepress/theme/Layout.vue b/v3-docs/docs/.vitepress/theme/Layout.vue index 91cf0e928e..4b21e79933 100644 --- a/v3-docs/docs/.vitepress/theme/Layout.vue +++ b/v3-docs/docs/.vitepress/theme/Layout.vue @@ -1,8 +1,9 @@ @@ -82,4 +94,4 @@ provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => { .VPSwitchAppearance .check { transform: none !important; } - + \ No newline at end of file diff --git a/v3-docs/docs/.vitepress/theme/redirects/redirects.json b/v3-docs/docs/.vitepress/theme/redirects/redirects.json index f6dc86f381..d57349da0c 100644 --- a/v3-docs/docs/.vitepress/theme/redirects/redirects.json +++ b/v3-docs/docs/.vitepress/theme/redirects/redirects.json @@ -1,4 +1,5 @@ { "meteor_call": "meteor-call", - "ddp_connect": "DDP-connect" + "ddp_connect": "DDP-connect", + "changelog": "history" } diff --git a/v3-docs/docs/.vitepress/theme/redirects/script.js b/v3-docs/docs/.vitepress/theme/redirects/script.js index 0e7268824c..6721d68b7d 100644 --- a/v3-docs/docs/.vitepress/theme/redirects/script.js +++ b/v3-docs/docs/.vitepress/theme/redirects/script.js @@ -1,18 +1,26 @@ -// import json from './redirects.json'; +import redirects from "./redirects.json"; /** * * @param {string} path */ export const redirect = (path) => { - let shouldRedirect = false; - console.log(path) + const lastPath = path.split("/").pop().split(".")[0]; + if (redirects[lastPath]) { + return { + path: path.replace(lastPath, redirects[lastPath]), + shouldRedirect: true, + }; + } + if (path.includes("_")) { - shouldRedirect = true; - path = path.replace("_", "-"); + return { + path: path.replace("_", "-"), + shouldRedirect: true, + }; } return { path, - shouldRedirect + shouldRedirect: false, }; }; diff --git a/v3-docs/docs/about/install.md b/v3-docs/docs/about/install.md index f12c459444..1d685e9ec3 100644 --- a/v3-docs/docs/about/install.md +++ b/v3-docs/docs/about/install.md @@ -1,4 +1,3 @@ - # Install You need to install the Meteor command line tool to create, run, and manage your Meteor.js projects. Check the prerequisites and follow the installation process below. @@ -9,22 +8,19 @@ npx meteor ## Prerequisites {#prereqs} - ### Operating System (OS) {#prereqs-os} - Meteor currently supports **OS X, Windows, and Linux**. Only 64-bit is supported. - Meteor supports Windows 7 / Windows Server 2008 R2 and up. - Apple M1 is natively supported from Meteor 2.5.1 onward (for older versions, rosetta terminal is required). -- If you are on a Mac M1 (Arm64 version) you need to have Rosetta 2 installed, as Meteor uses it for running MongoDB. Check how to install it [here](https://osxdaily.com/2020/12/04/how-install-rosetta-2-apple-silicon-mac/). +- If you are using Meteor <= 3.0.4 and you are on a Mac M1 (Arm64 version) you need to have Rosetta 2 installed, as Meteor uses it for running MongoDB. Check how to install it [here](https://osxdaily.com/2020/12/04/how-install-rosetta-2-apple-silicon-mac/). *No longer needed in Meteor 3.1*. - Disabling antivirus (Windows Defender, etc.) will improve performance. - For compatibility, Linux binaries are built with CentOS 6.4 i386/amd64. - ### Mobile Development {#prereqs-mobile} - iOS development requires the latest Xcode. - ## Installation Install the latest official version of Meteor.js from your terminal by running one of the commands below. You can check our [changelog](https://v3-docs.meteor.com/history.html) for the release notes. @@ -65,10 +61,9 @@ curl https://install.meteor.com/\?release\=2.8 | sh > Do not install the npm Meteor Tool in your project's package.json. This library is just an installer. - ## Troubleshooting {#troubleshooting} -If your user doesn't have permission to install global binaries, and you need to use sudo, it's necessary to append *--unsafe-perm* to the above command: +If your user doesn't have permission to install global binaries, and you need to use sudo, it's necessary to append _--unsafe-perm_ to the above command: ```bash sudo npm install -g meteor --unsafe-perm @@ -81,7 +76,6 @@ If you only use sudo because of a distribution default permission system, [check In some cases you can get this error `npm WARN checkPermissions Missing write access to /usr/local/lib/node_modules` because your Node.js installation was performed with wrong permissions. An easy way to fix this is to install Node.js using [nvm](https://github.com/nvm-sh/nvm) and forcing it to be used in your terminal. You can force it in the current session of your terminal by running `nvm use 14`. - ## PATH management {#path-management} By default, the Meteor installer adds its install path (by default, `~/.meteor/`) to your PATH by updating either your `.bashrc`, `.bash_profile`, or `.zshrc` as appropriate. To disable this behavior, install Meteor by running: @@ -90,11 +84,8 @@ By default, the Meteor installer adds its install path (by default, `~/.meteor/` npm install -g meteor --ignore-meteor-setup-exec-path --foreground-script ``` - - (or by setting the environment variable `npm_config_ignore_meteor_setup_exec_path=true`) - ## Old Versions on Apple M1 {#old-versions-m1} For Apple M1 computers, you can append Rosetta prefix as following, if you need to run older versions of Meteor (before 2.5.1): @@ -105,7 +96,6 @@ arch -x86_64 npm install -g meteor or select Terminal in the Applications folder, press CMD(⌘)+I and check the "Open using Rosetta" option. - ## Meteor Docker image {#meteor-docker} You can also use a Docker container for running Meteor inside your CI, or even in your local development toolchain. @@ -114,26 +104,20 @@ We do provide the meteor/meteor-base ubuntu-based Docker image, that comes pre-b You can refer to our meteor/galaxy-images repository to see how to use it, and the latest version. [More about meteor-base here.](https://github.com/meteor/galaxy-images/blob/master/meteor-base/README.md) - - ## Note for Windows users {#windows} On Windows, the installer runs faster when [Windows Developer Mode](https://docs.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development) is enabled. The installation extracts a large number of small files, which Windows Defender can cause to be very slow. - - ## Node version manager {#nvm} If you use a node version manager that uses a separate global `node_modules` folder for each Node version, you will need to re-install the `meteor` npm package when changing to a Node version for the first time. Otherwise, the `meteor` command will no longer be found. - ## Note for fish shell users (Linux) {#fish-shell} To be able to use the `meteor` command from fish it's needed to include `/home//.meteor` in `$PATH`; to do that just add this line in `/home//.config/fish/config.fish` file (replace `` with your username): `set PATH /home//.meteor $PATH` - ## Uninstalling Meteor {#uninstall} If you installed Meteor using npx, you can remove it by running: @@ -145,4 +129,4 @@ npx meteor uninstall If you installed Meteor using curl or as a fallback solution, run: `rm -rf ~/.meteor` -`sudo rm /usr/local/bin/meteor`Β  +`sudo rm /usr/local/bin/meteor` diff --git a/v3-docs/docs/about/roadmap.md b/v3-docs/docs/about/roadmap.md index 5233abb91c..a3eaee5c8c 100644 --- a/v3-docs/docs/about/roadmap.md +++ b/v3-docs/docs/about/roadmap.md @@ -4,7 +4,7 @@ Describes the high-level features and actions for the Meteor project in the near ## Introduction -**Last updated: August 22nd, 2024.** +**Last updated: March 6th, 2025.** The description of many items includes sentences and ideas from Meteor community members. @@ -16,51 +16,39 @@ Contributors are encouraged to focus their efforts on work that aligns with the ### Next releases -- Improve TypeScript support for Meteor and packages ([Discussion](https://github.com/meteor/meteor/discussions/12080)) -> Should be an ongoing effort to improve the TypeScript support in Meteor and packages, as we write new code in TypeScript and get more skin in the game, it should naturally improve. This doesn’t mean we will actively refactor working code. +- Bundle optimization +> We need to improve the bundle size and performance of Meteor apps. We should consider tree-shaking, code-splitting, +> and other optimizations to make our apps leaner and faster. +> To achieve that we plan to integrate or have an easy way to integrate with modern bundlers like RSPack, ESBuild, or Rollup. -- Performance improvements for Meteor 3.0 -> After removing fibers, we became heavily reliant on async resources and consequently Async Hooks/Async Local Storage, which has a performance cost, we need to optimize that. +- Support package.json exports fields ([Discussion](https://github.com/meteor/meteor/discussions/11727)) +- Tree-shaking +> Tree-shaking and exports fields may be implemented by integrating with more modern build tools. -- Bringing community packages to the core -> Some packages are widely used and should be part of the core, which involves identifying and moving them to the core. +- Capacitor support +> Capacitor is a modern alternative to Cordova; we should provide an easy way to build mobile apps using Capacitor. - MongoDB Change Streams support ([Discussion](https://github.com/meteor/meteor/discussions/11842)) > Change Streams is the official way to listen to changes in MongoDB. We should provide a way to use it seamlessly in Meteor. It has been planned for a long time, and now we’re in a position to do it. -- Integrated support for Vite (Client Bundler, [Discussion](https://github.com/meteor/meteor/discussions/11587)) -> Vite is a fast and modern client bundler with an amazing ecosystem. It has many potential benefits for Meteor: build performance, tree-shaking, making our codebase leaner, and focusing on what we do best. - -- Replace Babel with ESBuild, Rollup, SWC, or another tool for the server bundle ([Discussion](https://github.com/meteor/meteor/discussions/11587)) -> Babel is a great tool, but it's slow and has some limitations; we should consider replacing it with a faster and more modern tool for the server bundle. We could potentially use the same tools Vite uses. - -- Support package.json exports fields ([Discussion](https://github.com/meteor/meteor/discussions/11727)) -- Tree-shaking - -> Tree sharking and exports fields may be implemented by integrating with more modern build tools, see previous items. +- Improve TypeScript support for Meteor and packages ([Discussion](https://github.com/meteor/meteor/discussions/12080)) +> Should be an ongoing effort to improve the TypeScript support in Meteor and packages. We should provide a better experience for TypeScript users, including better type definitions and support for TypeScript features. - Improve release CI/CD speed and reliability (optimized build times will help) -> Sometimes our CI/CD takes too long to run, causing long queues and delays in our release process and feedback loop, we need to improve that. - -- Improve support for Windows 11 -> We had many complaints in the past, we need to research and make architectural improvements to make it easier to support Windows, not just punctual fixes. - -- Document better Windows with WSL -> It's already possible to use Meteor on Windows with WSL, but we need to document it better - -- HTTP/3 Support -> HTTP/3 is the next version of the HTTP protocol. We should support it in Meteor to leverage its performance and security benefits. +> Our CI/CD takes too long to run, causing long queues and delays in our release process and feedback loop; we need to improve that. ### Candidate items We need to discuss further to decide whether to proceed with these implementations. +- Performance improvements (Async Hooks/Async Local Storage optimization) +- HTTP/3 Support - Improve DDP Client - Improve Passwordless package ([Discussion](https://github.com/meteor/meteor/discussions/12075)) - Integrate with Tauri, it might replace Cordova and Electron in a single tool -- Support building mobile apps using CapacitorJS - Bring Redis-oplog to core ([Repository](https://github.com/Meteor-Community-Packages/redis-oplog)) - Better file upload support via DDP ([Discussion](https://github.com/meteor/meteor/discussions/11523)) +- Improve usage in Windows environments ### Finished items diff --git a/v3-docs/docs/api/DDPRateLimiter.md b/v3-docs/docs/api/DDPRateLimiter.md index d5c56679a8..4b4e61464d 100644 --- a/v3-docs/docs/api/DDPRateLimiter.md +++ b/v3-docs/docs/api/DDPRateLimiter.md @@ -2,7 +2,7 @@ Customize rate limiting for methods and subscriptions to avoid a high load of WebSocket messages in your app. -> Galaxy (Meteor hosting) offers additional App Protection, [read more](https://galaxy-guide.meteor.com/protection.html) and try it with our [free 30-day trial](https://www.meteor.com/hosting). +> Galaxy (Meteor hosting) offers additional App Protection, [read more](https://galaxy-support.meteor.com/en/article/ddos-mitigation-1qb032b/) and try it with our [free plans](https://galaxy-support.meteor.com/en/article/billing-4gyv1p/). By default, `DDPRateLimiter` is configured with a single rule. This rule limits login attempts, new user creation, and password resets to 5 attempts diff --git a/v3-docs/docs/api/Tracker.md b/v3-docs/docs/api/Tracker.md index b5242b548d..acbe6ad254 100644 --- a/v3-docs/docs/api/Tracker.md +++ b/v3-docs/docs/api/Tracker.md @@ -87,13 +87,13 @@ Tracker.autorun(async function example1(computation) { // Code before the first await will stay reactive. reactiveVar1.get(); // This will trigger a rerun. - let links = await LinksCollection.findAsync({}).fetch(); // First async call will stay reactive. + let links = await LinksCollection.find({}).fetchAsync(); // First async call will stay reactive. // Code after the first await looses Tracker.currentComputation: no reactivity. reactiveVar2.get(); // This won't trigger a rerun. // You can bring back reactivity with the Tracker.withCompuation wrapper: - let users = await Tracker.withComputation(computation, () => Meteor.users.findAsync({}).fetch()); + let users = await Tracker.withComputation(computation, () => Meteor.users.find({}).fetchAsync()); // Code below will again not be reactive, so you will need another Tracker.withComputation. const value = Tracker.withComputation(computation, () => reactiveVar3.get()); // This will trigger a rerun. diff --git a/v3-docs/docs/api/accounts.md b/v3-docs/docs/api/accounts.md index 676909156f..2ac9317275 100644 --- a/v3-docs/docs/api/accounts.md +++ b/v3-docs/docs/api/accounts.md @@ -19,7 +19,7 @@ Read more about customizing user accounts in the [Accounts](http://guide.meteor. ### Accounts with Session Storage {#accounts-session-storage} -By default, Meteor uses Local Storage to store, among other things, login tokens in your browser session. But, for some applications, it makes sense to use Session Storage instead. You can achieve this by adding this to your settings: +By default, Meteor uses Local Storage to store, among other things, login tokens in your browser session. But, for some applications, it makes sense to use Session Storage instead. Session Storage will not persist across client sessions. You can achieve this by adding this to your settings: ```json { @@ -772,11 +772,76 @@ authentication. In addition to the basic username and password-based sign-in process, it also supports email-based sign-in including address verification and password recovery emails. -The Meteor server stores passwords using the -[bcrypt](http://en.wikipedia.org/wiki/Bcrypt) algorithm. This helps +### Password encryption and security + +Starting from `accounts-passwords:4.0.0`, you can choose which algorithm is used by the Meteor server to store passwords : either [bcrypt](http://en.wikipedia.org/wiki/Bcrypt) or +[Argon2](http://en.wikipedia.org/wiki/Argon2) algorithm. Both are robust and contribute to protect against embarrassing password leaks if the server's database is compromised. +Before version 4.0.0, `bcrypt` was the only available option. argon2 has been introduced because it is considered the most secure option. This algorithm is specifically designed to resist GPU-based brute force attacks. For more details, see the [OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html). + +As of January 2025, **`bcrypt` is still the default option** to enable a smooth transition. In the future, `argon2` will replace `bcrypt` as default and `bcrypt` option will be deprecated. + +Passwords are hashed on the client using **SHA-256** algorithm before being sent to the server. This ensures that sensitive data is never transmitted in plain text. Once received by the server, the hashed value is further encrypted and securely stored in the `Meteor.users` collection. + + +**About the migration process from `bcrypt` to `argon2`** + +The transition from `bcrypt` to `argon2` happens automatically upon user login. If Argon2 encryption is enabled in an existing application, each user's password is re-encrypted during their next successful login. +- Step 1: The password is first validated against the existing `bcrypt` hash. +- Step 2: If authentication succeeds, the password is re-encrypted using `Argon2`. +- Step 3: The new `Argon2` hash replaces the old `bcrypt` hash in the database. + + +To monitor the migration progress, you can count users still using bcrypt: +```js +const bcryptUsers = await Meteor.users.find({ "services.password.bcrypt": { $exists: true } }).countAsync(); +const totalUsers = await Meteor.users.find({ "services.password": { $exists: true } }).countAsync(); +console.log("Remaining users to migrate:", bcryptUsers, "/", totalUsers); +``` +Once `bcryptUsers` reaches 0, the migration is complete. + +**Enabling Argon2 encryption** + +To enable Argon2 encryption, you need a small configuration change on the server: + +```js +Accounts.config({ + argon2Enabled: true, +}); +``` + +**Configuring `argon2` parameters** + +One enabled, the `accounts-password` package allows customization of Argon2's parameters. The configurable options include: + +- `type`: `argon2id` (provides a blend of resistance against GPU and side-channel attacks) +- `timeCost` (default: 2) – This controls the computational cost of the hashing process, affecting both the security level and performance. +- `memoryCost`: 19456 (19 MiB) - The amount of memory used by the algorithm in KiB per thread +- `parallelism`: 1 - The number of threads used by the algorithm + +To update the values, use the following configuration: +```js +Accounts.config({ + argon2Enabled: true, + argon2Type: "argon2id", + argon2TimeCost: 2, + argon2MemoryCost: 19456, + argon2Parallelism: 1, +}); +``` + +Other Argon2 parameters, such as `hashLength`, are kept to default values: +- `hashLength`: 32 bytes - The length of the hash output in bytes + +The default values are the minimum [OWASP recommendations for Argon2 parameters](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#introduction). When updating these values, consider the trade-offs between security and performance on the target infrastructure. + +For more information about Argon2's parameters, refer to the [argon2 options documentation](https://github.com/ranisalt/node-argon2/wiki/Options). + + +### Using passwords + To add password support to your application, run this command in your terminal: ```bash diff --git a/v3-docs/docs/api/collections.md b/v3-docs/docs/api/collections.md index 1575ad7d5f..a466de49df 100644 --- a/v3-docs/docs/api/collections.md +++ b/v3-docs/docs/api/collections.md @@ -526,8 +526,8 @@ restrictions. That includes methods that are called with `Meteor.call` relying on `allow` and `deny`. You can call `allow` as many times as you like, and each call can -include any combination of `insert`/`insertAsync`, `update`/`updateAsync`, -and `remove`/`removeAsync` functions. The functions should return `true` +include any combination of `insert`, `update`, +and `remove` functions. The functions should return `true` if they think the operation should be allowed. Otherwise they should return `false`, or nothing at all (`undefined`). In that case Meteor will continue searching through any other `allow` rules on the collection. @@ -536,18 +536,18 @@ The available callbacks are: ### Callbacks -- `insert(userId, doc)`/`insertAsync(userId, doc)` - The user `userId` wants to insert the +- `insert(userId, doc)` - The user `userId` wants to insert the document `doc` into the collection. Return `true` if this should be - allowed. + allowed. Supports async validations. `doc` will contain the `_id` field if one was explicitly set by the client, or if there is an active `transform`. You can use this to prevent users from specifying arbitrary `_id` fields. -- `update(userId, doc, fieldNames, modifier)`/`updateAsync(userId, doc, fieldNames, modifier)` - The user `userId` +- `update(userId, doc, fieldNames, modifier)` - The user `userId` wants to update a document `doc` in the database. (`doc` is the current version of the document from the database, without the - proposed update.) Return `true` to permit the change. + proposed update.) Return `true` to permit the change. Supports async validations. `fieldNames` is an array of the (top-level) fields in `doc` that the client wants to modify, for example @@ -562,8 +562,8 @@ The available callbacks are: \$-modifiers, the request will be denied without checking the `allow` functions. -- `remove(userId, doc)`/`removeAsync(userId, doc)` - the user `userId` wants to remove `doc` from the database. Return - `true` to permit this. +- `remove(userId, doc)` - the user `userId` wants to remove `doc` from the database. Return + `true` to permit this. Supports async validations. When calling `update`/`updateAsync` or `remove`/`removeAsync` Meteor will by default fetch the @@ -593,28 +593,12 @@ Posts.allow({ return doc.owner === userId; }, - remove(userId, doc) { + async remove(userId, doc) { + // Any custom async validation is supported + await Meteor.sleep(100); // Can only remove your own documents. return doc.owner === userId; }, - - async insertAsync(userId, doc) { - // Any custom async validation is supported - const allowed = await allowInsertAsync(userId, doc); - return userId && allowed; - }, - - async updateAsync(userId, doc, fields, modifier) { - // Any custom async validation is supported - const allowed = await allowUpdateAsync(userId, doc); - return userId && allowed; - }, - - async removeAsync(userId, doc) { - // Any custom async validation is supported - const allowed = await allowRemoveAsync(userId, doc); - return userId && allowed; - }, fetch: ["owner"], }); @@ -625,22 +609,12 @@ Posts.deny({ return _.contains(fields, "owner"); }, - remove(userId, doc) { + async remove(userId, doc) { + // Any custom async validation is supported + await Meteor.sleep(100); // Can't remove locked documents. return doc.locked; }, - - async updateAsync(userId, doc, fields, modifier) { - // Any custom async validation is supported - const denied = await denyUpdateAsync(userId, doc); - return userId && denied; - }, - - async removeAsync(userId, doc) { - // Any custom async validation is supported - const denied = await denyRemoveAsync(userId, doc); - return userId && denied; - }, fetch: ["locked"], // No need to fetch `owner` }); @@ -724,7 +698,7 @@ topPosts.forEach((post) => { ::: warning Client only. -For server/isomorphic usage see [removeAsync](#Mongo-Cursor-forEachAsync). +For server/isomorphic usage see [forEachAsync](#Mongo-Cursor-forEachAsync). ::: diff --git a/v3-docs/docs/api/packages-listing.md b/v3-docs/docs/api/packages-listing.md index f6f064191a..c4624c061e 100644 --- a/v3-docs/docs/api/packages-listing.md +++ b/v3-docs/docs/api/packages-listing.md @@ -45,7 +45,6 @@ ### [callback-hook](https://github.com/meteor/meteor/tree/devel/packages/callback-hook) {#callback-hook} ### [check](https://github.com/meteor/meteor/tree/devel/packages/check) {#check} ### [constraint-solver](https://github.com/meteor/meteor/tree/devel/packages/constraint-solver) {#constraint-solver} -### [context](https://github.com/meteor/meteor/tree/devel/packages/context) {#context} ### [core-runtime](https://github.com/meteor/meteor/tree/devel/packages/core-runtime) {#core-runtime} ### [crosswalk](https://github.com/meteor/meteor/tree/devel/packages/crosswalk) {#crosswalk} ### [ddp](https://github.com/meteor/meteor/tree/devel/packages/ddp) {#ddp} @@ -123,6 +122,7 @@ ### [reload](https://github.com/meteor/meteor/tree/devel/packages/reload) {#reload} ### [reload-safetybelt](https://github.com/meteor/meteor/tree/devel/packages/reload-safetybelt) {#reload-safetybelt} ### [retry](https://github.com/meteor/meteor/tree/devel/packages/retry) {#retry} +### [roles](https://github.com/meteor/meteor/tree/devel/packages/roles) {#roles} ### [routepolicy](https://github.com/meteor/meteor/tree/devel/packages/routepolicy) {#routepolicy} ### [server-render](https://github.com/meteor/meteor/tree/devel/packages/server-render) {#server-render} ### [service-configuration](https://github.com/meteor/meteor/tree/devel/packages/service-configuration) {#service-configuration} @@ -134,6 +134,7 @@ ### [standard-minifier-js](https://github.com/meteor/meteor/tree/devel/packages/standard-minifier-js) {#standard-minifier-js} ### [standard-minifiers](https://github.com/meteor/meteor/tree/devel/packages/standard-minifiers) {#standard-minifiers} ### [static-html](https://github.com/meteor/meteor/tree/devel/packages/static-html) {#static-html} +### [static-html-tools](https://github.com/meteor/meteor/tree/devel/packages/static-html-tools) {#static-html-tools} ### [test-helpers](https://github.com/meteor/meteor/tree/devel/packages/test-helpers) {#test-helpers} ### [test-in-browser](https://github.com/meteor/meteor/tree/devel/packages/test-in-browser) {#test-in-browser} ### [test-in-console](https://github.com/meteor/meteor/tree/devel/packages/test-in-console) {#test-in-console} @@ -145,9 +146,8 @@ ### [twitter-oauth](https://github.com/meteor/meteor/tree/devel/packages/twitter-oauth) {#twitter-oauth} ### [typescript](https://github.com/meteor/meteor/tree/devel/packages/typescript) {#typescript} ### [underscore](https://github.com/meteor/meteor/tree/devel/packages/underscore) {#underscore} -### [underscore-tests](https://github.com/meteor/meteor/tree/devel/packages/underscore-tests) {#underscore-tests} ### [url](https://github.com/meteor/meteor/tree/devel/packages/url) {#url} ### [webapp](https://github.com/meteor/meteor/tree/devel/packages/webapp) {#webapp} ### [webapp-hashing](https://github.com/meteor/meteor/tree/devel/packages/webapp-hashing) {#webapp-hashing} ### [weibo-config-ui](https://github.com/meteor/meteor/tree/devel/packages/weibo-config-ui) {#weibo-config-ui} -### [weibo-oauth](https://github.com/meteor/meteor/tree/devel/packages/weibo-oauth) {#weibo-oauth} +### [weibo-oauth](https://github.com/meteor/meteor/tree/devel/packages/weibo-oauth) {#weibo-oauth} \ No newline at end of file diff --git a/v3-docs/docs/cli/index.md b/v3-docs/docs/cli/index.md index 2a19726209..b576ccfee0 100644 --- a/v3-docs/docs/cli/index.md +++ b/v3-docs/docs/cli/index.md @@ -8,281 +8,349 @@ command-line tool. This is just an overview and does not mention every command or every option to every command; for more details, use the `meteor help` command. -## meteor help {meteorhelp} +## meteor help {#meteorhelp} -Get help on meteor command line usage. Running `meteor help` by -itself will list the common meteor -commands. Running meteor help command will print -detailed help about the command. +Get help on meteor command line usage. +```bash +meteor help +``` -## meteor run {meteorrun} +Lists the common meteor commands. -Run a meteor development server in the current project. Searches -upward from the current directory for the root directory of a Meteor -project. Whenever you change any of the application's source files, the -changes are automatically detected and applied to the running -application. +```bash +meteor help +``` -You can use the application by pointing your web browser at -localhost:3000. No Internet connection is -required. +Prints detailed help about the specific command. -This is the default command. Simply running `meteor` is the -same as `meteor run`. +## meteor run {#meteorrun} -To pass additional options to Node.js use the `SERVER_NODE_OPTIONS` environment variable. E.g. for Windows PowerShell: -`$env:SERVER_NODE_OPTIONS = '--inspect' | meteor run`. Or for Linux: `SERVER_NODE_OPTIONS=--inspect-brk meteor run`. +Run a meteor development server in the current project. -To specify a port to listen on (instead of the default 3000), use `--port [PORT]`. -(The development server also uses port `N+1` for the default MongoDB instance) +```bash +meteor run +``` -For example: `meteor run --port 4000` -will run the development server on `http://localhost:4000` -and the development MongoDB instance on `mongodb://localhost:4001`. +::: tip +This is the default command. Simply running `meteor` is the same as `meteor run`. +::: -Run `meteor help run` to see the full list of options. +### Features -## meteor debug {meteordebug} +- Automatically detects and applies changes to your application's source files +- No Internet connection required +- Accesses the application at [localhost:3000](http://localhost:3000) by default +- Searches upward from the current directory for the root directory of a Meteor project -Run the project, but suspend the server process for debugging. +### Options -> **NOTE:** The `meteor debug` command has been superseded by the more flexible -> `--inspect` and `--inspect-brk` command-line flags, which work for any `run`, -> `test`, or `test-packages` command. -> -> The syntax of these flags is the same as the equivalent Node.js -> [flags](https://nodejs.org/en/docs/inspector/#command-line-options), -> with two notable differences: -> -> * The flags affect the server process spawned by the build process, -> rather than affecting the build process itself. -> -> * The `--inspect-brk` flag causes the server process to pause just after -> server code has loaded but before it begins to execute, giving the -> developer a chance to set breakpoints in server code. +| Option | Description | +|--------|-------------| +| `--port`, `-p ` | Port to listen on (default: 3000). Also uses port N+1 and a port specified by --app-port. Specify as --port=host:port to bind to a specific interface | +| `--open`, `-o` | Opens a browser window when the app starts | +| `--inspect[-brk][=]` | Enable server-side debugging via debugging clients. With --inspect-brk, pauses at startup (default port: 9229) | +| `--mobile-server ` | Location where mobile builds connect (defaults to local IP and port). Can include URL scheme (e.g., https://example.com:443) | +| `--cordova-server-port ` | Local port where Cordova will serve content | +| `--production` | Simulate production mode. Minify and bundle CSS and JS files | +| `--raw-logs` | Run without parsing logs from stdout and stderr | +| `--settings`, `-s ` | Set optional data for Meteor.settings on the server | +| `--release ` | Specify the release of Meteor to use | +| `--verbose` | Print all output from builds logs | +| `--no-lint` | Don't run linters used by the app on every rebuild | +| `--no-release-check` | Don't run the release updater to check for new releases | +| `--allow-incompatible-update` | Allow packages to be upgraded or downgraded to potentially incompatible versions | +| `--extra-packages ` | Run with additional packages (comma separated, e.g., "package-name1, package-name2@1.2.3") | +| `--exclude-archs ` | Don't create bundles for certain web architectures (comma separated, e.g., "web.browser.legacy, web.cordova") | -The server process will be suspended just before the first statement of -server code that would normally execute. In order to continue execution of -server code, use either the web-based Node Inspector or the command-line -debugger (further instructions will be printed in the console). +### Node.js Options -Breakpoints can be set using the `debugger` keyword, or through the web UI of Node Inspector ("Sources" tab). +To pass additional options to Node.js, use the `SERVER_NODE_OPTIONS` environment variable: -The server process debugger will listen for incoming connections from -debugging clients, such as node-inspector, on port 5858 by default. To -specify a different port use the `--debug-port ` option. +**Windows PowerShell:** +```powershell +$env:SERVER_NODE_OPTIONS = '--inspect' | meteor run +``` -The same debugging functionality can be achieved by adding the `--debug-port ` -option to other `meteor` tool commands, such as `meteor run` and `meteor test-packages`. +**Linux/macOS:** +```bash +SERVER_NODE_OPTIONS=--inspect-brk meteor run +``` -> **Note:** Due to a [bug in `node-inspector`](https://github.com/node-inspector/node-inspector/issues/903), pushing "Enter" after a command on the Node Inspector Console will not successfully send the command to the server. If you require this functionality, please consider using Safari or `meteor shell` in order to interact with the server console until the `node-inspector` project [fixes the bug](https://github.com/node-inspector/node-inspector/pull/955). Alternatively, there is a hot-patch available [in this comment](https://github.com/meteor/meteor/issues/7991#issuecomment-266709459) on [#7991](https://github.com/meteor/meteor/issues/7991). +### Port Configuration Example +```bash +meteor run --port 4000 +``` -## meteor create _app-name_ {meteorcreate} +This command: +- Runs the development server on `http://localhost:4000` +- Runs the development MongoDB instance on `mongodb://localhost:4001` -The command `meteor create app-name` is the default command for creating a new Meteor project. It creates a subdirectory -named `app-name` and copies a template app into it. You can pass an absolute or relative path. If you pass a relative -path, it will be resolved relative to the current working directory. By default, it generates a React project. +::: info +The development server always uses port `N+1` for the default MongoDB instance, where `N` is the application port. +::: -See the flags below to learn how you can generate different types of apps. +## meteor debug {#meteordebug} +Run the project with the server process suspended for debugging. -## --apollo {apollo} +::: warning Deprecation Notice +The `meteor debug` command has been superseded by the more flexible `--inspect` and `--inspect-brk` command-line flags, which work with `run`, `test`, and `test-packages` commands. +::: -The command `meteor create --apollo app-name` creates a Meteor app with [React](https://react.dev/), -[Apollo](https://www.apollographql.com/) (GraphQL), and [MongoDB](https://www.mongodb.com/). To create a complete app, -including testing and deployment, follow the [React tutorial](https://react-tutorial.meteor.com/). To learn how to use -Apollo, refer to the [GraphQL section](https://react-tutorial.meteor.com/simple-todos-graphql/). +### Modern Debugging Approach -Npm packages included: `@apollo/client`, `@apollo/server`, `@babel/runtime`, `body-parser`, `express`, -`graphql` `meteor-node-stubs`, `react`, `react-dom`. +```bash +# Debug server with auto-attachment +meteor run --inspect -Meteor packages included: `meteor-base`, `mobile-experience`, `mongo`, `reactive-var`, `standard-minifier-css`, +# Debug server and pause at start +meteor run --inspect-brk +``` + +### Command Usage + +```bash +meteor debug [--debug-port ] +``` + +### How It Works + +- Server process suspends just before the first statement of server code execution +- Debugger listens for incoming connections on port 5858 by default +- Use `--debug-port ` to specify a different port + +### Setting Breakpoints + +- Use the [`debugger`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger) keyword in your code +- Set breakpoints through the debugging client's UI (e.g., in the "Sources" tab) + +### Debugging Clients + +You can use either: +- Web-based Node Inspector +- Command-line debugger + +::: details Node Inspector Console Bug +Due to a [bug in `node-inspector`](https://github.com/node-inspector/node-inspector/issues/903), pressing "Enter" after a command in the Node Inspector Console may not successfully send the command to the server. + +**Workarounds:** +- Use Safari browser +- Use `meteor shell` to interact with the server console +- Apply the hot-patch available in [this comment](https://github.com/meteor/meteor/issues/7991#issuecomment-266709459) +::: + +### Differences from Node.js Flags + +The Meteor `--inspect` and `--inspect-brk` flags work similarly to Node.js flags with two key differences: + +1. They affect the server process spawned by the build process, not the build process itself +2. The `--inspect-brk` flag pauses execution after server code has loaded but before it begins to execute + +### Alternative Approach + +The same debugging functionality can be achieved by adding the `--debug-port ` option to other Meteor commands: + +```bash +meteor run --debug-port 5858 +meteor test-packages --debug-port 5858 +``` + +## meteor profile {#meteorprofile} + +Run a performance profile for your Meteor application to analyze build and bundling performance. + +```bash +meteor profile [...] +``` + +::: info Availability +This command is available from Meteor 3.2 and newer. +::: + +### Usage + +This command monitors the bundler process and tracks key performance metrics to help analyze build and bundling performance. + +### Options + +| Option | Description | +|--------|-------------| +| `--size` | Monitor both bundle runtime and size | +| `--size-only` | Monitor only the bundle size | + +::: info +All other options from `meteor run` are also supported (e.g., `--settings`, `--exclude-archs`). +::: + +### Environment Variables + +| Variable | Description | Default | +|----------|-------------|---------| +| `METEOR_IDLE_TIMEOUT=` | Set a timeout for profiling | 90 seconds | +| `METEOR_CLIENT_ENTRYPOINT=` | Set a custom client entrypoint | From package.json | +| `METEOR_SERVER_ENTRYPOINT=` | Set a custom server entrypoint | From package.json | +| `METEOR_LOG_DIR=` | Set a custom log directory | Default log directory | + +::: tip +The default timeout (90s) is usually enough for each build step to complete. If you encounter errors due to early exits, increase the `METEOR_IDLE_TIMEOUT` value. +::: + +### Example Usage + +```bash +# Basic profile +meteor profile + +# Monitor bundle size only +meteor profile --size-only + +# Profile with custom settings and timeout +METEOR_IDLE_TIMEOUT=120 meteor profile --settings settings.json + +# Profile with custom entrypoints +METEOR_CLIENT_ENTRYPOINT=client/main.js METEOR_SERVER_ENTRYPOINT=server/main.js meteor profile +``` + +::: details Customizing the Profiling Process +You can pass any option that works with `meteor run` to customize the profiling process. This allows you to profile your application under specific conditions that match your deployment environment. +::: + +## meteor create _app-name_ {#meteorcreate} + +Create a new Meteor project in a directory called `app-name`. + +```bash +meteor create [options] app-name +``` + +::: tip Default Behavior +Without any flags, `meteor create app-name` generates a React project. +::: + +::: tip Interactive Wizard +If you run `meteor create` without arguments, Meteor will launch an interactive wizard that guides you through selecting your project name and application type: + +```bash + ~ What is the name/path of your app? + ~ Which skeleton do you want to use? + Blaze # To create an app using Blaze + Full # To create a more complete scaffolded app + Minimal # To create an app with as few Meteor packages as possible + React # To create a basic React-based app + Typescript # To create an app using TypeScript and React + Vue # To create a basic Vue3-based app + Svelte # To create a basic Svelte app + Tailwind # To create an app using React and Tailwind + Chakra-ui # To create an app Chakra UI and React + Solid # To create a basic Solid app + Apollo # To create a basic Apollo + React app + Bare # To create an empty app +``` +::: + +### Basic Options + +| Option | Description | +|--------|-------------| +| `--from ` | Clone a Meteor project from a URL | +| `--example ` | Use a specific example template | +| `--list` | Show list of available examples | +| `--release ` | Specify Meteor version (e.g., `--release 2.8`) | +| `--prototype` | Include `autopublish` and `insecure` packages for rapid prototyping (not for production) | + +### Application Types + +| Option | Description | Tutorial / Example | +|--------|-------------|----------| +| `--react` | Create a React app (default) | [Meteor 3 with React](https://docs.meteor.com/tutorials/react/), [Meteor 2 with React](https://react-tutorial.meteor.com/) | +| `--vue` | Vue 3 + Tailwind CSS + Vite | [Meteor 3 with Vue](https://docs.meteor.com/tutorials/vue/meteorjs3-vue3-vue-meteor-tracker.html), [Meteor 2 with Vue](https://vue3-tutorial.meteor.com/) | +| `--svelte` | Svelte | [Meteor 2 with Svelte](https://svelte-tutorial.meteor.com/) | +| `--blaze` | Basic Blaze app | [Meteor 2 with Blaze](https://blaze-tutorial.meteor.com/) | +| `--solid` | Solid + Vite | [Meteor 2 with Solid Example](https://github.com/fredmaiaarantes/meteor-solid-app/releases/tag/milestone-2.0) | +| `--apollo` | React + Apollo (GraphQL) | [Meteor 2 with GraphQL](https://react-tutorial.meteor.com/simple-todos-graphql/) | +| `--typescript` | React + TypeScript | [TypeScript Guide](https://guide.meteor.com/build-tool.html#typescript) | +| `--tailwind` | React + Tailwind CSS | - | +| `--chakra-ui` | React + Chakra UI | [Simple Tasks Example](https://github.com/fredmaiaarantes/simpletasks) | + +### Project Structure Options + +| Option | Description | +|--------|-------------| +| `--minimal` | Create with minimal Meteor packages | +| `--bare` | Create an empty app (Blaze + MongoDB) | +| `--full` | Create a fully scaffolded app with imports-based structure (Blaze + MongoDB) | +| `--package` | Create a new package instead of an application | + +::: warning Prototype Mode +The `--prototype` option adds packages that make development faster but shouldn't be used in production. See the [security checklist](https://guide.meteor.com/security.html#checklist). +::: + +### Included Packages + +
+React App (--react or default) + +**NPM packages:** +- `@babel/runtime`, `meteor-node-stubs`, `react`, `react-dom` + +**Meteor packages:** +- `meteor-base`, `mobile-experience`, `mongo`, `reactive-var`, `standard-minifier-css`, `standard-minifier-js`, `es5-shim`, `ecmascript`, `typescript`, `shell-server`, `hot-module-replacement`, `static-html`, -`react-meteor-data`, `apollo`, `swydo:graphql`. +`react-meteor-data` +
-## --bare {bare} +
+Apollo (GraphQL) App (--apollo) -The command `meteor create --bare app-name` creates an empty Meteor app with [Blaze](https://blazejs.org) and -[MongoDB](https://www.mongodb.com/). To create a complete app, including testing and deployment, follow the -[Blaze tutorial](https://blaze-tutorial.meteor.com/). +**NPM packages:** +- `@apollo/client`, `@apollo/server`, `@babel/runtime`, `graphql` `meteor-node-stubs`, `react`, `react-dom` -Npm packages included: `@babel/runtime`, `meteor-node-stubs`, `jquery`. +**Meteor packages:** +- `meteor-base`, `mobile-experience`, `mongo`, `reactive-var`, `standard-minifier-css`, +`standard-minifier-js`, `es5-shim`, `ecmascript`, `typescript`, `shell-server`, `hot-module-replacement`, `static-html`, +`apollo`, `compat:graphql` +
-Meteor packages included: `meteor-base`, `mobile-experience`, `mongo`, `reactive-var`, `tracker`, `standard-minifier-css`, -`standard-minifier-js`, `es5-shim`, `ecmascript`, `typescript`, `shell-server`. +
+Blaze App (--blaze) +**NPM packages:** +- `@babel/runtime`, `meteor-node-stubs`, `jquery` -## --blaze {blaze-app} - -The command `meteor create --blaze app-name` creates a Meteor app with [Blaze](https://blazejs.org) and -[MongoDB](https://www.mongodb.com/). To create a complete app, including testing and deployment, follow the -[Blaze tutorial](https://blaze-tutorial.meteor.com/). - -Npm packages included: `@babel/runtime`, `meteor-node-stubs`, `jquery`. - -Meteor packages included: `meteor-base`, `mobile-experience`, `mongo`, `blaze-html-templates`, `jquery`, `reactive-var`, +**Meteor packages:** +- `meteor-base`, `mobile-experience`, `mongo`, `blaze-html-templates`, `jquery`, `reactive-var`, `tracker`, `standard-minifier-css`, `standard-minifier-js`, `es5-shim`, `ecmascript`, `typescript`, `shell-server`, -`hot-module-replacement`, `blaze-hot`. +`hot-module-replacement`, `blaze-hot` +
+
+Vue App (--vue) -## --chakra-ui {chakra-ui} +**NPM packages:** +- `@babel/runtime`, `meteor-node-stubs`, `vue`, `vue-meteor-tracker`, `vue-router`, `@types/meteor`, `@vitejs/plugin-vue`, `autoprefixer`, `meteor-vite`, `postcss`, `tailwindcss`, `vite` -The command `meteor create --chakra-ui app-name` creates a Meteor app with [React](https://react.dev/), -[Chakra-UI](https://chakra-ui.com/), and [MongoDB](https://www.mongodb.com/). To create a complete app, including -testing and deployment, follow the [React tutorial](https://react-tutorial.meteor.com/). To learn how to use Chakra-UI, -refer to the [Simple Tasks](https://github.com/fredmaiaarantes/simpletasks) example. - -Npm packages included: `@babel/runtime`, `meteor-node-stubs`, `react`, `react-dom`, `@chakra-ui/icons`, `@chakra-ui/react`, `@emotion/react` -`@emotion/styled`, `@react-icons/all-files`, `framer-motion`. - -Meteor packages included: `meteor-base`, `mobile-experience`, `mongo`, `reactive-var`, `standard-minifier-css`, +**Meteor packages:** +- `meteor-base`, `mobile-experience`, `mongo`, `reactive-var`, `standard-minifier-css`, `standard-minifier-js`, `es5-shim`, `ecmascript`, `typescript`, `shell-server`, `hot-module-replacement`, `static-html`, -`react-meteor-data`. +`jorgenvatle:vite` +
+
+Minimal App (--minimal) -## --full {full} +**NPM packages:** +- `@babel/runtime`, `meteor-node-stubs` -The command `meteor create --full app-name` creates a Meteor app with [Blaze](https://blazejs.org) and -[MongoDB](https://www.mongodb.com/). It creates a more complete, imports-based project that closely matches the -[file structure](https://guide.meteor.com/structure.html#javascript-structure) recommended by the -[Meteor Guide](https://guide.meteor.com/). To create a complete app, including testing and deployment, follow the -[Blaze tutorial](https://blaze-tutorial.meteor.com/). - -Npm packages included: `@babel/runtime`, `meteor-node-stubs`, `jquery`, `chai`. - -Meteor packages included: `meteor-base`, `mobile-experience`, `mongo`, `blaze-html-templates`, `jquery`, `reactive-var`, -`tracker`, `standard-minifier-css`, `standard-minifier-js`, `es5-shim`, `ecmascript`, `typescript`, `shell-server`, -`ostrio:flow-router-extra`, `less`, `meteortesting:mocha`, `johanbrook:publication-collector`. - - -## --minimal {minimal} - -The command `meteor create --minimal app-name` creates a project with as few Meteor packages as possible. - -Npm packages included: `@babel/runtime`, `meteor-node-stubs`. - -Meteor packages included: `meteor`, `standard-minifier-css`, `standard-minifier-js`, `es5-shim`, `ecmascript`, `typescript`, `shell-server`, -`static-html`, `webapp`, `server-render`, `hot-module-replacement`. - - -## --package {package} - -The command `meteor create --package package-name` creates a new package. If used in an existing app, it will create a -package in the `packages` directory. Check the [Meteor Guide](https://guide.meteor.com/writing-atmosphere-packages.html) -for more information on how to get started writing packages. - - -## --prototype {prototype} - -The command `meteor create --prototype app-name` creates a project with the prototype purpose packages (`autopublish` -and `insecure`). If you use them, you can change your collections quickly and create prototype apps very quickly. -However, these packages are not supposed to be used in production. - -For more information about security, you can read our [security checklist](https://guide.meteor.com/security.html#checklist). -It can be used with other flags that create apps, such as `--react`, `blaze`, or `--typescript`. - - -## --react {react} - -The command `meteor create --react app-name` creates a Meteor app with [React](https://react.dev/) and -[MongoDB](https://www.mongodb.com/). It functions in the same way as if you don't use any flags. To create a complete -app, including testing and deployment, follow the [React tutorial](https://react-tutorial.meteor.com/). - -Npm packages included: `@babel/runtime`, `meteor-node-stubs`, `react`, `react-dom`. - -Meteor packages included: `meteor-base`, `mobile-experience`, `mongo`, `reactive-var`, `standard-minifier-css`, -`standard-minifier-js`, `es5-shim`, `ecmascript`, `typescript`, `shell-server`, `hot-module-replacement`, `static-html`, -`react-meteor-data`. - - - -## --release {release} - -The command `meteor create app-name --release {meteor-version}` creates a Meteor app with the release specified in the -command. For instance, you can create a Meteor app with the `2.8` release using `meteor create app-name --release 2.8`. -By default, it generates a React app, but you can use it with other flags that create apps such as `--blaze`, -`--svelte`, `--vue`, or `--typescript`. - - -## --solid {solid} - -The command `meteor create --solid app-name` creates a Meteor app with [Solid](https://www.solidjs.com/), -[Vite](https://vitejs.dev/), and [MongoDB](https://www.mongodb.com/). You can see an example on the -[meteor-solid-app](https://github.com/fredmaiaarantes/meteor-solid-app/releases/tag/milestone-2.0) repository. - -Npm packages included: `@babel/runtime`, `meteor-node-stubs`, `solid-js`, `babel-preset-solid`, `vite`, `vite-plugin-solid`, `vite-plugin-solid-svg`. - -Meteor packages included: `meteor-base`, `mobile-experience`, `mongo`, `reactive-var`, `standard-minifier-css`, -`standard-minifier-js`, `es5-shim`, `ecmascript`, `typescript`, `shell-server`, `hot-module-replacement`, `static-html`, -`vite:bundler`. - - -## --svelte {svelte} - -The command `meteor create --svelte app-name` creates a Meteor app with [Svelte](https://svelte.dev/) and -[MongoDB](https://www.mongodb.com/). To create a complete app, including testing and deployment, follow the -[Svelte tutorial](https://svelte-tutorial.meteor.com/). - -Npm packages included: `@babel/runtime`, `meteor-node-stubs`, `svelte`, `svelte-preprocess`. - -Meteor packages included: `meteor-base`, `mobile-experience`, `mongo`, `standard-minifier-css`, -`standard-minifier-js`, `es5-shim`, `ecmascript`, `typescript`, `shell-server`, `hot-module-replacement`, `static-html`, -`zodern:melte`, `zodern:types`. - -You can also use [Svelte](https://svelte.dev/) with [Vite](https://vitejs.dev/) by using the [jorgenvatle:meteor-vite](https://github.com/JorgenVatle/meteor-vite) package. -You can see an example on the [meteor-vite](https://github.com/JorgenVatle/meteor-vite/tree/release/examples/svelte) repository. - - -## --tailwind {tailwind} - -The command `meteor create --tailwind app-name` creates a Meteor app with [React](https://react.dev/), -[Tailwind CSS](https://tailwindcss.com), and [MongoDB](https://www.mongodb.com/). - -Npm packages included: `@babel/runtime`, `meteor-node-stubs`, `react`, `react-dom`, `autoprefixer`, `postcss`, `postcss-load-config`, `tailwindcss`. - -Meteor packages included: `meteor-base`, `mobile-experience`, `mongo`, `reactive-var`, `standard-minifier-css`, -`standard-minifier-js`, `es5-shim`, `ecmascript`, `typescript`, `shell-server`, `hot-module-replacement`, `static-html`, -`react-meteor-data`. - - -## --typescript {typescript} - -The command `meteor create --typescript app-name` creates a Meteor app with [React](https://react.dev/), -[TypeScript](https://www.typescriptlang.org/), and [MongoDB](https://www.mongodb.com/). Check the -[Meteor Guide](https://guide.meteor.com/build-tool.html#typescript) for more information about TypeScript and how to -use it with other UI frameworks. - -Npm packages included: `@babel/runtime`, `meteor-node-stubs`, `react`, `react-dom`, `@types/mocha`, `@types/node`, `@types/react`, `@types/react-dom`, `typescript`. - -Meteor packages included: `meteor-base`, `mobile-experience`, `mongo`, `reactive-var`, `standard-minifier-css`, -`standard-minifier-js`, `es5-shim`, `ecmascript`, `typescript`, `shell-server`, `hot-module-replacement`, `static-html`, -`react-meteor-data`, `zodern:types`. - - -## --vue {vue} - -The command `meteor create --vue app-name` creates a Meteor app with [Vue 3](https://vuejs.org/), -[Tailwind CSS](https://tailwindcss.com), [Vite](https://vitejs.dev/), and [MongoDB](https://www.mongodb.com/). To -create a complete app, including testing and deployment, follow the [Vue 3 tutorial](https://vue3-tutorial.meteor.com/). - -Npm packages included: `@babel/runtime`, `meteor-node-stubs`, `vue`, `vue-meteor-tracker`, `vue-router`, `@types/meteor`, `@vitejs/plugin-vue`, `autoprefixer`, `postcss`, `tailwindcss`, `vite`. - -Meteor packages included: `meteor-base`, `mobile-experience`, `mongo`, `reactive-var`, `standard-minifier-css`, -`standard-minifier-js`, `es5-shim`, `ecmascript`, `typescript`, `shell-server`, `hot-module-replacement`, `static-html`, -`vite:bundler`. - -You can also use Vue 3 with Vite by using the [jorgenvatle:meteor-vite](https://github.com/JorgenVatle/meteor-vite) -package. You can see an example on the [meteor-vite](https://github.com/JorgenVatle/meteor-vite/tree/release/examples/vue) -repository. - -Npm packages included: `@babel/runtime`, `meteor-node-stubs`, `vue`, `vue-meteor-tracker`. - -Meteor packages included: `meteor-base`, `mobile-experience`, `mongo`, `reactive-var`, `standard-minifier-css`, -`standard-minifier-js`, `es5-shim`, `ecmascript`, `typescript`, `shell-server`, `tracker`, `static-html`, `akryum:vue-component`, -`meteortesting:mocha`, `johanbrook:publication-collector`. +**Meteor packages:** +- `meteor`, `standard-minifier-css`, `standard-minifier-js`, `es5-shim`, `ecmascript`, `typescript`, `shell-server`, +`static-html`, `webapp`, `ddp`, `server-render`, `hot-module-replacement` +
+::: tip File Structure +To learn more about the recommended file structure for Meteor apps, check the [Meteor Guide](https://guide.meteor.com/structure.html#javascript-structure). +::: ## meteor generate {meteorgenerate} @@ -306,7 +374,7 @@ It will generate the following code in ``/imports/api`` That will have the following code: -## collection.js {meteorgenerate-collection.js} +### collection.js {meteorgenerate-collection.js} ```js import { Mongo } from 'meteor/mongo'; @@ -316,7 +384,7 @@ export const CustomerCollection = new Mongo.Collection('customer'); -## methods.js {meteorgenerate-methods.js} +### methods.js {meteorgenerate-methods.js} ```js import { Meteor } from 'meteor/meteor'; @@ -352,7 +420,7 @@ Meteor.methods({ -## publication.js {meteorgenerate-publication.js} +### publication.js {meteorgenerate-publication.js} ```js import { Meteor } from 'meteor/meteor'; @@ -366,7 +434,7 @@ Meteor.publish('allCustomers', function publishCustomers() { -## index.js {meteorgenerate-index.js} +### index.js {meteorgenerate-index.js} ```js export * from './collection'; @@ -376,7 +444,7 @@ export * from './publications'; Also, there is the same version of these methods using TypeScript, that will be shown bellow. -## path option {meteorgenerate-path} +### path option {meteorgenerate-path} If you want to create in another path, you can use the ``--path`` option in order to select where to place this boilerplate. It will generate the model in that path. Note that is used TypeScript in this example. @@ -392,7 +460,7 @@ It will generate in ``server/admin`` the another-client code: ![Screenshot 2022-11-09 at 11 32 39](https://user-images.githubusercontent.com/70247653/200857560-a4874e4c-1078-4b7a-9381-4c6590d2f63b.png) -## collection.ts {meteorgenerate-collection.ts} +### collection.ts {meteorgenerate-collection.ts} ```typescript import { Mongo } from 'meteor/mongo'; @@ -406,7 +474,7 @@ export type AnotherCustomer = { export const AnotherCustomerCollection = new Mongo.Collection('another-customer'); ``` -## methods.ts {meteorgenerate-methods.ts} +### methods.ts {meteorgenerate-methods.ts} ```typescript import { Meteor } from 'meteor/meteor'; @@ -443,7 +511,7 @@ Meteor.methods({ -## publications.ts {meteorgenerate-publications.ts} +### publications.ts {meteorgenerate-publications.ts} ```typescript import { Meteor } from 'meteor/meteor'; @@ -456,7 +524,7 @@ Meteor.publish('allAnotherCustomers', function publishAnotherCustomers() { -## index.ts {meteorgenerate-index.ts} +### index.ts {meteorgenerate-index.ts} ```typescript export * from './collection'; @@ -467,7 +535,7 @@ export * from './publications'; --- -## Using the Wizard {meteorgenerate-wizard} +### Using the Wizard {meteorgenerate-wizard} If you run the following command: @@ -485,7 +553,7 @@ It will prompt the following questions. --- -## Using your own template {meteorgenerate-templating} +### Using your own template {meteorgenerate-templating} `--templatePath` @@ -500,7 +568,7 @@ meteor generate feed --templatePath=/scaffolds-ts You can use your own templates for scaffolding your specific workloads. To do that, you should pass in a template directory URL so that it can copy it with its changes. -## How to rename things? {meteorgenerate-template-rename} +### How to rename things? {meteorgenerate-template-rename} Out of the box is provided a few functions such as replacing ``$$name$$``, ``$$PascalName$$`` and ``$$camelName$$`` @@ -518,7 +586,7 @@ const transformName = (name) => { } ``` -## How to bring your own templates? {meteorgenerate-template-faq} +### How to bring your own templates? {meteorgenerate-template-faq} `--replaceFn` @@ -570,238 +638,297 @@ export function transformContents(scaffoldName, contents, fileName) { return transformName(scaffoldName, contents); } ``` +## meteor login -## meteor login / logout {meteorloginlogout} +Logs you in to your Meteor developer account. -Log in and out of your account using Meteor's authentication system. - -You can pass `METEOR_SESSION_FILE=token.json` before `meteor login` to generate -a login session token so you don't have to share your login credentials with -third-party service providers. - -Once you have your account you can log in and log out from the command line, and -check your username with `meteor whoami`. - -## meteor deploy _site_ {meteordeploy} - -Deploy the project in your current directory to -Galaxy. - -Use `--owner` to decide which organization or user account you'd like to deploy -a new app to if you are a member of more than one Galaxy-enabled account. - -You can deploy in debug mode by passing `--debug`. This -will leave your source code readable by your favorite in-browser -debugger, just like it is in local development mode. - - - -To delete an application you've deployed, specify -the `--delete` option along with the site. - - - -You can add information specific to a particular deployment of your application -by using the `--settings` option. The argument to `--settings` is a file -containing any JSON string. The object in your settings file will appear on the -server side of your application in [`Meteor.settings`](../api/meteor#Meteor-settings). - -Settings are persistent. When you redeploy your app, the old value will be -preserved unless you explicitly pass new settings using the `--settings` option. -To unset `Meteor.settings`, pass an empty settings file. - -::: warning -`free` and `mongo` options were introduced in Meteor 2.0 -::: - -You can run your app for free using the option `--free`. But, there are some limitations. The first one is that you cannot use a custom domain to run a free app. Your domain must contain a Meteor domain name (`.meteorapp.com` to US region, `.au.meteorapp.com` to Asia region, or `.eu.meteorapp.com` to Europe region). Second thing you must know is that your free apps have Cold Start enabled. Cold Start means that your app will stop if it has no connection for 10 minutes, and it will go automatically up when someone tries to connect to it. The third thing you must know is that free apps run on one, and just one, Tiny container. This is important to know, because Tiny containers are NOT meant to production environment, so even small apps can crash with a lot of connections. To keep your app on free, you always need to provide this option. - -With the option `--mongo` you can deploy your app without having to pay for a MongoDB provider. By providing this option, Galaxy will create a database for you in our shared cluster and inject the mongo URL on your settings. So with this, you don't even need to provide the settings file anymore (if your settings files just have the mongo URL of course). This is great to test apps, but it shouldn't be used in a production environment, as you will be running in a shared Cluster with limited space. The rules behind this option are: If it is the first deploy of the app, and you provided the option `--mongo`, after the deploy is finished you will receive your mongo URL on your console (you can also see your URL on Galaxy in your app's version). You can put that URL on your settings file if want to. If you try to do a second without the option `--mongo` and without providing a mongo URL on your settings, your deploy will fail as usual. If you provide the option `--mongo` and a mongo URL, the mongo URL on your settings file is the one that will be used by Galaxy to connect your app to a MongoDB. One last thing, you need to have at least one document in your database so Meteor is really going to instantiate it. Then you will be able to access it using any MongoDB client with the provided URI. - -Use the options `--mongo` and `--free` to easily deploy a free app already with a mongo database connected to it. - -::: warning -Free apps and MongoDB shared hosting: Meteor Software reserves the right to stop or remove applications we deem to be abusing the free plan offering at any time. Please be advised that the free plan offering is not recommended for production applications. The shared MongoDB cluster that comes configured with the free plan does not provide backups or restoration resources. -::: - -::: warning -If you want to connect to your free MongoDB shared cluster using your on settings make sure you include this option in your settings in the Mongo package configuration section: +**Usage:** +```bash +meteor login [--email] ``` -packages: { - mongo: { - options: { - tlsAllowInvalidCertificates: true, - }, - }, + +**Details:** +- Prompts for your username and password +- Pass `--email` to log in by email address rather than by username +- You can set `METEOR_SESSION_FILE=token.json` before `meteor login` to generate a login session token, avoiding the need to share credentials with third-party service providers + +## meteor logout + +Logs you out of your Meteor developer account. + +**Usage:** +```bash +meteor logout +``` + +## meteor whoami + +Displays your currently logged-in username. + +**Usage:** +```bash +meteor whoami +``` + +## meteor deploy _site_ {#meteordeploy} + +Deploys the project in your current directory to [Galaxy](https://www.meteor.com/galaxy). + +### Basic Deployment + +```bash +meteor deploy your-app.meteorapp.com +``` + +### Deployment Options + +| Option | Description | +|--------|-------------| +| `--delete`, `-D` | Permanently delete this deployment | +| `--debug` | Deploy in debug mode (don't minify, etc.) | +| `--settings`, `-s ` | Set optional data for Meteor.settings | +| `--free` | Deploy as a free app (with limitations) | +| `--mongo` | Create and connect to a free shared MongoDB database | +| `--plan ` | Set app plan: `professional`, `essentials`, or `free` | +| `--container-size ` | Set container size: `tiny`, `compact`, `standard`, `double`, `quad`, `octa`, or `dozen` | +| `--owner` | Specify organization or user account to deploy to | +| `--cache-build` | Reuse the build if the git commit hash is the same | +| `--allow-incompatible-update` | Allow packages to be upgraded or downgraded to potentially incompatible versions | +| `--deploy-polling-timeout ` | Time to wait for build/deploy (defaults to 15 minutes) | +| `--no-wait` | Exit after code upload instead of waiting for deploy to complete | + +### Free Deployment + +Deploy a free app with MongoDB using: + +```bash +meteor deploy your-app.meteorapp.com --free --mongo +``` + +::: tip Quick Start +The combination of `--free` and `--mongo` is the fastest way to deploy an app without any additional configuration. +::: + +#### Free App Limitations + +- **Domain**: Must use a Meteor domain (`.meteorapp.com`, `.au.meteorapp.com`, or `.eu.meteorapp.com`) +- **Cold Start**: App stops after 30 minutes of inactivity and restarts on next connection +- **Resources**: Limited to one Tiny container (not recommended for production use) + + +### MongoDB Options + +#### Shared MongoDB (Free) + +The `--mongo` option creates a database in Galaxy's shared cluster: + +- On first deploy, you'll receive your MongoDB URI in the console +- The URI is also visible in your app's version details in Galaxy +- You must create at least one document to fully instantiate the database +- The database can be accessed using any MongoDB client with the provided URI + +::: warning +Free shared MongoDB is not recommended for production applications. The shared cluster doesn't provide backups or restoration resources. +::: + +#### MongoDB Connection Settings + +When connecting to the free MongoDB shared cluster using your own settings, include: + +```json +{ + "packages": { + "mongo": { + "options": { + "tlsAllowInvalidCertificates": true + } + } + } } ``` -This is necessary as our database provider does not have certificates installed on every machine and we don't want to force apps to have this certificate. More about this option [here](../api/collections.html#mongo_connection_options_settings) + +::: details Why is this needed? +This is necessary because the database provider doesn't have certificates installed on every machine. More about this option [here](../api/collections.html#mongo_connection_options_settings). ::: +### Important Notes -You can change the app plan by providing argument `--plan` with one of the following values: professional, essentials, or free. Be aware that this argument overwrites the `--free` argument. +- Settings persist between deployments unless explicitly changed +- Your project should be a git repository (commit hash is used to track code changes) +- Free apps and MongoDB shared hosting are not recommended for production use +- Meteor Software reserves the right to stop or remove applications that abuse the free plan -::: warning -The `plan` option is available since Meteor 2.1. +::: info Version Compatibility +- `--free` and `--mongo` options were introduced in Meteor 2.0 +- `--plan` option was introduced in Meteor 2.1 +- `--container-size` option was introduced in Meteor 2.4.1 +- `--cache-build` option is available since Meteor 1.11 ::: -Use `--cache-build` to keep the bundle in your temp folder after the deploy is finished, this is helpful when you want to deploy the same code to different environments. For example, a [background job](https://cloud-guide.meteor.com/background-jobs.html) app from the same code as the web app. +## meteor update -Your project should be a git repository as the commit hash is going to be used to decide if your code is still the same or not in the next deploy. +Updates your Meteor application while maintaining compatibility. + +**Usage:** +```bash +meteor update +meteor update --patch +meteor update --release +meteor update --packages-only +meteor update [packageName packageName2 ...] +meteor update --all-packages +``` + +**Update Types:** + +| Command | Description | +|---------|-------------| +| `meteor update` | Updates the Meteor release and compatible package versions | +| `meteor update --patch` | Updates to the latest patch release (recommended for bug fixes) | +| `meteor update --release ` | Updates to a specific Meteor release | +| `meteor update --packages-only` | Updates only packages, not the Meteor release | +| `meteor update [packageName ...]` | Updates specific named packages | +| `meteor update --all-packages` | Updates all packages including indirect dependencies | + +**Important Notes:** +- Every project is pinned to a specific Meteor release +- By default, updates will not break compatibility between packages +- Patch releases contain minor, critical bug fixes and are highly recommended +- The `--release` flag can override compatibility checks (may cause warnings) +- The `--all-packages` option will update all packages to their latest compatible versions, respecting dependency constraints + + +## meteor add *package* {#meteor-add} + +Adds packages to your Meteor project. + +**Usage:** +```bash +meteor add [package1] [package2] ... +meteor add package@version +``` + +**Version Constraints:** +- `package@1.1.0` - Version 1.1.0 or higher (but not 2.0.0+) +- `package@=1.1.0` - Exactly version 1.1.0 +- `package@=1.0.0 || =2.0.1` - Either version 1.0.0 or 2.0.1 exactly + +**Notes:** +- By convention, community packages include the maintainer's name (e.g., `iron:router`) +- To remove a version constraint, run `meteor add package` without specifying a version + +## meteor remove *package* {#meteor-remove} + +Removes a package previously added to your Meteor project. + +**Usage:** +```bash +meteor remove [package1] [package2] ... +``` + +**Notes:** +- For a list of currently used packages, run `meteor list` +- This removes the package entirely (to only remove version constraints, use [`meteor add`](#meteor-add)) +- Transitive dependencies aren't automatically downgraded unless necessary + +## meteor list {#meteor-list} + +Lists all packages added to your project, including versions and available updates. + +**Usage:** +```bash +meteor list [flags] +``` + +**Flags:** + +| Flag | Description | +|------|-------------| +| `--tree` | Outputs a tree showing package reference hierarchy | +| `--json` | Outputs an unformatted JSON string of package references | +| `--weak` | Shows weakly referenced dependencies (only with `--tree` or `--json`) | +| `--details` | Adds more package details (only with `--json`) | + + +## meteor add-platform *platform* {#meteor-add-platform} + +Adds platforms to your Meteor project. + +**Usage:** +```bash +meteor add-platform [platform1] [platform2] ... +``` + +**Notes:** +- Multiple platforms can be added with one command +- After adding, use `meteor run ` to run on that platform +- Use `meteor build` to build for all added platforms + + +## meteor remove-platform *platform* {#meteor-remove-platform} + +Removes a previously added platform. + +**Usage:** +```bash +meteor remove-platform [platform] +``` + +**Notes:** +- For a list of currently added platforms, use `meteor list-platforms` + + +## meteor list-platforms {#meteor-list-platforms} + +Lists all platforms explicitly added to your project. + +**Usage:** +```bash +meteor list-platforms +``` + + +## meteor ensure-cordova-dependencies {#meteor-ensure-cordova-dependencies} + +Checks if dependencies are installed, and installs them if necessary. + +**Usage:** +```bash +meteor ensure-cordova-dependencies +``` + + +## meteor mongo {#meteor-mongo} + +Opens a MongoDB shell on your local development database. + +**Usage:** +```bash +meteor mongo +``` ::: warning -The `cache-build` option is available since Meteor 1.11. +For now, you must already have your application running locally with `meteor run`. This will be easier in the future. ::: -With the argument `--container-size` you can change your app's container size using the deploy command. The valid arguments are: `tiny`, `compact`, `standard`, `double`, `quad`, `octa`, and `dozen`. One more thing to note here is that the `--container-size` flag can only be used when the `--plan` option is already specified, otherwise using the `--container-size` option will throw an error with the message : `Error deploying application: Internal error`. To see more about the difference and prices of each one you can check [here](https://www.meteor.com/cloud#pricing-section). +## meteor reset {#meteor-reset} + +Resets the current project to a fresh state and clears the local cache. + +**Usage:** +```bash +meteor reset [--db] +``` + +**Flags:** +- `--db` - Also removes the local MongoDB database ::: warning -The `--container-size` option is available since Meteor 2.4.1. -::: - -## meteor update {meteorupdate} - -Attempts to bring you to the latest version of Meteor, and then to upgrade your -packages to their latest versions. By default, update will not break -compatibility. - -For example, let's say packages A and B both depend on version 1.1.0 of package -X. If a new version of A depends on X@2.0.0, but there is no new version of -package B, running `meteor update` will not update A, because doing so will -break package B. - -You can pass in the flag `--packages-only` to update only the packages, and not -the release itself. Similarly, you can pass in names of packages -(`meteor update foo:kittens baz:cats`) to only update specific packages. - -Every project is pinned to a specific release of Meteor. You can temporarily try -using your package with another release by passing the `--release` option to any -command; `meteor update` changes the pinned release. - -Sometimes, Meteor will ask you to run `meteor update --patch`. Patch releases -are special releases that contain only very minor changes (usually crucial bug -fixes) from previous releases. We highly recommend that you always run `update ---patch` when prompted. - -You may also pass the `--release` flag to act as an override to update to a -specific release. This is an override: if it cannot find compatible versions of -packages, it will log a warning, but perform the update anyway. This will only -change your package versions if necessary. - - -## meteor add _package_ {meteoradd} - -Add packages to your Meteor project. By convention, names of community packages -include the name of the maintainer. For example: `meteor add iron:router`. You -can add multiple packages with one command. - -Optionally, adds version constraints. Running `meteor add package@1.1.0` will -add the package at version `1.1.0` or higher (but not `2.0.0` or higher). If you -want to use version `1.1.0` exactly, use `meteor add package@=1.1.0`. You can also -'or' constraints together: for example, `meteor add 'package@=1.0.0 || =2.0.1'` -means either 1.0.0 (exactly) or 2.0.1 (exactly). - -To remove a version constraint for a specific package, run `meteor add` again -without specifying a version. For example above, to stop using version `1.1.0` -exactly, run `meteor add package`. - - -## meteor remove _package_ {meteorremove} - -Removes a package previously added to your Meteor project. For a -list of the packages that your application is currently using, run -`meteor list`. - -This removes the package entirely. To continue using the package, -but remove its version constraint, use [`meteor add`](#meteor-add-package). - -Meteor does not downgrade transitive dependencies unless it's necessary. This -means that if running `meteor add A` upgrades A's parent package X to a new -version, your project will continue to use X at the new version even after you -run `meteor remove A`. - - -## meteor list {meteorlist} - -Lists all the packages that you have added to your project. For each package, -lists the version that you are using. Lets you know if a newer version of that -package is available. - -**Flags** - -Flags are optional and can be used to format the output. The default output -requires no flags whatsoever. The following flags are supported: - -`--tree` - -Outputs a tree showing how packages are referenced. - -`--json` - -Outputs an unformatted JSON String, showing how packages are referenced. - -`--weak` - -Show weakly referenced dependencies in the tree. -Only functional in combination with `--tree` or `--json`. - -`--details` - -Adds more package details to the JSON output. -Only functional in combination with `--json`. - - -## meteor add-platform _platform_ {meteoraddplatform} - -Adds platforms to your Meteor project. You can add multiple -platforms with one command. Once a platform has been added, you -can use 'meteor run platform' to run on the platform, and `meteor build` -to build the Meteor project for every added platform. - - -## meteor remove-platform _platform_ {meteorremoveplatform} - -Removes a platform previously added to your Meteor project. For a -list of the platforms that your application is currently using, see -`meteor list-platforms`. - - -## meteor list-platforms {meteorlistplatforms} - -Lists all of the platforms that have been explicitly added to your project. - - -## meteor ensure-cordova-dependencies {meteorensurecordovadependencies} - -Check if the dependencies are installed, otherwise install them. - -## meteor mongo {meteormongo} - -Open a MongoDB shell on your local development database, so that you -can view or manipulate it directly. - -::: warning -For now, you must already have your application running locally -with `meteor run`. This will be easier in the future. -::: - - -## meteor reset {meteorreset} - -Reset the current project to a fresh state and clear the local cache. - -To remove also the local mongo database use `--db` flag. - -::: warning -Reset with `--db` flag deletes your data! Make sure you do not have any information you -care about in your local mongo database by running `meteor mongo`. -From the mongo shell, use `show collections` -and db.collection.find() to inspect your data. +Reset with `--db` flag deletes your data! Make sure you do not have any information you care about in your local mongo database by running `meteor mongo`. From the mongo shell, use `show collections` and `db..find()` to inspect your data. ::: ::: warning -For now, you can not run this while a development server is -running. Quit all running meteor applications before running this. +For now, you cannot run this while a development server is running. Quit all running meteor applications before running this. ::: diff --git a/v3-docs/docs/community-packages/archive.md b/v3-docs/docs/community-packages/archive.md new file mode 100644 index 0000000000..ffa8f825de --- /dev/null +++ b/v3-docs/docs/community-packages/archive.md @@ -0,0 +1,93 @@ +# Archive + +- `Who maintains the package` – [Jam](https://github.com/jamauro) + +[[toc]] + +## What is this package? + +Archive is an easy way to add an archive mechanism to your Meteor app. When a document is archived, it's removed from its original collection and placed in an archive collection so that it can be restored if needed. Its key features are: + +* Zero config needed (though you can customize) +* Isomorphic so that it works with Optimistic UI +* Automatically overrides `removeAsync` to perform an archive (can be turned off) +* Explicitly archive with `archiveAsync` collection method (optional) +* Restore archived docs with `restoreAsync` collection method +* Optionally exclude specific collections +* Compatible with Meteor `3.0.2+` + +> **Note:** Alternative to archive is soft deletion. You can use [`jam:soft-delete`](./soft-delete.md) package for that. Be sure to compare those two approaches to pick the solution best suited for your application. + +## How to download it? + +Add the package to your app +```bash +meteor add jam:archive +``` + +### Sources + +* [GitHub repository](https://github.com/jamauro/archive) + +## How to use it? + +### Create your Archives collection +Create an Archives collection in your app just as you would any other collection. +```js +const Archives = new Mongo.Collection('archives'); +``` + +Documents in the Archives collection will have this shape: +```js +{ + _id, // auto-generated by Meteor as with other collection _ids + _collection, // the name of the collection, e.g. 'todos', that the doc belonged to originally + archivedAt, // the timestamp when the document was removed from its original collection and inserted into the archive + id, // the original doc _id renamed to prevent conflict with the auto-generated one above. when restored, it will be renamed back to _id automatically by this package + /* + ...rest of original doc + */ +} +``` + +### Deleting permanently +By default, this package overrides the `removeAsync` collection method so that it archives the document(s) rather that removing them from the database. To delete permanently, pass in the option `forever: true`, e.g.: +```js +Collection.removeAsync(/* your filter */, { forever: true }) +``` + +If you prefer, you can prevent overriding the `removeAsync` by setting `overrideRemove: false`. See [Configuring](#configuring-optional) for more details. + +### Explicitly archiving +If you prefer, you can explicity use `archiveAsync`, e.g.: +```js +Collection.archiveAsync(/* your filter */) +``` + +### Restoring a document +To restore an archived document, use `restoreAsync`, e.g.: +```js +Collection.restoreAsync(/* your filter */) +``` + +## Configuring (optional) +If you like the defaults, then you won't need to configure anything. But there is some flexibility in how you use this package. + +Here are the global defaults: +```js +const config = { + name: 'archives', // if your Archives collection uses a different name, you'll want to change this + overrideRemove: true, // overrides the Collection.removeAsync method to make it an archive instead + exclude: ['roles', 'role-assignment'] // exclude specific collections from using archive so that when they are removed, the are permanently removed from the db. defaults to excluding the collections created by the meteor roles package +}; +``` + +To change the global defaults, use: +```js +// put this in a file that's imported on both the client and server +import { Archive } from 'meteor/jam:archive'; + +Archive.configure({ + // ... change the defaults here ... // +}); +``` diff --git a/v3-docs/docs/community-packages/index.md b/v3-docs/docs/community-packages/index.md new file mode 100644 index 0000000000..ee31736a45 --- /dev/null +++ b/v3-docs/docs/community-packages/index.md @@ -0,0 +1,41 @@ +# Community Packages + +::: tip +The Meteor community is making great efforts to migrate popular packages to Meteor 3.0. + +A [spreadsheet maintained](https://docs.google.com/spreadsheets/u/0/d/1JbUZmJab3owZ9LV71Ubto32YX_QWQljRypJTOQupxL8/htmlview) by [@harryadel](https://github.com/harryadel) tracks the status of your favorite packages and offers opportunities to help. +::: + +There are some very popular community packages that do not have a documentation website or only have a readme file. +This section tries to list and add some information about usage, configuration, and examples for these packages. + +> Think this section as an `Awesome List`, similar to [`awesome-node`](https://github.com/sindresorhus/awesome-nodejs) or [`awesome-react`](https://github.com/enaqx/awesome-react) + +Many packages have been consolidated into the [Meteor Community organization](https://github.com/Meteor-Community-Packages). Others are maintained by individual developers or companies. + +If you use or have a package that you think would be useful to add to this list, please open a pull request. + +Please bear in mind if you are adding a package to this list, try providing as much information as possible, including: + +- `Who maintains the package` – how to get in touch to submit issues or questions +- `Why is this package for?` +- `API` +- `examples/guide` + +## List of Community Packages + +#### Method/Subscription helpers + +- [`meteor-rpc`](./meteor-rpc.md), Meteor Methods Evolved with type checking and runtime validation +- [`jam:method`](./jam-method.md), An easy way to create Meteor methods with extensions to offline handling +- [`jam:pub-sub`](./pub-sub.md), Publish / subscribe using a Method and/or Change Streams, and cache subscriptions for Meteor apps + +#### MongoDB collection extensions + +- [`jam:mongo-transactions`](./mongo-transactions.md), An easy way to use Mongo Transactions for Meteor apps +- [`jam:soft-delete`](./soft-delete.md), An easy way to add soft deletes to your Meteor app +- [`jam:archive`](./archive.md), + +#### Utilities + +- [`jam:offline`](./offline.md), An easy way to give your Meteor app offline capabilities and make it feel instant diff --git a/v3-docs/docs/community-packages/jam-method.md b/v3-docs/docs/community-packages/jam-method.md new file mode 100644 index 0000000000..0958974d6c --- /dev/null +++ b/v3-docs/docs/community-packages/jam-method.md @@ -0,0 +1,579 @@ +# Jam Method + +- `Who maintains the package` – [Jam](https://github.com/jamauro) + +[[toc]] + +## What is this package? + +Method is an easy way to create Meteor methods with Optimistic UI. It's built with Meteor 3 in mind and is compatible with Meteor 2 to make migration easy. It's meant to be a drop in replacement for Validated Method and comes with additional features: + +* Before and after hooks +* Global before and after hooks for all methods +* Pipe a series of functions +* Authed by default (can be overriden) +* Easily configure a rate limit +* Optionally run a method on the server only +* Attach the methods to Collections (optional) +* Validate with one of the supported schema packages or a custom validate function +* No need to use .call to invoke the method as with Validated Method + +## How to download it? + +Add the package to your app by running the following in your Meteor project: + +```bash +meteor add jam:method +``` +### Sources + +* [GitHub repository](https://github.com/jamauro/method) + +## How to use it? + +### Create a method +`name` is required and will be how Meteor's internals identifies it. + +`schema` will automatically validate using a [supported schema](#supported-schemas). + +`run` will be executed when the method is called. + +```js +import { createMethod } from 'meteor/jam:method'; // can import { Methods } from 'meteor/jam:method' instead and use Methods.create if you prefer + +export const create = createMethod({ + name: 'todos.create', + schema: Todos.schema, // using jam:easy-schema in this example + async run({ text }) { + const todo = { + text, + done: false, + createdAt: new Date(), + authorId: Meteor.userId(), // can also use this.userId instead of Meteor.userId() + } + const todoId = await Todos.insertAsync(todo); + return todoId; + } +}); +``` + +#### Supported schemas +Currently, these schemas are supported: +* [jam:easy-schema](https://github.com/jamauro/easy-schema) +* [check](https://docs.meteor.com/api/check.html) +* [zod](https://github.com/colinhacks/zod) +* [simpl-schema](https://github.com/longshotlabs/simpl-schema) + +If you're using `jam:easy-schema`, be sure to check out [Using with jam:easy-schema](#using-with-jameasy-schema) below for details on a way to write methods with less boilerplate. + +Here's a quick example of each one's syntax. They vary in features so pick the one that best fits your needs. +```js +// jam:easy-schema. you'll attach to a Collection so you can reference one {Collection}.schema in your methods +const schema = {text: String, isPrivate: Optional(Boolean)} +// check +const schema = {text: String, isPrivate: Match.Maybe(Boolean)} +// zod +const schema = z.object({text: z.string(), isPrivate: z.boolean().optional()}) +// simpl-schema +const schema = new SimpleSchema({text: String, isPrivate: {type: Boolean, optional: true}}) +``` + +#### Custom validate function +If you're not using one of the supported schemas, you can use `validate` to pass in a custom validation function. +**`Note`**: `validate` can be an async function if needed. + +```js +// import your schema from somewhere +// import your validator function from somewhere + +export const create = createMethod({ + name: 'todos.create', + validate(args) { + validator(args, schema) + }, + async run({ text }) { + const todo = { + text, + done: false, + createdAt: new Date(), + authorId: Meteor.userId() // can also use this.userId instead of Meteor.userId() + } + const todoId = await Todos.insertAsync(todo); + return todoId; + } +}); +``` + +### Import on the client and use +```js +import { create } from '/imports/api/todos/methods'; + +async function submit() { + try { + await create({text: 'book flight to Hawaii'}) + } catch(error) { + alert(error) + } +} +``` + +### Before and after hooks +You can execute functions `before` and / or `after` the method's `run` function. `before` and `after` accept a single function or an array of functions. + +When using `before`, the original input passed into the method will be available. The original input will be returned automatically from a `before` function so that `run` receives what was originally passed into the method. + +A great use case for using `before` is to verify the user has permission. For example: + +```js +async function checkOwnership({ _id }) { // the original input passed into the method is available here. destructuring for _id since that's all we need for this function + const todo = await Todos.findOneAsync(_id); + if (todo.authorId !== Meteor.userId()) { + throw new Meteor.Error('not-authorized') + } + + return true; // any code executed as a before function will automatically return the original input passed into the method so that they are available in the run function +} + +export const markDone = createMethod({ + name: 'todos.markDone', + schema: Todos.schema, + before: checkOwnership, + async run({ _id, done }) { + return await Todos.updateAsync(_id, {$set: {done}}); + } +}); +``` + +When using `after`, the result of the `run` function will be available as the first argument and the second argument will contain the original input that was passed into the method. The result of the `run` function will be automatically returned from an `after` function. + +```js +function exampleAfter(result, context) { + const { originalInput } = context; // the name of the method is also available here + // do stuff + + return 'success'; // any code executed as an after function will automatically return the result of the run function +} + +export const markDone = createMethod({ + name: 'todos.markDone', + schema: Todos.schema, + before: checkOwnership, + async run({ _id, done }) { + return await Todos.updateAsync(_id, {$set: {done}}); + }, + after: exampleAfter +}); +``` + +**`Note`**: If you use arrow functions for `before`, `run`, or `after`, you'll lose access to `this` – the methodInvocation. You may be willing to sacrifice that because `this.userId` can be replaced by `Meteor.userId()` and `this.isSimulation` can be replaced by `Meteor.isClient` but it's worth noting. + + +### Pipe a series of functions +Instead of using `run`, you can compose functions using `.pipe`. Each function's output will be available as an input for the next function. + +```js +// you'd define the functions in the pipe and then place them in the order you'd like them to execute within .pipe +// be sure that each function in the pipe returns what the next one expects, otherwise you can add an arrow function to the pipe to massage the data, e.g. (input) => manipulate(input) + +export const create = createMethod({ + name: 'todos.create', + schema: Todos.schema +}).pipe( + checkOwnership, + createTodo, + sendNotification +) +``` + +### Attach methods to its Collection (optional) +Instead of importing each method, you can attach them to the Collection. If you're using [jam:easy-schema](https://github.com/jamauro/easy-schema) be sure to attach the schema before attaching the methods. + +```js +// /imports/api/todos/collection +import { Mongo } from 'meteor/mongo'; +import { schema } from './schema'; + +export const Todos = new Mongo.Collection('todos'); + +Todos.attachSchema(schema); // if you're using jam:easy-schema + +const attach = async () => { + const methods = await import('./methods.js') // dynamic import is recommended + return Todos.attachMethods(methods); // if you prefer not to use dynamic import, you can simply call attachMethods synchronously +}; + +attach().catch(error => console.error('Error attaching methods', error)) +``` +`attachMethods` accepts the method `options` as an optional second parameter. See [Configuring](#configuring-optional) for a list of the `options`. + +With the methods attached you'll use them like this on the client: +```js +import { Todos } from '/imports/api/todos/collection'; +// no need to import each method individually + +async function submit() { + try { + await Todos.create({text: 'book flight to Hawaii'}) + } catch(error) { + alert(error) + } +} +``` + +### Executing code on the server only +By default, methods are optimistic meaning they will execute on the client and then on the server. If there's only part of the method that should execute on the server and not on the client, then simply wrap that piece of code in a `if (Meteor.isServer)` block. This way you can still maintain the benefits of Optimistic UI. For example: + +```js +export const create = createMethod({ + name: 'todos.create', + schema: Todos.schema, + async run(args) { + // rest of your function + // code running on both client and server + if (Meteor.isServer) { + // code running on the server only + import { secretCode } from '/server/secretCode'; // since it's in a /server folder the code will not be shipped to the client + // do something with secretCode + } + + // code running on both client and server + return Todos.insertAsync(todo) + } +}); +``` + +If you prefer, you can force the entire method to execute on the server only by setting `serverOnly: true`. It can be used with `run` or `.pipe`. Here's an example with `run`: + +```js +export const create = createMethod({ + name: 'todos.create', + schema: Todos.schema, + serverOnly: true, + async run(args) { + // all code here will execute only on the server + } +}); +``` + +You can also set all methods to be `serverOnly`. See [Configuring](#configuring-optional) below. + +#### Security note +**`Important`**: Since Meteor does not currently support tree shaking, the contents of the code inside `run` function or `.pipe` could still be visible to the client even if you use `if (Meteor.isServer)` or `serverOnly: true`. To prevent this, you have these options: + +1. Attach methods to its Collection with a dynamic import as shown above [Attach methods to its Collection (optional)](#attach-methods-to-its-collection-optional) + +2. Import function(s) from a file within a `/server` folder. Any code imported from a `/server` folder will not be shipped to the client. The `/server` folder can be located anywhere within your project's file structure and you can have multiple `/server` folders. For example, you can co-locate with your collection folder, e.g. `/imports/api/todos/server/`, or it can be at the root of your project. See [Secret server code](https://guide.meteor.com/security.html#secret-code) for more info. + +```js +export const create = createMethod({ + name: 'todos.create', + schema: Todos.schema, + serverOnly: true, + async run(args) { + import { serverFunction } from '/server/serverFunction'; + + serverFunction(args); + } +}); +``` + +3. Dynamically import function(s). These do not have to be inside a `/server` folder. This will prevent the code being inspectable via the browser console. + +```js +export const create = createMethod({ + name: 'todos.create', + schema: Todos.schema, + serverOnly: true, + async run(args) { + const { serviceFunction } = await import('./services'); + + serviceFunction(args); + } +}); +``` + +### Changing authentication rules +By default, all methods will be protected by authentication, meaning they will throw an error if there is *not* a logged-in user. You can change this for an individual method by setting `open: true`. See [Configuring](#configuring-optional) below to change it for all methods. + +```js +export const create = createMethod({ + name: 'todos.create', + schema: Todos.schema, + open: true, + async run({ text }) { + // ... // + } +}); +``` + +### Rate limiting +Easily rate limit a method by setting its max number of requests – the `limit` – within a given time period (milliseconds) – the `interval`. + +```js +export const create = createMethod({ + name: 'todos.create', + schema: Todos.schema, + rateLimit: { // rate limit to a max of 5 requests every second + limit: 5, + interval: 1000 + }, + async run({ text }) { + // ... // + } +}); +``` + +### Validate without executing the method +There may be occassions where you want to validate without executing the method. In these cases, you can use `.validate`. If you want to validate against only part of the schema, use `.validate.only`. + +```js +export const create = createMethod({ + name: 'todos.create', + schema: Todos.schema, + async run({ text }) { + // ... // + } +}); + +// validate against the schema without executing the method +create.validate({...}) + +// validate against only the relevant part of the schema based on the data passed in without executing the method +create.validate.only({...}) +``` + +If you're using a custom validate function instead of one of the supported schemas and you'd like to use `.validate.only`, you can simply append an `only` function onto the `validate` function that you supply. + +### Options for Meteor.applyAsync +When called, the method uses [Meteor.applyAsync](https://docs.meteor.com/api/methods#Meteor-applyAsync) under the hood to execute your `run` function or `.pipe` function(s). `Meteor.applyAsync` takes a few options which can be used to alter the way Meteor handles the method. If you want to change the defaults or include other supported options, pass in `options` when creating the method. + +```js +export const create = createMethod({ + name: 'todos.create', + schema: Todos.schema, + options: { + // ... // + }, + async run({ text }) { + // ... // + } +}); +``` + +By default, this package uses the following `options`: +```js +{ + // Make it possible to get the ID of an inserted item + returnStubValue: true, + + // Don't call the server method if the client stub throws an error, so that we don't end + // up doing validations twice + throwStubExceptions: true, +}; +``` +See [Configuring](#configuring-optional) below to set `options` for all methods. + +### Working with the stub result (Meteor 3.0+) +In Meteor 3.0+, you can optionally take action with the stub result, i.e. the result when the method simulation is run on the client, before the server has returned with the final result or error. This can come in handy when you want to make your app feel instant for the user and you're relatively sure the action will succeed, e.g. when inserting new documents into the database. + +```js + const { stubPromise, serverPromise } = create(); + const _id = await stubPromise.catch(error => { + // optionally handle a stub error + }); + + // take action with the _id stub result, for example, route to a new page + router.go(`/detail/${_id}`) + + await serverPromise.catch(error => { + // handle server error, rollback changes as needed, for example route to home + router.go('/') + alert('sorry, could not create') + }); +``` + +### Mocking the method context +You can mock the method invocation context, aka the `this` value inside the method, by invoking the method with `.call(context, args)`. This is particularly useful for unit testing to mock the `this.userId`: + +```js +const context = { + userId: 'fakeUserId', + // ... // +} + +await create.call(context, {...}) +``` + +## Configuring (optional) +If you like the defaults, then you won't need to configure anything. But there is some flexibility in how you use this package. + +Here are the global defaults: +```js +const config = { + before: [], // global before function(s) that will run before all methods + after: [], // global after function(s) that will run after all methods + serverOnly: false, // globally make all methods serverOnly, aka disable Optimistic UI, by setting to true + open: false, // by default all methods will be protected by authentication, override it for all methods by setting this to true + loggedOutError: new Meteor.Error('logged-out', 'You must be logged in'), // customize the logged out error + options: { + returnStubValue: true, // make it possible to get the ID of an inserted item on the client before the server finishes + throwStubExceptions: true, // don't call the server method if the client stub throws an error, so that we don't end up doing validations twice + } +}; +```` + +To change the global defaults, use: +```js +// put this in a file that's imported on both the client and server +import { Methods } from 'meteor/jam:method'; + +Methods.configure({ + // ... change the defaults here ... // +}); +``` + +### Global before and after hooks +You can create before and/or after functions to run before / after all methods. Both `before` and `after` accept a single function or an array of functions. + +```js +import { Methods } from 'meteor/jam:method'; + +const hello = () => { console.log('hello') } +const there = () => { console.log('there') } +const world = () => { console.log('world') } + +Methods.configure({ + before: [hello, there], + after: world +}); +``` + +### Helpful utility function to log your methods +Here's a helpful utility function - `log` - that you might consider adding. It isn't included in this package but you can copy and paste it into your codebase where you see fit. + +```js +// log will simply console.log or console.error when the Method finishes +function log(input, pipeline) { + pipeline.onResult((result) => { + console.log(`Method ${pipeline.name} finished`, input); + console.log('Result', result); + }); + + pipeline.onError((err) => { + console.error(`Method ${pipeline.name} failed`); + console.error('Error', err); + }); +}; +``` + +Then you could use it like this: +```js +import { Methods, server } from 'meteor/jam:method'; + +Methods.configure({ + after: server(log) +}); +``` + +## Alternative functional-style syntax +You can use a functional-style syntax to compose your methods if you prefer. Here's an example. + +```js +const fetchGifs = async({ searchTerm, limit }) => {...} + +export const getGifs = createMethod(server(schema({ searchTerm: String, limit: Number })(fetchGifs))) +``` +`getGifs` is callable from the client but will only run on the server. Internally it will be identified as `fetchGifs` + +**`Note`**: if you pass in a named function into `createMethod`, then that will be used to identify the method internally. Otherwise if you pass in an anonymous function, `jam:method` generates a unique name based on its schema to identify it internally. + +### Customizing methods when using functional-style syntax +There are a few functions available when you need to customize the method: `schema`, `server`, `open`, `close`. These can be composed when needed. + +#### schema +Specify the schema to validate against. + +```js +import { schema } from 'meteor/jam:method'; + +export const doSomething = schema({thing: String, num: Number})(async ({ thing, num }) => { + // ... // +}); +``` + +#### server +Make the method run on the server only. + +```js +import { server } from 'meteor/jam:method'; + +export const aServerOnlyMethod = server(async data => { + // ... // +}); +``` + +#### open +Make the method publically available so that a logged-in user isn't required. + +```js +import { open } from 'meteor/jam:method'; + +export const aPublicMethod = open(async data => { + // ... // +}); +``` + +#### close +Make the method check for a logged-in user. + +**`Note`**: by default, all methods require a logged-in user so if you stick with that default, then you won't need to use this function. See [Configuring](#configuring-optional). + +```js +import { close } from 'meteor/jam:method'; + +export const closedMethod = close(async data => { + // ... // +}); +``` + +## Using with `jam:easy-schema` +`jam:method` integrates with `jam:easy-schema` and offers a way to reduce boilerplate and make your methods even easier to write (though you can still use `createMethod` if you prefer). + +For example, instead of writing this: +```js +export const setDone = createMethod({ + name: 'todos.setDone', + schema: Todos.schema, + before: checkOwnership, + async run({ _id, done }) { + return Todos.updateAsync({ _id }, { $set: { done } }); + } +}); +``` + +You can write: +```js +export const setDone = async ({ _id, done }) => { + await checkOwnership({ _id }); + return Todos.updateAsync({ _id }, { $set: { done } }); +}; +``` + +**`Note`**: This assumes that you're attaching your methods to its collection. See [Attach methods to its Collection](#attach-methods-to-its-collection-optional). + +When you call `Todos.setDone` from the client, the arguments will be automatically checked against the `Todos.schema`. The method will automatically be named `todos.setDone` internally to identify it for app performance monitoring (APM) purposes. + +You can also compose with the functions available in the [function-style syntax](#alternative-functional-style-syntax). For example: +```js +export const setDone = server(async ({ _id, done }) => { + await checkOwnership({ _id }); + return Todos.updateAsync({ _id }, { $set: { done } }); +}); +``` +Now when you call `Todos.setDone` from the client it will only run on the server. + +## Using with `jam:offline` +`jam:method` integrates with `jam:offline` to automatically queue methods when a user is offline. You don't need to configure anything in `jam:method` for this. πŸŽ‰ `jam:offline` will then replay them when the user reconnects. See [jam:offline](https://github.com/jamauro/offline) for more details. + +## Coming from `Validated Method`? +You may be familiar with `mixins` and wondering where they are. With the features of this package - authenticated by default, `before` / `after` hooks, `.pipe` - your mixin code may no longer be needed or can be simplified. If you have another use case where your mixin doesn't translate, I'd love to hear it. Open a new discussion and let's chat. diff --git a/v3-docs/docs/community-packages/meteor-rpc.md b/v3-docs/docs/community-packages/meteor-rpc.md new file mode 100644 index 0000000000..683a98be16 --- /dev/null +++ b/v3-docs/docs/community-packages/meteor-rpc.md @@ -0,0 +1,608 @@ +# Meteor-RPC + +- `Who maintains the package` – [Grubba27](https://github.com/Grubba27), you can get in touch via [X](https://twitter.com/gab_grubba) + +[[toc]] + +## What is this package? + +_Inspired on [zodern:relay](https://github.com/zodern/meteor-relay) and on [tRPC](https://trpc.io/)_ + +This package provides functions for building E2E type-safe RPCs focused on React front ends. + +## How to download it? + +::: warning + +This package works only with Meteor 2.8 or higher. + +If you are not sure about the version of Meteor you are using, you can check it by running the following command in your terminal within your project: + +```bash +meteor --version +``` + +::: + +```bash +meteor npm i meteor-rpc @tanstack/react-query zod +``` + +::: warning + +Before continuing the installation, make sure you have `react-query` all set in your project; for more info, follow their [quick start guide](https://tanstack.com/query/latest/docs/framework/react/quick-start). + +::: + +## How to use it? + +There are a few concepts that are important while using this package: + +- This package is built on top of [`Meteor.methods`](../api/meteor.md#method-apis-methods) and [`Meteor.publish`](../api/meteor.md#publish-and-subscribe-pubsub) but with types and runtime validation, their understanding is important to use this package. +- Every method and publication uses `Zod` to validate the arguments, so you can be sure that the data you are receiving is what you expect. + +::: tip +If you are accepting any type of data, you can use `z.any()` as the schema or `z.void` when there is no argument +::: + +### `createModule` + +This function is used to create a module that will be used to call our methods and publications + +`subModule` without a namespace: `createModule()` is used to create the `main` server module, the one that will be exported to be used in the client.` + +`subModule` with a namespace: `createModule("namespace")` is used to create a submodule that will be added to the main module. + +> Remember to use `build` at the end of module creation to ensure that the module will be created. + +Example: + +::: code-group + +```typescript [server/main.ts] +import { createModule } from "meteor-rpc"; +import { Chat } from "./chat"; + +const server = createModule() // server has no namespace + .addMethod("bar", z.string(), (arg) => "bar" as const) + .addSubmodule(Chat) + .build(); + +export type Server = typeof server; +``` + +```typescript [server/chat.ts] +import { createModule } from "meteor-rpc"; +import { ChatCollection } from "/imports/api/chat"; +import { z } from "zod"; + +export const Chat = createModule("chat") + .addMethod("createChat", z.void(), async () => { + return ChatCollection.insertAsync({ createdAt: new Date(), messages: [] }); + }) + .buildSubmodule(); +``` + +```typescript [client/main.ts] +import { createClient } from "meteor-rpc"; +// you must import the type of the server +import type { Server } from "/imports/api/server"; + +const api = createClient(); +const bar: "bar" = await api.bar("some string"); +// ?^ 'bar' +const newChatId = await api.chat.createChat(); // with intellisense +``` + +::: + +### `module.addMethod` + +Type: + +```ts +addMethod( + name: string, + schema: ZodSchema, + handler: (args: ZodTypeInput) => T, + config?: Config, T> +) +``` + +This is the equivalent of `Meteor.methods` but with types and runtime validation. + +::: code-group + +```typescript [server/with-meteor-rpc.ts] +import { createModule } from "meteor-rpc"; +import { z } from "zod"; + +const server = createModule() + .addMethod("foo", z.string(), (arg) => "foo" as const) + .build(); +``` + +```typescript [server/without-meteor-rpc.ts] +import { Meteor } from "meteor/meteor"; +import { z } from "zod"; + +Meteor.methods({ + foo(arg: string) { + z.string().parse(arg); + return "foo"; + }, +}); +``` + +::: + +### `module.addPublication` + +Type: + +```typescript +addPublication( + name: string, + schema: ZodSchema, + handler: (args: ZodTypeInput) => Cursor +) +``` + +This is the equivalent of `Meteor.publish` but with types and runtime validation. + +::: code-group + +```typescript [server/with-meteor-rpc.ts] +import { createModule } from "meteor-rpc"; +import { ChatCollection } from "/imports/api/chat"; +import { z } from "zod"; + +const server = createModule() + .addPublication("chatRooms", z.void(), () => { + return ChatCollection.find(); + }) + .build(); +``` + +```typescript [server/without-meteor-rpc.ts] +import { Meteor } from "meteor/meteor"; +import { ChatCollection } from "/imports/api/chat"; + +Meteor.publish("chatRooms", function () { + return ChatCollection.find(); +}); +``` + +::: + +### `module.addSubmodule` + +This is used to add a submodule to the main module, adding namespaces for your methods and publications and making it easier to organize your code. + +> Remember to use `submodule.buildSubmodule` when creating a submodule + +::: code-group + +```typescript [server/chat.ts] +import { ChatCollection } from "/imports/api/chat"; +import { createModule } from "meteor-rpc"; + +export const chatModule = createModule("chat") + .addMethod("createChat", z.void(), async () => { + return ChatCollection.insertAsync({ createdAt: new Date(), messages: [] }); + }) + .buildSubmodule(); // <-- This is important so that this module can be added as a submodule +``` + +```typescript [server/chat.ts] +import { createModule } from "meteor-rpc"; +import { chatModule } from "./server/chat"; + +const server = createModule() + .addMethod("bar", z.string(), (arg) => "bar" as const) + .addSubmodule(chatModule) + .build(); + +server.chat; // <-- this is the namespace for the chat module +server.chat.createChat(); // <-- this is the method from the chat module and it gets autocompleted +``` + +::: + +### `module.addMiddlewares` + +Type: + +```typescript +type Middleware = (raw: unknown, parsed: unknown) => void; + +addMiddlewares(middlewares: Middleware[]) +``` + +This is used to add middleware to the module; it should be used to add side effects logic to the methods and publications, which is ideal for logging or rate limiting. + +The middleware ordering is last in, first out. Check the example below: + +::: code-group + +```typescript [server/chat.ts] +import { ChatCollection } from "/imports/api/chat"; +import { createModule } from "meteor-rpc"; + +export const chatModule = createModule("chat") + .addMiddlewares([ + (raw, parsed) => { + console.log("runs first"); + }, + ]) + .addMethod("createChat", z.void(), async () => { + return ChatCollection.insertAsync({ createdAt: new Date(), messages: [] }); + }) + .buildSubmodule(); +``` + +```typescript [server/main.ts] +import { createModule } from "meteor-rpc"; +import { chatModule } from "./server/chat"; + +const server = createModule() + .addMiddlewares([ + (raw, parsed) => { + console.log("runs second"); + }, + ]) + .addMethod("bar", z.string(), (arg) => "bar" as const) + .addSubmodule(chatModule) + .build(); +``` + +```typescript [client/main.ts] +import { createClient } from "meteor-rpc"; +import type { Server } from "/imports/api/server"; // you must import the type + +const api = createClient(); +await api.chat.createChat(); // logs "runs first" then "runs second" +await api.bar("str"); // logs "runs second" +``` + +::: + +### `module.build` + +This is used to build the module, it should be used at the end of the module creation to ensure that the exported type is correct. + +::: code-group + +```typescript [correct.ts] +// βœ… it has the build method +import { createModule } from "meteor-rpc"; +import { z } from "zod"; +const server = createModule() + .addMethod("bar", z.string(), (arg) => "bar" as const) + .build(); + +export type Server = typeof server; +``` + +```typescript [incorrect.ts] +// ❌ it is missing the build method +import { createModule } from "meteor-rpc"; +import { z } from "zod"; +const server = createModule().addMethod( + "bar", + z.string(), + (arg) => "bar" as const +); + +export type Server = typeof server; +``` + +::: + +### `module.buildSubmodule` + +This is used to build the submodule, it should be used at the end of the submodule creation and imported in the main module in the [`addSubmodule`](./meteor-rpc.md#module-addsubmodule) method. + +::: code-group + +```typescript [correct.ts] +import { createModule } from "meteor-rpc"; +import { z } from "zod"; + +export const chatModule = createModule("chat") + .addMethod("createChat", z.void(), async () => { + return "chat" as const; + }) + // βœ… it has the buildSubmodule method + .buildSubmodule(); +``` + +```typescript [incorrect.ts] +import { createModule } from "meteor-rpc"; +import { z } from "zod"; + +export const otherSubmodule = createModule("other") + .addMethod("otherMethod", z.void(), async () => { + return "other" as const; + }) + // ❌ it is missing the buildSubmodule method + .build(); + +export const otherSubmodule = createModule("other").addMethod( + "otherMethod", + z.void(), + async () => { + return "other" as const; + } +); // ❌ it is missing the buildSubmodule method +``` + +```typescript [server/main.ts] +import { createModule } from "meteor-rpc"; +import { chatModule } from "./server/chat"; + +const server = createModule() + .addMethod("bar", z.string(), (arg) => "bar" as const) + .addSubmodule(chatModule) + .build(); +``` + +::: + +## Using in the client + +When using in the client, you _have_ to use the `createModule` and `build` methods to create a module that will be used in the client +and be sure that you are exporting the type of the module + +_You should only create one client in your application_ + +You can have something like `api.ts` that will export the client and the type of the client + +::: code-group + +```typescript [server/main.ts] +import { createModule } from "meteor-rpc"; + +const server = createModule() + .addMethod("bar", z.string(), (arg) => "bar" as const) + .build(); + +export type Server = typeof server; +``` + +```typescript [client/main.ts] +// you must import the type +import type { Server } from "/imports/api/server"; +const app = createClient(); + +await app.bar("str"); // it will return "bar" +``` + +::: + +## React focused API + +Our package has a React-focused API that uses `react-query` to handle the data fetching and mutations. + +### `method.useMutation` + +It uses the [`useMutation`](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation#usemutation) from react-query to create a mutation that will call the method + +::: code-group + +```typescript [server/main.ts] +import { createModule } from "meteor-rpc"; + +const server = createModule() + .addMethod("bar", z.string(), (arg) => { + console.log("Server received", arg); + return "bar" as const; + }) + .build(); + +export type Server = typeof server; +``` + +```tsx [client.ts] +// you must import the type +import type { Server } from "/imports/api/server"; +const app = createClient(); + +export const Component = () => { + const { mutate, isLoading, isError, error, data } = app.bar.useMutation(); + + return ( + + ); +}; +``` + +::: + +### `method.useQuery` + +It uses the [`useQuery`](https://tanstack.com/query/latest/docs/framework/react/reference/useSuspenseQuery#usesuspensequery) from react-query to create a query that will call the method, it uses `suspense` to handle loading states + +::: code-group + +```typescript [server/main.ts] +import { createModule } from "meteor-rpc"; + +const server = createModule() + .addMethod("bar", z.string(), (arg) => "bar" as const) + .build(); + +export type Server = typeof server; +``` + +```tsx [client.ts] +// you must import the type of the server +import type { Server } from "/imports/api/server"; +const app = createClient(); + +export const Component = () => { + const { data } = app.bar.useQuery("str"); // this will trigger suspense + + return
{data}
; +}; +``` + +::: + +### `publication.useSubscription` + +Subscriptions on the client have `useSubscription` method that can be used as a hook to subscribe to a publication. It uses `suspense` to handle loading states + +::: code-group + +```typescript [server/main.ts] +// server/main.ts +import { createModule } from "meteor-rpc"; +import { ChatCollection } from "/imports/api/chat"; +import { z } from "zod"; + +const server = createModule() + .addPublication("chatRooms", z.void(), () => { + return ChatCollection.find(); + }) + .build(); + +export type Server = typeof server; +``` + +```tsx [client.ts] +import type { Server } from "/imports/api/server"; // you must import the type +const app = createClient(); + +export const Component = () => { + // it will trigger suspense and `rooms` is reactive in this context. + // When there is a change in the collection it will rerender + const { data: rooms, collection: chatCollection } = + api.chatRooms.usePublication(); + + return ( +
+ {rooms.map((room) => ( +
{room.name}
+ ))} +
+ ); +}; +``` + +::: + +## Examples + +Currently, we have: + +- [chat-app](https://github.com/Grubba27/testing-meteor-rpc) that uses this package to create a chat-app +- [askme](https://github.com/fredmaiaarantes/askme) that uses this package to create a Q&A app, you can check it live [here](https://askmeaquestion.meteorapp.com/) + +## Advanced usage + +You can take advantage of the hooks to add custom logic to your methods, checking the raw and parsed data and the result of the method, +If the method fails, you can also check the error. + +::: code-group + +```typescript [on-method-after-creation.ts] +import { createModule } from "meteor-rpc"; +import { z } from "zod"; + +const server = createModule() + .addMethod("bar", z.string(), (arg) => "bar" as const) + .build(); + +// you can add hooks after the method has been created +server.bar.addBeforeResolveHook((raw, parsed) => { + console.log("before resolve", raw, parsed); +}); + +server.bar.addAfterResolveHook((raw, parsed, result) => { + console.log("after resolve", raw, parsed, result); +}); + +server.bar.addErrorResolveHook((err, raw, parsed) => { + console.log("on error", err, raw, parsed); +}); + +export type Server = typeof server; +``` + +```typescript [on-method-creation.ts] +import { createModule } from "meteor-rpc"; +import { z } from "zod"; + +const server = createModule() + // Or you can add hooks when creating the method + .addMethod("bar", z.any(), () => "str", { + hooks: { + onBeforeResolve: [ + (raw, parsed) => { + console.log("before resolve", raw, parsed); + }, + ], + onAfterResolve: [ + (raw, parsed, result) => { + console.log("after resolve", raw, parsed, result); + }, + ], + onErrorResolve: [ + (err, raw, parsed) => { + console.log("on error", err, raw, parsed); + }, + ], + }, + }) + .build(); + +export type Server = typeof server; +``` + +::: + +## Known issues + +if you are getting a similar error like this one: + +```text + +=> Started MongoDB. +Typescript processing requested for web.browser using Typescript 5.7.2 +Creating new Typescript watcher for /app +Starting compilation in watch mode... +Compiling server/chat/model.ts +Compiling server/chat/module.ts +Compiling server/main.ts +Writing .meteor/local/plugin-cache/refapp_meteor-typescript/0.5.6/v2cache/buildfile.tsbuildinfo +Compilation finished in 0.3 seconds. 3 files were (re)compiled. +did not find /app/.meteor/local/plugin-cache/refapp_meteor-typescript/0.5.6/v2cache/out/client/main.js +did not find /app/.meteor/local/plugin-cache/refapp_meteor-typescript/0.5.6/v2cache/out/client/main.js +Nothing emitted for client/main.tsx +node:internal/crypto/hash:115 + throw new ERR_INVALID_ARG_TYPE( + ^ + +TypeError [ERR_INVALID_ARG_TYPE]: The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received null + at Hash.update (node:internal/crypto/hash:115:11) + at /Users/user/.meteor/packages/meteor-tool/.3.0.4.1tddsze.as7rh++os.osx.arm64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.arm64/tools/fs/tools/fs/watch.ts:329:28 + at Array.forEach () + at /Users/user/.meteor/packages/meteor-tool/.3.0.4.1tddsze.as7rh++os.osx.arm64+web.browser+web.browser.legacy+web.cordova/mt-os.osx.arm64/tools/fs/tools/fs/watch.ts:329:8 + at JsOutputResource._get (/tools/isobuild/compiler-plugin.js:1002:19) { + code: 'ERR_INVALID_ARG_TYPE' +} + +Node.js v20.18.0 +``` + +Please check if you are using `refapp:meteor-typescript` package, if so, you can remove it and use the `typescript` package instead. +The `refapp:meteor-typescript` package is currently incompatible with the `meteor-rpc` package. + +If it is still not working, please open an issue in the [repo](https://github.com/Grubba27/meteor-rpc) diff --git a/v3-docs/docs/community-packages/mongo-transactions.md b/v3-docs/docs/community-packages/mongo-transactions.md new file mode 100644 index 0000000000..0997f17c25 --- /dev/null +++ b/v3-docs/docs/community-packages/mongo-transactions.md @@ -0,0 +1,86 @@ +# Mongo-transactions + +- `Who maintains the package` – [Jam](https://github.com/jamauro) + +[[toc]] + +## What is this package? + +`jam:mongo-transactions` enables an easy way to work with Mongo Transactions in Meteor apps. Here are a few of the benefits: + +* Write with the standard Meteor collection methods you're accustomed to. You don't need to worry about using `rawCollection()`, though if you need a particular `rawCollection()` method, you can still use it. +* You don't need to worry about passing `session` around. This package takes care of that for you. +* Because it's a low-level solution, ID generation works as expected. +* Works out-of-the-box with other packages that automatically validate on DB writes, like `jam:easy-schema` and `aldeed:collection2`. +* One simple API to use. Mongo has made things complicated with two APIs for Transactions, the Callback API and the Core API. This package defaults to using the Callback API as recommended by Mongo, but allows you to use the Core API by passing in `autoRetry: false`. +* Can be used isomorphically. +* Compatible with Meteor 2.8.1 and up, including support for Meteor 3.0+ + +> **Important**: This package expects that you'll use the promise-based `*Async` Meteor collection methods introduced in `v2.8.1` though it will technically work with the older syntax without the `*Async` suffix as long as you don't use callbacks. It does **not** cover using callbacks with Meteor collection methods. + +## How to download it? +Add the package to your app +```bash +meteor add jam:mongo-transactions +``` +### Sources + +* [GitHub repository](https://github.com/jamauro/mongo-transactions) + +## How to use it? + +### Create a Transaction + +> **Note**: there's no need to pass `session` into the `*Async` collection methods. This package handles that for you. + +```js +import { Mongo } from 'meteor/mongo'; + +async function purchase(purchaseData) { + try { + const { invoiceId } = await Mongo.withTransaction(async () => { + const invoiceId = await Invoices.insertAsync(purchaseData); + const changeQuantity = await Items.updateAsync(purchaseData.itemId, { $set: {...} }); + return { invoiceId, changeQuantity } // you can return whatever you'd like + }); + return invoiceId; + } catch (error) { + // something went wrong with the transaction and it could not be automatically retried + // handle the error as you see fit + } +} +``` + +### Passing Transaction options +If you want to customize how the Transaction runs, pass in the Transaction options as the second argument, for example: +```js +await Mongo.withTransaction(async () => { + ... +}, { writeConcern: { w: 1 } }); +``` +Refer to the [Mongo Node API docs](https://mongodb.github.io/node-mongodb-native/6.3/interfaces/TransactionOptions.html) for more information about Transaction options. + +### Preventing automatic retries if the Transaction fails +Most of the time, you'll want the default behavior where the Transaction is automatically retried for a `TransientTransactionError` or `UnknownTransactionCommitResult` commit error. But if you don't want that behavior, simply pass in `{ autoRetry: false }` like this: + +```js +await Mongo.withTransaction(async () => { + ... +}, { autoRetry: false }); +``` + +Setting `{ autoRetry: false }`, means the Transactions Core API will be used rather than the Callback API and you'll be responsible for handling all errors. You can read more about the differences in the [Mongo Docs](https://www.mongodb.com/docs/manual/core/transactions-in-applications/). + +### Determine if you're in a Transaction +To determine if the code is currently running in a Transaction, use: +```js +Mongo.inTransaction(); // returns true or false +``` + +### Using Isomorphically for Optimistic UI +You can write `Mongo.withTransaction` and `Mongo.inTransaction` isomorphically for Optimistic UI however note that the Transaction will only truly be performed on the server. + +As with any isomorphic code, you should be aware that it may fail because the operation can't succeed on the client but will succeed on the server. For example, let's say you're using `.find` within the Transaction and Minimongo on the client doesn't have that particular data, the client will fail but the server should still succeed. You can wrap specific server-only code with `if (Meteor.isServer)`, but in these cases, you'll likely want to avoid isomorphic code and make sure the entire Transaction code only runs on the server. + +## Using Mongo Atlas as your DB? +> **Important**: In my experience, you must use a paid tier for Transactions to work as expected with Meteor. The free tier would not tail the oplog for Transactions. So if you're trying this out in production, be sure to use a paid tier. diff --git a/v3-docs/docs/community-packages/offline.md b/v3-docs/docs/community-packages/offline.md new file mode 100644 index 0000000000..f811454652 --- /dev/null +++ b/v3-docs/docs/community-packages/offline.md @@ -0,0 +1,161 @@ +# Offline + +- `Who maintains the package` – [Jam](https://github.com/jamauro) + +[[toc]] + +## What is this package? + +Offline is an easy way to give your Meteor app offline capabilities and make it feel instant. Its key features are: + +* Offline access – Saves Minimongo data in IndexedDB for offline access and instant reloading when reconnected. +* Automatic syncing – Syncs offline actions automatically once reconnected. +* Cross-Tab syncing – Keeps changes in sync across multiple tabs while offline. +* Seamless reconciliation – Reconciles data with the server when reconnected. + +## How to download it? + +Add the package to your app +```bash +meteor add jam:offline +``` + +### Sources + +* [GitHub repository](https://github.com/jamauro/offline) + +## How to use it? + +### Keeping data offline +By default, offline data will be kept for all collections using the global defaults in [Offline.config](#configuring-optional) without any configuration needed. To change the data that is kept for a specific collection, define `.keep` for the collection in a file that's imported on both client and server. + +If you don't want all collections to be kept, be sure to configure `keepAll: false`. See [Configuring](#configuring-optional) for more details. + +`.keep` accepts a `filter`, and `sort` and `limit` options much like a `.find`. For example: + +```js +const Todos = new Mongo.Collection('todos'); +Todos.keep({ $or: [ { isPrivate: { $ne: true } }, { owner: Meteor.userId() } ]}, { limit: 200 }) // this will override any global defaults +``` + +If you have specific restrictions on what should be kept, e.g. permissions to a document like in the example above, these should be included in the `.keep` filter. Be aware that when the user comes back online, this filter will be used to reconcile with the server. In general, it's recommended that you only use the `.keep` filter where absolutely needed. + +**`Note`**: You can use `Meteor.userId()` in your `.keep` filter if needed. + +### Clearing offline data for a specific collection +If you ever need to clear offline data for a specific collection, call `clear`: + +```js +Todos.clear(); +``` + +### Clearing all offline data +If you ever need to completely clear all offline data, you can use `clearAll`: + +```js +import { clearAll } from 'meteor/jam:offline'; + +clearAll(); +``` + +### Queuing methods when offline +When a user is offline, you can queue any actions they take for replay when they come back online using `queueMethod`: + +```js +import { queueMethod } from 'meteor/jam:offline'; + +if (!Meteor.status().connected) { // check that the user is offline + queueMethod(name, arg1, arg2...) // the arguments should be the same form that you'd use for Meteor.callAsync +} +``` +where name is the method's name and the arguments are what the method expects. You'll still want to call the method when offline and it's recommended that you use [Meteor.applyAsync](https://docs.meteor.com/api/methods.html#Meteor-applyAsync) with the option `noRetry: true` to avoid using Meteor's built-in retry mechanism since we'll be handling replay separately. For this to work as intended, the methods that you call should be isomorphic so that they're availble to the client. + +**`Note`**: If you're using [jam:method](https://github.com/jamauro/method), queuing methods for replay is handled automatically for you. πŸŽ‰ + +**`Note`**: When queuing methods than involve an `insert` to a collection, make sure the method returns the new document's `_id`. By doing this, you ensure that any subsequent changes made to the document while still offline are handled correctly. + +### Auto syncing +By default, the package auto syncs when the user comes back online. This includes: + +1. Replaying queued methods +2. Removing offline data for each collection that no longer belongs because it doesn't match the configured `filter` or the collection's `.keep` filter + +The benefit of this sequential-replay syncing strategy is any business logic contained in your methods will be respected. For example, if a user makes changes to a document but loses permission to it while offline, when they come back online, that permission will be respected when the attempted replay occurs. If there are any errors during auto sync, they will be made available in the `handleSyncErrors` function. You can use it to make your user aware that their changes failed. See [Configuring](#configuring-optional) for more details on how to customize this. + +When reconciling with the server, this package assumes that you'll use one of the following mechanisms when removing documents: + +1. `archive`. With this mechanism, when a document is removed from its original collection, it's placed in an `archive` collection. If your app doesn't already employ an archive mechanism, check out the [jam:archive](https://github.com/jamauro/archive) package to make this easy. By simply adding the `jam:archive` package, you won't need to do any further configuration. + +Otherwise, be sure to check out the `archive` configuration options. **`Note`**: it's assumed that the data in your `archive` collection will include the original document data at the root level, e.g.: +```js +{ + _id, // auto-generated by Meteor as with other collection _ids + _collection, // the name of the collection, e.g. 'todos', that the doc belonged to originally + archivedAt, // the timestamp when the document was removed from its original collection and inserted into the archive + id, // the original doc _id renamed to prevent conflict with the auto-generated one above. when restored, it will be renamed back to _id automatically by this package + /* + ...rest of original doc + */ +} +``` + +2. `soft delete`. If your app doesn't already employ a soft delete mechanism, check out the [jam:soft-delete](https://github.com/jamauro/soft-delete) package to make this easy. If you're using something other than `deleted` as the flag name for your soft deletes, be sure to configure `filter` appropriately. See [Configuring](#configuring-optional) for more details. + +To know when an auto sync is processing, you can use `isSyncing()` which is a reactive variable. + +```js +import { isSyncing } from 'meteor/jam:offline'; + +isSyncing(); // you can wrap this in a Tracker.autorun to detect when it changes +``` + +If you prefer not to have the behavior provided by auto sync, be sure to configure `autoSync: false`. When `autoSync` is false, Minimongo data from when the user disconnects will be kept offline so you still benefit from faster reloads on reconnects but you'll be responsible for designing any syncing logic needed. If the user hasn't refreshed the page and has a brief disconnect / reconnect, then you'll still benefit from Meteor's built-in retry mechanism for methods. But if they do refresh or exit the app and come back later, any actions performed while offline will be lost. + +I think it would be great to have better support for custom syncing. If you have ideas here, let me know. At this time, I'm not sure what primitives would be most useful for you. + +## Configuring (optional) +If you like the defaults, then you won't need to configure anything. But there is some flexibility in how you use this package. You may want to pay special attention to `filter` and `handleSyncErrors` to customize the experience for your users. + +Here are the global defaults: +```js +const config = { + filter: /** {} or { deleted: false } **/ // filter the documents to keep across all collections. if you're not using jam:archive or don't have the archive config below set, it will assume you're using soft deletes. + sort: { updatedAt: -1 }, // keep the most recent documents assuming you have an updatedAt on each doc. if you're using a different field name for timestamps, you'll want to change this. + limit: 100, // limit offline documents to a max of 100 for each collection + keepAll: true, // keep data for offline use for all collections using the global filter, sort, limit. to keep data for only certain collections, set this to false and then use collection.keep() for the collections you want to use offline. + autoSync: true, // auto sync changes made offline when the user comes back online + handleSyncErrors: async ({ replayErrors, keepErrors }) => { + if (replayErrors) console.error('replay', replayErrors); // if there are errors when the Meteor methods are replayed, they will be in array here with the name of the method, the method's args, and the error itself. you can use it to alert your user, logging purposes, etc. + + if (keepErrors) { // when syncing, if you're using a .keep filter or you have a global filter in the config that isn't an empty object, and there are errors reconciling with the server, they will be in an array here with the name of the collection and the error itself. you can customize how you handle these. by default, we clear the offline database for the collection since it could have stale data and reload the page. + await Promise.allSettled(keepErrors.map(({ name }) => clear(name))); + console.error('keep', keepErrors) + } + + return; + }, + ...(Archive && { // Archive is provided by jam:archive. if you're using a different archive mechanism, you'll need to configure these manually + archive: { + name: 'archives', + collectionKey: '_collection', + primaryIdKey: 'id', + timestampKey: 'archivedAt' + } + }) +}; +```` + +To change the global defaults, use: +```js +// put this in a file that's imported on both the client and server +import { Offline } from 'meteor/jam:offline'; + +Offline.configure({ + // ... change the defaults here ... // +}); +``` + +## Adding a service worker +You'll likely want to add a service worker as well to cache your HTML, CSS, Javascript so your users can continue to use the app offline, even if they accidentally click refresh. + +Follow these [instructions](https://github.com/jamauro/pwa-kit) to add a service worker and go even further by making it a PWA (progressive web app). diff --git a/v3-docs/docs/community-packages/pub-sub.md b/v3-docs/docs/community-packages/pub-sub.md new file mode 100644 index 0000000000..7015efe054 --- /dev/null +++ b/v3-docs/docs/community-packages/pub-sub.md @@ -0,0 +1,232 @@ +# Pub-sub + +- `Who maintains the package` – [Jam](https://github.com/jamauro) + +[[toc]] + +## What is this package? + +`jam:pub-sub` brings three key features to Meteor apps: + +1. Method-based publish / subscribe +2. Change Streams-based publish / subscribe +3. Subscription caching + +> **Important**: This package expects that you'll use the promise-based `*Async` Meteor collection methods introduced in `v2.8.1`. + +## Method-based publish / subscribe +Meteor's traditional `publish / subscribe` is truly wonderful. However, there is a cost to pushing updates reactively to all connected clients – it's resource intensive and will eventually place limits on your ability to scale your app. + +One way to reduce the need for the traditional `publish / subscribe` is to fetch the data via a `Meteor Method` but there's a big problem here: the data **won't** be automatically merged into Minimongo and you completely lose Meteor's magical reactivity. Minimongo is great to work with and makes things easy as the source of truth on the client. Without it, you'll need to create your own stores on the client and essentially duplicate Minimongo. + +With `jam:pub-sub`, you use `Meteor.publish.once` and the same `Meteor.subscribe` to have the data fetched via a Meteor Method and merged automatically in Minimongo so you can work with it as you're accustomed to. It also automatically preserves reactivity for the user when they make database writes. Note that these writes will **not** be broadcast in realtime to all connected clients by design but in many cases you might find that you don't need that feature of Meteor's traditional `publish / subscribe`. + +## How to download it? + +Add the package to your app +```bash +meteor add jam:pub-sub +``` +### Sources + +* [GitHub repository](https://github.com/jamauro/pub-sub) + +## How to use it? + +## Change Streams-based publish / subscribe +**`Alpha`** + +With `jam:pub-sub` and MongoDB Change Streams, you can preserve Meteor's magical reactivity for all clients while opting out of the traditional `publish / subscribe` and its use of the `oplog`. Use `Meteor.publish.stream` instead of using `Meteor.publish` and subscribe using the same `Meteor.subscribe` on the client. + +**Important**: Change Streams will work best when the filter you use can be shared. To that end, if you have a publication that includes a `userId`, this package will filter out that condition when setting up the Change Stream because it will result in too many unique change streams. As an example, lets say you have this publication: + +```js +Meteor.publish.stream('todos', function() { + return Todos.find({ + $or: [ + { isPrivate: false }, + { owner: this.userId } + ] + }); +}); +``` + +When this publication is invoked, it will pull all the `Todos` that match the filter above and then begin watching a Change Stream with this filter: +```js + { isPrivate: false } +``` + +For this particular filter, it should behave as you'd expect so you wouldn't need to make changes. However, if you have complex filters involving the `userId`, you'll need to be sure that the behavior you expect remains when using `.stream`. If it's not meeting your needs, you could split the one publication into two publications, using a `.stream` with a filter than can be shared and a `.once` for the `userId`: + +```js +Meteor.publish.stream('todos.public', function() { + return Todos.find({ isPrivate: false }); +}); + +Meteor.publish.once('todos.owned', function() { + return Todos.find({ owner: this.userId }); +}); +``` + +The downside by splitting into two is it could result in over-fetching but the data will be merged correctly into Minimongo. + +**Note**: In most cases, you'd likely benefit the most from using `Meteor.publish.once` anywhere you can and using `Meteor.publish.stream` only when you really need it and with a filter than can be shared. + +**Note**: If you decide to entirely opt-out of using the traditional `Meteor.publish`, then you'll also want to disable the `oplog` entirely — add the `disable-oplog` package with `meteor add disable-oplog`. + +At the moment, this feature is considered in an `alpha` state. Based on previous [Change Streams experiments](https://github.com/meteor/meteor/discussions/11842#discussioncomment-4061112) by the Meteor Community, it seems that using Change Streams as a wholesale replacement for the traditional `publish / subscribe` could "just work". However, in practice it may be a "Your Mileage May Vary" type of situation depending on the frequency of writes, number of connected clients, how you model your data, and how you set up the cursors inside of `Meteor.publish.stream`. With that said, if you're interested in this feature, I'd encourage you to try it out and share your findings. + +## Subscription caching +Normally, when a user moves between routes or components, the subscriptions will be stopped. When a user is navigating back and forth in your app, each time will result in a re-subscribe which means more spinners, a slower experience, and is generally a waste. + +By caching your subscriptions, you can create a better user experience for your users. Since the subscription itself is being cached, the data in Minimongo will be updated in the background until the `cacheDuration` expires for that subscription at which point it will be stopped and the data will be removed from Minimongo as expected. + +## Usage + +### Add the package to your app +`meteor add jam:pub-sub` + +### Define a Method-based publication +Define a publication using `Meteor.publish.once` and subscribe just as you do currently. `Meteor.publish.once` expects you to return a cursor or an array of cursors just like `Meteor.publish`. + +```js +// server +Meteor.publish.once('notes.all', function() { + return Notes.find(); +}); +``` + +```js +// client +// Since each view layer (Blaze, React, Svelte, Vue, etc) has a different way of using `Tracker.autorun`, I've omitted it for brevity. You'd subscribe just as you do currently in your view layer of choice. +Meteor.subscribe('notes.all') + +// work with the Notes collection in Minimongo as you're accustomed to +Notes.find().fetch(); +``` +That's it. By using `Meteor.publish.once`, it will fetch the data initally and automatically merge it into Minimongo. Any database writes to the `Notes` collection will be sent reactively to the user that made the write. + +> **Important**: when naming your publications be sure to include the collection name(s) in it. This is generally common practice and this package relies on that convention. If you don't do this and you're caching the subscription, Minimongo data may be unexpectedly removed or retained when the subscription stops. It's recommended that you follow this convention for all publications including `Meteor.publish`. Here are some examples of including the collection name in the publication name: +```js +// the name you assign inside Mongo.Collection should be in your publication name(s), in this example 'notes' +const Notes = new Mongo.Collection('notes') + +// as long as it appears somewhere in your publication name, you're good to go. here are some examples: +Meteor.publish.once('notes'); +Meteor.publish.once('notes.all'); +Meteor.publish.once('notes/single'); +Meteor.publish.once('somethingAndNotes'); +``` + +It also works just as you'd expect for an array of cursors: + +```js +// server +Meteor.publish.once('notes.todos.all', function() { + return [Notes.find(), Todos.find()]; +}); +``` + +```js +// client +Meteor.subscribe('notes.todos.all'); + +// work with the Notes collection in Minimongo as you're accustomed to +Notes.find().fetch(); + +// work with the Todos collection in Minimongo as you're accusomted to +Todos.find().fetch(); +``` + +Inside `Meteor.publish.once`, `this.userId` and [this.added](https://docs.meteor.com/api/pubsub.html#Subscription-added) can still be used. The added document will be included in the final result data. The rest of the low-level `publish` API will be disregarded, as they no longer fit into the context of a Method-based data fetch. + +```js +Meteor.publish.once('notes.all', function() { + // ... // + const userId = this.userId; + + this.added('notes', _id, fields); + // ... // + return Notes.find(); +}) +``` + +### Define a Change Streams-based publication +Define a publication using `Meteor.publish.stream` and subscribe just as you do currently. `Meteor.publish.stream` expects you to return a cursor or an array of cursors just like `Meteor.publish`. + +```js +// server +Meteor.publish.stream('notes.all', function() { + return Notes.find(); +}); +``` + +```js +// client +// Since each view layer (Blaze, React, Svelte, Vue, etc) has a different way of using `Tracker.autorun`, I've omitted it for brevity. You'd subscribe just as you do currently in your view layer of choice. +Meteor.subscribe('notes.all') + +// work with the Notes collection in Minimongo as you're accustomed to +Notes.find().fetch(); +``` +That's it. By using `Meteor.publish.stream`, any database writes to the `Notes` collection will be sent reactively to **all** connected clients just as with `Meteor.publish`. + +#### Setting the `maxPoolSize` for Change Streams +`maxPoolSize` defaults to `100` which may not need adjusting. If you need to adjust it, you can set it in [Meteor.settings](https://docs.meteor.com/api/collections.html#mongo_connection_options_settings) like this: +```js +{ + //...// + "packages": { + "mongo": { + "options": { + "maxPoolSize": 200 // or whatever is appropriate for your application + } + } + } + // ... // +} +``` + +### Turn on subscription caching +With `jam:pub-sub`, you can enable subscription caching globally or at a per-subscription level. Subscription caching is turned off by default to preserve the current behavior in Meteor. Any subscription can be cached, regardless of how it's published. + +To enable subscription caching globally for every subscription: +```js +// put this in a file that's imported on the client at a minimum. it can be used isomorphically but the configuration only applies to the client. +import { PubSub } from 'meteor/jam:pub-sub'; + +PubSub.configure({ + cache: true // defaults to false +}); +``` + +The global `cacheDuration` is set to `60 seconds` by default. This is from when the subscription was originally set to be stopped, i.e. when the component housing the subscription was destroyed because the user navigated away. If the user comes right back, then the cache will be used. If they don't, after `60 seconds`, the subscription cache will be removed. If you want to change the global `cacheDuration`, change it with a value in `seconds`: + +```js +import { PubSub } from 'meteor/jam:pub-sub'; + +PubSub.configure({ + cacheDuration: 5 * 60 // sets the cacheDuration to 5 minutes. defaults to 1 min +}); +``` + +You can also configure `cache` and `cacheDuration` for each individual subscription when you use `Meteor.subscribe`. For example: +```js +Meteor.subscribe('todos.single', _id, { cacheDuration: 30 }) // caches for 30 seconds, overriding the global default +Meteor.subscribe('notes.all', { cache: true }) // turns caching on, overriding the global default, and uses the global default cacheDuration +``` + +> **Note**: the rest of the [Meteor.subscribe](https://docs.meteor.com/api/pubsub.html#Meteor-subscribe) API (e.g. `onStop`, `onReady`) works just as you'd expect. + +> **Note**: Because the data will remain in Minimongo while the subscription is cached, you should be mindful of your Minimongo `.find` selectors. Be sure to use specific selectors to `.find` the data you need for that particular subscription. This is generally considered [best practice](https://guide.meteor.com/data-loading#fetching) so this is mainly a helpful reminder. + +#### Clearing the cache +Each individual subcription will be automatically removed from the cache when its `cacheDuration` elapses. + +Though it shouldn't be necessary, you can programmatically clear all cached subscriptions: + +```js +import { PubSub } from 'meteor/jam:pub-sub'; + +PubSub.clearCache(); +``` diff --git a/v3-docs/docs/community-packages/soft-delete.md b/v3-docs/docs/community-packages/soft-delete.md new file mode 100644 index 0000000000..497b0d0232 --- /dev/null +++ b/v3-docs/docs/community-packages/soft-delete.md @@ -0,0 +1,77 @@ +# Soft-delete + +- `Who maintains the package` – [Jam](https://github.com/jamauro) + +[[toc]] + +## What is this package? + +Soft Delete is an easy way to add soft deletes to your Meteor app. Its key features are: + +* Zero config needed (though you can customize) +* Isomorphic so that it works with Optimistic UI +* Automatically overrides `removeAsync` to make it a soft delete +* Automatically adds the soft delete flag on `insertAsync` and to the filter for your queries, e.g. `.find`, so you don't need to make any changes to them +* Recover soft deleted docs with `recoverAsync` collection method +* Explicitly soft delete with `softRemoveAsync` collection method (optional) +* Optionally add a `deletedAt` timestamp +* Optionally exclude specific collections +* Compatible with Meteor `2.8.1+` and `3.0+` + +> **Note:** Alternative to soft deletion is to archive documents in your collection. You can use [`jam:archive`](./archive.md) package for that. Be sure to compare those two approaches to pick the solution best suited for your application. + +## How to download it? + +Add the package to your app +```bash +meteor add jam:soft-delete +``` +### Sources + +* [GitHub repository](https://github.com/jamauro/soft-delete) + +## How to use it? + +### Deleting permanently +By default, this package overrides the `removeAsync` collection method so that it soft deletes the document(s) with a boolean flag rather that removing them from the database. To delete permanently, pass in the option `soft: false`, e.g.: +```js +Collection.removeAsync(/* your filter */, { soft: false }) +``` + +If you prefer, you can prevent overriding the `removeAsync` by setting `overrideRemove: false`. See [Configuring](#configuring-optional) for more details. + +### Explicitly soft deleting +If you prefer, you can explicity use `softRemoveAsync`, e.g.: +```js +Collection.softRemoveAsync(/* your filter */) +``` + +### Recovering a document +To recover a soft deleted document, use `recoverAsync`, e.g.: +```js +Collection.recoverAsync(/* your filter */) +``` + +## Configuring (optional) +If you like the defaults, then you won't need to configure anything. But there is some flexibility in how you use this package. + +Here are the global defaults: +```js +const config = { + deleted: 'deleted', // the field name used for the boolean flag. you can update to your preference, e.g. 'isDeleted' + deletedAt: '', // add the name of the field you'd like to use for a deletedAt timestamp, e.g. 'deletedAt', if you want to include it on your docs + autoFilter: true, // automatically adds the { [deleted]: false } filter to your queries + overrideRemove: true, // overrides the Collection.removeAsync method to make it a soft delete instead + exclude: ['roles', 'role-assignment'] // exclude specific collections from using soft delete. defaults to excluding the collections created the meteor roles package +}; +``` + +To change the global defaults, use: +```js +// put this in a file that's imported on both the client and server +import { SoftDelete } from 'meteor/jam:soft-delete'; + +SoftDelete.configure({ + // ... change the defaults here ... // +}); +``` diff --git a/v3-docs/docs/generators/changelog/README.md b/v3-docs/docs/generators/changelog/README.md index bd795db186..6af9fc099d 100644 --- a/v3-docs/docs/generators/changelog/README.md +++ b/v3-docs/docs/generators/changelog/README.md @@ -32,7 +32,7 @@ organized changelog, since all the files will be reflecting at least one version ## Update ordering. -If you want to make sure that the changelog is correcly ordered, take a look at the `order-packages.js` file. +If you want to make sure that the changelog is correctly ordered, take a look at the `order-packages.js` file. to use it, run the command below: ```bash diff --git a/v3-docs/docs/generators/changelog/versions/3.1.0.md b/v3-docs/docs/generators/changelog/versions/3.1.0.md new file mode 100644 index 0000000000..fa1bc45e01 --- /dev/null +++ b/v3-docs/docs/generators/changelog/versions/3.1.0.md @@ -0,0 +1,96 @@ +## v3.1.0, 2024-11-20 + +### Highlights + +- Upgrade to Node v22 +- Upgrade Mongo Driver to v6 +- Upgrade Express to v5 +- HMR performance improvements +- Bring the `roles` package into the core +- Solve remaining issues with Top Level Await and Reify +- Refactor part of the real-time code in the `mongo` package into TypeScript +- Deprecate `underscore` and eliminate it from packages +- Fix Cordova dependency issues +- Simplify Meteor.EnvironmentVariable and make sure it works in all cases +- Stop using Rosetta for development MongoDB on Apple Silicon +- Test improvements +- Bump `http` package to 3.0.0 + +#### Breaking Changes + +- Express is now at v5 and some it's APIs have changed. + +#### Internal API changes + +N/A + +#### Migration Steps + +Please run the following command to update your project: + +```bash + +meteor update --release 3.1 + +``` + +Make sure to migrate userland code to use the new Express APIs: + +https://expressjs.com/en/guide/migrating-5.html + +The MongoDB Node.js driver implemented a significant breaking change regarding operation handling. Applications leveraging `rawCollection()` or `rawDatabase()` methods must now utilize Promise-based syntax exclusively, as the callback-pattern API has been deprecated and removed. This modification affects all direct MongoDB operations previously using callback functions. + + +#### Bumped Meteor Packages + +- accounts-password@3.0.3 +- babel-compiler@7.11.2 +- boilerplate-generator-tests@1.5.3 +- ddp-client@3.0.3 +- ddp-server@3.0.3 +- http@3.0.0 +- ecmascript@0.16.10 +- email@3.1.1 +- meteor-tool@3.1.0 +- meteor@2.0.2 +- minifier-js@3.0.1 +- minimongo@2.0.2 +- modules@0.20.3 +- mongo@2.0.3 +- coffeescript-compiler@2.4.2 +- coffeescript@2.7.2 +- mongo-decimal@0.2.0 +- npm-mongo@6.10.0 +- shell-server@0.6.1 +- test-helpers@2.0.2 +- test-in-console@2.0.1 +- tinytest@1.3.1 +- typescript@5.6.3 +- url@1.3.5 +- webapp@2.0.4 + + +#### Bumped NPM Packages + +- meteor-babel@7.20.1 +- meteor-installer@3.1.0 +- meteor-node-stubs@1.2.12 + + +#### Special thanks to + +✨✨✨ + +- [@denihs](https://github.com/denihs) +- [@nachocodoner](https://github.com/nachocodoner) +- [@leonardoventurini](https://github.com/leonardoventurini) +- [@StorytellerCZ](https://github.com/StorytellerCZ) +- [@zodern](https://github.com/zodern) +- [@harry97](https://github.com/harry97) +- [@permb](https://github.com/permb) +- [@9Morello](https://github.com/9Morello) +- [@wreiske](https://github.com/wreiske) +- [@MarcoTribuz](https://github.com/MarcoTribuz) +- [@MeteorCoder](https://forums.meteor.com/u/meteorcoder/summary) + +✨✨✨ diff --git a/v3-docs/docs/generators/changelog/versions/3.1.1.md b/v3-docs/docs/generators/changelog/versions/3.1.1.md new file mode 100644 index 0000000000..e143fee669 --- /dev/null +++ b/v3-docs/docs/generators/changelog/versions/3.1.1.md @@ -0,0 +1,71 @@ +## v3.1.1, 2025-01-15 + +### Highlights + +- Node upgrade: v22.13.0 +- Real-time Performance Boost: Refactored AsynchronousQueue for parallel processing and optimized MongoDB observers for faster initial document handling. +- Allow/Deny Rules Update: Deprecated async rules and updated documentation and types. +- Mongo Driver Downgrade: Reverted to a stable version to prevent issues in Meteor. +- Support for Meteor.deprecate: Added deprecation warnings with stack trace for outdated code usage. +- OAuth Fixes: Resolved issues with cancel popup event and callback. +- Windows Fixes: Fixed issues with running from folders with spaces and post-npm install build errors on Windows. +- Security Updates: Upgraded json5 and semver from babel compiler. + +#### Breaking Changes + +N/A + +#### Internal API changes + +N/A + +#### Migration Steps + +Please run the following command to update your project: + +```bash + +meteor update --release 3.1.1 + +``` + +#### Bumped Meteor Packages + +- accounts-base@3.0.4 +- accounts-oauth@1.4.6 +- accounts-passwordless@3.0.1 +- allow-deny@2.1.0 +- babel-compiler@7.11.3 +- ddp-client@3.1.0 +- ddp-server@3.1.0 +- meteor-tool@3.1.1 +- facebook-oauth@1.11.5 +- meteor@2.1.0 +- meteor-tool@3.1.1 +- mongo@2.1.0 +- npm-mongo@6.10.2 +- oauth@3.0.1 +- roles@@1.0.1 +- socket-stream-client@0.6.0 + +#### Bumped NPM Packages + +N/A + +#### Special thanks to + +✨✨✨ + +- [@leonardoventurini](https://github.com/leonardoventurini) +- [@nachocodoner](https://github.com/nachocodoner) +- [@Grubba27](https://github.com/Grubba27) +- [@9Morello](https://github.com/9Morello) +- [@perbergland](https://github.com/perbergland) +- [@StorytellerCZ](https://github.com/StorytellerCZ) +- [@MrSpark2591](https://github.com/MrSpark2591) +- [@jstarpl](https://github.com/jstarpl) +- [@minhna](https://github.com/minhna) +- [@svolkomorov](https://github.com/svolkomorov) +- [@quyetdgroup](https://github.com/quyetdgroup) + +✨✨✨ diff --git a/v3-docs/docs/generators/changelog/versions/3.1.2.md b/v3-docs/docs/generators/changelog/versions/3.1.2.md new file mode 100644 index 0000000000..b8fb483385 --- /dev/null +++ b/v3-docs/docs/generators/changelog/versions/3.1.2.md @@ -0,0 +1,49 @@ +## v3.1.2, 2025-02-06 + +### Highlights + +- Node upgrade: v22.13.1 +- Updated Meteor skeletons for Vue and Solid +- Improved browser detection and related documentation +- Security updates: nodemailer-openpgp and others in Meteor packages + +#### Breaking Changes + +N/A + +#### Internal API changes + +N/A + +#### Migration Steps + +Please run the following command to update your project: + +```bash + +meteor update --release 3.1.2 + +``` + +#### Bumped Meteor Packages + +- email@3.1.2 +- meteor-tool@3.1.2 +- modern-browsers@0.2.0 +- webapp@2.0.5 + +#### Bumped NPM Packages + +N/A + +#### Special thanks to + +✨✨✨ + +- [@nachocodoner](https://github.com/nachocodoner) +- [@Grubba27](https://github.com/Grubba27) +- [@perbergland](https://github.com/perbergland) +- [@StorytellerCZ](https://github.com/StorytellerCZ) +- [@julio-rocketchat](https://github.com/julio-rocketchat) + +✨✨✨ diff --git a/v3-docs/docs/generators/changelog/versions/3.2.0.md b/v3-docs/docs/generators/changelog/versions/3.2.0.md new file mode 100644 index 0000000000..fb39410f55 --- /dev/null +++ b/v3-docs/docs/generators/changelog/versions/3.2.0.md @@ -0,0 +1,66 @@ +## v3.2.0, 2025-03-18 + +### Highlights + +- Upgrade to Node 22.14.0 & Mongo 7.0.16. +- Implement `meteor profile` command to show performance metrics of Meteor apps. +- Fix Meteor profiler to handle promises and display metrics correctly. +- Support for argon2 to improve password security over bcrypt. [PR #13554](https://github.com/meteor/meteor/pull/13554) +- Improve and fix package resolution logic. [PR #13604](https://github.com/meteor/meteor/pull/13604) +- Fix rare oplog issue that could cause data loss for clients. [PR #13603](https://github.com/meteor/meteor/pull/13603) + +All detailed PRs can be found here: https://github.com/meteor/meteor/pulls?q=is%3Apr+is%3Amerged+base%3Arelease-3.2 + +#### Breaking Changes + +N/A + +#### Internal API changes + +N/A + +#### Migration Steps + +Please run the following command to update your project: + +```bash + +meteor update --release 3.2 + +``` + +#### Bumped Meteor Packages + +- accounts-base@3.1.0 +- accounts-password@3.1.0 +- ecmascript-runtime-client@0.12.3 +- facebook-oauth@1.11.6 +- fetch@0.1.6 +- logging@1.3.6 +- logic-solver@3.1.0 +- minifier-css@2.0.1 +- meteor-tool@3.2.0 +- modern-browsers@0.2.1 +- mongo@2.1.1 +- oauth@3.0.2 +- test-helpers@2.0.3 + +#### Bumped NPM Packages + +- meteor-node-stubs@1.2.13 + +#### Special thanks to + +✨✨✨ + +- [@nachocodoner](https://github.com/nachocodoner) +- [@Grubba27](https://github.com/Grubba27) +- [@vparpoil](https://github.com/vparpoil) +- [@cunneen](https://github.com/cunneen) +- [@gbuerk](https://github.com/gbuerk) +- [@julio-rocketchat](https://github.com/julio-rocketchat) +- [@StorytellerCZ](https://github.com/StorytellerCZ) +- [@dr-dimitru](https://github.com/dr-dimitru) +- [@Seb-Dion](https://github.com/Seb-Dion) + +✨✨✨ diff --git a/v3-docs/docs/generators/codegen.js b/v3-docs/docs/generators/codegen.js index f740041028..a6ed7ab00a 100644 --- a/v3-docs/docs/generators/codegen.js +++ b/v3-docs/docs/generators/codegen.js @@ -1,10 +1,11 @@ const { generateChangelog } = require("./changelog/script.js"); const { listPackages } = require("./packages-listing/script.js"); - +const { generateMeteorVersions } = require("./meteor-versions/script.js"); async function main() { console.log("πŸš‚ Started codegen πŸš‚"); await generateChangelog(); await listPackages(); + await generateMeteorVersions(); console.log("πŸš€ Done codegen πŸš€"); } diff --git a/v3-docs/docs/generators/meteor-versions/README.md b/v3-docs/docs/generators/meteor-versions/README.md new file mode 100644 index 0000000000..6307b198bf --- /dev/null +++ b/v3-docs/docs/generators/meteor-versions/README.md @@ -0,0 +1,22 @@ +## Meteor version generator for docs + +This is a generator for the meteor versions for the docs, this is used to generate the links in the docs +to the correct version of the meteor release and docs version. + + +## Why? + +This is a way to ensure that the links in the docs are always pointing to the correct version of the docs and release. +In an automated way. + + +## How to use + +To use this generator you must run the following command: + +```bash +node script.js +``` + +and it will check in the `changelog` dir for every version and generate a `versions.generated.json` file that will be used by the docs to generate the links to the correct version of the docs. + diff --git a/v3-docs/docs/generators/meteor-versions/metadata.generated.js b/v3-docs/docs/generators/meteor-versions/metadata.generated.js new file mode 100644 index 0000000000..c21367d193 --- /dev/null +++ b/v3-docs/docs/generators/meteor-versions/metadata.generated.js @@ -0,0 +1,34 @@ +export default { + "versions": [ + { + "version": "v3.0", + "url": "https://release-3-0.docs.meteor.com/" + }, + { + "version": "v3.0.2", + "url": "https://release-3-0-2.docs.meteor.com/" + }, + { + "version": "v3.0.3", + "url": "https://release-3-0-3.docs.meteor.com/" + }, + { + "version": "v3.0.4", + "url": "https://release-3-0-4.docs.meteor.com/" + }, + { + "version": "v3.1.0", + "url": "https://release-3-1-0.docs.meteor.com/" + }, + { + "version": "v3.1.1", + "url": "https://release-3-1-1.docs.meteor.com/" + }, + { + "version": "v3.1.2", + "url": "https://release-3-1-2.docs.meteor.com/", + "isCurrent": true + } + ], + "currentVersion": "v3.1.2" +} \ No newline at end of file diff --git a/v3-docs/docs/generators/meteor-versions/script.js b/v3-docs/docs/generators/meteor-versions/script.js new file mode 100644 index 0000000000..ad3900fa9e --- /dev/null +++ b/v3-docs/docs/generators/meteor-versions/script.js @@ -0,0 +1,44 @@ +const _fs = require("fs"); +const fs = _fs.promises; + +const getDocsUrl = (version = "") => + `https://release-${version}.docs.meteor.com/`; + +exports.generateMeteorVersions = async () => { + console.log("Reading meteor versions..."); + const files = await fs.readdir("./generators/changelog/versions", "utf8"); + + const versions = files + .filter((f) => f.startsWith("3.")) + .map((f) => f.replace(".md", "")) + .filter((v) => v !== "3.0.1") // there is no 3.0.1 version + .map((v) => (v === "3.0.0" ? v.slice(0, -2) : v)) // 3.0 doesn't have a patch version in the URL + .map((version) => { + return { + version: `v${version}`, + url: getDocsUrl(`${version}`.replaceAll(".", "-")), + }; + }) + .map((v, index, arr) => { + const isLast = index === arr.length - 1; + if (isLast) { + v.isCurrent = true; + } + return v; + }); + const { version: currentVersion } = versions.find((v) => v.isCurrent); + + console.log("Writing meteor versions..."); + await fs.writeFile( + "./generators/meteor-versions/metadata.generated.js", + `export default ${JSON.stringify( + { + versions, + currentVersion, + }, + null, + 2 + )}` + ); + console.log("Meteor versions generated!"); +}; diff --git a/v3-docs/docs/history.md b/v3-docs/docs/history.md index fb537c579f..ac8ec9acc1 100644 --- a/v3-docs/docs/history.md +++ b/v3-docs/docs/history.md @@ -10,7 +10,398 @@ This is a complete history of changes for Meteor releases. [//]: # (go to meteor/docs/generators/changelog/docs) +## v3.2.0, 2025-03-18 +### Highlights + +- Upgrade to Node 22.14.0 & Mongo 7.0.16. +- Implement `meteor profile` command to show performance metrics of Meteor apps. +- Fix Meteor profiler to handle promises and display metrics correctly. +- Support for argon2 to improve password security over bcrypt. [PR #13554](https://github.com/meteor/meteor/pull/13554) +- Improve and fix package resolution logic. [PR #13604](https://github.com/meteor/meteor/pull/13604) +- Fix rare oplog issue that could cause data loss for clients. [PR #13603](https://github.com/meteor/meteor/pull/13603) + +All detailed PRs can be found here: https://github.com/meteor/meteor/pulls?q=is%3Apr+is%3Amerged+base%3Arelease-3.2 + +#### Breaking Changes + +N/A + +#### Internal API changes + +N/A + +#### Migration Steps + +Please run the following command to update your project: + +```bash + +meteor update --release 3.2 + +``` + +#### Bumped Meteor Packages + +- accounts-base@3.1.0 +- accounts-password@3.1.0 +- ecmascript-runtime-client@0.12.3 +- facebook-oauth@1.11.6 +- fetch@0.1.6 +- logging@1.3.6 +- logic-solver@3.1.0 +- minifier-css@2.0.1 +- meteor-tool@3.2.0 +- modern-browsers@0.2.1 +- mongo@2.1.1 +- oauth@3.0.2 +- test-helpers@2.0.3 + +#### Bumped NPM Packages + +- meteor-node-stubs@1.2.13 + +#### Special thanks to + +✨✨✨ + +- [@nachocodoner](https://github.com/nachocodoner) +- [@Grubba27](https://github.com/Grubba27) +- [@vparpoil](https://github.com/vparpoil) +- [@cunneen](https://github.com/cunneen) +- [@gbuerk](https://github.com/gbuerk) +- [@julio-rocketchat](https://github.com/julio-rocketchat) +- [@StorytellerCZ](https://github.com/StorytellerCZ) +- [@dr-dimitru](https://github.com/dr-dimitru) +- [@Seb-Dion](https://github.com/Seb-Dion) + +✨✨✨ + + +## v3.1.2, 2025-02-06 + +### Highlights + +- Node upgrade: v22.13.1 +- Updated Meteor skeletons for Vue and Solid +- Improved browser detection and related documentation +- Security updates: nodemailer-openpgp and others in Meteor packages + +#### Breaking Changes + +N/A + +#### Internal API changes + +N/A + +#### Migration Steps + +Please run the following command to update your project: + +```bash + +meteor update --release 3.1.2 + +``` + +#### Bumped Meteor Packages + +- email@3.1.2 +- meteor-tool@3.1.2 +- modern-browsers@0.2.0 +- webapp@2.0.5 + +#### Bumped NPM Packages + +N/A + +#### Special thanks to + +✨✨✨ + +- [@nachocodoner](https://github.com/nachocodoner) +- [@Grubba27](https://github.com/Grubba27) +- [@perbergland](https://github.com/perbergland) +- [@StorytellerCZ](https://github.com/StorytellerCZ) +- [@julio-rocketchat](https://github.com/julio-rocketchat) + +✨✨✨ +## v3.1.1, 2025-01-15 + +### Highlights + +- Node upgrade: v22.13.0 +- Real-time Performance Boost: Refactored AsynchronousQueue for parallel processing and optimized MongoDB observers for faster initial document handling. +- Allow/Deny Rules Update: Deprecated async rules and updated documentation and types. +- Mongo Driver Downgrade: Reverted to a stable version to prevent issues in Meteor. +- Support for Meteor.deprecate: Added deprecation warnings with stack trace for outdated code usage. +- OAuth Fixes: Resolved issues with cancel popup event and callback. +- Windows Fixes: Fixed issues with running from folders with spaces and post-npm install build errors on Windows. +- Security Updates: Upgraded json5 and semver from babel compiler. + +#### Breaking Changes + +N/A + +#### Internal API changes + +N/A + +#### Migration Steps + +Please run the following command to update your project: + +```bash + +meteor update --release 3.1.1 + +``` + +#### Bumped Meteor Packages + +- accounts-base@3.0.4 +- accounts-oauth@1.4.6 +- accounts-passwordless@3.0.1 +- allow-deny@2.1.0 +- babel-compiler@7.11.3 +- ddp-client@3.1.0 +- ddp-server@3.1.0 +- meteor-tool@3.1.1 +- facebook-oauth@1.11.5 +- meteor@2.1.0 +- meteor-tool@3.1.1 +- mongo@2.1.0 +- npm-mongo@6.10.2 +- oauth@3.0.1 +- roles@@1.0.1 +- socket-stream-client@0.6.0 + +#### Bumped NPM Packages + +N/A + +#### Special thanks to + +✨✨✨ + +- [@leonardoventurini](https://github.com/leonardoventurini) +- [@nachocodoner](https://github.com/nachocodoner) +- [@Grubba27](https://github.com/Grubba27) +- [@9Morello](https://github.com/9Morello) +- [@perbergland](https://github.com/perbergland) +- [@StorytellerCZ](https://github.com/StorytellerCZ) +- [@MrSpark2591](https://github.com/MrSpark2591) +- [@jstarpl](https://github.com/jstarpl) +- [@minhna](https://github.com/minhna) +- [@svolkomorov](https://github.com/svolkomorov) +- [@quyetdgroup](https://github.com/quyetdgroup) + +✨✨✨ +## v3.1.0, 2024-11-20 + +### Highlights + +- Upgrade to Node v22 +- Upgrade Mongo Driver to v6 +- Upgrade Express to v5 +- HMR performance improvements +- Bring the `roles` package into the core +- Solve remaining issues with Top Level Await and Reify +- Refactor part of the real-time code in the `mongo` package into TypeScript +- Deprecate `underscore` and eliminate it from packages +- Fix Cordova dependency issues +- Simplify Meteor.EnvironmentVariable and make sure it works in all cases +- Stop using Rosetta for development MongoDB on Apple Silicon +- Test improvements +- Bump `http` package to 3.0.0 + +#### Breaking Changes + +- Express is now at v5 and some it's APIs have changed. + +#### Internal API changes + +N/A + +#### Migration Steps + +Please run the following command to update your project: + +```bash + +meteor update --release 3.1 + +``` + +Make sure to migrate userland code to use the new Express APIs: + +https://expressjs.com/en/guide/migrating-5.html + +The MongoDB Node.js driver implemented a significant breaking change regarding operation handling. Applications leveraging `rawCollection()` or `rawDatabase()` methods must now utilize Promise-based syntax exclusively, as the callback-pattern API has been deprecated and removed. This modification affects all direct MongoDB operations previously using callback functions. + + +#### Bumped Meteor Packages + +- accounts-password@3.0.3 +- babel-compiler@7.11.2 +- boilerplate-generator-tests@1.5.3 +- ddp-client@3.0.3 +- ddp-server@3.0.3 +- http@3.0.0 +- ecmascript@0.16.10 +- email@3.1.1 +- meteor-tool@3.1.0 +- meteor@2.0.2 +- minifier-js@3.0.1 +- minimongo@2.0.2 +- modules@0.20.3 +- mongo@2.0.3 +- coffeescript-compiler@2.4.2 +- coffeescript@2.7.2 +- mongo-decimal@0.2.0 +- npm-mongo@6.10.0 +- shell-server@0.6.1 +- test-helpers@2.0.2 +- test-in-console@2.0.1 +- tinytest@1.3.1 +- typescript@5.6.3 +- url@1.3.5 +- webapp@2.0.4 + + +#### Bumped NPM Packages + +- meteor-babel@7.20.1 +- meteor-installer@3.1.0 +- meteor-node-stubs@1.2.12 + + +#### Special thanks to + +✨✨✨ + +- [@denihs](https://github.com/denihs) +- [@nachocodoner](https://github.com/nachocodoner) +- [@leonardoventurini](https://github.com/leonardoventurini) +- [@StorytellerCZ](https://github.com/StorytellerCZ) +- [@zodern](https://github.com/zodern) +- [@harry97](https://github.com/harry97) +- [@permb](https://github.com/permb) +- [@9Morello](https://github.com/9Morello) +- [@wreiske](https://github.com/wreiske) +- [@MarcoTribuz](https://github.com/MarcoTribuz) +- [@MeteorCoder](https://forums.meteor.com/u/meteorcoder/summary) + +✨✨✨ +## v3.0.4, 2024-10-15 + +### Highlights + +- Node 20.18.0 & Typescript 5.6.2 +- Updated webapp dependencies. +- DDP-server and DDP-client removed underscore +- Remove dependencies on Blaze packages when using static-html +- Fix Cordova on Windows +- Fix Cordova build on using plugins describing dependencies +- Various Windows specific fixes + +#### Breaking Changes + +N/A + +#### Internal API changes + +N/A + +#### Migration Steps + +Please run the following command to update your project: + +```bash + +meteor update --release 3.0.4 + +``` + +#### Meteor Version Release + +* `Bumped packages`: + - accounts-base@3.0.3 + - babel-compiler@7.11.1 + - caching-compiler@2.0.1 + - check@1.4.4 + - ddp-client@3.0.2 + - ddp-server@3.0.2 + - ecmascript-runtime@0.8.3 + - modules@0.20.2 + - static-html-tools@1.0.0 + - static-html@1.4.0 + - url@1.3.4 + - webapp@2.0.3 + - meteor-tool@3.0.4 + +#### Special thanks to +N/A +## v3.0.3, 2024-09-11 + +### Highlights + +- Fixed `Meteor.userId` only being invoked with `bindEnvironment`. +- Updated to Node `20.17.x`. +- Fixed an issue where `meteor --open` opens the browser before the app is started. +- Investigated and addressed the error when installing the `jam:method` package. +- Improved the message for new available versions when running an app. +- Updated the documentation link inside `install.sh`. +- Resolved the issue where subscriptions stopped after a parameter change. +- Added MongoDB connection telemetry. +- Bumped the `email` package to prevent update errors. +- Cordova package updates + +#### Breaking Changes + +N/A + +#### Internal API changes + +- Some internal changes to how async contexts are handled, ensuring better performance and garbage collection. + +#### Migration Steps + +Please run the following command to update your project: + +```bash + +meteor update --release 3.0.3 + +``` + +If you've had your Meteor installation for over a year, we suggest reinstalling it to avoid any package installation issues. You can do this by running a few quick commands: + +```bash +npx meteor uninstall // or rm -rf ~/.meteor +npx meteor +``` + + + +#### Meteor Version Release + +* `Bumped packages`: + - accounts-base@3.0.2 + - accounts-password@3.0.2 + - email@3.1.0 + - mongo@2.0.2 + + +#### Special thanks to + +- [@ayewo](https://github.com/ayewo). +- [@denihs](https://github.com/denihs). +- [@harryadel](https://github.com/harryadel). +- [@kbarr1212](https://github.com/kbarr1212). +- [@leonardoventurini](https://github.com/leonardoventurini). +- [@nachocodoner](https://github.com/nachocodoner). ## v3.0.2, 2024-08-14 diff --git a/v3-docs/docs/index.md b/v3-docs/docs/index.md index 84434df490..8f97a7ecb7 100644 --- a/v3-docs/docs/index.md +++ b/v3-docs/docs/index.md @@ -3,8 +3,8 @@ layout: home hero: - text: "Meteor.js Docs" - tagline: "Documentation, resources, and API references to help you build and deploy with Meteor.js." + text: "Meteor Docs" + tagline: "Documentation, resources, and API references to help you build and deploy with Meteor." image: src: /logo.png alt: Meteor diff --git a/v3-docs/docs/package-lock.json b/v3-docs/docs/package-lock.json index 55d9e175c4..8c1d06310d 100644 --- a/v3-docs/docs/package-lock.json +++ b/v3-docs/docs/package-lock.json @@ -5,43 +5,43 @@ "packages": { "": { "dependencies": { - "vue-collapsed": "^1.3.0" + "vue-collapsed": "^1.3.4" }, "devDependencies": { "canonical-json": "^0.0.4", - "jsdoc": "^4.0.2", - "vitepress": "1.0.1" + "jsdoc": "^4.0.4", + "vitepress": "1.6.3" } }, "node_modules/@algolia/autocomplete-core": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", - "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", + "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", "dev": true, "dependencies": { - "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", - "@algolia/autocomplete-shared": "1.9.3" + "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", + "@algolia/autocomplete-shared": "1.17.7" } }, "node_modules/@algolia/autocomplete-plugin-algolia-insights": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", - "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", + "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", "dev": true, "dependencies": { - "@algolia/autocomplete-shared": "1.9.3" + "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "node_modules/@algolia/autocomplete-preset-algolia": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", - "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", + "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", "dev": true, "dependencies": { - "@algolia/autocomplete-shared": "1.9.3" + "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", @@ -49,149 +49,221 @@ } }, "node_modules/@algolia/autocomplete-shared": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", - "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", "dev": true, "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, - "node_modules/@algolia/cache-browser-local-storage": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.22.1.tgz", - "integrity": "sha512-Sw6IAmOCvvP6QNgY9j+Hv09mvkvEIDKjYW8ow0UDDAxSXy664RBNQk3i/0nt7gvceOJ6jGmOTimaZoY1THmU7g==", + "node_modules/@algolia/client-abtesting": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.20.0.tgz", + "integrity": "sha512-YaEoNc1Xf2Yk6oCfXXkZ4+dIPLulCx8Ivqj0OsdkHWnsI3aOJChY5qsfyHhDBNSOhqn2ilgHWxSfyZrjxBcAww==", "dev": true, "dependencies": { - "@algolia/cache-common": "4.22.1" - } - }, - "node_modules/@algolia/cache-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.22.1.tgz", - "integrity": "sha512-TJMBKqZNKYB9TptRRjSUtevJeQVXRmg6rk9qgFKWvOy8jhCPdyNZV1nB3SKGufzvTVbomAukFR8guu/8NRKBTA==", - "dev": true - }, - "node_modules/@algolia/cache-in-memory": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.22.1.tgz", - "integrity": "sha512-ve+6Ac2LhwpufuWavM/aHjLoNz/Z/sYSgNIXsinGofWOysPilQZPUetqLj8vbvi+DHZZaYSEP9H5SRVXnpsNNw==", - "dev": true, - "dependencies": { - "@algolia/cache-common": "4.22.1" - } - }, - "node_modules/@algolia/client-account": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.22.1.tgz", - "integrity": "sha512-k8m+oegM2zlns/TwZyi4YgCtyToackkOpE+xCaKCYfBfDtdGOaVZCM5YvGPtK+HGaJMIN/DoTL8asbM3NzHonw==", - "dev": true, - "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "5.20.0", + "@algolia/requester-browser-xhr": "5.20.0", + "@algolia/requester-fetch": "5.20.0", + "@algolia/requester-node-http": "5.20.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/client-analytics": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.22.1.tgz", - "integrity": "sha512-1ssi9pyxyQNN4a7Ji9R50nSdISIumMFDwKNuwZipB6TkauJ8J7ha/uO60sPJFqQyqvvI+px7RSNRQT3Zrvzieg==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.20.0.tgz", + "integrity": "sha512-CIT9ni0+5sYwqehw+t5cesjho3ugKQjPVy/iPiJvtJX4g8Cdb6je6SPt2uX72cf2ISiXCAX9U3cY0nN0efnRDw==", "dev": true, "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "5.20.0", + "@algolia/requester-browser-xhr": "5.20.0", + "@algolia/requester-fetch": "5.20.0", + "@algolia/requester-node-http": "5.20.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/client-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.22.1.tgz", - "integrity": "sha512-IvaL5v9mZtm4k4QHbBGDmU3wa/mKokmqNBqPj0K7lcR8ZDKzUorhcGp/u8PkPC/e0zoHSTvRh7TRkGX3Lm7iOQ==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.20.0.tgz", + "integrity": "sha512-iSTFT3IU8KNpbAHcBUJw2HUrPnMXeXLyGajmCL7gIzWOsYM4GabZDHXOFx93WGiXMti1dymz8k8R+bfHv1YZmA==", + "dev": true, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.20.0.tgz", + "integrity": "sha512-w9RIojD45z1csvW1vZmAko82fqE/Dm+Ovsy2ElTsjFDB0HMAiLh2FO86hMHbEXDPz6GhHKgGNmBRiRP8dDPgJg==", "dev": true, "dependencies": { - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "5.20.0", + "@algolia/requester-browser-xhr": "5.20.0", + "@algolia/requester-fetch": "5.20.0", + "@algolia/requester-node-http": "5.20.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/client-personalization": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.22.1.tgz", - "integrity": "sha512-sl+/klQJ93+4yaqZ7ezOttMQ/nczly/3GmgZXJ1xmoewP5jmdP/X/nV5U7EHHH3hCUEHeN7X1nsIhGPVt9E1cQ==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.20.0.tgz", + "integrity": "sha512-p/hftHhrbiHaEcxubYOzqVV4gUqYWLpTwK+nl2xN3eTrSW9SNuFlAvUBFqPXSVBqc6J5XL9dNKn3y8OA1KElSQ==", "dev": true, "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "5.20.0", + "@algolia/requester-browser-xhr": "5.20.0", + "@algolia/requester-fetch": "5.20.0", + "@algolia/requester-node-http": "5.20.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.20.0.tgz", + "integrity": "sha512-m4aAuis5vZi7P4gTfiEs6YPrk/9hNTESj3gEmGFgfJw3hO2ubdS4jSId1URd6dGdt0ax2QuapXufcrN58hPUcw==", + "dev": true, + "dependencies": { + "@algolia/client-common": "5.20.0", + "@algolia/requester-browser-xhr": "5.20.0", + "@algolia/requester-fetch": "5.20.0", + "@algolia/requester-node-http": "5.20.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/client-search": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.22.1.tgz", - "integrity": "sha512-yb05NA4tNaOgx3+rOxAmFztgMTtGBi97X7PC3jyNeGiwkAjOZc2QrdZBYyIdcDLoI09N0gjtpClcackoTN0gPA==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.20.0.tgz", + "integrity": "sha512-KL1zWTzrlN4MSiaK1ea560iCA/UewMbS4ZsLQRPoDTWyrbDKVbztkPwwv764LAqgXk0fvkNZvJ3IelcK7DqhjQ==", "dev": true, "dependencies": { - "@algolia/client-common": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-common": "5.20.0", + "@algolia/requester-browser-xhr": "5.20.0", + "@algolia/requester-fetch": "5.20.0", + "@algolia/requester-node-http": "5.20.0" + }, + "engines": { + "node": ">= 14.0.0" } }, - "node_modules/@algolia/logger-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.22.1.tgz", - "integrity": "sha512-OnTFymd2odHSO39r4DSWRFETkBufnY2iGUZNrMXpIhF5cmFE8pGoINNPzwg02QLBlGSaLqdKy0bM8S0GyqPLBg==", - "dev": true - }, - "node_modules/@algolia/logger-console": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.22.1.tgz", - "integrity": "sha512-O99rcqpVPKN1RlpgD6H3khUWylU24OXlzkavUAMy6QZd1776QAcauE3oP8CmD43nbaTjBexZj2nGsBH9Tc0FVA==", + "node_modules/@algolia/ingestion": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.20.0.tgz", + "integrity": "sha512-shj2lTdzl9un4XJblrgqg54DoK6JeKFO8K8qInMu4XhE2JuB8De6PUuXAQwiRigZupbI0xq8aM0LKdc9+qiLQA==", "dev": true, "dependencies": { - "@algolia/logger-common": "4.22.1" + "@algolia/client-common": "5.20.0", + "@algolia/requester-browser-xhr": "5.20.0", + "@algolia/requester-fetch": "5.20.0", + "@algolia/requester-node-http": "5.20.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.20.0.tgz", + "integrity": "sha512-aF9blPwOhKtWvkjyyXh9P5peqmhCA1XxLBRgItT+K6pbT0q4hBDQrCid+pQZJYy4HFUKjB/NDDwyzFhj/rwKhw==", + "dev": true, + "dependencies": { + "@algolia/client-common": "5.20.0", + "@algolia/requester-browser-xhr": "5.20.0", + "@algolia/requester-fetch": "5.20.0", + "@algolia/requester-node-http": "5.20.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.20.0.tgz", + "integrity": "sha512-T6B/WPdZR3b89/F9Vvk6QCbt/wrLAtrGoL8z4qPXDFApQ8MuTFWbleN/4rHn6APWO3ps+BUePIEbue2rY5MlRw==", + "dev": true, + "dependencies": { + "@algolia/client-common": "5.20.0", + "@algolia/requester-browser-xhr": "5.20.0", + "@algolia/requester-fetch": "5.20.0", + "@algolia/requester-node-http": "5.20.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.22.1.tgz", - "integrity": "sha512-dtQGYIg6MteqT1Uay3J/0NDqD+UciHy3QgRbk7bNddOJu+p3hzjTRYESqEnoX/DpEkaNYdRHUKNylsqMpgwaEw==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.20.0.tgz", + "integrity": "sha512-t6//lXsq8E85JMenHrI6mhViipUT5riNhEfCcvtRsTV+KIBpC6Od18eK864dmBhoc5MubM0f+sGpKOqJIlBSCg==", "dev": true, "dependencies": { - "@algolia/requester-common": "4.22.1" + "@algolia/client-common": "5.20.0" + }, + "engines": { + "node": ">= 14.0.0" } }, - "node_modules/@algolia/requester-common": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.22.1.tgz", - "integrity": "sha512-dgvhSAtg2MJnR+BxrIFqlLtkLlVVhas9HgYKMk2Uxiy5m6/8HZBL40JVAMb2LovoPFs9I/EWIoFVjOrFwzn5Qg==", - "dev": true + "node_modules/@algolia/requester-fetch": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.20.0.tgz", + "integrity": "sha512-FHxYGqRY+6bgjKsK4aUsTAg6xMs2S21elPe4Y50GB0Y041ihvw41Vlwy2QS6K9ldoftX4JvXodbKTcmuQxywdQ==", + "dev": true, + "dependencies": { + "@algolia/client-common": "5.20.0" + }, + "engines": { + "node": ">= 14.0.0" + } }, "node_modules/@algolia/requester-node-http": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.22.1.tgz", - "integrity": "sha512-JfmZ3MVFQkAU+zug8H3s8rZ6h0ahHZL/SpMaSasTCGYR5EEJsCc8SI5UZ6raPN2tjxa5bxS13BRpGSBUens7EA==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.20.0.tgz", + "integrity": "sha512-kmtQClq/w3vtPteDSPvaW9SPZL/xrIgMrxZyAgsFwrJk0vJxqyC5/hwHmrCraDnStnGSADnLpBf4SpZnwnkwWw==", "dev": true, "dependencies": { - "@algolia/requester-common": "4.22.1" + "@algolia/client-common": "5.20.0" + }, + "engines": { + "node": ">= 14.0.0" } }, - "node_modules/@algolia/transporter": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.22.1.tgz", - "integrity": "sha512-kzWgc2c9IdxMa3YqA6TN0NW5VrKYYW/BELIn7vnLyn+U/RFdZ4lxxt9/8yq3DKV5snvoDzzO4ClyejZRdV3lMQ==", + "node_modules/@babel/helper-string-parser": { + "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==", "dev": true, - "dependencies": { - "@algolia/cache-common": "4.22.1", - "@algolia/logger-common": "4.22.1", - "@algolia/requester-common": "4.22.1" + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", - "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", + "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", "dev": true, + "dependencies": { + "@babel/types": "^7.26.7" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -199,32 +271,45 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/types": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", + "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@docsearch/css": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.0.tgz", - "integrity": "sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==", + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz", + "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==", "dev": true }, "node_modules/@docsearch/js": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.6.0.tgz", - "integrity": "sha512-QujhqINEElrkIfKwyyyTfbsfMAYCkylInLYMRqHy7PHc8xTBQCow73tlo/Kc7oIwBrCLf0P3YhjlOeV4v8hevQ==", + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz", + "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==", "dev": true, "dependencies": { - "@docsearch/react": "3.6.0", + "@docsearch/react": "3.8.2", "preact": "^10.0.0" } }, "node_modules/@docsearch/react": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.0.tgz", - "integrity": "sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==", + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz", + "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==", "dev": true, "dependencies": { - "@algolia/autocomplete-core": "1.9.3", - "@algolia/autocomplete-preset-algolia": "1.9.3", - "@docsearch/css": "3.6.0", - "algoliasearch": "^4.19.1" + "@algolia/autocomplete-core": "1.17.7", + "@algolia/autocomplete-preset-algolia": "1.17.7", + "@docsearch/css": "3.8.2", + "algoliasearch": "^5.14.2" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 19.0.0", @@ -248,9 +333,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], @@ -264,9 +349,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], @@ -280,9 +365,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], @@ -296,9 +381,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], @@ -312,9 +397,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], @@ -328,9 +413,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], @@ -344,9 +429,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], @@ -360,9 +445,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], @@ -376,9 +461,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], @@ -392,9 +477,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], @@ -408,9 +493,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], @@ -424,9 +509,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], @@ -440,9 +525,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], @@ -456,9 +541,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], @@ -472,9 +557,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], @@ -488,9 +573,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], @@ -504,9 +589,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], @@ -520,9 +605,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], @@ -536,9 +621,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], @@ -552,9 +637,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], @@ -568,9 +653,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], @@ -584,9 +669,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], @@ -600,9 +685,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], @@ -615,10 +700,25 @@ "node": ">=12" } }, + "node_modules/@iconify-json/simple-icons": { + "version": "1.2.22", + "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.22.tgz", + "integrity": "sha512-0UzThRMwHuOJfgpp+tyV/y2uEBLjFVrxC4igv9iWjSEQEBK4tNjWZNTRCBCYyv/FwWVYyKAsA8tZQ8vUYzvFnw==", + "dev": true, + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true + }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true }, "node_modules/@jsdoc/salty": { @@ -634,9 +734,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", - "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.32.0.tgz", + "integrity": "sha512-G2fUQQANtBPsNwiVFg4zKiPQyjVKZCUdQUol53R8E71J7AsheRMV/Yv/nB8giOcOVqP7//eB5xPqieBYZe9bGg==", "cpu": [ "arm" ], @@ -647,9 +747,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", - "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.32.0.tgz", + "integrity": "sha512-qhFwQ+ljoymC+j5lXRv8DlaJYY/+8vyvYmVx074zrLsu5ZGWYsJNLjPPVJJjhZQpyAKUGPydOq9hRLLNvh1s3A==", "cpu": [ "arm64" ], @@ -660,9 +760,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", - "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.32.0.tgz", + "integrity": "sha512-44n/X3lAlWsEY6vF8CzgCx+LQaoqWGN7TzUfbJDiTIOjJm4+L2Yq+r5a8ytQRGyPqgJDs3Rgyo8eVL7n9iW6AQ==", "cpu": [ "arm64" ], @@ -673,9 +773,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", - "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.32.0.tgz", + "integrity": "sha512-F9ct0+ZX5Np6+ZDztxiGCIvlCaW87HBdHcozUfsHnj1WCUTBUubAoanhHUfnUHZABlElyRikI0mgcw/qdEm2VQ==", "cpu": [ "x64" ], @@ -685,10 +785,49 @@ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.32.0.tgz", + "integrity": "sha512-JpsGxLBB2EFXBsTLHfkZDsXSpSmKD3VxXCgBQtlPcuAqB8TlqtLcbeMhxXQkCDv1avgwNjF8uEIbq5p+Cee0PA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.32.0.tgz", + "integrity": "sha512-wegiyBT6rawdpvnD9lmbOpx5Sph+yVZKHbhnSP9MqUEDX08G4UzMU+D87jrazGE7lRSyTRs6NEYHtzfkJ3FjjQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", - "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.32.0.tgz", + "integrity": "sha512-3pA7xecItbgOs1A5H58dDvOUEboG5UfpTq3WzAdF54acBbUM+olDJAPkgj1GRJ4ZqE12DZ9/hNS2QZk166v92A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.32.0.tgz", + "integrity": "sha512-Y7XUZEVISGyge51QbYyYAEHwpGgmRrAxQXO3siyYo2kmaj72USSG8LtlQQgAtlGfxYiOwu+2BdbPjzEpcOpRmQ==", "cpu": [ "arm" ], @@ -699,9 +838,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", - "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.32.0.tgz", + "integrity": "sha512-r7/OTF5MqeBrZo5omPXcTnjvv1GsrdH8a8RerARvDFiDwFpDVDnJyByYM/nX+mvks8XXsgPUxkwe/ltaX2VH7w==", "cpu": [ "arm64" ], @@ -712,9 +851,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", - "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.32.0.tgz", + "integrity": "sha512-HJbifC9vex9NqnlodV2BHVFNuzKL5OnsV2dvTw6e1dpZKkNjPG6WUq+nhEYV6Hv2Bv++BXkwcyoGlXnPrjAKXw==", "cpu": [ "arm64" ], @@ -724,10 +863,36 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.32.0.tgz", + "integrity": "sha512-VAEzZTD63YglFlWwRj3taofmkV1V3xhebDXffon7msNz4b14xKsz7utO6F8F4cqt8K/ktTl9rm88yryvDpsfOw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.32.0.tgz", + "integrity": "sha512-Sts5DST1jXAc9YH/iik1C9QRsLcCoOScf3dfbY5i4kH9RJpKxiTBXqm7qU5O6zTXBTEZry69bGszr3SMgYmMcQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", - "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.32.0.tgz", + "integrity": "sha512-qhlXeV9AqxIyY9/R1h1hBD6eMvQCO34ZmdYvry/K+/MBs6d1nRFLm6BOiITLVI+nFAAB9kUB6sdJRKyVHXnqZw==", "cpu": [ "riscv64" ], @@ -737,10 +902,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.32.0.tgz", + "integrity": "sha512-8ZGN7ExnV0qjXa155Rsfi6H8M4iBBwNLBM9lcVS+4NcSzOFaNqmt7djlox8pN1lWrRPMRRQ8NeDlozIGx3Omsw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", - "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.32.0.tgz", + "integrity": "sha512-VDzNHtLLI5s7xd/VubyS10mq6TxvZBp+4NRWoW+Hi3tgV05RtVm4qK99+dClwTN1McA6PHwob6DEJ6PlXbY83A==", "cpu": [ "x64" ], @@ -751,9 +929,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", - "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.32.0.tgz", + "integrity": "sha512-qcb9qYDlkxz9DxJo7SDhWxTWV1gFuwznjbTiov289pASxlfGbaOD54mgbs9+z94VwrXtKTu+2RqwlSTbiOqxGg==", "cpu": [ "x64" ], @@ -764,9 +942,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", - "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.32.0.tgz", + "integrity": "sha512-pFDdotFDMXW2AXVbfdUEfidPAk/OtwE/Hd4eYMTNVVaCQ6Yl8et0meDaKNL63L44Haxv4UExpv9ydSf3aSayDg==", "cpu": [ "arm64" ], @@ -777,9 +955,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", - "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.32.0.tgz", + "integrity": "sha512-/TG7WfrCAjeRNDvI4+0AAMoHxea/USWhAzf9PVDFHbcqrQ7hMMKp4jZIy4VEjk72AAfN5k4TiSMRXRKf/0akSw==", "cpu": [ "ia32" ], @@ -790,9 +968,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", - "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.32.0.tgz", + "integrity": "sha512-5hqO5S3PTEO2E5VjCePxv40gIgyS2KvO7E7/vvC/NbIW4SIRamkMr1hqj+5Y67fbBWv/bQLB6KelBQmXlyCjWA==", "cpu": [ "x64" ], @@ -803,46 +981,134 @@ ] }, "node_modules/@shikijs/core": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.2.0.tgz", - "integrity": "sha512-OlFvx+nyr5C8zpcMBnSGir0YPD6K11uYhouqhNmm1qLiis4GA7SsGtu07r9gKS9omks8RtQqHrJL4S+lqWK01A==", - "dev": true - }, - "node_modules/@shikijs/transformers": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.2.0.tgz", - "integrity": "sha512-xKn7DtA65DQV4FOfYsrvqM80xOy2xuXnxWWKsZmHv1VII/IOuDUDsWDu3KnpeLH6wqNJWp1GRoNUsHR1aw/VhQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.1.0.tgz", + "integrity": "sha512-v795KDmvs+4oV0XD05YLzfDMe9ISBgNjtFxP4PAEv5DqyeghO1/TwDqs9ca5/E6fuO95IcAcWqR6cCX9TnqLZA==", "dev": true, "dependencies": { - "shiki": "1.2.0" + "@shikijs/engine-javascript": "2.1.0", + "@shikijs/engine-oniguruma": "2.1.0", + "@shikijs/types": "2.1.0", + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.4" } }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "node_modules/@shikijs/engine-javascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.1.0.tgz", + "integrity": "sha512-cgIUdAliOsoaa0rJz/z+jvhrpRd+fVAoixVFEVxUq5FA+tHgBZAIfVJSgJNVRj2hs/wZ1+4hMe82eKAThVh0nQ==", + "dev": true, + "dependencies": { + "@shikijs/types": "2.1.0", + "@shikijs/vscode-textmate": "^10.0.1", + "oniguruma-to-es": "^2.3.0" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.1.0.tgz", + "integrity": "sha512-Ujik33wEDqgqY2WpjRDUBECGcKPv3eGGkoXPujIXvokLaRmGky8NisSk8lHUGeSFxo/Cz5sgFej9sJmA9yeepg==", + "dev": true, + "dependencies": { + "@shikijs/types": "2.1.0", + "@shikijs/vscode-textmate": "^10.0.1" + } + }, + "node_modules/@shikijs/langs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.1.0.tgz", + "integrity": "sha512-Jn0gS4rPgerMDPj1ydjgFzZr5fAIoMYz4k7ZT3LJxWWBWA6lokK0pumUwVtb+MzXtlpjxOaQejLprmLbvMZyww==", + "dev": true, + "dependencies": { + "@shikijs/types": "2.1.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.1.0.tgz", + "integrity": "sha512-oS2mU6+bz+8TKutsjBxBA7Z3vrQk21RCmADLpnu8cy3tZD6Rw0FKqDyXNtwX52BuIDKHxZNmRlTdG3vtcYv3NQ==", + "dev": true, + "dependencies": { + "@shikijs/types": "2.1.0" + } + }, + "node_modules/@shikijs/transformers": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.1.0.tgz", + "integrity": "sha512-3sfvh6OKUVkT5wZFU1xxiq1qqNIuCwUY3yOb9ZGm19y80UZ/eoroLE2orGNzfivyTxR93GfXXZC/ghPR0/SBow==", + "dev": true, + "dependencies": { + "@shikijs/core": "2.1.0", + "@shikijs/types": "2.1.0" + } + }, + "node_modules/@shikijs/types": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.1.0.tgz", + "integrity": "sha512-OFOdHA6VEVbiQvepJ8yqicC6VmBrKxFFhM2EsHHrZESqLVAXOSeRDiuSYV185lIgp15TVic5vYBYNhTsk1xHLg==", + "dev": true, + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.1.tgz", + "integrity": "sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==", "dev": true }, + "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/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/linkify-it": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", - "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", "dev": true }, "node_modules/@types/markdown-it": { - "version": "13.0.7", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-13.0.7.tgz", - "integrity": "sha512-U/CBi2YUUcTHBt5tjO2r5QV/x0Po6nsYwQU4Y04fBS6vfoImaiZ6f8bi3CjTCxBPQSO1LMyUqkByzi8AidyxfA==", + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", "dev": true, "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "dependencies": { + "@types/unist": "*" } }, "node_modules/@types/mdurl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", - "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "dev": true }, "node_modules/@types/web-bluetooth": { @@ -851,219 +1117,199 @@ "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", "dev": true }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true + }, "node_modules/@vitejs/plugin-vue": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz", - "integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz", + "integrity": "sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==", "dev": true, "engines": { "node": "^18.0.0 || >=20.0.0" }, "peerDependencies": { - "vite": "^5.0.0", + "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "node_modules/@vue/compiler-core": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.21.tgz", - "integrity": "sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", + "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", "dev": true, "dependencies": { - "@babel/parser": "^7.23.9", - "@vue/shared": "3.4.21", + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-dom": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.21.tgz", - "integrity": "sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", + "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", "dev": true, "dependencies": { - "@vue/compiler-core": "3.4.21", - "@vue/shared": "3.4.21" + "@vue/compiler-core": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.21.tgz", - "integrity": "sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", + "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", "dev": true, "dependencies": { - "@babel/parser": "^7.23.9", - "@vue/compiler-core": "3.4.21", - "@vue/compiler-dom": "3.4.21", - "@vue/compiler-ssr": "3.4.21", - "@vue/shared": "3.4.21", + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.13", + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13", "estree-walker": "^2.0.2", - "magic-string": "^0.30.7", - "postcss": "^8.4.35", - "source-map-js": "^1.0.2" + "magic-string": "^0.30.11", + "postcss": "^8.4.48", + "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.21.tgz", - "integrity": "sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", + "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", "dev": true, "dependencies": { - "@vue/compiler-dom": "3.4.21", - "@vue/shared": "3.4.21" + "@vue/compiler-dom": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/devtools-api": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.0.16.tgz", - "integrity": "sha512-fZG2CG8624qphMf4aj59zNHckMx1G3lxODUuyM9USKuLznXCh66TP+tEbPOCcml16hA0GizJ4D8w6F34hrfbcw==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.1.tgz", + "integrity": "sha512-Cexc8GimowoDkJ6eNelOPdYIzsu2mgNyp0scOQ3tiaYSb9iok6LOESSsJvHaI+ib3joRfqRJNLkHFjhNuWA5dg==", "dev": true, "dependencies": { - "@vue/devtools-kit": "^7.0.16" + "@vue/devtools-kit": "^7.7.1" } }, "node_modules/@vue/devtools-kit": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.0.16.tgz", - "integrity": "sha512-IA8SSGiZbNgOi4wLT3mRvd71Q9KE0KvMfGk6haa2GZ6bL2K/xMA8Fvvj3o1maspfUXrGcCXutaqbLqbGx/espQ==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.1.tgz", + "integrity": "sha512-yhZ4NPnK/tmxGtLNQxmll90jIIXdb2jAhPF76anvn5M/UkZCiLJy28bYgPIACKZ7FCosyKoaope89/RsFJll1w==", "dev": true, "dependencies": { - "@vue/devtools-shared": "^7.0.16", + "@vue/devtools-shared": "^7.7.1", + "birpc": "^0.2.19", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", - "speakingurl": "^14.0.1" - }, - "peerDependencies": { - "vue": "^3.0.0" + "speakingurl": "^14.0.1", + "superjson": "^2.2.1" } }, "node_modules/@vue/devtools-shared": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.0.16.tgz", - "integrity": "sha512-Lew4FrGjDjmanaUWSueNE1Rre83k7jQpttc17MaoVw0eARWU5DgZ1F/g9GNUMZXVjbP9rwE+LL3gd9XfXCfkvA==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.1.tgz", + "integrity": "sha512-BtgF7kHq4BHG23Lezc/3W2UhK2ga7a8ohAIAGJMBr4BkxUFzhqntQtCiuL1ijo2ztWnmusymkirgqUrXoQKumA==", "dev": true, "dependencies": { - "rfdc": "^1.3.1" + "rfdc": "^1.4.1" } }, "node_modules/@vue/reactivity": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.21.tgz", - "integrity": "sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz", + "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==", "dev": true, "dependencies": { - "@vue/shared": "3.4.21" + "@vue/shared": "3.5.13" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.21.tgz", - "integrity": "sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz", + "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==", "dev": true, "dependencies": { - "@vue/reactivity": "3.4.21", - "@vue/shared": "3.4.21" + "@vue/reactivity": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.21.tgz", - "integrity": "sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz", + "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==", "dev": true, "dependencies": { - "@vue/runtime-core": "3.4.21", - "@vue/shared": "3.4.21", + "@vue/reactivity": "3.5.13", + "@vue/runtime-core": "3.5.13", + "@vue/shared": "3.5.13", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.21.tgz", - "integrity": "sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz", + "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==", "dev": true, "dependencies": { - "@vue/compiler-ssr": "3.4.21", - "@vue/shared": "3.4.21" + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13" }, "peerDependencies": { - "vue": "3.4.21" + "vue": "3.5.13" } }, "node_modules/@vue/shared": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz", - "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", + "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==", "dev": true }, "node_modules/@vueuse/core": { - "version": "10.9.0", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.9.0.tgz", - "integrity": "sha512-/1vjTol8SXnx6xewDEKfS0Ra//ncg4Hb0DaZiwKf7drgfMsKFExQ+FnnENcN6efPen+1kIzhLQoGSy0eDUVOMg==", + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.5.0.tgz", + "integrity": "sha512-GVyH1iYqNANwcahAx8JBm6awaNgvR/SwZ1fjr10b8l1HIgDp82ngNbfzJUgOgWEoxjL+URAggnlilAEXwCOZtg==", "dev": true, "dependencies": { "@types/web-bluetooth": "^0.0.20", - "@vueuse/metadata": "10.9.0", - "@vueuse/shared": "10.9.0", - "vue-demi": ">=0.14.7" + "@vueuse/metadata": "12.5.0", + "@vueuse/shared": "12.5.0", + "vue": "^3.5.13" }, "funding": { "url": "https://github.com/sponsors/antfu" } }, - "node_modules/@vueuse/core/node_modules/vue-demi": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", - "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", - "dev": true, - "hasInstallScript": true, - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, "node_modules/@vueuse/integrations": { - "version": "10.9.0", - "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-10.9.0.tgz", - "integrity": "sha512-acK+A01AYdWSvL4BZmCoJAcyHJ6EqhmkQEXbQLwev1MY7NBnS+hcEMx/BzVoR9zKI+UqEPMD9u6PsyAuiTRT4Q==", + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.5.0.tgz", + "integrity": "sha512-HYLt8M6mjUfcoUOzyBcX2RjpfapIwHPBmQJtTmXOQW845Y/Osu9VuTJ5kPvnmWJ6IUa05WpblfOwZ+P0G4iZsQ==", "dev": true, "dependencies": { - "@vueuse/core": "10.9.0", - "@vueuse/shared": "10.9.0", - "vue-demi": ">=0.14.7" + "@vueuse/core": "12.5.0", + "@vueuse/shared": "12.5.0", + "vue": "^3.5.13" }, "funding": { "url": "https://github.com/sponsors/antfu" }, "peerDependencies": { - "async-validator": "*", - "axios": "*", - "change-case": "*", - "drauu": "*", - "focus-trap": "*", - "fuse.js": "*", - "idb-keyval": "*", - "jwt-decode": "*", - "nprogress": "*", - "qrcode": "*", - "sortablejs": "*", - "universal-cookie": "*" + "async-validator": "^4", + "axios": "^1", + "change-case": "^5", + "drauu": "^0.4", + "focus-trap": "^7", + "fuse.js": "^7", + "idb-keyval": "^6", + "jwt-decode": "^4", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^7" }, "peerDependenciesMeta": { "async-validator": { @@ -1104,99 +1350,49 @@ } } }, - "node_modules/@vueuse/integrations/node_modules/vue-demi": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", - "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", - "dev": true, - "hasInstallScript": true, - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, "node_modules/@vueuse/metadata": { - "version": "10.9.0", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.9.0.tgz", - "integrity": "sha512-iddNbg3yZM0X7qFY2sAotomgdHK7YJ6sKUvQqbvwnf7TmaVPxS4EJydcNsVejNdS8iWCtDk+fYXr7E32nyTnGA==", + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.5.0.tgz", + "integrity": "sha512-Ui7Lo2a7AxrMAXRF+fAp9QsXuwTeeZ8fIB9wsLHqzq9MQk+2gMYE2IGJW48VMJ8ecvCB3z3GsGLKLbSasQ5Qlg==", "dev": true, "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@vueuse/shared": { - "version": "10.9.0", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.9.0.tgz", - "integrity": "sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw==", + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.5.0.tgz", + "integrity": "sha512-vMpcL1lStUU6O+kdj6YdHDixh0odjPAUM15uJ9f7MY781jcYkIwFA4iv2EfoIPO6vBmvutI1HxxAwmf0cx5ISQ==", "dev": true, "dependencies": { - "vue-demi": ">=0.14.7" + "vue": "^3.5.13" }, "funding": { "url": "https://github.com/sponsors/antfu" } }, - "node_modules/@vueuse/shared/node_modules/vue-demi": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", - "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", - "dev": true, - "hasInstallScript": true, - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, "node_modules/algoliasearch": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.22.1.tgz", - "integrity": "sha512-jwydKFQJKIx9kIZ8Jm44SdpigFwRGPESaxZBaHSV0XWN2yBJAOT4mT7ppvlrpA4UGzz92pqFnVKr/kaZXrcreg==", + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.20.0.tgz", + "integrity": "sha512-groO71Fvi5SWpxjI9Ia+chy0QBwT61mg6yxJV27f5YFf+Mw+STT75K6SHySpP8Co5LsCrtsbCH5dJZSRtkSKaQ==", "dev": true, "dependencies": { - "@algolia/cache-browser-local-storage": "4.22.1", - "@algolia/cache-common": "4.22.1", - "@algolia/cache-in-memory": "4.22.1", - "@algolia/client-account": "4.22.1", - "@algolia/client-analytics": "4.22.1", - "@algolia/client-common": "4.22.1", - "@algolia/client-personalization": "4.22.1", - "@algolia/client-search": "4.22.1", - "@algolia/logger-common": "4.22.1", - "@algolia/logger-console": "4.22.1", - "@algolia/requester-browser-xhr": "4.22.1", - "@algolia/requester-common": "4.22.1", - "@algolia/requester-node-http": "4.22.1", - "@algolia/transporter": "4.22.1" + "@algolia/client-abtesting": "5.20.0", + "@algolia/client-analytics": "5.20.0", + "@algolia/client-common": "5.20.0", + "@algolia/client-insights": "5.20.0", + "@algolia/client-personalization": "5.20.0", + "@algolia/client-query-suggestions": "5.20.0", + "@algolia/client-search": "5.20.0", + "@algolia/ingestion": "1.20.0", + "@algolia/monitoring": "1.20.0", + "@algolia/recommend": "5.20.0", + "@algolia/requester-browser-xhr": "5.20.0", + "@algolia/requester-fetch": "5.20.0", + "@algolia/requester-node-http": "5.20.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/argparse": { @@ -1205,6 +1401,15 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/birpc": { + "version": "0.2.19", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.19.tgz", + "integrity": "sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -1229,12 +1434,95 @@ "node": ">= 10" } }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "dev": true, + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "dev": true }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/emoji-regex-xs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", + "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", + "dev": true + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -1248,9 +1536,9 @@ } }, "node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, "bin": { @@ -1260,29 +1548,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/escape-string-regexp": { @@ -1301,9 +1589,9 @@ "dev": true }, "node_modules/focus-trap": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz", - "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==", + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.4.tgz", + "integrity": "sha512-xx560wGBk7seZ6y933idtjJQc1l+ck+pI3sKvhKozdBV1dRZoKhkW5xoCaFv9tQiX5RH1xfSxjuNu6g+lmN/gw==", "dev": true, "dependencies": { "tabbable": "^6.2.0" @@ -1329,12 +1617,70 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, + "node_modules/hast-util-to-html": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.4.tgz", + "integrity": "sha512-wxQzXtdbhiwGAUKrnQJXlOPmHnEehzphwkK7aluUPQ+lEc1xefC8pblMgpp2w5ldBTEfveRIrADcrhGIWrlTDA==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hookable": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", "dev": true }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "dev": true, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/js2xmlparser": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", @@ -1345,21 +1691,22 @@ } }, "node_modules/jsdoc": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", - "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.4.tgz", + "integrity": "sha512-zeFezwyXeG4syyYHbvh1A967IAqq/67yXtXvuL5wnqCkFZe8I0vKfm+EO+YEvLguo6w9CDUbrAXVtJSHh2E8rw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@babel/parser": "^7.20.15", "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^12.2.3", + "@types/markdown-it": "^14.1.1", "bluebird": "^3.7.2", "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.2", "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", + "markdown-it": "^14.1.0", + "markdown-it-anchor": "^8.6.7", "marked": "^4.0.10", "mkdirp": "^1.0.4", "requizzle": "^0.2.3", @@ -1373,16 +1720,6 @@ "node": ">=12.0.0" } }, - "node_modules/jsdoc/node_modules/@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", - "dev": true, - "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, "node_modules/klaw": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", @@ -1393,12 +1730,13 @@ } }, "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", "dev": true, + "license": "MIT", "dependencies": { - "uc.micro": "^1.0.1" + "uc.micro": "^2.0.0" } }, "node_modules/lodash": { @@ -1408,15 +1746,12 @@ "dev": true }, "node_modules/magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "dev": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/mark.js": { @@ -1426,19 +1761,21 @@ "dev": true }, "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, "bin": { - "markdown-it": "bin/markdown-it.js" + "markdown-it": "bin/markdown-it.mjs" } }, "node_modules/markdown-it-anchor": { @@ -1451,15 +1788,6 @@ "markdown-it": "*" } }, - "node_modules/markdown-it/node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/marked": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", @@ -1472,16 +1800,127 @@ "node": ">= 12" } }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] }, "node_modules/minisearch": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-6.3.0.tgz", - "integrity": "sha512-ihFnidEeU8iXzcVHy74dhkxh/dn8Dc08ERl0xwoMMGqp4+LvRSCgicb+zGqWthVokQKvCSxITlh3P08OzdTYCQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.1.1.tgz", + "integrity": "sha512-b3YZEYCEH4EdCAtYP7OlDyx7FdPwNzuNwLQ34SfJpM9dlbBZzeXndGavTrC+VCiRWomL21SWfMc6SCKO/U2ZNw==", "dev": true }, "node_modules/mitt": { @@ -1503,9 +1942,9 @@ } }, "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": [ { @@ -1520,6 +1959,17 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/oniguruma-to-es": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-2.3.0.tgz", + "integrity": "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g==", + "dev": true, + "dependencies": { + "emoji-regex-xs": "^1.0.0", + "regex": "^5.1.1", + "regex-recursion": "^5.1.1" + } + }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", @@ -1527,15 +1977,15 @@ "dev": true }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "dev": true, "funding": [ { @@ -1552,24 +2002,69 @@ } ], "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "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" } }, "node_modules/preact": { - "version": "10.20.0", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.20.0.tgz", - "integrity": "sha512-wU7iZw2BjsaKDal3pDRDy/HpPB6cuFOnVUCcw9aIPKG98+ZrXx3F+szkos8BVME5bquyKDKvRlOJFG8kMkcAbg==", + "version": "10.25.4", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.25.4.tgz", + "integrity": "sha512-jLdZDb+Q+odkHJ+MpW/9U5cODzqnB+fy2EiHSZES7ldV5LK7yjlVzTp7R8Xy6W6y75kfK8iWYtFVH7lvjwrCMA==", "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" } }, + "node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/regex": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/regex/-/regex-5.1.1.tgz", + "integrity": "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==", + "dev": true, + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-5.1.1.tgz", + "integrity": "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==", + "dev": true, + "dependencies": { + "regex": "^5.1.1", + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "dev": true + }, "node_modules/requizzle": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", @@ -1580,18 +2075,18 @@ } }, "node_modules/rfdc": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", - "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true }, "node_modules/rollup": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", - "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "version": "4.32.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.32.0.tgz", + "integrity": "sha512-JmrhfQR31Q4AuNBjjAX4s+a/Pu/Q8Q9iwjWBsjRH1q52SPFE2NqRMK6fUZKKnvKO6id+h7JIRf0oYsph53eATg==", "dev": true, "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -1601,47 +2096,70 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.0", - "@rollup/rollup-android-arm64": "4.13.0", - "@rollup/rollup-darwin-arm64": "4.13.0", - "@rollup/rollup-darwin-x64": "4.13.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", - "@rollup/rollup-linux-arm64-gnu": "4.13.0", - "@rollup/rollup-linux-arm64-musl": "4.13.0", - "@rollup/rollup-linux-riscv64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-musl": "4.13.0", - "@rollup/rollup-win32-arm64-msvc": "4.13.0", - "@rollup/rollup-win32-ia32-msvc": "4.13.0", - "@rollup/rollup-win32-x64-msvc": "4.13.0", + "@rollup/rollup-android-arm-eabi": "4.32.0", + "@rollup/rollup-android-arm64": "4.32.0", + "@rollup/rollup-darwin-arm64": "4.32.0", + "@rollup/rollup-darwin-x64": "4.32.0", + "@rollup/rollup-freebsd-arm64": "4.32.0", + "@rollup/rollup-freebsd-x64": "4.32.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.32.0", + "@rollup/rollup-linux-arm-musleabihf": "4.32.0", + "@rollup/rollup-linux-arm64-gnu": "4.32.0", + "@rollup/rollup-linux-arm64-musl": "4.32.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.32.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.32.0", + "@rollup/rollup-linux-riscv64-gnu": "4.32.0", + "@rollup/rollup-linux-s390x-gnu": "4.32.0", + "@rollup/rollup-linux-x64-gnu": "4.32.0", + "@rollup/rollup-linux-x64-musl": "4.32.0", + "@rollup/rollup-win32-arm64-msvc": "4.32.0", + "@rollup/rollup-win32-ia32-msvc": "4.32.0", + "@rollup/rollup-win32-x64-msvc": "4.32.0", "fsevents": "~2.3.2" } }, "node_modules/search-insights": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.13.0.tgz", - "integrity": "sha512-Orrsjf9trHHxFRuo9/rzm0KIWmgzE8RMlZMzuhZOJ01Rnz3D0YBAe+V6473t6/H6c7irs6Lt48brULAiRWb3Vw==", + "version": "2.17.3", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", + "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", "dev": true, "peer": true }, "node_modules/shiki": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.2.0.tgz", - "integrity": "sha512-xLhiTMOIUXCv5DqJ4I70GgQCtdlzsTqFLZWcMHHG3TAieBUbvEGthdrlPDlX4mL/Wszx9C6rEcxU6kMlg4YlxA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.1.0.tgz", + "integrity": "sha512-yvKPdNGLXZv7WC4bl7JBbU3CEcUxnBanvMez8MG3gZXKpClGL4bHqFyLhTx+2zUvbjClUANs/S22HXb7aeOgmA==", "dev": true, "dependencies": { - "@shikijs/core": "1.2.0" + "@shikijs/core": "2.1.0", + "@shikijs/engine-javascript": "2.1.0", + "@shikijs/engine-oniguruma": "2.1.0", + "@shikijs/langs": "2.1.0", + "@shikijs/themes": "2.1.0", + "@shikijs/types": "2.1.0", + "@shikijs/vscode-textmate": "^10.0.1", + "@types/hast": "^3.0.4" } }, "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, "engines": { "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/speakingurl": { "version": "14.0.1", "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", @@ -1651,6 +2169,20 @@ "node": ">=0.10.0" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -1663,17 +2195,40 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/superjson": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", + "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", + "dev": true, + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/tabbable": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", "dev": true }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" }, "node_modules/underscore": { "version": "1.13.6", @@ -1681,15 +2236,111 @@ "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true }, - "node_modules/vite": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.3.tgz", - "integrity": "sha512-+i1oagbvkVIhEy9TnEV+fgXsng13nZM90JQbrcPrf6DvW2mXARlz+DK7DLiDP+qeKoD1FCVx/1SpFL1CLq9Mhw==", + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", "dev": true, "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.36", - "rollup": "^4.13.0" + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "5.4.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", + "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", + "dev": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -1708,6 +2359,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -1725,6 +2377,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -1737,26 +2392,29 @@ } }, "node_modules/vitepress": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.0.1.tgz", - "integrity": "sha512-eNr5pOBppYUUjEhv8S0S2t9Tv95LQ6mMeHj6ivaGwfHxpov70Vduuwl/QQMDRznKDSaP0WKV7a82Pb4JVOaqEw==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.3.tgz", + "integrity": "sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw==", "dev": true, "dependencies": { - "@docsearch/css": "^3.6.0", - "@docsearch/js": "^3.6.0", - "@shikijs/core": "^1.2.0", - "@shikijs/transformers": "^1.2.0", - "@types/markdown-it": "^13.0.7", - "@vitejs/plugin-vue": "^5.0.4", - "@vue/devtools-api": "^7.0.16", - "@vueuse/core": "^10.9.0", - "@vueuse/integrations": "^10.9.0", - "focus-trap": "^7.5.4", + "@docsearch/css": "3.8.2", + "@docsearch/js": "3.8.2", + "@iconify-json/simple-icons": "^1.2.21", + "@shikijs/core": "^2.1.0", + "@shikijs/transformers": "^2.1.0", + "@shikijs/types": "^2.1.0", + "@types/markdown-it": "^14.1.2", + "@vitejs/plugin-vue": "^5.2.1", + "@vue/devtools-api": "^7.7.0", + "@vue/shared": "^3.5.13", + "@vueuse/core": "^12.4.0", + "@vueuse/integrations": "^12.4.0", + "focus-trap": "^7.6.4", "mark.js": "8.11.1", - "minisearch": "^6.3.0", - "shiki": "^1.2.0", - "vite": "^5.2.2", - "vue": "^3.4.21" + "minisearch": "^7.1.1", + "shiki": "^2.1.0", + "vite": "^5.4.14", + "vue": "^3.5.13" }, "bin": { "vitepress": "bin/vitepress.js" @@ -1775,16 +2433,16 @@ } }, "node_modules/vue": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz", - "integrity": "sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", + "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", "dev": true, "dependencies": { - "@vue/compiler-dom": "3.4.21", - "@vue/compiler-sfc": "3.4.21", - "@vue/runtime-dom": "3.4.21", - "@vue/server-renderer": "3.4.21", - "@vue/shared": "3.4.21" + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-sfc": "3.5.13", + "@vue/runtime-dom": "3.5.13", + "@vue/server-renderer": "3.5.13", + "@vue/shared": "3.5.13" }, "peerDependencies": { "typescript": "*" @@ -1796,15 +2454,26 @@ } }, "node_modules/vue-collapsed": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/vue-collapsed/-/vue-collapsed-1.3.0.tgz", - "integrity": "sha512-d2qwv0fj4aaoekI+6x1RgxpoK0s5K8HLj70E+dWxIKkgzADcZ9kUrnXRazgIz9ETYkKOi6j2CWv/8+rS8Kr3LQ==" + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/vue-collapsed/-/vue-collapsed-1.3.4.tgz", + "integrity": "sha512-W92b+QT3n5iTrfrH6kyvx3TsriYPfy/Ymsb6DaanjeMkJYMdVl1S4wzKRYVDsxWQPDxiC+5m+UwPwT/8YAYodA==", + "license": "MIT" }, "node_modules/xmlcreate": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", "dev": true + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/v3-docs/docs/package.json b/v3-docs/docs/package.json index 748ffdba66..28c1e57a79 100644 --- a/v3-docs/docs/package.json +++ b/v3-docs/docs/package.json @@ -9,10 +9,10 @@ }, "devDependencies": { "canonical-json": "^0.0.4", - "jsdoc": "^4.0.2", - "vitepress": "1.0.1" + "jsdoc": "^4.0.4", + "vitepress": "1.6.3" }, "dependencies": { - "vue-collapsed": "^1.3.0" + "vue-collapsed": "^1.3.4" } } diff --git a/v3-docs/docs/packages/community-packages.md b/v3-docs/docs/packages/community-packages.md deleted file mode 100644 index 626a7e871e..0000000000 --- a/v3-docs/docs/packages/community-packages.md +++ /dev/null @@ -1,17 +0,0 @@ - - -[//]: # (Do not edit this file by hand.) - -[//]: # (This is a generated file.) - -[//]: # (If you want to change something in this file) - -[//]: # (go to meteor/docs/generators/packages-listing) - -# Community Packages - -The Meteor community is making great efforts to migrate popular packages to Meteor 3.0. - -Many packages have been consolidated into the [Meteor Community organization](https://github.com/Meteor-Community-Packages). Others are maintained by individual developers or companies. - -A [spreadsheet maintained](https://docs.google.com/spreadsheets/u/0/d/1JbUZmJab3owZ9LV71Ubto32YX_QWQljRypJTOQupxL8/htmlview) by [@harryadel](https://github.com/harryadel) tracks the status of your favorite packages and offers opportunities to help. diff --git a/v3-docs/docs/packages/logging.md b/v3-docs/docs/packages/logging.md index dd52d78aee..9aee4da917 100644 --- a/v3-docs/docs/packages/logging.md +++ b/v3-docs/docs/packages/logging.md @@ -11,29 +11,47 @@ meteor add logging ``` You can then import the utility anywhere in you code like this: + ```javascript -import { Log } from 'meteor/logging' +import { Log } from "meteor/logging"; ``` You can then call the logging functions in one of the following ways: + ```javascript -Log('starting up') // or Log.info('starting up') -Log.error('error message') -Log.warn('warning') -Log.debug('this will show only in development') +Log("starting up"); // or Log.info('starting up') +Log.error("error message"); +Log.warn("warning"); +Log.debug("this will show only in development"); ``` Besides passing in strings, you can also pass in objects. This has few exceptions and special functions associated. First in the root of the object the following keys are not allowed: + ```javascript -'time', 'timeInexact', 'level', 'file', 'line', 'program', 'originApp', 'satellite', 'stderr' + "time", + "timeInexact", + "level", + "file", + "line", + "program", + "originApp", + "satellite", + "stderr"; ``` On the other hand there is `message` and `app`, which are also reserved, but they will display in more prominent manner: + ```javascript -Log.info({message: 'warning', app: 'DESKTOP', error: { property1: 'foo', property2: 'bar', property3: { foo: 'bar' }} }) +Log.info({ + message: "warning", + app: "DESKTOP", + error: { property1: "foo", property2: "bar", property3: { foo: "bar" } }, +}); ``` + will turn into: + ```shell E20200519-17:57:41.655(9) [DESKTOP] (main.js:36) warning {"error":{"property1":"foo","property2":"bar","property3":{"foo":"bar"}}} ``` @@ -41,4 +59,24 @@ E20200519-17:57:41.655(9) [DESKTOP] (main.js:36) warning {"error":{"property1":" The display of each log is color coded. Info is `blue`, warn is `magenta`, debug is `green` and error is in `red`. ### Log.debug + The `Log.debug()` logging is different from the other calls as these messages will not be displayed in production. + +### Options + +You can set the following options: + +```javascript +// Either 'json' or 'colored-text'. Defaults to 'json'. +// +// When this is set to 'json', print JSON documents to the console. +// +// When this is set to 'colored-text', call 'Log.format' before printing. +// This should be used for logging to the console in a human-readable format. +Log.outputFormat = "json"; + +// Boolean. Defaults to true. +// for cloud environments is interesting to leave it false as most of them have the timestamp in the console. +// Only works in server with colored-text output format. +Log.showTime = true; +``` diff --git a/v3-docs/docs/packages/modern-browsers.md b/v3-docs/docs/packages/modern-browsers.md new file mode 100644 index 0000000000..5556a6326f --- /dev/null +++ b/v3-docs/docs/packages/modern-browsers.md @@ -0,0 +1,33 @@ +# Modern-browsers + + +API for defining the boundary between modern and legacy JavaScript clients. + +You can use this package to define the minimum browser versions for which +a browser engine will be considered modern. All browsers that do not meet +the threshold will receive the legacy bundle. This way you can easily keep +on using modern features that you need. + +You can read more about this in [Meteor 1.7 announcement blog](https://blog.meteor.com/meteor-1-7-and-the-evergreen-dream-a8c1270b0901). + + + + + + + + + +## Configure Unknown Browsers to default to Modern + +Browsers not explicitly listed in `setMinimumBrowserVersions` are considered "legacy" by default. + +To change this and treat unknown browsers as "modern," update the relevant option in your settings file: + +``` js +Meteor.settings.packages = { + "modern-browsers": { + "unknownBrowsersAssumedModern": true + } +}; +``` diff --git a/v3-docs/docs/packages/react-meteor-data.md b/v3-docs/docs/packages/react-meteor-data.md new file mode 100644 index 0000000000..50717c82b1 --- /dev/null +++ b/v3-docs/docs/packages/react-meteor-data.md @@ -0,0 +1,491 @@ +# react-meteor-data + +This package provides an integration between React and [`Tracker`](https://atmospherejs.com/meteor/tracker), Meteor's reactive data system. + +## Table of Contents + +[[toc]] + +## Install + +::: tip + +This package is included with `meteor create` on react options. No need to install it manually. + +::: + +To install the package, use `meteor add`: + +```bash +meteor add react-meteor-data +``` + +You'll also need to install `react` if you have not already: + +```bash +meteor npm install react +``` + +### Changelog + +[check recent changes here](https://github.com/meteor/react-packages/blob/master/packages/react-meteor-data/CHANGELOG.md) + +## Usage + +This package provides two ways to use Tracker reactive data in your React components: + +- a hook: `useTracker` (v2 only, requires React `^16.8`) +- a higher-order component (HOC): `withTracker` (v1 and v2). + +The `useTracker` hook, introduced in version 2.0.0, embraces the [benefits of hooks](https://reactjs.org/docs/hooks-faq.html). Like all React hooks, it can only be used in function components, not in class components. + +The `withTracker` HOC can be used with all components, function or class based. + +It is not necessary to rewrite existing applications to use the `useTracker` hook instead of the existing `withTracker` HOC. + +### `useTracker(reactiveFn)` + +You can use the `useTracker` hook to get the value of a Tracker reactive function in your React "function components." The reactive function will get re-run whenever its reactive inputs change, and the component will re-render with the new value. + +`useTracker` manages its own state, and causes re-renders when necessary. There is no need to call React state setters from inside your `reactiveFn`. Instead, return the values from your `reactiveFn` and assign those to variables directly. When the `reactiveFn` updates, the variables will be updated, and the React component will re-render. + +Arguments: + +- `reactiveFn`: A Tracker reactive function (receives the current computation). + +The basic way to use `useTracker` is to simply pass it a reactive function, with no further fuss. This is the preferred configuration in many cases. + +#### `useTracker(reactiveFn, deps)` + +You can pass an optional deps array as a second value. When provided, the computation will be retained, and reactive updates after the first run will run asynchronously from the react render execution frame. This array typically includes all variables from the outer scope "captured" in the closure passed as the 1st argument. For example, the value of a prop used in a subscription or a minimongo query; see example below. + +This should be considered a low level optimization step for cases where your computations are somewhat long running - like a complex minimongo query. In many cases it's safe and even preferred to omit deps and allow the computation to run synchronously with render. + +Arguments: + +- `reactiveFn` +- `deps`: An optional array of "dependencies" of the reactive function. This is very similar to how the `deps` argument for [React's built-in `useEffect`, `useCallback` or `useMemo` hooks](https://reactjs.org/docs/hooks-reference.html) work. + +```jsx +import { useTracker } from 'meteor/react-meteor-data'; + +// React function component. +function Foo({ listId }) { + // This computation uses no value from the outer scope, + // and thus does not needs to pass a 'deps' argument. + // However, we can optimize the use of the computation + // by providing an empty deps array. With it, the + // computation will be retained instead of torn down and + // rebuilt on every render. useTracker will produce the + // same results either way. + const currentUser = useTracker(() => Meteor.user(), []); + + // The following two computations both depend on the + // listId prop. When deps are specified, the computation + // will be retained. + const listLoading = useTracker(() => { + // Note that this subscription will get cleaned up + // when your component is unmounted or deps change. + const handle = Meteor.subscribe('todoList', listId); + return !handle.ready(); + }, [listId]); + const tasks = useTracker(() => Tasks.find({ listId }).fetch(), [listId]); + + return ( +

Hello {currentUser.username}

+ {listLoading ? ( +
Loading
+ ) : ( +
+ Here is the Todo list {listId}: +
    + {tasks.map(task => ( +
  • {task.label}
  • + ))} +
+
+ )} + ); +} +``` + +**Note:** the [eslint-plugin-react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks) package provides ESLint hints to help detect missing values in the `deps` argument of React built-in hooks. It can be configured to also validate the `deps` argument of the `useTracker` hook or some other hooks, with the following `eslintrc` config: + +```json +"react-hooks/exhaustive-deps": ["warn", { "additionalHooks": "useTracker|useSomeOtherHook|..." }] +``` + +#### `useTracker(reactiveFn, deps, skipUpdate)` or `useTracker(reactiveFn, skipUpdate)` + +You may optionally pass a function as a second or third argument. The `skipUpdate` function can evaluate the return value of `reactiveFn` for changes, and control re-renders in sensitive cases. _Note:_ This is not meant to be used with a deep compare (even fast-deep-equals), as in many cases that may actually lead to worse performance than allowing React to do it's thing. But as an example, you could use this to compare an `updatedAt` field between updates, or a subset of specific fields, if you aren't using the entire document in a subscription. As always with any optimization, measure first, then optimize second. Make sure you really need this before implementing it. + +Arguments: + +- `reactiveFn` +- `deps?` - optional - you may omit this, or pass a "falsy" value. +- `skipUpdate` - A function which receives two arguments: `(prev, next) => (prev === next)`. `prev` and `next` will match the type or data shape as that returned by `reactiveFn`. Note: A return value of `true` means the update will be "skipped". `false` means re-render will occur as normal. So the function should be looking for equivalence. + +```jsx +import { useTracker } from 'meteor/react-meteor-data'; + +// React function component. +function Foo({ listId }) { + const tasks = useTracker( + () => Tasks.find({ listId }).fetch(), [listId], + (prev, next) => { + // prev and next will match the type returned by the reactiveFn + return prev.every((doc, i) => ( + doc._id === next[i] && doc.updatedAt === next[i] + )) && prev.length === next.length; + } + ); + + return ( +

Hello {currentUser.username}

+
+ Here is the Todo list {listId}: +
    + {tasks.map(task => ( +
  • {task.label}
  • + ))} +
+
+ ); +} +``` + +### `withTracker(reactiveFn)` + +You can use the `withTracker` HOC to wrap your components and pass them additional props values from a Tracker reactive function. The reactive function will get re-run whenever its reactive inputs change, and the wrapped component will re-render with the new values for the additional props. + +Arguments: + +- `reactiveFn`: a Tracker reactive function, getting the props as a parameter, and returning an object of additional props to pass to the wrapped component. + +```jsx +import { withTracker } from 'meteor/react-meteor-data'; + +// React component (function or class). +function Foo({ listId, currentUser, listLoading, tasks }) { + return ( +

Hello {currentUser.username}

+ {listLoading ? +
Loading
: +
+ Here is the Todo list {listId}: +
    {tasks.map(task =>
  • {task.label}
  • )}
+ { + // Do all your reactive data access in this function. + // Note that this subscription will get cleaned up when your component is unmounted + const handle = Meteor.subscribe('todoList', listId); + + return { + currentUser: Meteor.user(), + listLoading: !handle.ready(), + tasks: Tasks.find({ listId }).fetch(), + }; +})(Foo); +``` + +The returned component will, when rendered, render `Foo` (the "lower-order" component) with its provided props in addition to the result of the reactive function. So `Foo` will receive `{ listId }` (provided by its parent) as well as `{ currentUser, listLoading, tasks }` (added by the `withTracker` HOC). + +For more information, see the [React article](http://guide.meteor.com/react.html) in the Meteor Guide. + +### `withTracker({ reactiveFn, pure, skipUpdate })` + +The `withTracker` HOC can receive a config object instead of a simple reactive function. + +- `getMeteorData` - The `reactiveFn`. +- `pure` - `true` by default. Causes the resulting Container to be wrapped with React's `memo()`. +- `skipUpdate` - A function which receives two arguments: `(prev, next) => (prev === next)`. `prev` and `next` will match the type or data shape as that returned by `reactiveFn`. Note: A return value of `true` means the update will be "skipped". `false` means re-render will occur as normal. So the function should be looking for equivalence. + +```jsx +import { withTracker } from 'meteor/react-meteor-data'; + +// React component (function or class). +function Foo({ listId, currentUser, listLoading, tasks }) { + return ( +

Hello {currentUser.username}

+ {listLoading ? +
Loading
: +
+ Here is the Todo list {listId}: +
    {tasks.map(task =>
  • {task.label}
  • )}
+ ( + doc._id === next[i] && doc.updatedAt === next[i] + )) + && prev.tasks.length === next.tasks.length + ); + } +})(Foo); +``` + +### `useSubscribe(subName, ...args)` + +`useSubscribe` is a convenient short hand for setting up a subscription. It is particularly useful when working with `useFind`, which should NOT be used for setting up subscriptions. At its core, it is a very simple wrapper around `useTracker` (with no deps) to create the subscription in a safe way, and allows you to avoid some of the ceremony around defining a factory and defining deps. Just pass the name of your subscription, and your arguments. + +`useSubscribe` returns an `isLoading` function. You can call `isLoading()` to react to changes in the subscription's loading state. The `isLoading` function will both return the loading state of the subscription, and set up a reactivity for the loading state change. If you don't call this function, no re-render will occur when the loading state changes. + +```jsx +// Note: isLoading is a function! +const isLoading = useSubscribe("posts", groupId); +const posts = useFind(() => Posts.find({ groupId }), [groupId]); + +if (isLoading()) { + return ; +} else { + return ( +
    + {posts.map((post) => ( +
  • {post.title}
  • + ))} +
+ ); +} +``` + +If you want to conditionally subscribe, you can set the `name` field (the first argument) to a falsy value to bypass the subscription. + +```jsx +const needsData = false; +const isLoading = useSubscribe(needsData ? "my-pub" : null); + +// When a subscription is not used, isLoading() will always return false +``` + +### `useFind(cursorFactory, deps)` + +The `useFind` hook can substantially speed up the rendering (and rerendering) of lists coming from mongo queries (subscriptions). It does this by controlling document object references. By providing a highly tailored cursor management within the hook, using the `Cursor.observe` API, `useFind` carefully updates only the object references changed during a DDP update. This approach allows a tighter use of core React tools and philosophies to turbo charge your list renders. It is a very different approach from the more general purpose `useTracker`, and it requires a bit more set up. A notable difference is that you should NOT call `.fetch()`. `useFind` requires its factory to return a `Mongo.Cursor` object. You may also return `null`, if you want to conditionally set up the Cursor. + +Here is an example in code: + +```jsx +import React, { memo } from "react"; +import { useFind } from "meteor/react-meteor-data"; +import TestDocs from "/imports/api/collections/TestDocs"; + +// Memoize the list item +const ListItem = memo(({ doc }) => { + return ( +
  • + {doc.id},{doc.updated} +
  • + ); +}); + +const Test = () => { + const docs = useFind(() => TestDocs.find(), []); + return ( +
      + {docs.map((doc) => ( + + ))} +
    + ); +}; + +// Later on, update a single document - notice only that single component is updated in the DOM +TestDocs.update({ id: 2 }, { $inc: { someProp: 1 } }); +``` + +If you want to conditionally call the find method based on some props configuration or anything else, return `null` from the factory. + +```jsx +const docs = useFind(() => { + if (props.skip) { + return null; + } + return TestDocs.find(); +}, []); +``` + +### Concurrent Mode, Suspense and Error Boundaries + +There are some additional considerations to keep in mind when using Concurrent Mode, Suspense and Error Boundaries, as +each of these can cause React to cancel and discard (toss) a render, including the result of the first run of your +reactive function. One of the things React developers often stress is that we should not create "side-effects" directly +in the render method or in functional components. There are a number of good reasons for this, including allowing the +React runtime to cancel renders. Limiting the use of side-effects allows features such as concurrent mode, suspense and +error boundaries to work deterministically, without leaking memory or creating rogue processes. Care should be taken to +avoid side effects in your reactive function for these reasons. (Note: this caution does not apply to Meteor specific +side-effects like subscriptions, since those will be automatically cleaned up when `useTracker`'s computation is +disposed.) + +Ideally, side-effects such as creating a Meteor computation would be done in `useEffect`. However, this is problematic +for Meteor, which mixes an initial data query with setting up the computation to watch those data sources all in one +initial run. If we wait to do that in `useEffect`, we'll end up rendering a minimum of 2 times (and using hacks for the +first one) for every component which uses `useTracker` or `withTracker`, or not running at all in the initial render and +still requiring a minimum of 2 renders, and complicating the API. + +To work around this and keep things running fast, we are creating the computation in the render method directly, and +doing a number of checks later in `useEffect` to make sure we keep that computation fresh and everything up to date, +while also making sure to clean things up if we detect the render has been tossed. For the most part, this should all be +transparent. + +The important thing to understand is that your reactive function can be initially called more than once for a single +render, because sometimes the work will be tossed. Additionally, `useTracker` will not call your reactive function +reactively until the render is committed (until `useEffect` runs). If you have a particularly fast changing data source, +this is worth understanding. With this very short possible suspension, there are checks in place to make sure the +eventual result is always up to date with the current state of the reactive function. Once the render is "committed", +and the component mounted, the computation is kept running, and everything will run as expected. + +## Suspendable version of hooks + +### `useTracker` + +This is a version of `useTracker` that can be used with React Suspense. + +For its first argument, a key is necessary, witch is used to identify the computation and to avoid recreating it when the +component is re-rendered. + +Its second argument is a function that can be async and reactive, +this argument works similar to the original `useTracker` that does not suspend. + +For its _optional_ third argument, the dependency array, works similar to the `useTracker` that does not suspend, +you pass in an array of variables that this tracking function depends upon. + +For its _optional_ fourth argument, the options object, works similar to the `useTracker` that does not suspend, +you pass in a function for when should skip the update. + +```jsx +import { useTracker } from "meteor/react-meteor-data/suspense"; +import { useSubscribe } from "meteor/react-meteor-data/suspense"; + +function Tasks() { + // this component will suspend + useSubscribe("tasks"); + const { username } = useTracker("user", () => Meteor.user()); // Meteor.user() is async meteor 3.0 + const tasksByUser = useTracker( + "tasksByUser", + () => + TasksCollection.find( + { username }, + { sort: { createdAt: -1 } } + ).fetchAsync() // async call + ); + + // render the tasks +} +``` + +### Maintaining the reactive context + +To maintain a reactive context using the new Meteor Async methods, we are using the new `Tracker.withComputation` API to maintain the reactive context of an +async call, this is needed because otherwise it would be only called once, and the computation would never run again, +this way, every time we have a new Link being added, this useTracker is ran. + +```jsx +// needs Tracker.withComputation because otherwise it would be only called once, and the computation would never run again +const docs = useTracker("name", async (c) => { + const placeholders = await fetch( + "https://jsonplaceholder.typicode.com/todos" + ).then((x) => x.json()); + console.log(placeholders); + return await Tracker.withComputation(c, () => + LinksCollection.find().fetchAsync() + ); +}); +``` + +A rule of thumb is that if you are using a reactive function for example `find` + `fetchAsync`, it is nice to wrap it +inside `Tracker.withComputation` to make sure that the computation is kept alive, if you are just calling that function +that is not necessary, like the one bellow, will be always reactive. + +```jsx +const docs = useTracker("name", () => LinksCollection.find().fetchAsync()); +``` + +### `useSubscribe` + +This is a version of `useSubscribe` that can be used with React Suspense. +It is similar to `useSubscribe`, it throws a promise and suspends the rendering until the promise is resolved. +It does not return a Meteor Handle to control the subscription + +```jsx +import { useTracker } from "meteor/react-meteor-data/suspense"; +import { useSubscribe } from "meteor/react-meteor-data/suspense"; + +function Tasks() { + // this component will suspend + useSubscribe("tasks"); + const { username } = useTracker("user", () => Meteor.user()); // Meteor.user() is async meteor 3.0 + const tasksByUser = useTracker( + "tasksByUser", + () => + TasksCollection.find( + { username }, + { sort: { createdAt: -1 } } + ).fetchAsync() // async call + ); + + // render the tasks +} +``` + +### `useFind` + +This is a version of `useFind` that can be used with React Suspense. +It has a few differences from the `useFind` without suspense, it throws a promise and suspends the rendering until the promise is resolved. +It returns the result and it is reactive. +You should pass as the first parameter the collection where is being searched upon and as the second parameter an array with the arguments, +the same arguments that you would pass to the `find` method of the collection, third parameter is optional, and it is dependency array object. +It's meant for the SSR, you don't have to use it if you're not interested in SSR. + +```jsx +import { useFind } from "meteor/react-meteor-data/suspense"; +import { useSubscribe } from "meteor/react-meteor-data/suspense"; + +function Tasks() { + // this component will suspend + useSubscribe("tasks"); + const tasksByUser = useFind(TasksCollection, [ + {}, + { sort: { createdAt: -1 } }, + ]); + + // render the tasks +} +``` + +## Version compatibility notes + +- `react-meteor-data` v2.x : + + - `useTracker` hook + `withTracker` HOC + - Requires React `^16.8`. + - Implementation is compatible with "React Suspense", concurrent mode and error boundaries. + - The `withTracker` HOC is strictly backwards-compatible with the one provided in v1.x, the major version number is only motivated by the bump of React version requirement. Provided a compatible React version, existing Meteor apps leveraging the `withTracker` HOC can freely upgrade from v1.x to v2.x, and gain compatibility with future React versions. + - The previously deprecated `createContainer` has been removed. + +- `react-meteor-data` v0.x : + - `withTracker` HOC (+ `createContainer`, kept for backwards compatibility with early v0.x releases) + - Requires React `^15.3` or `^16.0`. + - Implementation relies on React lifecycle methods (`componentWillMount` / `componentWillUpdate`) that are [marked for deprecation in future React versions](https://reactjs.org/blog/2018/03/29/react-v-16-3.html#component-lifecycle-changes) ("React Suspense"). diff --git a/v3-docs/docs/packages/roles.md b/v3-docs/docs/packages/roles.md new file mode 100644 index 0000000000..4461c2dbec --- /dev/null +++ b/v3-docs/docs/packages/roles.md @@ -0,0 +1,480 @@ +# Roles + +Authorization package for Meteor - compatible with built-in accounts package. + +> Available since Meteor 3.1.0 (previously alanning:roles) + +## Installation + +To add roles to your application, run this command in your terminal: + +```bash +meteor add roles +``` + +## Overview + +The roles package lets you attach roles to users and then check against those roles when deciding whether to grant access to Meteor methods or publish data. The core concept is simple - you create role assignments for users and then verify those roles later. This package provides helper methods to make the process of adding, removing, and verifying roles easier. + +## Concepts + +### Roles vs Permissions + +Although named "roles", you can define your **roles**, **scopes** or **permissions** however you like. They are essentially tags assigned to users that you can check later. + +You can have traditional roles like `admin` or `webmaster`, or more granular permissions like `view-secrets`, `users.view`, or `users.manage`. Often, more granular permissions are better as they handle edge cases without creating many higher-level roles. + +### Role Hierarchy + +Roles can be organized in a hierarchy: + +- Roles can have multiple parents and children (subroles) +- If a parent role is assigned to a user, all its descendant roles also apply +- This allows creating "super roles" that aggregate permissions + +Example hierarchy setup: + +```js +import { Roles } from "meteor/roles"; + +// Create base roles +await Roles.createRoleAsync("user"); +await Roles.createRoleAsync("admin"); + +// Create permission roles +await Roles.createRoleAsync("USERS_VIEW"); +await Roles.createRoleAsync("POST_EDIT"); + +// Set up hierarchy +await Roles.addRolesToParentAsync("USERS_VIEW", "admin"); +await Roles.addRolesToParentAsync("POST_EDIT", "admin"); +await Roles.addRolesToParentAsync("POST_EDIT", "user"); +``` + +### Scopes + +Scopes allow users to have independent sets of roles. Use cases include: + +- Different communities within your app +- Multiple tenants in a multi-tenant application +- Different resource groups + +Users can have both scoped roles and global roles: + +- Global roles apply across all scopes +- Scoped roles only apply within their specific scope +- Scopes are independent of each other + +Example using scopes: + +```js +// Assign scoped roles +await Roles.addUsersToRolesAsync(userId, ["manage-team"], "team-a"); +await Roles.addUsersToRolesAsync(userId, ["player"], "team-b"); + +// Check scoped roles +await Roles.userIsInRoleAsync(userId, "manage-team", "team-a"); // true +await Roles.userIsInRoleAsync(userId, "manage-team", "team-b"); // false + +// Assign global role +await Roles.addUsersToRolesAsync(userId, "super-admin", null); + +// Global roles work in all scopes +await Roles.userIsInRoleAsync(userId, ["manage-team", "super-admin"], "team-b"); // true +``` + +## Role Management + + + +Example: + +```js +import { Roles } from "meteor/roles"; + +// Create a new role +await Roles.createRoleAsync("admin"); + +// Create if doesn't exist +await Roles.createRoleAsync("editor", { unlessExists: true }); +``` + +### Modifying Roles + + + +Example: + +```js +// Make 'editor' a child role of 'admin' +await Roles.addRolesToParentAsync("editor", "admin"); + +// Add multiple child roles +await Roles.addRolesToParentAsync(["editor", "moderator"], "admin"); +``` + + + +Example: + +```js +// Remove 'editor' as child role of 'admin' +await Roles.removeRolesFromParentAsync("editor", "admin"); +``` + + + +Example: + +```js +// Delete role and all its assignments +await Roles.deleteRoleAsync("temp-role"); +``` + + + +Example: + +```js +// Rename an existing role +await Roles.renameRoleAsync("editor", "content-editor"); +``` + +### Assigning Roles + + + +Example: + +```js +// Add global roles +await Roles.addUsersToRolesAsync(userId, ["admin", "editor"]); + +// Add scoped roles +await Roles.addUsersToRolesAsync(userId, ["manager"], "department-a"); + +// Add roles to multiple users +await Roles.addUsersToRolesAsync([user1Id, user2Id], ["user"]); +``` + + + +Example: + +```js +// Replace user's global roles +await Roles.setUserRolesAsync(userId, ["editor"]); + +// Replace scoped roles +await Roles.setUserRolesAsync(userId, ["viewer"], "project-x"); + +// Clear all roles in scope +await Roles.setUserRolesAsync(userId, [], "project-x"); +``` + + + +Example: + +```js +// Remove global roles +await Roles.removeUsersFromRolesAsync(userId, ["admin"]); + +// Remove scoped roles +await Roles.removeUsersFromRolesAsync(userId, ["manager"], "department-a"); + +// Remove roles from multiple users +await Roles.removeUsersFromRolesAsync([user1Id, user2Id], ["temp-role"]); +``` + + + +Example: + +```js +// Rename a scope +await Roles.renameScopeAsync("department-1", "marketing"); +``` + + + +Example: + +```js +// Remove a scope and all its role assignments +await Roles.removeScopeAsync("old-department"); +``` + + + +Example: + +```js +// Get all roles sorted by name +const roles = Roles.getAllRoles({ sort: { _id: 1 } }); + +// Get roles with custom query +const customRoles = Roles.getAllRoles({ + fields: { _id: 1, children: 1 }, + sort: { _id: -1 }, +}); +``` + + + +Example: + +```js +// Find all admin users +const adminUsers = await Roles.getUsersInRoleAsync("admin"); + +// Find users with specific roles in a scope +const scopedUsers = await Roles.getUsersInRoleAsync( + ["editor", "writer"], + "blog" +); + +// Find users with custom options +const users = await Roles.getUsersInRoleAsync("manager", { + scope: "department-a", + queryOptions: { + sort: { createdAt: -1 }, + limit: 10, + }, +}); +``` + +## Checking Roles + + + +Example: + +```js +// Check global role +const isAdmin = await Roles.userIsInRoleAsync(userId, "admin"); + +// Check any of multiple roles +const canEdit = await Roles.userIsInRoleAsync(userId, ["editor", "admin"]); + +// Check scoped role +const isManager = await Roles.userIsInRoleAsync( + userId, + "manager", + "department-a" +); + +// Check role in any scope +const hasRole = await Roles.userIsInRoleAsync(userId, "viewer", { + anyScope: true, +}); +``` + + + +Example: + +```js +// Get user's global roles +const globalRoles = await Roles.getRolesForUserAsync(userId); + +// Get scoped roles +const deptRoles = await Roles.getRolesForUserAsync(userId, "department-a"); + +// Get all roles including inherited +const allRoles = await Roles.getRolesForUserAsync(userId, { + anyScope: true, + fullObjects: true, +}); +``` + + + +Example: + +```js +// Check if admin is a parent of editor +const isParent = await Roles.isParentOfAsync("admin", "editor"); + +// Can be used to check inheritance chains +const hasPermission = await Roles.isParentOfAsync("super-admin", "post-edit"); +``` + + + +Example: + +```js +// Get all scopes for user +const allScopes = await Roles.getScopesForUserAsync(userId); + +// Get scopes where user has specific roles +const editorScopes = await Roles.getScopesForUserAsync(userId, ["editor"]); +``` + +## Publishing Roles + +Role assignments need to be published to be available on the client. Example publication: + +```js +// Publish user's own roles +Meteor.publish(null, function () { + if (this.userId) { + return Meteor.roleAssignment.find({ "user._id": this.userId }); + } + this.ready(); +}); + +// Publish roles for specific scope +Meteor.publish("scopeRoles", function (scope) { + if (this.userId) { + return Meteor.roleAssignment.find({ scope: scope }); + } + this.ready(); +}); +``` + +## Client only APIs + +On the client alongside the async methods, you can use the `sync` versions of the functions: + +- `Roles.userIsInRole(userId, roles, scope)` +- `Roles.getRolesForUser(userId, scope)` +- `Roles.getScopesForUser(userId)` +- `Roles.isParentOf(parent, child)` +- `Roles.getUsersInRole(role, scope)` +- `Roles.getAllRoles(options)` +- `Roles.createRole(role, options)` +- `Roles.addUsersToRoles(userId, roles, scope)` +- `Roles.setUserRoles(userId, roles, scope)` +- `Roles.removeUsersFromRoles(userId, roles, scope)` +- `Roles.addRolesToParent(child, parent)` +- `Roles.removeRolesFromParent(child, parent)` +- `Roles.deleteRole(role)` +- `Roles.renameRole(oldRole, newRole)` +- `Roles.renameScope(oldScope, newScope)` +- `Roles.removeScope(scope)` + +## Using with Templates + +The roles package automatically provides an `isInRole` helper for templates: + +```handlebars +{{#if isInRole "admin"}} +
    + +
    +{{/if}} + +{{#if isInRole "editor,writer" "blog"}} +
    + +
    +{{/if}} +``` + +## Migration to Core Version + +If you are currently using the `alanning:roles` package, follow these steps to migrate to the core version: + +1. Make sure you are on version 3.6 of `alanning:roles` first +2. Run any pending migrations from previous versions +3. Switch all server-side role operations to use the async versions of the functions: + - createRoleAsync + - deleteRoleAsync + - addUsersToRolesAsync + - setUserRolesAsync + - removeUsersFromRolesAsync + - etc. +4. Remove `alanning:roles` package: + ```bash + meteor remove alanning:roles + ``` +5. Add the core roles package: + ```bash + meteor add roles + ``` +6. Update imports to use the new package: + ```js + import { Roles } from "meteor/roles"; + ``` + +The sync versions of these functions are still available on the client. + +## Security Considerations + +1. Client-side role checks are for convenience only - always verify permissions on the server +2. Publish only the role data that users need +3. Use scopes to properly isolate role assignments +4. Validate role names and scopes to prevent injection attacks +5. Consider using more granular permissions over broad role assignments + +## Example Usage + +### Method Security + +```js +// server/methods.js +Meteor.methods({ + deletePost: async function (postId) { + check(postId, String); + + const canDelete = await Roles.userIsInRoleAsync( + this.userId, + ["admin", "moderator"], + "posts" + ); + + if (!canDelete) { + throw new Meteor.Error("unauthorized", "Not authorized to delete posts"); + } + + Posts.remove(postId); + }, +}); +``` + +### Publication Security + +```js +// server/publications.js +Meteor.publish("secretDocuments", async function (scope) { + check(scope, String); + + const canView = await Roles.userIsInRoleAsync( + this.userId, + ["view-secrets", "admin"], + scope + ); + + if (canView) { + return SecretDocs.find({ scope: scope }); + } + + this.ready(); +}); +``` + +### User Management + +```js +// server/users.js +Meteor.methods({ + promoteToEditor: async function (userId, scope) { + check(userId, String); + check(scope, String); + + const canPromote = await Roles.userIsInRoleAsync( + this.userId, + "admin", + scope + ); + + if (!canPromote) { + throw new Meteor.Error("unauthorized"); + } + + await Roles.addUsersToRolesAsync(userId, ["editor"], scope); + }, +}); +``` diff --git a/v3-docs/docs/packages/standard-minifier-css.md b/v3-docs/docs/packages/standard-minifier-css.md index f3d55e8ea0..ace7a833e8 100644 --- a/v3-docs/docs/packages/standard-minifier-css.md +++ b/v3-docs/docs/packages/standard-minifier-css.md @@ -24,7 +24,7 @@ module.exports = { > The example config enables the `autoprefixer` postcss plugin. You can install the plugin by running `meteor npm install -D autoprefixer`. -Learn more about [configuring postcss](https://github.com/postcss/postcss-load-config#packagejson) or find a list of [available plugins](https://www.postcss.parts/). +Learn more about [configuring postcss](https://github.com/postcss/postcss-load-config#packagejson) or find a list of [available plugins](https://postcss.org/docs/postcss-plugins). After making changes to the PostCSS Config, `meteor` must be restarted for it to use the new config. diff --git a/v3-docs/docs/packages/underscore.md b/v3-docs/docs/packages/underscore.md deleted file mode 100644 index b82b2cb28b..0000000000 --- a/v3-docs/docs/packages/underscore.md +++ /dev/null @@ -1,19 +0,0 @@ -# Underscore - -[Underscore](http://underscorejs.org/) is a utility-belt library for -JavaScript that provides support for functional programming. It is -invaluable for writing clear, concise JavaScript in a functional style. - -The `underscore` package defines the `_` namespace on both the client -and the server. - -::: warning -We have slightly modified the way Underscore differentiates between -objects and arrays in [collection functions](http://underscorejs.org/#each). -The original Underscore logic is to treat any object with a numeric `length` -property as an array (which helps it work properly on -[`NodeList`s](https://developer.mozilla.org/en-US/docs/Web/API/NodeList)). -In Meteor's version of Underscore, objects with a numeric `length` property -are treated as objects if they have no prototype (specifically, if -`x.constructor === Object`. -::: diff --git a/v3-docs/docs/performance/websocket-compression.md b/v3-docs/docs/performance/websocket-compression.md new file mode 100644 index 0000000000..e7c117f155 --- /dev/null +++ b/v3-docs/docs/performance/websocket-compression.md @@ -0,0 +1,104 @@ +# Websocket Compression in Meteor + +::: warning +Modifying websocket compression settings without understanding your application's DDP messaging patterns can negatively impact performance. Before changing these settings, you should: +- Use [Meteor DevTools Evolved](https://chromewebstore.google.com/detail/meteor-devtools-evolved/ibniinmoafhgbifjojidlagmggecmpgf) or your browser's Network tab to monitor WebSocket traffic +- Analyze your DDP message frequency and payload sizes +- Test changes in a staging environment with realistic data and user load +::: + +Meteor's stream server uses the permessage-deflate extension for websocket compression by default. While compression can help reduce bandwidth usage, it may impact performance in reactivity-intensive applications due to the computational overhead of compressing numerous DDP messages. + +## Configuration + +### Disabling Compression + +You can disable websocket compression by setting the `SERVER_WEBSOCKET_COMPRESSION` environment variable to `false`: + +```bash +SERVER_WEBSOCKET_COMPRESSION=false +``` + +### Custom Compression Settings + +To customize compression settings, set `SERVER_WEBSOCKET_COMPRESSION` to a JSON string with your desired configuration: + +```bash +# Example with custom settings +SERVER_WEBSOCKET_COMPRESSION='{"threshold": 2048, "level": 1}' +``` + +Available configuration options: + +- `threshold`: Minimum message size (in bytes) before compression is applied (default: 1024) +- `level`: Compression level (0-9, where 0=none, 1=fastest, 9=best compression) +- `memLevel`: Memory level (1-9, lower uses less memory) +- `noContextTakeover`: When true, compressor resets for each message (default: true) +- `maxWindowBits`: Window size for compression (9-15, lower uses less memory) + +## Configuration Examples + +Here are recommended configurations for different types of applications: + +### High-Frequency Updates / Real-Time Dashboard + +For applications with frequent small updates (e.g., real-time dashboards, trading platforms): + +```bash +# Disable compression for optimal performance with small, frequent updates +SERVER_WEBSOCKET_COMPRESSION=false +``` + +### Large Data Transfers + +For applications transferring large datasets (e.g., file sharing, data visualization): + +```bash +# Optimize for large data transfers +SERVER_WEBSOCKET_COMPRESSION='{"threshold": 1024, "level": 6, "memLevel": 8}' +``` + +### Memory-Constrained Environment + +For deployments with limited memory resources: + +```bash +# Minimize memory usage while maintaining compression +SERVER_WEBSOCKET_COMPRESSION='{"threshold": 2048, "level": 1, "memLevel": 1, "maxWindowBits": 9}' +``` + +### Balanced Configuration + +For typical applications with mixed message sizes: + +```bash +# Balance between compression and performance +SERVER_WEBSOCKET_COMPRESSION='{"threshold": 1536, "level": 3, "memLevel": 4}' +``` + +## Verifying Compression Status + +You can check if compression is enabled through the Meteor shell: + +```javascript +Meteor.server.stream_server.server.options.faye_server_options.extensions +``` + +Results interpretation: +- `[]` (empty array): Compression is disabled +- `[{}]` (array with object): Compression is enabled + +## Performance Considerations + +- For apps with high message throughput or frequent small updates, disabling compression may improve performance +- Large message payloads may benefit from compression, especially over slower network connections +- Consider monitoring CPU usage and response times when adjusting compression settings + +## Default Configuration + +When enabled, the default configuration uses: +- Compression threshold: 1024 bytes +- Compression level: Z_BEST_SPEED (fastest) +- Memory level: Z_MIN_MEMLEVEL (minimum memory usage) +- Context takeover: Disabled +- Window bits: Z_MIN_WINDOWBITS (minimum window size) diff --git a/v3-docs/docs/public/logo.png b/v3-docs/docs/public/logo.png index c333537981..8644c2a46e 100644 Binary files a/v3-docs/docs/public/logo.png and b/v3-docs/docs/public/logo.png differ diff --git a/v3-docs/docs/public/meteor-blue.png b/v3-docs/docs/public/meteor-blue.png index c91f7a9718..0934189a3e 100644 Binary files a/v3-docs/docs/public/meteor-blue.png and b/v3-docs/docs/public/meteor-blue.png differ diff --git a/v3-docs/docs/public/meteor-logo.png b/v3-docs/docs/public/meteor-logo.png index ca7bb89de5..3b848982ca 100644 Binary files a/v3-docs/docs/public/meteor-logo.png and b/v3-docs/docs/public/meteor-logo.png differ diff --git a/v3-docs/docs/troubleshooting/expired-certificate.md b/v3-docs/docs/troubleshooting/expired-certificate.md index 5678a59e04..fa8e9cea12 100644 --- a/v3-docs/docs/troubleshooting/expired-certificate.md +++ b/v3-docs/docs/troubleshooting/expired-certificate.md @@ -69,7 +69,7 @@ sudo certbot certonly --manual --preferred-chain "ISRG Root X1" --preferred-chal More info can be obtained [here](https://letsencrypt.org/certificates). -If you are using Galaxy, you need to follow the requirements and steps [here](https://galaxy-guide.meteor.com/encryption.html#Custom%20certificate) after generating the certificate. Galaxy only accepts custom certs in `.pem` format, the same as nginx uses. +If you are using Galaxy, you need to follow the requirements and steps [here](https://galaxy-support.meteor.com/en/article/encryption-pt8wbl/) after generating the certificate. Galaxy only accepts custom certs in `.pem` format, the same as nginx uses. This is not a Meteor or Galaxy issue, but it's a change in the Let's Encrypt certificate you are using. diff --git a/v3-docs/docs/troubleshooting/known-issues.md b/v3-docs/docs/troubleshooting/known-issues.md deleted file mode 100644 index f533d80a76..0000000000 --- a/v3-docs/docs/troubleshooting/known-issues.md +++ /dev/null @@ -1,39 +0,0 @@ - -# Known issues in 2.13 - -Troubleshooting in Meteor 2.13 - -## Cannot extract version of meteor tool {#cannot-extract-meteor-tool} - - -For some users, the `meteor update` to version 2.13 command may fail with the following error or similar: - -```shell -Error: incorrect data check - at Zlib.zlibOnError [as onerror] (zlib.js:187:17) - => awaited here: - ... - at /tools/cli/main.js:1165:7 { - errno: -3, - code: 'Z_DATA_ERROR' - } - -``` - -## The issue {#the-issue} - -It seems related to [our first ESM version of Node.js v14.21.4](https://github.com/meteor/node-v14-esm) and the `zlib` package. -We have been able to reproduce this issue only in Mac Intel. - -You can follow along with the [GitHub issue](https://github.com/meteor/meteor/issues/12731) for updates. - -## Solution {#solution} - -The solution for this issue is running the following command in your terminal: - -```shell - -curl https://install.meteor.com/\?release\=2.13.3 | sh - -``` - diff --git a/v3-docs/docs/troubleshooting/mongodb-connection.md b/v3-docs/docs/troubleshooting/mongodb-connection.md new file mode 100644 index 0000000000..90bbd0aaef --- /dev/null +++ b/v3-docs/docs/troubleshooting/mongodb-connection.md @@ -0,0 +1,17 @@ +# Topology Closed Error + +One possible scenario we've seen is that after migrating to Meteor 3, some apps become partially unresponsive and throw `MongoTopologyClosedError: Topology is closed` errors on startup. + +In this case, you might consider increasing the server selection timeout for your MongoDB instance, like this: + +```json +{ + "packages": { + "mongo": { + "options": { + "serverSelectionTimeoutMS": 120000 + } + } + } +} +``` \ No newline at end of file diff --git a/v3-docs/docs/tutorials/react/7.adding-user-accounts.md b/v3-docs/docs/tutorials/react/7.adding-user-accounts.md index 60491ef06d..6b1513afbb 100644 --- a/v3-docs/docs/tutorials/react/7.adding-user-accounts.md +++ b/v3-docs/docs/tutorials/react/7.adding-user-accounts.md @@ -271,7 +271,7 @@ import { Accounts } from "meteor/accounts-base"; import { TasksCollection } from "/imports/api/TasksCollection"; const insertTask = (taskText, user) => - TasksCollection.insert({ + TasksCollection.insertAsync({ text: taskText, userId: user._id, createdAt: new Date(), diff --git a/v3-docs/docs/tutorials/react/8.deploying.md b/v3-docs/docs/tutorials/react/8.deploying.md index 0dc656763b..11f3e85f0e 100644 --- a/v3-docs/docs/tutorials/react/8.deploying.md +++ b/v3-docs/docs/tutorials/react/8.deploying.md @@ -21,7 +21,7 @@ As your app uses MongoDB the first step is to set up a MongoDB database, Galaxy In any MongoDB provider you will have a MongoDB URL which you must use it. If you use the free option provided by Galaxy, the initial setup is done for you. Galaxy MongoDB URL will be like this: `mongodb://username:@org-dbname-01.mongodb.galaxy-cloud.io` . -> You can read more about Galaxy MongoDB [here](https://galaxy-guide.meteor.com/galaxy-database-mongodb-general) and general MongoDB set up [here](https://galaxy-guide.meteor.com/mongodb.html). +> You can read more about Galaxy MongoDB [here](https://galaxy-support.meteor.com/en/article/mongodb-general-1syd5af/). ### 8.3: Set up settings @@ -53,7 +53,7 @@ You also need to choose a subdomain to publish your app. We are going to use the In this example we are going to use `react-meteor-3.meteorapp.com` but make sure you select a different one, otherwise you are going to receive an error. -> You can learn how to use custom domains on Galaxy [here](https://galaxy-guide.meteor.com/custom-domains.html). Custom domains are available starting with the Essentials plan. +> You can learn how to use custom domains on Galaxy [here](https://galaxy-support.meteor.com/en/article/domains-16cijgc/). Custom domains are available starting with the Essentials plan. Run the deployment command: @@ -82,7 +82,7 @@ For details, visit https://galaxy.meteor.com/app/react-meteor-3.meteorapp.com This process usually takes just a few minutes, but it depends on your internet speed as it’s going to send your app bundle to Galaxy servers. -> Galaxy builds a new Docker image that contains your app bundle and then deploy containers using it, [read more](https://galaxy-guide.meteor.com/container-environment.html). +> Galaxy builds a new Docker image that contains your app bundle and then deploy containers using it, [read more](https://galaxy-support.meteor.com/en/article/container-environment-lfd6kh/). You can check your logs on Galaxy, including the part that Galaxy is building your Docker image and deploying it. ### 8.5: Access the app and enjoy @@ -94,5 +94,5 @@ You can also access your app on Galaxy 2.0 which is currently in beta at `https: You can access the app at [react-meteor-3.meteorapp.com](https://react-meteor-3.meteorapp.com/)! Just use your subdomain to access yours! -> We deployed to Galaxy running in the US (us-east-1), we also have Galaxy running in other regions in the world, check the list [here](https://galaxy-guide.meteor.com/deploy-region.html). +> We deployed to Galaxy running in the US (us-east-1), we also have Galaxy running in other regions in the world, check the list [here](https://galaxy-support.meteor.com/en/article/regions-1vucejm/). This is huge, you have your app running on Galaxy, ready to be used by anyone in the world! diff --git a/v3-docs/docs/tutorials/react/9.next-steps.md b/v3-docs/docs/tutorials/react/9.next-steps.md index 4443047fa6..b9a2e58a80 100644 --- a/v3-docs/docs/tutorials/react/9.next-steps.md +++ b/v3-docs/docs/tutorials/react/9.next-steps.md @@ -11,7 +11,7 @@ You can find the final version of this app in our [GitHub repository](https://gi Here are some options for what you can do next: - Check out the complete [documentation](https://v3-docs.meteor.com/) to learn more about Meteor 3. -- Read the [Galaxy Guide](https://galaxy-guide.meteor.com/) to learn more about deploying your app. +- Read the [Galaxy Guide](https://galaxy-support.meteor.com/en/article/deploy-to-galaxy-18gd6e2/) to learn more about deploying your app. - Join our community on the [Meteor Forums](https://forums.meteor.com/) and the [Meteor Lounge on Discord](https://discord.gg/hZkTCaVjmT) to ask questions and share your experiences. We can't wait to see what you build next! diff --git a/v3-docs/docs/tutorials/vue/meteorjs3-vue3-vue-meteor-tracker.md b/v3-docs/docs/tutorials/vue/meteorjs3-vue3-vue-meteor-tracker.md index 1080849c5a..171d56d105 100644 --- a/v3-docs/docs/tutorials/vue/meteorjs3-vue3-vue-meteor-tracker.md +++ b/v3-docs/docs/tutorials/vue/meteorjs3-vue3-vue-meteor-tracker.md @@ -1279,7 +1279,7 @@ As your app uses MongoDB the first step is to set up a MongoDB database, Galaxy In any MongoDB provider you will have a MongoDB URL which you must use it. If you use the free option provided by Galaxy, the initial setup is done for you. Galaxy MongoDB URL will be like this: `mongodb://username:@org-dbname-01.mongodb.galaxy-cloud.io` . -> You can read more about Galaxy MongoDB [here](https://galaxy-guide.meteor.com/galaxy-database-mongodb-general) and general MongoDB set up [here](https://galaxy-guide.meteor.com/mongodb.html). +> You can read more about Galaxy MongoDB [here](https://galaxy-support.meteor.com/en/article/mongodb-general-1syd5af/). ### 8.3: Set up settings @@ -1312,7 +1312,7 @@ You also need to choose a subdomain to publish your app. We are going to use the In this example we are going to use `vue3-meteor-3.meteorapp.com` but make sure you select a different one, otherwise you are going to receive an error. -> You can learn how to use custom domains on Galaxy [here](https://galaxy-guide.meteor.com/custom-domains.html). Custom domains are available starting with the Essentials plan. +> You can learn how to use custom domains on Galaxy [here](https://galaxy-support.meteor.com/en/article/domains-16cijgc/). Custom domains are available starting with the Essentials plan. Run the deployment command: @@ -1343,7 +1343,7 @@ For details, visit https://galaxy.meteor.com/app/vue3-meteor-3.meteorapp.com This process usually takes just a few minutes, but it depends on your internet speed as it’s going to send your app bundle to Galaxy servers. -> Galaxy builds a new Docker image that contains your app bundle and then deploy containers using it, [read more](https://galaxy-guide.meteor.com/container-environment.html). +> Galaxy builds a new Docker image that contains your app bundle and then deploy containers using it, [read more](https://galaxy-support.meteor.com/en/article/container-environment-lfd6kh/). You can check your logs on Galaxy, including the part that Galaxy is building your Docker image and deploying it. @@ -1356,7 +1356,7 @@ You can also access your app on Galaxy 2.0 which is currently in beta at `https: You can access the app at [vue3-meteor-3.meteorapp.com](https://vue3-meteor-3.meteorapp.com/)! Just use your subdomain to access yours! -> We deployed to Galaxy running in the US (us-east-1), we also have Galaxy running in other regions in the world, check the list [here](https://galaxy-guide.meteor.com/deploy-region.html). +> We deployed to Galaxy running in the US (us-east-1), we also have Galaxy running in other regions in the world, check the list [here](https://galaxy-support.meteor.com/en/article/regions-1vucejm/). This is huge, you have your app running on Galaxy, ready to be used by anyone in the world! @@ -1373,7 +1373,7 @@ You can find the final version of this app in our [GitHub repository](https://gi Here are some options for what you can do next: - Check out the complete [documentation](https://v3-docs.meteor.com/) to learn more about Meteor 3. -- Read the [Galaxy Guide](https://galaxy-guide.meteor.com/) to learn more about deploying your app. +- Read the [Galaxy Guide](https://galaxy-support.meteor.com/en/article/deploy-to-galaxy-18gd6e2/) to learn more about deploying your app. - Join our community on the [Meteor Forums](https://forums.meteor.com/) and the [Meteor Lounge on Discord](https://discord.gg/hZkTCaVjmT) to ask questions and share your experiences. We can't wait to see what you build next! diff --git a/v3-docs/v3-migration-docs/breaking-changes/index.md b/v3-docs/v3-migration-docs/breaking-changes/index.md index cc822958fa..0c3cd4b397 100644 --- a/v3-docs/v3-migration-docs/breaking-changes/index.md +++ b/v3-docs/v3-migration-docs/breaking-changes/index.md @@ -40,10 +40,10 @@ The `meteor reset` command clears only the local cache by default. Using the `-- This command is often recommended to fix your development project by clearing the cache. Previously, it also cleared the local MongoDB, which could accidentally delete important data. -## Node v20 +## Node v22 -Meteor 3.0 is now using Node v20. This means that if you have any dependencies or usages -of Node v14, you will need to update them to be compatible with Node v20. +Meteor 3.0 is now using Node v22. This means that if you have any dependencies or usages +of Node v14, you will need to update them to be compatible with Node v22. ## NPM Installer Update @@ -119,7 +119,8 @@ await Meteor.callAsync('otherMethod') // [!code highlight] ``` -## WebApp Switches to Express +## WebApp Switches to Express 5 + ::: tip @@ -128,6 +129,12 @@ If you've customized the WebApp package before, please verify if those customiza ::: +::: tip + +Starting with Meteor 3.1, Express has been updated to version 5. If you're upgrading from an earlier version, [refer to the migration guide](https://expressjs.com/en/guide/migrating-5.html). + +::: + The `webapp` package now exports these new properties: ```ts @@ -331,3 +338,9 @@ function patchPublish(publish: typeof Meteor.publish) { ``` This example demonstrates the migration applied to the [`universe:i18n` package](https://github.com/vazco/meteor-universe-i18n/pull/191). + +## Mongo driver + +In Meteor 3, the MongoDB driver has been updated to version 6.x. This version removes callbacks for async operations, replacing them with standard promises. If you use rawCollection or rawDatabase, ensure your code is updated to use promises. + +For details on breaking changes, see [here](https://github.com/mongodb/node-mongodb-native/blob/HEAD/HISTORY.md). diff --git a/v3-docs/v3-migration-docs/index.md b/v3-docs/v3-migration-docs/index.md index cf07f5ffe7..4d55017747 100644 --- a/v3-docs/v3-migration-docs/index.md +++ b/v3-docs/v3-migration-docs/index.md @@ -1,3 +1,8 @@ +--- +meteor_version: 3.1.0 +node_version: 22.14.0 +npm_version: 10.9.2 +--- # Meteor 3.0 Migration Guide :::info You are reading the migration guide for Meteor 3! @@ -11,24 +16,24 @@ This guide is for users with Meteor 2.x projects understand the changes between Meteor 3.0 is currently in its official version! -**Latest version:** `3.0.4`
    -**Node.js version:** `20.18.0 LTS`
    -**NPM version:** `10.8.2` +**Latest version:** `{{ $frontmatter.meteor_version }}`
    +**Node.js version:** `{{ $frontmatter.node_version }} LTS`
    +**NPM version:** `{{ $frontmatter.npm_version }}` ## How to try Meteor 3.0? You can create a new Meteor 3.0 project by running the command below: -```bash -meteor create my-new-project --release 3.0.4 +```bash-vue + meteor create my-new-project --release {{ $frontmatter.meteor_version }} ``` ## How to upgrade from Meteor 2.x? You can upgrade your Meteor 2.x project by running the command below inside your project folder: -```bash -meteor update --release 3.0.4 +```bash-vue +meteor update --release {{ $frontmatter.meteor_version }} meteor reset # resets project to a fresh state ``` @@ -53,6 +58,7 @@ This guide covers the necessary topics for migrating your application from Meteo - [Frequently Asked Questions](./frequently-asked-questions/index.md), answers to common questions. - [Breaking Changes](./breaking-changes/index.md), an overview of the changes that will affect your application. + - [Meteor.call x Meteor.callAsync](./breaking-changes/call-x-callAsync.md), why should you change your methods to use `Async` methods. - [Upgrading packages](./breaking-changes/upgrading-packages.md), how to upgrade your packages to the be compatible with Meteor v3. @@ -69,14 +75,15 @@ This guide covers the necessary topics for migrating your application from Meteo We are aware of these articles and guides to assist with your migration: - - [Prepare your Meteor.js project for the big 3.0 release](https://dev.to/jankapunkt/prepare-your-meteorjs-project-for-the-big-30-release-14bf) - - [Gradually upgrading a Meteor.js project to 3.0](https://dev.to/meteor/gradually-upgrading-a-meteorjs-project-to-30-5aj0) - - [Meteor 3.0 Migration Guide, from Daniel](https://docs.google.com/document/d/1XxHE5MQaS0-85HQ-bkiXxmGlYi41ggkX3F-9Rjb9HhE/edit#heading=h.65xi3waq9bb) - - [Illustreets Migration Guide, large SaaS migrated to 3.0](https://forums.meteor.com/t/large-saas-migrated-to-3-0/61113) & their how-to [post](https://forums.meteor.com/t/meteor-3-0-beta-6-is-out/61277/12) - +- [Prepare your Meteor.js project for the big 3.0 release](https://dev.to/jankapunkt/prepare-your-meteorjs-project-for-the-big-30-release-14bf) +- [Gradually upgrading a Meteor.js project to 3.0](https://dev.to/meteor/gradually-upgrading-a-meteorjs-project-to-30-5aj0) +- [Meteor 3.0 Migration Guide, from Daniel](https://docs.google.com/document/d/1XxHE5MQaS0-85HQ-bkiXxmGlYi41ggkX3F-9Rjb9HhE/edit#heading=h.65xi3waq9bb) +- [Illustreets Migration Guide, large SaaS migrated to 3.0](https://forums.meteor.com/t/large-saas-migrated-to-3-0/61113) & their how-to [post](https://forums.meteor.com/t/meteor-3-0-beta-6-is-out/61277/12) + ### Videos Migrating apps to Meteor 3.0: + - TicTacToe & others: [YouTube](https://www.youtube.com/watch?v=MtStd0aeyQA) - Complex Svelte todo list & others: [YouTube](https://www.youtube.com/watch?v=-XW8xwSk-zU) - Meteor University with v3 diff --git a/v3-docs/v3-migration-docs/package-lock.json b/v3-docs/v3-migration-docs/package-lock.json index e0cc9531e2..e2e4559942 100644 --- a/v3-docs/v3-migration-docs/package-lock.json +++ b/v3-docs/v3-migration-docs/package-lock.json @@ -247,13 +247,14 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" @@ -263,13 +264,14 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -279,13 +281,14 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -295,13 +298,14 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -311,13 +315,14 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -327,13 +332,14 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -343,13 +349,14 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -359,13 +366,14 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -375,13 +383,14 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -391,13 +400,14 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -407,13 +417,14 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -423,13 +434,14 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -439,13 +451,14 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -455,13 +468,14 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -471,13 +485,14 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -487,13 +502,14 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -503,13 +519,14 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -519,13 +536,14 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -535,13 +553,14 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -551,13 +570,14 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" @@ -567,13 +587,14 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -583,13 +604,14 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -599,13 +621,14 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -621,179 +644,277 @@ "dev": true }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz", - "integrity": "sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.32.1.tgz", + "integrity": "sha512-/pqA4DmqyCm8u5YIDzIdlLcEmuvxb0v8fZdFhVMszSpDTgbQKdw3/mB3eMUHIbubtJ6F9j+LtmyCnHTEqIHyzA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz", - "integrity": "sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.32.1.tgz", + "integrity": "sha512-If3PDskT77q7zgqVqYuj7WG3WC08G1kwXGVFi9Jr8nY6eHucREHkfpX79c0ACAjLj3QIWKPJR7w4i+f5EdLH5Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz", - "integrity": "sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.32.1.tgz", + "integrity": "sha512-zCpKHioQ9KgZToFp5Wvz6zaWbMzYQ2LJHQ+QixDKq52KKrF65ueu6Af4hLlLWHjX1Wf/0G5kSJM9PySW9IrvHA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz", - "integrity": "sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.32.1.tgz", + "integrity": "sha512-sFvF+t2+TyUo/ZQqUcifrJIgznx58oFZbdHS9TvHq3xhPVL9nOp+yZ6LKrO9GWTP+6DbFtoyLDbjTpR62Mbr3Q==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.32.1.tgz", + "integrity": "sha512-NbOa+7InvMWRcY9RG+B6kKIMD/FsnQPH0MWUvDlQB1iXnF/UcKSudCXZtv4lW+C276g3w5AxPbfry5rSYvyeYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.32.1.tgz", + "integrity": "sha512-JRBRmwvHPXR881j2xjry8HZ86wIPK2CcDw0EXchE1UgU0ubWp9nvlT7cZYKc6bkypBt745b4bglf3+xJ7hXWWw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz", - "integrity": "sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.32.1.tgz", + "integrity": "sha512-PKvszb+9o/vVdUzCCjL0sKHukEQV39tD3fepXxYrHE3sTKrRdCydI7uldRLbjLmDA3TFDmh418XH19NOsDRH8g==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.32.1.tgz", + "integrity": "sha512-9WHEMV6Y89eL606ReYowXuGF1Yb2vwfKWKdD1A5h+OYnPZSJvxbEjxTRKPgi7tkP2DSnW0YLab1ooy+i/FQp/Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz", - "integrity": "sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.32.1.tgz", + "integrity": "sha512-tZWc9iEt5fGJ1CL2LRPw8OttkCBDs+D8D3oEM8mH8S1ICZCtFJhD7DZ3XMGM8kpqHvhGUTvNUYVDnmkj4BDXnw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz", - "integrity": "sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.32.1.tgz", + "integrity": "sha512-FTYc2YoTWUsBz5GTTgGkRYYJ5NGJIi/rCY4oK/I8aKowx1ToXeoVVbIE4LGAjsauvlhjfl0MYacxClLld1VrOw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.32.1.tgz", + "integrity": "sha512-F51qLdOtpS6P1zJVRzYM0v6MrBNypyPEN1GfMiz0gPu9jN8ScGaEFIZQwteSsGKg799oR5EaP7+B2jHgL+d+Kw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.32.1.tgz", + "integrity": "sha512-wO0WkfSppfX4YFm5KhdCCpnpGbtgQNj/tgvYzrVYFKDpven8w2N6Gg5nB6w+wAMO3AIfSTWeTjfVe+uZ23zAlg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz", - "integrity": "sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.32.1.tgz", + "integrity": "sha512-iWswS9cIXfJO1MFYtI/4jjlrGb/V58oMu4dYJIKnR5UIwbkzR0PJ09O0PDZT0oJ3LYWXBSWahNf/Mjo6i1E5/g==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.32.1.tgz", + "integrity": "sha512-RKt8NI9tebzmEthMnfVgG3i/XeECkMPS+ibVZjZ6mNekpbbUmkNWuIN2yHsb/mBPyZke4nlI4YqIdFPgKuoyQQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz", - "integrity": "sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.32.1.tgz", + "integrity": "sha512-WQFLZ9c42ECqEjwg/GHHsouij3pzLXkFdz0UxHa/0OM12LzvX7DzedlY0SIEly2v18YZLRhCRoHZDxbBSWoGYg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz", - "integrity": "sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.32.1.tgz", + "integrity": "sha512-BLoiyHDOWoS3uccNSADMza6V6vCNiphi94tQlVIL5de+r6r/CCQuNnerf+1g2mnk2b6edp5dk0nhdZ7aEjOBsA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz", - "integrity": "sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.32.1.tgz", + "integrity": "sha512-w2l3UnlgYTNNU+Z6wOR8YdaioqfEnwPjIsJ66KxKAf0p+AuL2FHeTX6qvM+p/Ue3XPBVNyVSfCrfZiQh7vZHLQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz", - "integrity": "sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.32.1.tgz", + "integrity": "sha512-Am9H+TGLomPGkBnaPWie4F3x+yQ2rr4Bk2jpwy+iV+Gel9jLAu/KqT8k3X4jxFPW6Zf8OMnehyutsd+eHoq1WQ==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz", - "integrity": "sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.32.1.tgz", + "integrity": "sha512-ar80GhdZb4DgmW3myIS9nRFYcpJRSME8iqWgzH2i44u+IdrzmiXVxeFnExQ5v4JYUSpg94bWjevMG8JHf1Da5Q==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "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, + "license": "MIT" }, "node_modules/@types/hast": { "version": "3.0.4", @@ -1254,11 +1375,12 @@ } }, "node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -1266,29 +1388,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/estree-walker": { @@ -1312,6 +1434,7 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1605,9 +1728,9 @@ } }, "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": [ { @@ -1615,6 +1738,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -1635,15 +1759,16 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "dev": true, "funding": [ { @@ -1659,10 +1784,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -1689,12 +1815,13 @@ } }, "node_modules/rollup": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.0.tgz", - "integrity": "sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==", + "version": "4.32.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.32.1.tgz", + "integrity": "sha512-z+aeEsOeEa3mEbS1Tjl6sAZ8NE3+AalQz1RJGj81M+fizusbdDMoEJwdJNHfaB40Scr4qNu+welOfes7maKonA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -1704,19 +1831,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.12.0", - "@rollup/rollup-android-arm64": "4.12.0", - "@rollup/rollup-darwin-arm64": "4.12.0", - "@rollup/rollup-darwin-x64": "4.12.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.12.0", - "@rollup/rollup-linux-arm64-gnu": "4.12.0", - "@rollup/rollup-linux-arm64-musl": "4.12.0", - "@rollup/rollup-linux-riscv64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-musl": "4.12.0", - "@rollup/rollup-win32-arm64-msvc": "4.12.0", - "@rollup/rollup-win32-ia32-msvc": "4.12.0", - "@rollup/rollup-win32-x64-msvc": "4.12.0", + "@rollup/rollup-android-arm-eabi": "4.32.1", + "@rollup/rollup-android-arm64": "4.32.1", + "@rollup/rollup-darwin-arm64": "4.32.1", + "@rollup/rollup-darwin-x64": "4.32.1", + "@rollup/rollup-freebsd-arm64": "4.32.1", + "@rollup/rollup-freebsd-x64": "4.32.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.32.1", + "@rollup/rollup-linux-arm-musleabihf": "4.32.1", + "@rollup/rollup-linux-arm64-gnu": "4.32.1", + "@rollup/rollup-linux-arm64-musl": "4.32.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.32.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.32.1", + "@rollup/rollup-linux-riscv64-gnu": "4.32.1", + "@rollup/rollup-linux-s390x-gnu": "4.32.1", + "@rollup/rollup-linux-x64-gnu": "4.32.1", + "@rollup/rollup-linux-x64-musl": "4.32.1", + "@rollup/rollup-win32-arm64-msvc": "4.32.1", + "@rollup/rollup-win32-ia32-msvc": "4.32.1", + "@rollup/rollup-win32-x64-msvc": "4.32.1", "fsevents": "~2.3.2" } }, @@ -1746,10 +1879,11 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "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" } @@ -1906,14 +2040,15 @@ } }, "node_modules/vite": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.5.tgz", - "integrity": "sha512-BdN1xh0Of/oQafhU+FvopafUp6WaYenLU/NFoL5WyJL++GxkNfieKzBhM24H3HVsPQrlAqB7iJYTHabzaRed5Q==", + "version": "5.4.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", + "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", "dev": true, + "license": "MIT", "dependencies": { - "esbuild": "^0.19.3", - "postcss": "^8.4.35", - "rollup": "^4.2.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -1932,6 +2067,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -1949,6 +2085,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, diff --git a/v3-docs/v3-migration-docs/package.json b/v3-docs/v3-migration-docs/package.json index 23a9213fd2..a84b26ec04 100644 --- a/v3-docs/v3-migration-docs/package.json +++ b/v3-docs/v3-migration-docs/package.json @@ -5,9 +5,10 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "docs:dev": "vitepress dev", - "docs:build": "vitepress build", - "docs:preview": "vitepress preview", + "generators:versions": "node ./versions-generator.js", + "docs:dev": "npm run generators:versions && vitepress dev", + "docs:build": "npm run generators:versions && vitepress build", + "docs:preview": "npm run generators:versions && vitepress preview", "deploy:preview": "npm run docs:build && npm run docs:preview" }, "repository": { diff --git a/v3-docs/v3-migration-docs/public/meteor-blue.png b/v3-docs/v3-migration-docs/public/meteor-blue.png index c91f7a9718..0934189a3e 100644 Binary files a/v3-docs/v3-migration-docs/public/meteor-blue.png and b/v3-docs/v3-migration-docs/public/meteor-blue.png differ diff --git a/v3-docs/v3-migration-docs/public/meteor-logo.png b/v3-docs/v3-migration-docs/public/meteor-logo.png index ca7bb89de5..3b848982ca 100644 Binary files a/v3-docs/v3-migration-docs/public/meteor-logo.png and b/v3-docs/v3-migration-docs/public/meteor-logo.png differ diff --git a/v3-docs/v3-migration-docs/versions-generator.js b/v3-docs/v3-migration-docs/versions-generator.js new file mode 100644 index 0000000000..8573648b46 --- /dev/null +++ b/v3-docs/v3-migration-docs/versions-generator.js @@ -0,0 +1,43 @@ +const fs = require("node:fs"); + +const develDevBundleLink = + "https://raw.githubusercontent.com/meteor/meteor/refs/heads/devel/scripts/build-dev-bundle-common.sh"; + +const meteorToolLink = + "https://raw.githubusercontent.com/meteor/meteor/refs/heads/devel/packages/meteor-tool/package.js"; + +const getMeteorVersionFromDevel = async () => { + const response = await fetch(meteorToolLink); + const text = await response.text(); + const version = text + .match(/version: (.*)/)[1] + .replaceAll("'", "") + .replaceAll('"', "") + .replaceAll(",", ""); + return version; +}; + +const getNodeAndNpmVersionFromDevel = async () => { + const response = await fetch(develDevBundleLink); + const text = await response.text(); + const nodeVersion = text.match(/NODE_VERSION=(.*)/)[1]; + const npmVersion = text.match(/NPM_VERSION=(.*)/)[1]; + return { nodeVersion, npmVersion }; +}; + +async function main() { + const [meteorVersion, { nodeVersion, npmVersion }] = await Promise.all([ + getMeteorVersionFromDevel(), + getNodeAndNpmVersionFromDevel(), + ]); + + const newIndexFile = fs + .readFileSync("index.md", "utf8") + .replace(/meteor_version: (.*)/, `meteor_version: ${meteorVersion}`) + .replace(/node_version: (.*)/, `node_version: ${nodeVersion}`) + .replace(/npm_version: (.*)/, `npm_version: ${npmVersion}`); + + fs.writeFileSync("index.md", newIndexFile); +} + +main();