diff --git a/.circleci/config.yml b/.circleci/config.yml index 3ef186a796..8dfa556f94 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -116,7 +116,8 @@ set_fibers_env: &set_fibers_env matrix_for_fibers: &matrix_for_fibers matrix: parameters: - fibers: [true, false] + # If we want to run with Fibers and without, just append false here. + fibers: [true] jobs: diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 88603611be..1861d20d9e 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -9,6 +9,10 @@ name: Labeler on: - pull_request_target +permissions: + contents: read # to determine modified files (actions/labeler) + pull-requests: write # to add labels to PRs (actions/labeler) + jobs: label: runs-on: ubuntu-latest diff --git a/.github/workflows/npm-eslint-plugin-meteor.yml b/.github/workflows/npm-eslint-plugin-meteor.yml index cb283e3c54..33c0ca5921 100644 --- a/.github/workflows/npm-eslint-plugin-meteor.yml +++ b/.github/workflows/npm-eslint-plugin-meteor.yml @@ -6,6 +6,10 @@ on: pull_request: paths: - "npm-packages/eslint-plugin-meteor/**" + +permissions: + contents: read # to fetch code (actions/checkout) + jobs: test: runs-on: ubuntu-latest diff --git a/.github/workflows/npm-meteor-babel.yml b/.github/workflows/npm-meteor-babel.yml index 6a20fb61b2..c2a260b3ae 100644 --- a/.github/workflows/npm-meteor-babel.yml +++ b/.github/workflows/npm-meteor-babel.yml @@ -6,6 +6,10 @@ on: pull_request: paths: - "npm-packages/meteor-babel/**" + +permissions: + contents: read # to fetch code (actions/checkout) + jobs: test: runs-on: ubuntu-latest diff --git a/.github/workflows/npm-meteor-promise.yml b/.github/workflows/npm-meteor-promise.yml index 247a5a76c2..6351c718fd 100644 --- a/.github/workflows/npm-meteor-promise.yml +++ b/.github/workflows/npm-meteor-promise.yml @@ -6,6 +6,10 @@ on: pull_request: paths: - "npm-packages/meteor-promise/**" + +permissions: + contents: read # to fetch code (actions/checkout) + jobs: test: runs-on: ubuntu-latest diff --git a/.travis.yml b/.travis.yml index d3f1d88c1d..35e4e9f859 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,8 @@ env: - phantom=false - PUPPETEER_DOWNLOAD_PATH=~/.npm/chromium jobs: - - DISABLE_FIBERS=1 + # We don't want to run the tests without fibers anymore. + # - DISABLE_FIBERS=1 # Use a different flag, since node would use false as a string. - FIBERS_ENABLED=1 addons: diff --git a/docs/_config.yml b/docs/_config.yml index 78db90b0a2..1bd31f0460 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,6 +1,7 @@ title: Meteor API Docs subtitle: API Docs versions: + - '2.9' - '2.8' - '2.7' - '2.6' @@ -86,6 +87,7 @@ sidebar_categories: Command Line: - commandline - environment-variables + - using-core-types Troubleshooting: - expired-certificate - windows diff --git a/docs/history.md b/docs/history.md index c748fda441..cf90d38dde 100644 --- a/docs/history.md +++ b/docs/history.md @@ -1,16 +1,259 @@ -## 2.8.1, Unreleased +## v2.9, 2022-12-12 -#### Highlights +### Highlights + +* TypeScript update to v4.6.4 [PR](https://github.com/meteor/meteor/pull/12204) by [@StorytellerCZ](https://github.com/StorytellerCZ). +* Create Email.sendAsync method without using Fibers [PR](https://github.com/meteor/meteor/pull/12101) + by [edimarlnx](https://github.com/edimarlnx). +* Create async method CssTools.minifyCssAsync [PR](https://github.com/meteor/meteor/pull/12105) + by [edimarlnx](https://github.com/edimarlnx). +* Change Accounts and Oauth to use Async methods [PR](https://github.com/meteor/meteor/pull/12156) + by [edimarlnx](https://github.com/edimarlnx). +* TinyTest package without Future [PR](https://github.com/meteor/meteor/pull/12222) + by [matheusccastroo](https://github.com/matheusccastroo). +* Feat: user accounts base async [PR](https://github.com/meteor/meteor/pull/12274) + by [Grubba27](https://github.com/Grubba27). +* Move somed methods from OAuth of out of accounts-base [PR](https://github.com/meteor/meteor/pull/12202) + by [StorytellerCZ](https://github.com/StorytellerCZ). +* Feat: not using insecure & autopublish [PR](https://github.com/meteor/meteor/pull/12220) + by [Grubba27](https://github.com/Grubba27). +* Don't apply babel async-await plugin when not running on Fibers [PR](https://github.com/meteor/meteor/pull/12221). + by [matheusccastroo](https://github.com/matheusccastroo). +* Implemented Fibers-less MongoDB count methods [PR](https://github.com/meteor/meteor/pull/12295) + by [radekmie](https://github.com/radekmie). +* Feat: Generate scaffold in cli [PR](https://github.com/meteor/meteor/pull/12298) + by [Grubba27](https://github.com/Grubba27). +* Update types [PR](https://github.com/meteor/meteor/pull/12306) by [piotrpospiech](https://github.com/piotrpospiech). +* Remove underscore from package-version-parser [PR](https://github.com/meteor/meteor/pull/12248) + by [harryadel](https://github.com/harryadel). +* Update MongoDB driver version [PR](https://github.com/meteor/meteor/pull/12333) by [Grubba27](https://github.com/Grubba27). +* New Vue3 Skeleton [PR](https://github.com/meteor/meteor/pull/12302) + by [henriquealbert](https://github.com/henriquealbert). #### Breaking Changes +* OAuth related code has been moved from `accounts-base` to `accounts-oauth`, removing the dependency on `service-configuration` +more can be seen in this [discussion](https://github.com/meteor/meteor/discussions/12171) and in the [PR](https://github.com/meteor/meteor/pull/12202). + +#### Migration Steps + +You can follow in [here](https://guide.meteor.com/2.9-migration.html). + +#### Meteor Version Release + +* `eslint-plugin-meteor@7.4.0`: + - updated Typescript deps and meteor babel. +* `eslint-plugin-meteor@7.4.0`: + - updated Typescript deps and meteor babel. +* `accounts-base@2.2.6` + - Moved some functions to accounts-oauth. +* `accounts-oauth@1.4.2` + - Received functions from accounts-base. +* `accounts-password@2.3.2` + - Asyncfied functions such as `changePassword`, `forgotPassword`, `resetPassword`, `verifyEmail`, `setPasswordAsync`. +* `babel-compiler@7.10.1` + - Updated babel to 7.17.1. +* `email@2.2.3` + - Create Email.sendAsync method without using Fibers. +* `facebook-oauth@1.11.2` + - Updated facebook-oauth to use async functions. +* `github-oauth@1.4.1` + - Updated github-oauth to use async functions. +* `google-oauth@1.4.3` + - Updated google-oauth to use async functions. +* `meetup-oauth@1.1.2` + - Updated meetup-oauth to use async functions. +* `meteor-developer-oauth@1.3.2` + - Updated meteor-developer-oauth to use async functions. +* `meteor@1.10.3` + - Added Async Local Storage helpers. +* `minifier-css@1.6.2` + - Asyncfied `minifyCss` function. +* `minimongo@1.9.1` + - Implemented Fibers-less MongoDB count methods. +* `mongo@1.16.2` + - Implemented Fibers-less MongoDB count methods. +* `npm-mongo@4.12.1` + - Updated npm-mongo to 4.12. +* `oauth@2.1.3` + - Asyncfied methods. +* `oauth1@1.5.1` + - Asyncfied methods. +* `oauth2@1.3.2` + - Asyncfied methods. +* `package-version-parser@3.2.1` + - Removed underscore. +* `promise@0.12.2` + - Added DISABLE_FIBERS flag. +* `standard-minifier-css@1.8.3` + - Asyncfied minify method. +* `test-helpers@1.3.1` + - added runAndThrowIfNeeded function. +* `test-in-browser@1.3.2` + - Adjusted e[type] to e.type +* `tinytest@1.2.2` + - TinyTest package without Future. +* `twitter-oauth@1.3.2` + - Asyncfied methods. +* `typescript@4.6.4` + - updated typescript to 4.6.4. +* `weibo-oauth@1.3.2` + - Asyncfied methods. + +#### Special thanks to +- [@henriquealbert](https://github.com/henriquealbert); +- [@edimarlnx](https://github.com/edimarlnx); +- [@matheusccastroo](https://github.com/matheusccastroo); +- [@Grubba27](https://github.com/Grubba27); +- [@StorytellerCZ](https://github.com/StorytellerCZ); +- [@radekmie](https://github.com/radekmie); +- [@piotrpospiech](https://github.com/piotrpospiech); +- [@harryadel](https://github.com/harryadel); + +For making this great framework even better! + + +## v2.8.2, 2022-11-29 + +#### Highlights +* `mongo@1.16.2`: + - Make count NOT create a cursor. [PR](https://github.com/meteor/meteor/pull/12326). +* `meteorjs/babel@7.16.1-beta.0` + - Adjusted config to Auto import React on jsx,tsx files [PR](https://github.com/meteor/meteor/pull/12327). + - needs to use directly from npm the meteorjs/babel@7.16.1-beta.0. + +#### Breaking Changes +N/A + #### Migration Steps #### Meteor Version Release +* `mongo@1.16.2`: + - Make count NOT create a cursor. [PR](https://github.com/meteor/meteor/pull/12326). + +#### Special thanks to +- [@henriquealbert](https://github.com/henriquealbert); +- [@znewsham](https://github.com/znewsham); + +For making this great framework even better! + + + +## v2.8.1, 2022-11-14 + +#### Highlights + +- modernize tools/run-updater.js by [afrokick](https://github.com/afrokick) +- feat(error message): Especifing error message when cross-boundary by [Grubba27](https://github.com/Grubba27) +- Type definitions for core packages by [piotrpospiech](https://github.com/piotrpospiech) +- Add https proxy support to meteor-installer by [heschong](https://github.com/heschong) +- Fix case insensitive lookup resource overuse by [ToyboxZach](https://github.com/ToyboxZach) +- Update default Facebook API to v15 and fix local changelog by [StorytellerCZ](https://github.com/StorytellerCZ) +- Bump to Node v14.21.1 by [StorytellerCZ](https://github.com/StorytellerCZ) +- Use true mongo binary types by [znewsham](https://github.com/znewsham) +- Add docs for Accounts.registerLoginHandler by [shivam1646](https://github.com/shivam1646) +- Updated MongoDB driver to 4.11 by [radekmie](https://github.com/radekmie) +- Show port in restart message by [harryadel](https://github.com/harryadel) +- In the client, don't wait if the stub doesn't return a promise by [denihs](https://github.com/denihs) +- The rest of type definitions for core packages by [piotrpospiech](https://github.com/piotrpospiech) +- Removing underscore in packages by [harryadel](https://github.com/harryadel): + - [twitter-oauth] Remove underscore + - [test-in-browser] Remove underscore + - [webapp-hashing] Remove underscore + - [browser-policy] Remove underscore + - [ecmascript] Remove underscore + - [browser-policy-framing] Remove underscore + - [diff-sequence] Remove underscore + - [facts-ui] Remove underscore + - [geojson-utils] Remove underscore + +#### Breaking Changes + +N/A + +#### Migration Steps + +_In case you want types in your app using the core packages types/zodern:types (now you do have the option)_ + +1. Remove `@types/meteor` package +2. Install [`zodern:types`](https://github.com/zodern/meteor-types) package +3. Follow [installation guide for the Meteor Apps](https://github.com/zodern/meteor-types#meteor-apps) to update + +#### Meteor Version Release + +* `accounts-base@2.2.5` + - added types for package. +* `browser-policy@1.1.1` + - adjusted package tests. +* `browser-policy-common@1.0.12` + - added types for package. +* `browser-policy-framing@1.1.1` + - removed underscore. +* `check@1.3.2` + - added types for package. +* `ddp@1.4.0` + - added types for package. +* `ddp-client@2.6.1` + - In the client, don't wait if the stub doesn't return a promise. +* `ddp-rate-limiter@1.1.1` + - added types for package. +* `diff-sequence@1.1.2` + - removed underscore. +* `ecmascript@0.16.3` + - removed underscore. +* `ejson@1.1.3` + - added types for package. +* `ejson@2.2.2` + - added types for package. * `facebook-oauth@1.12.0` - Updated default version of Facebook GraphAPI to v15 - - +* `facts-ui@1.0.1` + - removed underscore. +* `fetch@0.1.2` + - added types for package. +* `geojson-utils@1.0.11` + - removed underscore. +* `hot-module-replacement@0.5.2` + - added types for package. +* `meteor@1.10.2` + - added types for package. +* `modern-browsers@0.1.9` + - added types for package. +* `modules-runtime@0.13.2` + - added accurate error messages. +* `modules-runtime-hot@0.14.1` + - added accurate error messages. +* `mongo@1.16.1` + - added types for package. + - added true mongo binary +* `npm-mongo@4.11.0` + - updated npm mongo version to match npm one. +* `promise@0.13.0` + - added types for package. +* `random@1.2.1` + - added types for package. +* `reactive-dict@1.3.1` + - added types for package. +* `reactive-dict@1.0.12` + - added types for package. +* `server-render@0.4.1` + - added types for package. +* `service-configuration@1.3.1` + - added types for package. +* `session@1.2.1` + - added types for package. +* `test-in-browser@1.3.1` + - removed underscore. +* `tracker@1.2.1` +- added types for package. +* `twitter-oauth@1.3.1` + - removed underscore. +* `underscore@1.0.11` + - added types for package. +* `webapp@1.13.2` + - added types for package. +* `webapp-hashing@1.1.1` + - added types for package. ## v2.8, 2022-10-19 #### Highlights @@ -53,7 +296,16 @@ Read our [Migration Guide](https://guide.meteor.com/2.8-migration.html) for this - Validates required Node.js version. [PR](https://github.com/meteor/meteor/pull/12066). * `npm-mongo@4.9.0`: - Updated MongoDB driver to 4.9. [PR](https://github.com/meteor/meteor/pull/12163). - +* `@meteorjs/babel@7.17.0` + - Upgrade TypeScript to `4.6.4` +* `babel-compiler@7.10.0` + - Upgrade TypeScript to `4.6.4` +* `ecmascript@0.16.3` + - Upgrade TypeScript to `4.6.4` +* `typescript@4.6.4` + - Upgrade TypeScript to `4.6.4` +* `eslint-plugin-meteor@7.4.0` + - Upgrade TypeScript to `4.6.4` #### Independent Releases * `accounts-passwordless@2.1.3`: @@ -683,7 +935,7 @@ This version should be ignored. Proceed to 2.5.5 above. * Typescript updated to [v4.3.5](https://github.com/Microsoft/TypeScript/releases/tag/v4.3.5) * Email package now allows setting `Email.customTransport` to override sending method. * Use `createIndex` instead of `_ensureIndex` to align with new MongoDB naming. -* Apollo skeleton has been upgraded for [Apollo server v3](https://github.com/apollographql/apollo-server/blob/main/CHANGELOG.md#v300) +* Apollo skeleton has been upgraded for [Apollo server v3](https://github.com/apollographql/apollo-server/blob/main/CHANGELOG_historical.md#v300) * `reify` has been updated to v0.22.2 which reduces the overhead of `import` statements and some uses of `export ... from`, especially when a module is imported a large number of times or re-exports a large number of exports from other modules. PRs [1](https://github.com/benjamn/reify/pull/246), [2](https://github.com/benjamn/reify/pull/291) * Meteor NPM installer is [now available for all platforms](https://github.com/meteor/meteor/pull/11590). * DDP server now allows you to set publication strategies for your publications to control mergebox behavior @@ -1053,7 +1305,7 @@ This version should be ignored. Proceed to 2.5.5 above. - The undocumented environment variable `DDP_DEFAULT_CONNECTION_URL` behavior has changed. Setting `DDP_DEFAULT_CONNECTION_URL` when running the server (development: `meteor run` or production: `node main.js`) sets the default DDP server value for meteor. But this did not work for `cordova` apps. Now you can define the `cordova` app default DDP server value by setting `DDP_DEFAULT_CONNECTION_URL` when building (`meteor build`). - Skeletons dependencies updated to latest version - Svelte skeleton now has HMR - - New deploy option: `--build-only`. Helpful if you want to build first and after some validations proceeding with the upload and deploy. [Read more](https://cloud-guide.meteor.com/deploy-guide.html#cache-only) + - New deploy option: `--build-only`. Helpful if you want to build first and after some validations proceeding with the upload and deploy. [Read more](https://galaxy-guide.meteor.com/deploy-command-line.html#cache-only) - Improved watched system to properly rebuild `client` even when a file is outside of `client` or `imports` folders. See [PR](https://github.com/meteor/meteor/pull/11474) for details. - Fix an issue when `App.appendToConfig` crashed Cordova build. - Reify compiler now uses cache in runtime. [Read more](https://github.com/meteor/meteor/pull/11400) @@ -1541,7 +1793,7 @@ N/A * `meteor create --vue` is now available thanks to [@chris-visser](https://github.com/chris-visser). PR [#11086](https://github.com/meteor/meteor/pull/11086) -* `--cache-build` option is now available on `meteor deploy` command and you can use it safely all the time if you are using a Git repository to run your deploy. This is helpful if your upload is failing then you can retry just the upload and also if you deploy the same bundle to multiple environments. [Read more](https://cloud-guide.meteor.com/deploy-guide.html#cache-build). +* `--cache-build` option is now available on `meteor deploy` command and you can use it safely all the time if you are using a Git repository to run your deploy. This is helpful if your upload is failing then you can retry just the upload and also if you deploy the same bundle to multiple environments. [Read more](https://galaxy-guide.meteor.com/deploy-command-line.html#cache-build) * Multiple optimizations in build performance, many of them for Windows thanks to [@zodern](https://github.com/zodern). PRs [#10838](https://github.com/meteor/meteor/pull/10838), [#11114](https://github.com/meteor/meteor/pull/11114), [#11115](https://github.com/meteor/meteor/pull/11115), [#11102](https://github.com/meteor/meteor/pull/11102), [#10839](https://github.com/meteor/meteor/pull/10839) @@ -3923,9 +4175,9 @@ N/A > Note: With this version of Reify, `import` declarations are compiled to `module.watch(require(id), ...)` instead of `module.importSync(id, ...)` -or the older `module.import(id, ...)`. The behavior of the compiled code -should be the same as before, but the details seemed different enough to -warrant a note. +> or the older `module.import(id, ...)`. The behavior of the compiled code +> should be the same as before, but the details seemed different enough to +> warrant a note. * The `install` npm package has been upgraded to version 0.10.1. @@ -4269,15 +4521,15 @@ https://github.com/meteor/meteor/commit/0cbd25111d1249a61ca7adce23fad5215408c821 are once again constrained by the current Meteor release. > Before Meteor 1.4, the current release dictated the exact version of -every installed core package, which meant newer core packages could not -be installed without publishing a new Meteor release. In order to -support incremental development of core packages, Meteor 1.4 removed all -release-based constraints on core package versions +> every installed core package, which meant newer core packages could not +> be installed without publishing a new Meteor release. In order to +> support incremental development of core packages, Meteor 1.4 removed all +> release-based constraints on core package versions ([#7084](https://github.com/meteor/meteor/pull/7084)). Now, in Meteor -1.4.3, core package versions must remain patch-compatible with the -versions they had when the Meteor release was published. This middle -ground restores meaning to Meteor releases, yet still permits patch -updates to core packages. +> 1.4.3, core package versions must remain patch-compatible with the +> versions they had when the Meteor release was published. This middle +> ground restores meaning to Meteor releases, yet still permits patch +> updates to core packages. * The `cordova-lib` npm package has been updated to 6.4.0, along with cordova-android (6.1.1) and cordova-ios (4.3.0), and various plugins. @@ -4367,11 +4619,11 @@ updates to core packages. change was deemed too significant for this release. > Note: The decision to revert the above change was made late in the -Meteor 1.4.2.4 release process, before it was ever recommended but too -late in the process to avoid the additional increment of the version number. -See [#8311](https://github.com/meteor/meteor/pull/8311) for additional -information. This change will still be released in an upcoming version -of Meteor with a more seamless upgrade. +> Meteor 1.4.2.4 release process, before it was ever recommended but too +> late in the process to avoid the additional increment of the version number. +> See [#8311](https://github.com/meteor/meteor/pull/8311) for additional +> information. This change will still be released in an upcoming version +> of Meteor with a more seamless upgrade. ## v1.4.2.4, 2017-02-02 @@ -4380,7 +4632,7 @@ of Meteor with a more seamless upgrade. * The `npm` npm package has been upgraded from version 3.10.9 to 4.1.2. > Note: This change was later deemed too substantial for a point release -and was reverted in Meteor 1.4.2.7. +> and was reverted in Meteor 1.4.2.7. * Fix for [Issue #8136](https://github.com/meteor/meteor/issues/8136). @@ -4407,9 +4659,9 @@ and was reverted in Meteor 1.4.2.7. > Note: Meteor 1.4.2.2 was finalized before [#8045](https://github.com/meteor/meteor/pull/8045) was merged, but -those changes were [deemed important +> those changes were [deemed important enough](https://github.com/meteor/meteor/pull/8044#issuecomment-260913739) -to skip recommending 1.4.2.2 and instead immediately release 1.4.2.3. +> to skip recommending 1.4.2.2 and instead immediately release 1.4.2.3. ## v1.4.2.2, 2016-11-15 @@ -4496,10 +4748,10 @@ to skip recommending 1.4.2.2 and instead immediately release 1.4.2.3. See https://github.com/meteor/meteor/pull/7668 for more details. > Note: the `METEOR_PROFILE` environment variable now provides data for -server startup time as well as build time, which should make it easier -to tell which of your packages are responsible for slow startup times. -Please include the output of `METEOR_PROFILE=10 meteor run` with any -GitHub issue about rebuild performance. +> server startup time as well as build time, which should make it easier +> to tell which of your packages are responsible for slow startup times. +> Please include the output of `METEOR_PROFILE=10 meteor run` with any +> GitHub issue about rebuild performance. * `npm` has been upgraded to version 3.10.9. @@ -5076,8 +5328,8 @@ GitHub issue about rebuild performance. ## v1.3.2.4, 2016-04-20 > Meteor 1.3.2.4 was published because publishing 1.3.2.3 failed in an -unrecoverable way. Meteor 1.3.2.4 contains no additional changes beyond -the changes in 1.3.2.3. +> unrecoverable way. Meteor 1.3.2.4 contains no additional changes beyond +> the changes in 1.3.2.3. ## v1.3.2.3, 2016-04-20 diff --git a/docs/source/commandline.md b/docs/source/commandline.md index ae79273d84..ebb9381d3a 100644 --- a/docs/source/commandline.md +++ b/docs/source/commandline.md @@ -129,7 +129,19 @@ Create a basic [Blaze](https://blazejs.org/) app. `--vue` -Create a basic vue-based app. See the [Vue guide](https://guide.meteor.com/vue.html) +Create a basic [Vue 3](https://vuejs.org/) app. + +`--react` + +Create a basic react app. See the section on [React tutorial](https://guide.meteor.com/react.html#react-tutorial) +for more information. This is the default. + +`--angular` +for more information. + +`--vue-2` + +Create a basic vue2-based app. See the [Vue guide](https://vue-tutorial.meteor.com/) for more information. `--svelte` @@ -146,43 +158,350 @@ Create a basic [React](https://reactjs.org) + [Chakra-UI](https://chakra-ui.com/ `--solid` -Create a basic [solid](https://www.solidjs.com/) app. +Create a basic [Solid](https://www.solidjs.com/) app. **Packages** -| | Default (`--react`) | `--bare` | `--full` | `--minimal` | `--blaze` | `--apollo` | `--vue` | `--svelte` | `--tailwind` | `--chakra-ui` | `--solid` | -|------------------------------------------------------------------------------------------------------|:-------------------:|:--------:|:--------:|:-----------:|:---------:|:----------:|:-------:|:----------:|:------------:|:-------------:|:---------:| -| [autopublish](https://atmospherejs.com/meteor/autopublish) | X | | | | X | | | X | X | X | X | -| [akryum:vue-component](https://atmospherejs.com/akryum/vue-component) | | | | | | | X | | | | | -| [apollo](https://atmospherejs.com/meteor/apollo) | | | | | | X | | | | | | -| [blaze-html-templates](https://atmospherejs.com/meteor/blaze-html-templates) | | | X | | X | | | | | | | -| [ecmascript](https://atmospherejs.com/meteor/ecmascript) | X | X | X | X | X | X | X | X | X | X | X | -| [es5-shim](https://atmospherejs.com/meteor/es5-shim) | X | X | X | X | X | X | X | X | X | X | X | -| [hot-module-replacement](https://atmospherejs.com/meteor/hot-module-replacement) | X | | | | X | X | | | X | X | X | -| [insecure](https://atmospherejs.com/meteor/insecure) | X | | | | X | | | X | X | X | X | -| [johanbrook:publication-collector](https://atmospherejs.com/meteor/johanbrook/publication-collector) | | | X | | | X | | | | | | -| [jquery](https://atmospherejs.com/meteor/jquery) | | | X | | X | | | | | | | -| [ostrio:flow-router-extra](https://atmospherejs.com/meteor/ostrio/flow-router-extra) | | | X | | | | | | | | | -| [less](https://atmospherejs.com/meteor/less) | | | X | | | | | | | | | -| [meteor](https://atmospherejs.com/meteor/meteor) | | | | X | | | | | | | | -| [meteor-base](https://atmospherejs.com/meteor/meteor-base) | X | X | X | | X | X | X | X | X | X | X | -| [mobile-experience](https://atmospherejs.com/meteor/mobile-experience) | X | X | X | | X | X | X | X | X | X | X | -| [mongo](https://atmospherejs.com/meteor/mongo) | X | X | X | | X | X | X | X | X | X | X | -| [meteortesting:mocha](https://atmospherejs.com/meteortesting/mocha) | | | X | | | | X | | | | | -| [reactive-var](https://atmospherejs.com/meteor/reactive-var) | X | X | X | | X | X | X | X | X | X | X | -| [rdb:svelte-meteor-data](https://atmospherejs.com/rdb/svelte-meteor-data) | | | | | | | | X | | | | -| [server-render](https://atmospherejs.com/meteor/server-render) | | | | X | | X | X | | | | | -| [shell-server](https://atmospherejs.com/meteor/shell-server) | | X | | X | X | X | X | X | X | X | X | -| [standard-minifier-css](https://atmospherejs.com/meteor/standard-minifier-css) | X | X | X | X | X | X | X | X | X | X | X | -| [standard-minifier-js](https://atmospherejs.com/meteor/standard-minifier-js) | X | X | X | X | X | X | X | X | X | X | X | -| [static-html](https://atmospherejs.com/meteor/static-html) | | X | | X | | X | X | X | | | | -| [svelte:compiler](https://atmospherejs.com/svelte/compiler) | | | | | | | | X | | | | -| [swydo:graphql](https://atmospherejs.com/swydo/graphql) | | | | | | X | | | | | | -| [tailwindcss](https://tailwindcss.com) | | X | X | | X | | X | | X | | | -| [tracker](https://atmospherejs.com/meteor/tracker) | | X | X | | X | | X | | | | | -| [typescript](https://atmospherejs.com/meteor/typescript) | X | X | X | X | X | X | X | X | X | X | X | -| [webapp](https://atmospherejs.com/meteor/webapp) | | | | X | | | | | | | | -| [react-meteor-data](https://atmospherejs.com/meteor/react-meteor-data) | X | | | | | | | | X | X | | +| | Default (`--react`) | `--bare` | `--full` | `--minimal` | `--blaze` | `--apollo` | `--vue-2` | `--svelte` | `--tailwind` | `--chakra-ui` | `--solid` | `--vue` | +|------------------------------------------------------------------------------------------------------|:-------------------:|:--------:|:--------:|:-----------:|:---------:|:----------:|:---------:|:----------:|:------------:|:-------------:|:---------:|:-------:| +| [autopublish](https://atmospherejs.com/meteor/autopublish) | X | | | | X | | | X | X | X | X | | +| [akryum:vue-component](https://atmospherejs.com/akryum/vue-component) | | | | | | | X | | | | | | +| [apollo](https://atmospherejs.com/meteor/apollo) | | | | | | X | | | | | | | +| [blaze-html-templates](https://atmospherejs.com/meteor/blaze-html-templates) | | | X | | X | | | | | | | | +| [ecmascript](https://atmospherejs.com/meteor/ecmascript) | X | X | X | X | X | X | X | X | X | X | X | X | +| [es5-shim](https://atmospherejs.com/meteor/es5-shim) | X | X | X | X | X | X | X | X | X | X | X | X | +| [hot-module-replacement](https://atmospherejs.com/meteor/hot-module-replacement) | X | | | | X | X | | | X | X | X | X | +| [insecure](https://atmospherejs.com/meteor/insecure) | X | | | | X | | | X | X | X | X | X | +| [johanbrook:publication-collector](https://atmospherejs.com/meteor/johanbrook/publication-collector) | | | X | | | X | | | | | | | +| [jquery](https://atmospherejs.com/meteor/jquery) | | | X | | X | | | | | | | | +| [ostrio:flow-router-extra](https://atmospherejs.com/meteor/ostrio/flow-router-extra) | | | X | | | | | | | | | | +| [less](https://atmospherejs.com/meteor/less) | | | X | | | | | | | | | | +| [meteor](https://atmospherejs.com/meteor/meteor) | | | | X | | | | | | | | | +| [meteor-base](https://atmospherejs.com/meteor/meteor-base) | X | X | X | | X | X | X | X | X | X | X | X | +| [mobile-experience](https://atmospherejs.com/meteor/mobile-experience) | X | X | X | | X | X | X | X | X | X | X | X | +| [mongo](https://atmospherejs.com/meteor/mongo) | X | X | X | | X | X | X | X | X | X | X | X | +| [meteortesting:mocha](https://atmospherejs.com/meteortesting/mocha) | | | X | | | | X | | | | | | +| [reactive-var](https://atmospherejs.com/meteor/reactive-var) | X | X | X | | X | X | X | X | X | X | X | X | +| [rdb:svelte-meteor-data](https://atmospherejs.com/rdb/svelte-meteor-data) | | | | | | | | X | | | | | +| [server-render](https://atmospherejs.com/meteor/server-render) | | | | X | | X | X | | | | | | +| [shell-server](https://atmospherejs.com/meteor/shell-server) | | X | | X | X | X | X | X | X | X | X | X | +| [standard-minifier-css](https://atmospherejs.com/meteor/standard-minifier-css) | X | X | X | X | X | X | X | X | X | X | X | X | +| [standard-minifier-js](https://atmospherejs.com/meteor/standard-minifier-js) | X | X | X | X | X | X | X | X | X | X | X | X | +| [static-html](https://atmospherejs.com/meteor/static-html) | | X | | X | | X | X | X | | | | | +| [svelte:compiler](https://atmospherejs.com/svelte/compiler) | | | | | | | | X | | | | | +| [swydo:graphql](https://atmospherejs.com/swydo/graphql) | | | | | | X | | | | | | | +| [tailwindcss](https://tailwindcss.com) | | X | X | | X | | X | | X | | | | +| [tracker](https://atmospherejs.com/meteor/tracker) | | X | X | | X | | X | | | | | | +| [typescript](https://atmospherejs.com/meteor/typescript) | X | X | X | X | X | X | X | X | X | X | X | | +| [webapp](https://atmospherejs.com/meteor/webapp) | | | | X | | | | | | | | | +| [react-meteor-data](https://atmospherejs.com/meteor/react-meteor-data) | X | | | | | | | | X | X | | | +| [vite:bundler](https://atmospherejs.com/vite/bundler) | | | | | | | | | | | X | X | + +

meteor generate

+ +``meteor generate`` is a command for generating scaffolds for your current project. When ran without arguments, it will ask +you what is the name of the model you want to generate, if you do want methods for your api and publications. It can be +used as a command line only operation as well. + +running +```bash +meteor generate customer + +``` + +It will generate the following code in ``/imports/api`` +![Screenshot 2022-11-09 at 11 28 29](https://user-images.githubusercontent.com/70247653/200856551-71c100f5-8714-4b34-9678-4f08780dcc8b.png) + +That will have the following code: + + +

collection.js

+ +```js + + import { Mongo } from 'meteor/mongo'; + +export const CustomerCollection = new Mongo.Collection('customer'); + +``` + + + +

methods.js

+ +```js +import { Meteor } from 'meteor/meteor'; +import { check } from 'meteor/check'; +import { CustomerCollection } from './collection'; + +export async function create(data) { + return CustomerCollection.insertAsync({ ...data }); +} + +export async function update(_id, data) { + check(_id, String); + return CustomerCollection.updateAsync(_id, { ...data }); +} + +export async function remove(_id) { + check(_id, String); + return CustomerCollection.removeAsync(_id); +} + +export async function findById(_id) { + check(_id, String); + return CustomerCollection.findOneAsync(_id); +} + +Meteor.methods({ + 'Customer.create': create, + 'Customer.update': update, + 'Customer.remove': remove, + 'Customer.find': findById +}); + +``` + + + +

publication.js

+ +```js + +import { Meteor } from 'meteor/meteor'; +import { CustomerCollection } from './collection'; + +Meteor.publish('allCustomers', function publishCustomers() { + return CustomerCollection.find({}); +}); + + +``` + + + + +

index.js

+ +```js + +export * from './collection'; +export * from './methods'; +export * from './publications'; + +``` + +Also, there is the same version of these methods using TypeScript, that will be shown bellow. + +

path option

+ +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. + +```bash + +meteor generate another-customer --path=server/admin + +``` + +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

+ +```typescript + +import { Mongo } from 'meteor/mongo'; + +export type AnotherCustomer = { + _id?: string; + name: string; + createdAt: Date; +} + +export const AnotherCustomerCollection = new Mongo.Collection('another-customer'); + +``` + +

methods.ts

+ +```typescript + +import { Meteor } from 'meteor/meteor'; +import { Mongo } from 'meteor/mongo'; +import { check } from 'meteor/check'; +import { AnotherCustomer, AnotherCustomerCollection } from './collection'; + +export async function create(data: AnotherCustomer) { + return AnotherCustomerCollection.insertAsync({ ...data }); +} + +export async function update(_id: string, data: Mongo.Modifier) { + check(_id, String); + return AnotherCustomerCollection.updateAsync(_id, { ...data }); +} + +export async function remove(_id: string) { + check(_id, String); + return AnotherCustomerCollection.removeAsync(_id); +} + +export async function findById(_id: string) { + check(_id, String); + return AnotherCustomerCollection.findOneAsync(_id); +} + +Meteor.methods({ + 'AnotherCustomer.create': create, + 'AnotherCustomer.update': update, + 'AnotherCustomer.remove': remove, + 'AnotherCustomer.find': findById +}); + + +``` + + + +

publications.ts

+ +```typescript + +import { Meteor } from 'meteor/meteor'; +import { AnotherCustomerCollection } from './collection'; + +Meteor.publish('allAnotherCustomers', function publishAnotherCustomers() { + return AnotherCustomerCollection.find({}); +}); + +``` + + + +

index.ts

+ +```typescript + +export * from './collection'; +export * from './methods'; +export * from './publications'; + +``` + + + +--- + + +

Using the Wizard

+ + +If you run the following command: + +```bash +meteor generate +``` + +It will prompt the following questions. + +![Screenshot 2022-11-09 at 11 38 29](https://user-images.githubusercontent.com/70247653/200859087-a2ef63b6-7ac1-492b-8918-0630cbd30686.png) + + + + +--- + +

Using your own template

+ +`--templatePath` + +```bash +meteor generate feed --templatePath=/scaffolds-ts +``` +![Screenshot 2022-11-09 at 11 42 47](https://user-images.githubusercontent.com/70247653/200860178-2341befe-bcfd-422f-a4bd-7c9918abfd97.png) + +> Note that this is not a CLI framework inside meteor but just giving some solutions for really common problems out of the box. +> Check out Yargs, Inquirer or Commander for more information about CLI frameworks. + + +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?

+ +Out of the box is provided a few functions such as replacing ``$$name$$``, ``$$PascalName$$`` and ``$$camelName$$`` + +these replacements come from this function: + +_Note that scaffoldName is the name that you have passed as argument_ + +```js +const transformName = (name) => { + return name.replace(/\$\$name\$\$|\$\$PascalName\$\$|\$\$camelName\$\$/g, function (substring, args) { + if (substring === '$$name$$') return scaffoldName; + if (substring === '$$PascalName$$') return toPascalCase(scaffoldName); + if (substring === '$$camelName$$') return toCamelCase(scaffoldName); + }) + } +``` + +

How to bring your own templates?

+ +`--replaceFn` + +There is an option called ``--replaceFn`` that when you pass in given a .js file with two functions it will override all templating that we have defaulted to use your given function. +_example of a replacer file_ +```js +export function transformFilename(scaffoldName, filename) { + console.log(scaffoldName, filename); + return filename +} + +export function transformContents(scaffoldName, contents, fileName) { + console.log(fileName, contents); + return contents +} + +``` +If you run your command like this: + +```bash + meteor generate feed --replaceFn=/fn/replace.js +``` +It will generate files full of ``$$PascalCase$$``using the meteor provided templates. + +A better example of this feature would be the following js file: +```js +const toPascalCase = (str) => { + if(!str.includes('-')) return str.charAt(0).toUpperCase() + str.slice(1); + else return str.split('-').map(toPascalCase).join(''); +} +const toCamelCase = (str) => { + if(!str.includes('-')) return str.charAt(0).toLowerCase() + str.slice(1); + else return str.split('-').map(toPascalCase).join(''); +} + +const transformName = (scaffoldName, str) => { + return str.replace(/\$\$name\$\$|\$\$PascalName\$\$|\$\$camelName\$\$/g, function (substring, args) { + if (substring === '$$name$$') return scaffoldName; + if (substring === '$$PascalName$$') return toPascalCase(scaffoldName); + if (substring === '$$camelName$$') return toCamelCase(scaffoldName); + }) + +} + +export function transformFilename(scaffoldName, filename) { + return transformName(scaffoldName, filename); +} + +export function transformContents(scaffoldName, contents, fileName) { + return transformName(scaffoldName, contents); +} +``` + + +

meteor login / logout

@@ -604,8 +923,8 @@ The `meteor node` command calls the [`node`](https://nodejs.org) version bundled with Meteor itself. > This is not to be confused with [`meteor shell`](#meteorshell), which provides -an almost identical experience but also gives you access to the "server" context -of a Meteor application. Typically, `meteor shell` will be preferred. +> an almost identical experience but also gives you access to the "server" context +> of a Meteor application. Typically, `meteor shell` will be preferred. Additional parameters can be passed in the same way as the `node` command, and the [Node.js documentation](https://nodejs.org/dist/latest-v4.x/docs/api/cli.html) diff --git a/docs/source/using-core-types.md b/docs/source/using-core-types.md new file mode 100644 index 0000000000..89ac1636af --- /dev/null +++ b/docs/source/using-core-types.md @@ -0,0 +1,39 @@ +--- +title: Using Core Types +description: Using core types with zodern:types +--- + +For MeteorJS in its version 2.8.1 we have introduced to our core packages an integration with the [zodern:types](https://github.com/zodern/meteor-types) package. +This package allows you to use the TypeScript types for the Meteor core packages in your TypeScript code or JavaScript code. +in order to use the types you need to install the package by running the command: + +```bash +meteor add zodern:types +``` + +And add the following line to your `tsconfig.json` file (if you do not have one, create one and add the code bellow): + +```json +{ + "compilerOptions": { + "preserveSymlinks": true, + "paths": { + "meteor/*": [ + "node_modules/@types/meteor/*", + ".meteor/local/types/packages.d.ts" + ] + } + } +} +``` + +then run the command: + +```bash +meteor lint +``` + +this will create a file within your .meteor folder that will have your types for the core packages. +You can continue to use your code as you did before, but now you can use the types for the core packages even if you are in JavaScript. + +for more information about the package please visit the [zodern:types](https://github.com/zodern/meteor-types). diff --git a/guide/_config.yml b/guide/_config.yml index 7a99eb70f2..a28b565f8f 100644 --- a/guide/_config.yml +++ b/guide/_config.yml @@ -5,6 +5,7 @@ edit_branch: 'devel' edit_path: 'guide' content_root: 'content' versions: + - '2.9' - '2.8' - '2.7' - '2.6' @@ -37,7 +38,7 @@ sidebar_categories: - index - code-style - structure - - 2.8-migration + - 2.9-migration Data: - collections - data-loading diff --git a/guide/source/2.8-migration.md b/guide/source/2.8-migration.md index 1e1ccb4a8b..8d89edf12f 100644 --- a/guide/source/2.8-migration.md +++ b/guide/source/2.8-migration.md @@ -157,7 +157,7 @@ As `callAsync` returns a promise, it'll be solved in the future. So you need to It's also important to understand what will happen if you call an async method with `Meteor.call`, and vice versa. -If you can an async method with `Meteor.call` in the client, and you don't have the package `insecure` on your project, an error like this will be thrown: +If you call an async method with `Meteor.call` in the client, and you don't have the package `insecure` on your project, an error like this will be thrown: diff --git a/guide/source/2.9-migration.md b/guide/source/2.9-migration.md new file mode 100644 index 0000000000..6da327420a --- /dev/null +++ b/guide/source/2.9-migration.md @@ -0,0 +1,102 @@ +--- +title: Migrating to Meteor 2.9 +description: How to migrate your application to Meteor 2.9. +--- + +Meteor `2.9` introduces some changes in the `accounts` packages, the new method `Email.sendAsync`, the new method `Meteor.userAsync`, and more. For a complete breakdown of the changes, please refer to the [changelog](http://docs.meteor.com/changelog.html). + + +

Why is this new API important?

+ +You may know that on Meteor we use a package called [Fibers](https://github.com/laverdet/node-fibers). This package is what makes it possible to call an async function inside Meteor in a sync way (without having to wait for the promise to resolve). + +But starting from Node 16, Fibers will stop working, so Meteor needs to move away from Fibers, otherwise, we'll be stuck on Node 14. + +If you want to know more about the plan, you can check this [discussion](https://github.com/meteor/meteor/discussions/11505). + +

Why doing this change now?

+ +This will be a considerable change for older Meteor applications, and some parts of the code of any Meteor app will have to be adjusted eventually. So it's important to start the migration process as soon as possible. + +The migration process started in version 2.8. We recommend you [check that out](2.8-migration.htm) first in case you skipped. + +

Can I update to this version without changing my app?

+ +Yes. You can update to this version without changing your app. + +

What's new?

+ +Let's start with the accounts and OAuth packages. Some methods had to be restructured to work without Fibers in the future. The current methods will continue working as of today, but if you use some of the methods we'll mention below in custom login packages, we recommend you adapt them. + +Internal methods that are now async: + +- **_attemptLogin** +- **_loginMethod** +- **_runLoginHandlers** +- **Accounts._checkPassword**: still works as always, but now has a new version called `Accounts._checkPasswordAsync`. + + +We also have changes to asynchronous context in the registry of handlers for OAuth services. + +Now, the OAuth.Register method accepts an async handler, and it is possible to use the await option internally, avoiding to use methods that run on Fibers, such as **HTTP** (deprecated Meteor package), `Meteor.wrapAsync` and `Promise.await`. + +Before the changes you would have something like: + +```js +OAuth.registerService('github', 2, null, (query) => { + const accessTokenCall = Meteor.wrapAsync(getAccessToken); + const accessToken = accessTokenCall(query); + const identityCall = Meteor.wrapAsync(getIdentity); +… +}); +``` + +Now you have: + +```js +OAuth.registerService('github', 2, null, async (query) => { + const accessToken = await getAccessToken(query); + const identity = await getIdentity(accessToken); + const emails = await getEmails(accessToken); +… +}); +``` + +

New async methods

+ +We now have async version of methods that you already use. They are: + +- [Email.sendAsync()](https://github.com/meteor/meteor/pull/12101/files#diff-b2453acdfd34fb563a1e258956d2733ab06a2aa77c87e402cfa53a86a48133a8R86-R107) +- [Meteor.userAsync()](https://github.com/meteor/meteor/pull/12274) +- [CssTools.minifyCssAsync()](https://github.com/meteor/meteor/pull/12105) + +

Accounts-base without service-configuration

+ +Now `accounts-base` is [no longer tied up](https://github.com/meteor/meteor/pull/12202) with `service-configuration`. So, if you don't use third-party login on your project, you don't need to add the package `service-configuration` anymore. + +

Migrating from a version older than 2.8?

+ +If you're migrating from a version of Meteor older than Meteor 2.8, there may be important considerations not listed in this guide. Please review the older migration guides for details: + +* [Migrating to Meteor 2.8](2.8-migration.html) (from 2.7) +* [Migrating to Meteor 2.7](2.7-migration.html) (from 2.6) +* [Migrating to Meteor 2.6](2.6-migration.html) (from 2.5) +* [Migrating to Meteor 2.5](2.5-migration.html) (from 2.4) +* [Migrating to Meteor 2.4](2.4-migration.html) (from 2.3) +* [Migrating to Meteor 2.3](2.3-migration.html) (from 2.2) +* [Migrating to Meteor 2.2](2.2-migration.html) (from 2.0) +* [Migrating to Meteor 2.0](2.0-migration.html) (from 1.12) +* [Migrating to Meteor 1.12](1.12-migration.html) (from 1.11) +* [Migrating to Meteor 1.11](1.11-migration.html) (from 1.10.2) +* [Migrating to Meteor 1.10.2](1.10.2-migration.html) (from 1.10) +* [Migrating to Meteor 1.10](1.10-migration.html) (from 1.9.3) +* [Migrating to Meteor 1.9.3](1.9.3-migration.html) (from 1.9) +* [Migrating to Meteor 1.9](1.9-migration.html) (from 1.8.3) +* [Migrating to Meteor 1.8.3](1.8.3-migration.html) (from 1.8.2) +* [Migrating to Meteor 1.8.2](1.8.2-migration.html) (from 1.8) +* [Migrating to Meteor 1.8](1.8-migration.html) (from 1.7) +* [Migrating to Meteor 1.7](1.7-migration.html) (from 1.6) +* [Migrating to Meteor 1.6](1.6-migration.html) (from 1.5) +* [Migrating to Meteor 1.5](1.5-migration.html) (from 1.4) +* [Migrating to Meteor 1.4](1.4-migration.html) (from 1.3) +* [Migrating to Meteor 1.3](1.3-migration.html) (from 1.2) diff --git a/meteor b/meteor index dff5d789b0..dadfa87b20 100755 --- a/meteor +++ b/meteor @@ -1,6 +1,6 @@ #!/usr/bin/env bash -BUNDLE_VERSION=14.20.1.0 +BUNDLE_VERSION=14.21.1.6 # OS Check. Put here because here is where we download the precompiled # bundles that are arch specific. diff --git a/npm-packages/eslint-config-meteor/README.md b/npm-packages/eslint-config-meteor/README.md index 27c8ba5595..046c174268 100644 --- a/npm-packages/eslint-config-meteor/README.md +++ b/npm-packages/eslint-config-meteor/README.md @@ -1,4 +1,4 @@ -# eslint-config-meteor +# @meteorjs/eslint-config-meteor This is an [ESLint](https://eslint.org) configuration for [Meteor](https://www.meteor.com) apps which implements the recommendations from the [Meteor Guide](https://guide.meteor.com/)'s section on [Code style](https://guide.meteor.com/code-style.html#eslint). diff --git a/npm-packages/eslint-config-meteor/index.js b/npm-packages/eslint-config-meteor/index.js index 5e71e32103..3c191d2ebc 100644 --- a/npm-packages/eslint-config-meteor/index.js +++ b/npm-packages/eslint-config-meteor/index.js @@ -1,21 +1,16 @@ module.exports = { parser: 'babel-eslint', parserOptions: { - allowImportExportEverywhere: true + allowImportExportEverywhere: true, }, env: { node: true, - browser: true + browser: true, }, - plugins: [ - 'meteor' - ], - extends: [ - 'airbnb', - 'plugin:meteor/recommended' - ], + plugins: ['meteor'], + extends: ['airbnb', 'plugin:meteor/recommended'], settings: { - 'import/resolver': 'meteor' + 'import/resolver': 'meteor', }, rules: { 'react/jsx-filename-extension': 0, @@ -30,24 +25,21 @@ module.exports = { 'no-underscore-dangle': [ 'error', { - allow: [ - '_id', - '_ensureIndex' - ] - } + allow: ['_id', '_ensureIndex'], + }, ], 'object-shorthand': [ 'error', 'always', { - avoidQuotes: false - } + avoidQuotes: false, + }, ], 'space-before-function-paren': 0, - + // for Meteor API's that rely on `this` context, e.g. Template.onCreated and publications 'func-names': 0, - 'prefer-arrow-callback': 0 - } + 'prefer-arrow-callback': 0, + }, }; diff --git a/npm-packages/eslint-config-meteor/package.json b/npm-packages/eslint-config-meteor/package.json index faff59b2ad..6b1d62defe 100644 --- a/npm-packages/eslint-config-meteor/package.json +++ b/npm-packages/eslint-config-meteor/package.json @@ -8,14 +8,14 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/meteor/eslint-config-meteor.git" + "url": "git+https://github.com/meteor/meteor.git" }, "author": "David Burles", "license": "MIT", "bugs": { - "url": "https://github.com/meteor/eslint-config-meteor/issues" + "url": "https://github.com/meteor/meteor/issues" }, - "homepage": "https://github.com/meteor/eslint-config-meteor#readme", + "homepage": "https://github.com/meteor/meteor/tree/devel/npm-packages/eslint-config-meteor#readme", "peerDependencies": { "babel-eslint": ">= 7", "eslint": ">= 3", diff --git a/npm-packages/eslint-plugin-meteor/package.json b/npm-packages/eslint-plugin-meteor/package.json index 6f01831721..7845d884a3 100644 --- a/npm-packages/eslint-plugin-meteor/package.json +++ b/npm-packages/eslint-plugin-meteor/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-meteor", - "version": "7.3.0", + "version": "7.4.0", "author": "Dominik Ferber ", "description": "Meteor specific linting rules for ESLint", "main": "lib/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 17b63e201f..de71198d42 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 @@ -14,8 +14,8 @@ var packageJson = { pacote: "https://github.com/meteor/pacote/tarball/a81b0324686e85d22c7688c47629d4009000e8b8", "node-gyp": "8.0.0", "node-pre-gyp": "0.15.0", - typescript: "4.5.4", - "@meteorjs/babel": "7.16.0-beta.7", + typescript: "4.6.4", + "@meteorjs/babel": "7.17.1-beta.0", // Keep the versions of these packages consistent with the versions // found in dev-bundle-server-package.js. "meteor-promise": "0.9.0", diff --git a/npm-packages/meteor-babel/options.js b/npm-packages/meteor-babel/options.js index dc215572b8..e1287b8ca5 100644 --- a/npm-packages/meteor-babel/options.js +++ b/npm-packages/meteor-babel/options.js @@ -185,13 +185,14 @@ function getDefaultsForNode8(features) { // Ensure that async functions run in a Fiber, while also taking // full advantage of native async/await support in Node 8. - if (!process.env.DISABLE_FIBERS) { - combined.plugins.push([require("./plugins/async-await.js"), { - // Do not transform `await x` to `Promise.await(x)`, since Node - // 8 has native support for await expressions. - useNativeAsyncAwait: false - }]); - } + const isFiberDisabled = process.env.DISABLE_FIBERS || false; + combined.plugins.push([require("./plugins/async-await.js"), { + // Do not transform `await x` to `Promise.await(x)`, since Node + // 8 has native support for await expressions. + useNativeAsyncAwait: !isFiberDisabled, + isFiberDisabled: isFiberDisabled, + overwriteFiberExit: process.env.OVERWRITE_FIBERS_EXIT === '1', + }]); // Enable async generator functions proposal. combined.plugins.push(require("@babel/plugin-proposal-async-generator-functions")); diff --git a/npm-packages/meteor-babel/package-lock.json b/npm-packages/meteor-babel/package-lock.json index 8e5d6a7e5d..17c5612fb7 100644 --- a/npm-packages/meteor-babel/package-lock.json +++ b/npm-packages/meteor-babel/package-lock.json @@ -1,8 +1,3875 @@ { "name": "@meteorjs/babel", - "version": "7.16.0-beta.7", - "lockfileVersion": 1, + "version": "7.17.2-beta.0", + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "@meteorjs/babel", + "version": "7.17.2-beta.0", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.17.2", + "@babel/parser": "^7.17.0", + "@babel/plugin-proposal-class-properties": "^7.16.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-modules-commonjs": "^7.16.8", + "@babel/plugin-transform-runtime": "^7.17.0", + "@babel/preset-react": "^7.16.7", + "@babel/runtime": "7.17.2", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.0", + "@babel/types": "^7.17.0", + "@meteorjs/reify": "0.23.0", + "babel-preset-meteor": "^7.10.0", + "babel-preset-minify": "^0.5.1", + "convert-source-map": "^1.6.0", + "lodash": "^4.17.21", + "meteor-babel-helpers": "0.0.3", + "typescript": "~4.6.4" + }, + "devDependencies": { + "@babel/plugin-proposal-decorators": "7.14.5", + "@babel/plugin-syntax-decorators": "7.14.5", + "d3": "4.13.0", + "fibers": "5.0.0", + "meteor-promise": "0.9.0", + "mocha": "6.2.3", + "promise": "8.1.0", + "source-map": "0.6.1" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.1.tgz", + "integrity": "sha512-Aolwjd7HSC2PyY0fDj/wA/EimQT4HfEnFYNp5s9CQlrdhyvWTtvZ5YzrUPu6R6/1jKiUlxu8bUhkdSnKHNAHMA==", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dependencies": { + "@babel/highlight": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.9.tgz", + "integrity": "sha512-p3QjZmMGHDGdpcwEYYWu7i7oJShJvtgMjJeb0W95PPhSm++3lm8YXYOh45Y6iCN9PkZLTZ7CIX5nFrp7pw7TXw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.17.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.2.tgz", + "integrity": "sha512-R3VH5G42VSDolRHyUO4V2cfag8WHcZyxdq5Z/m8Xyb92lW/Erm/6kM+XtRFGf3Mulre3mveni2NHfEUws8wSvw==", + "dependencies": { + "@ampproject/remapping": "^2.0.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.0", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helpers": "^7.17.2", + "@babel/parser": "^7.17.0", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.0", + "@babel/types": "^7.17.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/@babel/compat-data": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.0.tgz", + "integrity": "sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core/node_modules/@babel/helper-compilation-targets": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", + "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", + "dependencies": { + "@babel/compat-data": "^7.16.4", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/core/node_modules/@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core/node_modules/browserslist": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "dependencies": { + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", + "escalade": "^3.1.1", + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/@babel/core/node_modules/caniuse-lite": { + "version": "1.0.30001312", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz", + "integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/@babel/core/node_modules/electron-to-chromium": { + "version": "1.4.68", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.68.tgz", + "integrity": "sha512-cId+QwWrV8R1UawO6b9BR1hnkJ4EJPCPAr4h315vliHUtVUJDk39Sg1PMNnaWKfj5x+93ssjeJ9LKL6r8LaMiA==" + }, + "node_modules/@babel/core/node_modules/node-releases": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==" + }, + "node_modules/@babel/generator": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.0.tgz", + "integrity": "sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==", + "dependencies": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz", + "integrity": "sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA==", + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.14.5.tgz", + "integrity": "sha512-YTA/Twn0vBXDVGJuAX6PwW7x5zQei1luDDo2Pl6q1qZ7hVNl0RZrhHCQG/ArGpR29Vl7ETiB8eJyrvpuRp300w==", + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz", + "integrity": "sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw==", + "dependencies": { + "@babel/compat-data": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.16.6", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.14.8", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.8.tgz", + "integrity": "sha512-bpYvH8zJBWzeqi1o+co8qOrw+EXzQ/0c74gVmY205AWXy9nifHrOg77y+1zwxX5lXE7Icq4sPlSQ4O2kWBrteQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.14.5", + "@babel/helper-function-name": "^7.14.5", + "@babel/helper-member-expression-to-functions": "^7.14.7", + "@babel/helper-optimise-call-expression": "^7.14.5", + "@babel/helper-replace-supers": "^7.14.5", + "@babel/helper-split-export-declaration": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.5.tgz", + "integrity": "sha512-TLawwqpOErY2HhWbGJ2nZT5wSkR192QpN+nBg1THfBfftrlvOh+WbhrxXCH4q4xJ9Gl16BGPR/48JA+Ryiho/A==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.14.5", + "regexpu-core": "^4.7.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz", + "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.14.5.tgz", + "integrity": "sha512-Htb24gnGJdIGT4vnRKMdoXiOIlqOLmdiUYpAQ0mYfgVT/GDm8GOYhgi4GL+hMKrkiPRohO4ts34ELFsGAPQLDQ==", + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz", + "integrity": "sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==", + "dependencies": { + "@babel/helper-get-function-arity": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz", + "integrity": "sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==", + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz", + "integrity": "sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA==", + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", + "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", + "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms/node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz", + "integrity": "sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==", + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", + "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.14.5.tgz", + "integrity": "sha512-rLQKdQU+HYlxBwQIj8dk4/0ENOUEhA/Z0l4hN8BexpvmSMN9oA9EagjnhnDpNsRdWCfjwa4mn/HyBXO9yhQP6A==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.14.5", + "@babel/helper-wrap-function": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz", + "integrity": "sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.14.5", + "@babel/helper-optimise-call-expression": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", + "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.14.5.tgz", + "integrity": "sha512-dmqZB7mrb94PZSAOYtr+ZN5qt5owZIAgqtoTuqiFbHFtxgEcmQlRJVI+bO++fciBunXtB6MK7HrzrfcAzIz2NQ==", + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz", + "integrity": "sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==", + "dependencies": { + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.14.5.tgz", + "integrity": "sha512-YEdjTCq+LNuNS1WfxsDCNpgXkJaIyqco6DAelTUjT4f2KIWC1nBcaCaSdHTBqQVLnTBexBcVcFhLSU1KnYuePQ==", + "dependencies": { + "@babel/helper-function-name": "^7.14.5", + "@babel/template": "^7.14.5", + "@babel/traverse": "^7.14.5", + "@babel/types": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.17.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.2.tgz", + "integrity": "sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==", + "dependencies": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.0", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz", + "integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.9.tgz", + "integrity": "sha512-d1lnh+ZnKrFKwtTYdw320+sQWCTwgkB9fmUhNXRADA4akR6wLjaruSGnIEUjpt9HCOwTr4ynFTKu19b7rFRpmw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.14.5", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", + "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties/node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.17.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.1.tgz", + "integrity": "sha512-JBdSr/LtyYIno/pNnJ75lBcqc3Z1XXujzPanHqjvvrhOA+DTceTFuJi8XjmWTZh4r3fsdfqaCMN0iZemdkxZHQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties/node_modules/@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dependencies": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties/node_modules/@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties/node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz", + "integrity": "sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties/node_modules/@babel/helper-optimise-call-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", + "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties/node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties/node_modules/@babel/helper-replace-supers": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", + "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties/node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.14.5.tgz", + "integrity": "sha512-LYz5nvQcvYeRVjui1Ykn28i+3aUiXwQ/3MGoEy0InTaz1pJo/lAzmIDXX+BQny/oufgHzJ6vnEEiXQ8KZjEVFg==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-decorators": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.5.tgz", + "integrity": "sha512-YGn2AvZAo9TwyhlLvCCWxD90Xq8xJ4aSgaX3G5D/8DW94L8aaT+dS5cSP+Z06+rCJERGSr9GxMBZ601xoc2taw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.5.tgz", + "integrity": "sha512-gun/SOnMqjSb98Nkaq2rTKMwervfdAoz6NphdY0vTfuzMfryj+tDGb2n6UkDKwez+Y8PZDhE3D143v6Gepp4Hg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.7.tgz", + "integrity": "sha512-082hsZz+sVabfmDWo1Oct1u1AgbKbUAyVgmX4otIc7bdsRgHBXwTwb3DpDmD4Eyyx6DNiuz5UAATT655k+kL5g==", + "dependencies": { + "@babel/compat-data": "^7.14.7", + "@babel/helper-compilation-targets": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.5.tgz", + "integrity": "sha512-3Oyiixm0ur7bzO5ybNcZFlmVsygSIQgdOa7cTfOYCMY+wEPAYhZAJxi3mixKFCTCKUhQXuCTtQ1MzrpL3WT8ZQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.5.tgz", + "integrity": "sha512-ycz+VOzo2UbWNI1rQXxIuMOzrDdHGrI23fRiz/Si2R4kv2XZQ1BK8ccdHwehMKBlcH/joGW/tzrUmo67gbJHlQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.14.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.14.5.tgz", + "integrity": "sha512-c4sZMRWL4GSvP1EXy0woIP7m4jkVcEuG8R1TOZxPBPtp4FSM/kiPZub9UIs/Jrb5ZAOzvTUSGYrWsrSu1JvoPw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz", + "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx/node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz", + "integrity": "sha512-KOnO0l4+tD5IfOdi4x8C1XmEIRWUjNRV8wc6K2vz/3e8yAOoZZvsRXRRIF/yo/MAOFb4QjtAw9xSxMXbSMRy8A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.14.5.tgz", + "integrity": "sha512-szkbzQ0mNk0rpu76fzDdqSyPu0MuvpXgC+6rz5rpMb5OIRxdmHfQxrktL8CYolL2d8luMCZTR0DpIMIdL27IjA==", + "dependencies": { + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.14.5.tgz", + "integrity": "sha512-dtqWqdWZ5NqBX3KzsVCWfQI3A53Ft5pWFCT2eCVUftWZgjc5DpDponbIF1+c+7cSGk2wN0YK7HGL/ezfRbpKBQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.5.tgz", + "integrity": "sha512-LBYm4ZocNgoCqyxMLoOnwpsmQ18HWTQvql64t3GvMUzLQrNoV1BDG0lNftC8QKYERkZgCCT/7J5xWGObGAyHDw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.9.tgz", + "integrity": "sha512-NfZpTcxU3foGWbl4wxmZ35mTsYJy8oQocbeIMoDAGGFarAmSQlL+LWMkDx/tj6pNotpbX3rltIA4dprgAPOq5A==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.14.5", + "@babel/helper-function-name": "^7.14.5", + "@babel/helper-optimise-call-expression": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-replace-supers": "^7.14.5", + "@babel/helper-split-export-declaration": "^7.14.5", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.14.5.tgz", + "integrity": "sha512-pWM+E4283UxaVzLb8UBXv4EIxMovU4zxT1OPnpHJcmnvyY9QbPPTKZfEj31EUvG3/EQRbYAGaYEUZ4yWOBC2xg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.7.tgz", + "integrity": "sha512-0mDE99nK+kVh3xlc5vKwB6wnP9ecuSj+zQCa/n0voENtP/zymdT4HH6QEb65wjjcbqr1Jb/7z9Qp7TF5FtwYGw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.14.5.tgz", + "integrity": "sha512-jFazJhMBc9D27o9jDnIE5ZErI0R0m7PbKXVq77FFvqFbzvTMuv8jaAwLZ5PviOLSFttqKIW0/wxNSDbjLk0tYA==", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.14.5.tgz", + "integrity": "sha512-CfmqxSUZzBl0rSjpoQSFoR9UEj3HzbGuGNL21/iFTmjb5gFggJp3ph0xR1YBhexmLoKRHzgxuFvty2xdSt6gTA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.14.5.tgz", + "integrity": "sha512-ql33+epql2F49bi8aHXxvLURHkxJbSmMKl9J5yHqg4PLtdE6Uc48CH1GS6TQvZ86eoB/ApZXwm7jlA+B3kra7A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz", + "integrity": "sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==", + "dependencies": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-simple-access": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs/node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.14.5.tgz", + "integrity": "sha512-MKfOBWzK0pZIrav9z/hkRqIk/2bTv9qvxHzPQc12RcVkMOzpIKnFCNYJip00ssKWYkd8Sf5g0Wr7pqJ+cmtuFg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-replace-supers": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.5.tgz", + "integrity": "sha512-Tl7LWdr6HUxTmzQtzuU14SqbgrSKmaR77M0OKyq4njZLQTPfOvzblNKyNkGwOfEFCEx7KeYHQHDI0P3F02IVkA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.14.5.tgz", + "integrity": "sha512-r1uilDthkgXW8Z1vJz2dKYLV1tuw2xsbrp3MrZmD99Wh9vsfKoob+JTgri5VUb/JqyKRXotlOtwgu4stIYCmnw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz", + "integrity": "sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name/node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz", + "integrity": "sha512-8D16ye66fxiE8m890w0BpPpngG9o9OVBBy0gH2E+2AR7qMR2ZpTYJEqLxAsoroenMId0p/wMW+Blc0meDgu0Ag==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-jsx": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz", + "integrity": "sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx/node_modules/@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx/node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz", + "integrity": "sha512-hs71ToC97k3QWxswh2ElzMFABXHvGiJ01IB1TbYQDGeWRKWz/MPUTh5jGExdHvosYKpnJW5Pm3S4+TA3FyX+GA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations/node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.14.5.tgz", + "integrity": "sha512-NVIY1W3ITDP5xQl50NgTKlZ0GrotKtLna08/uGY6ErQt6VEQZXla86x/CTddm5gZdcr+5GSsvMeTmWA5Ii6pkg==", + "dependencies": { + "regenerator-transform": "^0.14.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.0.tgz", + "integrity": "sha512-fr7zPWnKXNc1xoHfrIU9mN/4XKX4VLZ45Q+oMhfsYIaHvg7mHgmhfOy/ckRWqDK7XF3QDigRpkh5DKq6+clE8A==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz", + "integrity": "sha512-xLucks6T1VmGsTB+GWK5Pl9Jl5+nRXD1uoFdA5TSO6xtiNjtXTjKkmPdFXVLGlK5A2/or/wQMKfmQ2Y0XJfn5g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.14.6.tgz", + "integrity": "sha512-Zr0x0YroFJku7n7+/HH3A2eIrGMjbmAIbJSVv0IZ+t3U2WUQUA64S/oeied2e+MaGSjmt4alzBCsK9E8gh+fag==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.14.5.tgz", + "integrity": "sha512-Z7F7GyvEMzIIbwnziAZmnSNpdijdr4dWt+FJNBnBLz5mwDFkqIXU9wmBcWWad3QeJF5hMTkRe4dAq2sUZiG+8A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.14.5.tgz", + "integrity": "sha512-22btZeURqiepOfuy/VkFr+zStqlujWaarpMErvay7goJS6BWwdd6BY9zQyDLDa4x2S3VugxFb162IZ4m/S/+Gg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.14.5.tgz", + "integrity": "sha512-lXzLD30ffCWseTbMQzrvDWqljvZlHkXU+CnseMhkMNqU1sASnCsz3tSzAaH3vCUXb9PHeUb90ZT1BdFTm1xxJw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.14.5.tgz", + "integrity": "sha512-UygduJpC5kHeCiRw/xDVzC+wj8VaYSoKl5JNVmbP7MadpNinAm3SvZCxZ42H37KZBKztz46YC73i9yV34d0Tzw==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.16.7.tgz", + "integrity": "sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-option": "^7.16.7", + "@babel/plugin-transform-react-display-name": "^7.16.7", + "@babel/plugin-transform-react-jsx": "^7.16.7", + "@babel/plugin-transform-react-jsx-development": "^7.16.7", + "@babel/plugin-transform-react-pure-annotations": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-react/node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/preset-react/node_modules/@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.17.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz", + "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.0.tgz", + "integrity": "sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg==", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.0", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.0", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dependencies": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", + "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", + "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", + "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@meteorjs/reify": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@meteorjs/reify/-/reify-0.23.0.tgz", + "integrity": "sha512-sHQCbZHoM+PxT+pWvkJDsaOpJP+tMQ31Mr2t1T0YcXl18eScb0bQNafe8TugNCc4pngByppfscVX4ppr84MzDw==", + "dependencies": { + "acorn": "^6.1.1", + "acorn-dynamic-import": "^4.0.0", + "magic-string": "^0.25.3", + "periscopic": "^2.0.3", + "semver": "^5.7.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@meteorjs/reify/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==" + }, + "node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-dynamic-import": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", + "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==", + "deprecated": "This is probably built in to whatever tool you're using. If you still need it... idk", + "peerDependencies": { + "acorn": "^6.0.0" + } + }, + "node_modules/ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true + }, + "node_modules/babel-helper-evaluate-path": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-helper-evaluate-path/-/babel-helper-evaluate-path-0.5.0.tgz", + "integrity": "sha512-mUh0UhS607bGh5wUMAQfOpt2JX2ThXMtppHRdRU1kL7ZLRWIXxoV2UIV1r2cAeeNeU1M5SB5/RSUgUxrK8yOkA==" + }, + "node_modules/babel-helper-flip-expressions": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-flip-expressions/-/babel-helper-flip-expressions-0.4.3.tgz", + "integrity": "sha1-NpZzahKKwYvCUlS19AoizrPB0/0=" + }, + "node_modules/babel-helper-is-nodes-equiv": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/babel-helper-is-nodes-equiv/-/babel-helper-is-nodes-equiv-0.0.1.tgz", + "integrity": "sha1-NOmzALFHnd2Y7HfqC76TQt/jloQ=" + }, + "node_modules/babel-helper-is-void-0": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-is-void-0/-/babel-helper-is-void-0-0.4.3.tgz", + "integrity": "sha1-fZwBtFYee5Xb2g9u7kj1tg5nMT4=" + }, + "node_modules/babel-helper-mark-eval-scopes": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.4.3.tgz", + "integrity": "sha1-0kSjvvmESHJgP/tG4izorN9VFWI=" + }, + "node_modules/babel-helper-remove-or-void": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.4.3.tgz", + "integrity": "sha1-pPA7QAd6D/6I5F0HAQ3uJB/1rmA=" + }, + "node_modules/babel-helper-to-multiple-sequence-expressions": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.5.0.tgz", + "integrity": "sha512-m2CvfDW4+1qfDdsrtf4dwOslQC3yhbgyBFptncp4wvtdrDHqueW7slsYv4gArie056phvQFhT2nRcGS4bnm6mA==" + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-minify-builtins": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-builtins/-/babel-plugin-minify-builtins-0.5.0.tgz", + "integrity": "sha512-wpqbN7Ov5hsNwGdzuzvFcjgRlzbIeVv1gMIlICbPj0xkexnfoIDe7q+AZHMkQmAE/F9R5jkrB6TLfTegImlXag==" + }, + "node_modules/babel-plugin-minify-constant-folding": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-constant-folding/-/babel-plugin-minify-constant-folding-0.5.0.tgz", + "integrity": "sha512-Vj97CTn/lE9hR1D+jKUeHfNy+m1baNiJ1wJvoGyOBUx7F7kJqDZxr9nCHjO/Ad+irbR3HzR6jABpSSA29QsrXQ==", + "dependencies": { + "babel-helper-evaluate-path": "^0.5.0" + } + }, + "node_modules/babel-plugin-minify-dead-code-elimination": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.5.1.tgz", + "integrity": "sha512-x8OJOZIrRmQBcSqxBcLbMIK8uPmTvNWPXH2bh5MDCW1latEqYiRMuUkPImKcfpo59pTUB2FT7HfcgtG8ZlR5Qg==", + "dependencies": { + "babel-helper-evaluate-path": "^0.5.0", + "babel-helper-mark-eval-scopes": "^0.4.3", + "babel-helper-remove-or-void": "^0.4.3", + "lodash": "^4.17.11" + } + }, + "node_modules/babel-plugin-minify-flip-comparisons": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-flip-comparisons/-/babel-plugin-minify-flip-comparisons-0.4.3.tgz", + "integrity": "sha1-AMqHDLjxO0XAOLPB68DyJyk8llo=", + "dependencies": { + "babel-helper-is-void-0": "^0.4.3" + } + }, + "node_modules/babel-plugin-minify-guarded-expressions": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-guarded-expressions/-/babel-plugin-minify-guarded-expressions-0.4.4.tgz", + "integrity": "sha512-RMv0tM72YuPPfLT9QLr3ix9nwUIq+sHT6z8Iu3sLbqldzC1Dls8DPCywzUIzkTx9Zh1hWX4q/m9BPoPed9GOfA==", + "dependencies": { + "babel-helper-evaluate-path": "^0.5.0", + "babel-helper-flip-expressions": "^0.4.3" + } + }, + "node_modules/babel-plugin-minify-infinity": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-infinity/-/babel-plugin-minify-infinity-0.4.3.tgz", + "integrity": "sha1-37h2obCKBldjhO8/kuZTumB7Oco=" + }, + "node_modules/babel-plugin-minify-mangle-names": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-mangle-names/-/babel-plugin-minify-mangle-names-0.5.0.tgz", + "integrity": "sha512-3jdNv6hCAw6fsX1p2wBGPfWuK69sfOjfd3zjUXkbq8McbohWy23tpXfy5RnToYWggvqzuMOwlId1PhyHOfgnGw==", + "dependencies": { + "babel-helper-mark-eval-scopes": "^0.4.3" + } + }, + "node_modules/babel-plugin-minify-numeric-literals": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-numeric-literals/-/babel-plugin-minify-numeric-literals-0.4.3.tgz", + "integrity": "sha1-jk/VYcefeAEob/YOjF/Z3u6TwLw=" + }, + "node_modules/babel-plugin-minify-replace": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-replace/-/babel-plugin-minify-replace-0.5.0.tgz", + "integrity": "sha512-aXZiaqWDNUbyNNNpWs/8NyST+oU7QTpK7J9zFEFSA0eOmtUNMU3fczlTTTlnCxHmq/jYNFEmkkSG3DDBtW3Y4Q==" + }, + "node_modules/babel-plugin-minify-simplify": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-simplify/-/babel-plugin-minify-simplify-0.5.1.tgz", + "integrity": "sha512-OSYDSnoCxP2cYDMk9gxNAed6uJDiDz65zgL6h8d3tm8qXIagWGMLWhqysT6DY3Vs7Fgq7YUDcjOomhVUb+xX6A==", + "dependencies": { + "babel-helper-evaluate-path": "^0.5.0", + "babel-helper-flip-expressions": "^0.4.3", + "babel-helper-is-nodes-equiv": "^0.0.1", + "babel-helper-to-multiple-sequence-expressions": "^0.5.0" + } + }, + "node_modules/babel-plugin-minify-type-constructors": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-type-constructors/-/babel-plugin-minify-type-constructors-0.4.3.tgz", + "integrity": "sha1-G8bxW4f3qxCF1CszC3F2V6IVZQA=", + "dependencies": { + "babel-helper-is-void-0": "^0.4.3" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", + "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==", + "dependencies": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.3.1", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", + "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.1", + "core-js-compat": "^3.21.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", + "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-transform-inline-consecutive-adds": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz", + "integrity": "sha1-Mj1Ho+pjqDp6w8gRro5pQfrysNE=" + }, + "node_modules/babel-plugin-transform-member-expression-literals": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-member-expression-literals/-/babel-plugin-transform-member-expression-literals-6.9.4.tgz", + "integrity": "sha1-NwOcmgwzE6OUlfqsL/OmtbnQOL8=" + }, + "node_modules/babel-plugin-transform-merge-sibling-variables": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-merge-sibling-variables/-/babel-plugin-transform-merge-sibling-variables-6.9.4.tgz", + "integrity": "sha1-hbQi/DN3tEnJ0c3kQIcgNTJAHa4=" + }, + "node_modules/babel-plugin-transform-minify-booleans": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-minify-booleans/-/babel-plugin-transform-minify-booleans-6.9.4.tgz", + "integrity": "sha1-rLs+VqNVXdI5KOS1gtKFFi3SsZg=" + }, + "node_modules/babel-plugin-transform-property-literals": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-property-literals/-/babel-plugin-transform-property-literals-6.9.4.tgz", + "integrity": "sha1-mMHSHiVXNlc/k+zlRFn2ziSYXTk=", + "dependencies": { + "esutils": "^2.0.2" + } + }, + "node_modules/babel-plugin-transform-regexp-constructors": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regexp-constructors/-/babel-plugin-transform-regexp-constructors-0.4.3.tgz", + "integrity": "sha1-WLd3W2OvzzMyj66aX4j71PsLSWU=" + }, + "node_modules/babel-plugin-transform-remove-console": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz", + "integrity": "sha1-uYA2DAZzhOJLNXpYjYB9PINSd4A=" + }, + "node_modules/babel-plugin-transform-remove-debugger": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-debugger/-/babel-plugin-transform-remove-debugger-6.9.4.tgz", + "integrity": "sha1-QrcnYxyXl44estGZp67IShgznvI=" + }, + "node_modules/babel-plugin-transform-remove-undefined": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-undefined/-/babel-plugin-transform-remove-undefined-0.5.0.tgz", + "integrity": "sha512-+M7fJYFaEE/M9CXa0/IRkDbiV3wRELzA1kKQFCJ4ifhrzLKn/9VCCgj9OFmYWwBd8IB48YdgPkHYtbYq+4vtHQ==", + "dependencies": { + "babel-helper-evaluate-path": "^0.5.0" + } + }, + "node_modules/babel-plugin-transform-simplify-comparison-operators": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-simplify-comparison-operators/-/babel-plugin-transform-simplify-comparison-operators-6.9.4.tgz", + "integrity": "sha1-9ir+CWyrDh9ootdT/fKDiIRxzrk=" + }, + "node_modules/babel-plugin-transform-undefined-to-void": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.9.4.tgz", + "integrity": "sha1-viQcqBQEAwZ4t0hxcyK4nQyP4oA=" + }, + "node_modules/babel-preset-meteor": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/babel-preset-meteor/-/babel-preset-meteor-7.10.0.tgz", + "integrity": "sha512-bcdNfRCQAjTV42cUcmaG5/ltLZZQLpZajUcP+o0Lr+aLTY/XLNkGfASM5383wdXiAkEFl0sDOXeknnLlQtrmdg==", + "dependencies": { + "@babel/plugin-proposal-async-generator-functions": "^7.13.15", + "@babel/plugin-proposal-class-properties": "^7.13.0", + "@babel/plugin-proposal-logical-assignment-operators": "^7.13.8", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", + "@babel/plugin-proposal-object-rest-spread": "^7.13.8", + "@babel/plugin-proposal-optional-catch-binding": "^7.13.8", + "@babel/plugin-proposal-optional-chaining": "^7.13.12", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.13.0", + "@babel/plugin-transform-async-to-generator": "^7.13.0", + "@babel/plugin-transform-block-scoped-functions": "^7.12.13", + "@babel/plugin-transform-block-scoping": "^7.13.16", + "@babel/plugin-transform-classes": "^7.13.0", + "@babel/plugin-transform-computed-properties": "^7.13.0", + "@babel/plugin-transform-destructuring": "^7.13.17", + "@babel/plugin-transform-exponentiation-operator": "^7.12.13", + "@babel/plugin-transform-for-of": "^7.13.0", + "@babel/plugin-transform-literals": "^7.12.13", + "@babel/plugin-transform-object-super": "^7.12.13", + "@babel/plugin-transform-parameters": "^7.13.0", + "@babel/plugin-transform-property-literals": "^7.12.13", + "@babel/plugin-transform-regenerator": "^7.13.15", + "@babel/plugin-transform-shorthand-properties": "^7.12.13", + "@babel/plugin-transform-spread": "^7.13.0", + "@babel/plugin-transform-sticky-regex": "^7.12.13", + "@babel/plugin-transform-template-literals": "^7.13.0", + "@babel/plugin-transform-typeof-symbol": "^7.12.13", + "@babel/plugin-transform-unicode-regex": "^7.12.13" + } + }, + "node_modules/babel-preset-minify": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-minify/-/babel-preset-minify-0.5.1.tgz", + "integrity": "sha512-1IajDumYOAPYImkHbrKeiN5AKKP9iOmRoO2IPbIuVp0j2iuCcj0n7P260z38siKMZZ+85d3mJZdtW8IgOv+Tzg==", + "dependencies": { + "babel-plugin-minify-builtins": "^0.5.0", + "babel-plugin-minify-constant-folding": "^0.5.0", + "babel-plugin-minify-dead-code-elimination": "^0.5.1", + "babel-plugin-minify-flip-comparisons": "^0.4.3", + "babel-plugin-minify-guarded-expressions": "^0.4.4", + "babel-plugin-minify-infinity": "^0.4.3", + "babel-plugin-minify-mangle-names": "^0.5.0", + "babel-plugin-minify-numeric-literals": "^0.4.3", + "babel-plugin-minify-replace": "^0.5.0", + "babel-plugin-minify-simplify": "^0.5.1", + "babel-plugin-minify-type-constructors": "^0.4.3", + "babel-plugin-transform-inline-consecutive-adds": "^0.4.3", + "babel-plugin-transform-member-expression-literals": "^6.9.4", + "babel-plugin-transform-merge-sibling-variables": "^6.9.4", + "babel-plugin-transform-minify-booleans": "^6.9.4", + "babel-plugin-transform-property-literals": "^6.9.4", + "babel-plugin-transform-regexp-constructors": "^0.4.3", + "babel-plugin-transform-remove-console": "^6.9.4", + "babel-plugin-transform-remove-debugger": "^6.9.4", + "babel-plugin-transform-remove-undefined": "^0.5.0", + "babel-plugin-transform-simplify-comparison-operators": "^6.9.4", + "babel-plugin-transform-undefined-to-void": "^6.9.4", + "lodash": "^4.17.11" + } + }, + "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==", + "dev": 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==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dependencies": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001248", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001248.tgz", + "integrity": "sha512-NwlQbJkxUFJ8nMErnGtT0QTM2TJ33xgz4KXJSMIrjXIbDVdaYueGyjOrLKRtJC+rTiWfi6j5cnZN1NBiSBJGNw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/core-js-compat": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.0.tgz", + "integrity": "sha512-OSXseNPSK2OPJa6GdtkMz/XxeXx8/CJvfhQWTqd6neuUraujcL4jVsjkLQz1OWnax8xVQJnRPe0V2jqNWORA+A==", + "dependencies": { + "browserslist": "^4.19.1", + "semver": "7.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat/node_modules/browserslist": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "dependencies": { + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", + "escalade": "^3.1.1", + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/core-js-compat/node_modules/caniuse-lite": { + "version": "1.0.30001312", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz", + "integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } + }, + "node_modules/core-js-compat/node_modules/electron-to-chromium": { + "version": "1.4.68", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.68.tgz", + "integrity": "sha512-cId+QwWrV8R1UawO6b9BR1hnkJ4EJPCPAr4h315vliHUtVUJDk39Sg1PMNnaWKfj5x+93ssjeJ9LKL6r8LaMiA==" + }, + "node_modules/core-js-compat/node_modules/node-releases": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==" + }, + "node_modules/core-js-compat/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/d3": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-4.13.0.tgz", + "integrity": "sha512-l8c4+0SldjVKLaE2WG++EQlqD7mh/dmQjvi2L2lKPadAVC+TbJC4ci7Uk9bRi+To0+ansgsS0iWfPjD7DBy+FQ==", + "dev": true, + "dependencies": { + "d3-array": "1.2.1", + "d3-axis": "1.0.8", + "d3-brush": "1.0.4", + "d3-chord": "1.0.4", + "d3-collection": "1.0.4", + "d3-color": "1.0.3", + "d3-dispatch": "1.0.3", + "d3-drag": "1.2.1", + "d3-dsv": "1.0.8", + "d3-ease": "1.0.3", + "d3-force": "1.1.0", + "d3-format": "1.2.2", + "d3-geo": "1.9.1", + "d3-hierarchy": "1.1.5", + "d3-interpolate": "1.1.6", + "d3-path": "1.0.5", + "d3-polygon": "1.0.3", + "d3-quadtree": "1.0.3", + "d3-queue": "3.0.7", + "d3-random": "1.1.0", + "d3-request": "1.0.6", + "d3-scale": "1.0.7", + "d3-selection": "1.3.0", + "d3-shape": "1.2.0", + "d3-time": "1.0.8", + "d3-time-format": "2.1.1", + "d3-timer": "1.0.7", + "d3-transition": "1.1.1", + "d3-voronoi": "1.1.2", + "d3-zoom": "1.7.1" + } + }, + "node_modules/d3-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz", + "integrity": "sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw==", + "dev": true + }, + "node_modules/d3-axis": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.8.tgz", + "integrity": "sha1-MacFoLU15ldZ3hQXOjGTMTfxjvo=", + "dev": true + }, + "node_modules/d3-brush": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.4.tgz", + "integrity": "sha1-AMLyOAGfJPbAoZSibUGhUw/+e8Q=", + "dev": true, + "dependencies": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, + "node_modules/d3-chord": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.4.tgz", + "integrity": "sha1-fexPC6iG9xP+ERxF92NBT290yiw=", + "dev": true, + "dependencies": { + "d3-array": "1", + "d3-path": "1" + } + }, + "node_modules/d3-collection": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.4.tgz", + "integrity": "sha1-NC39EoN8kJdPM/HMCnha6lcNzcI=", + "dev": true + }, + "node_modules/d3-color": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz", + "integrity": "sha1-vHZD/KjlOoNH4vva/6I2eWtYUJs=", + "dev": true + }, + "node_modules/d3-dispatch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", + "integrity": "sha1-RuFJHqqbWMNY/OW+TovtYm54cfg=", + "dev": true + }, + "node_modules/d3-drag": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.1.tgz", + "integrity": "sha512-Cg8/K2rTtzxzrb0fmnYOUeZHvwa4PHzwXOLZZPwtEs2SKLLKLXeYwZKBB+DlOxUvFmarOnmt//cU4+3US2lyyQ==", + "dev": true, + "dependencies": { + "d3-dispatch": "1", + "d3-selection": "1" + } + }, + "node_modules/d3-dsv": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.8.tgz", + "integrity": "sha512-IVCJpQ+YGe3qu6odkPQI0KPqfxkhbP/oM1XhhE/DFiYmcXKfCRub4KXyiuehV1d4drjWVXHUWx4gHqhdZb6n/A==", + "dev": true, + "dependencies": { + "commander": "2", + "iconv-lite": "0.4", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json", + "csv2tsv": "bin/dsv2dsv", + "dsv2dsv": "bin/dsv2dsv", + "dsv2json": "bin/dsv2json", + "json2csv": "bin/json2dsv", + "json2dsv": "bin/json2dsv", + "json2tsv": "bin/json2dsv", + "tsv2csv": "bin/dsv2dsv", + "tsv2json": "bin/dsv2json" + } + }, + "node_modules/d3-ease": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz", + "integrity": "sha1-aL+8NJM4o4DETYrMT7wzBKotjA4=", + "dev": true + }, + "node_modules/d3-force": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.1.0.tgz", + "integrity": "sha512-2HVQz3/VCQs0QeRNZTYb7GxoUCeb6bOzMp/cGcLa87awY9ZsPvXOGeZm0iaGBjXic6I1ysKwMn+g+5jSAdzwcg==", + "dev": true, + "dependencies": { + "d3-collection": "1", + "d3-dispatch": "1", + "d3-quadtree": "1", + "d3-timer": "1" + } + }, + "node_modules/d3-format": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.2.2.tgz", + "integrity": "sha512-zH9CfF/3C8zUI47nsiKfD0+AGDEuM8LwBIP7pBVpyR4l/sKkZqITmMtxRp04rwBrlshIZ17XeFAaovN3++wzkw==", + "dev": true + }, + "node_modules/d3-geo": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.9.1.tgz", + "integrity": "sha512-l9wL/cEQkyZQYXw3xbmLsH3eQ5ij+icNfo4r0GrLa5rOCZR/e/3am45IQ0FvQ5uMsv+77zBRunLc9ufTWSQYFA==", + "dev": true, + "dependencies": { + "d3-array": "1" + } + }, + "node_modules/d3-hierarchy": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz", + "integrity": "sha1-ochFxC+Eoga88cAcAQmOpN2qeiY=", + "dev": true + }, + "node_modules/d3-interpolate": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.1.6.tgz", + "integrity": "sha512-mOnv5a+pZzkNIHtw/V6I+w9Lqm9L5bG3OTXPM5A+QO0yyVMQ4W1uZhR+VOJmazaOZXri2ppbiZ5BUNWT0pFM9A==", + "dev": true, + "dependencies": { + "d3-color": "1" + } + }, + "node_modules/d3-path": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz", + "integrity": "sha1-JB6xhJvZ6egCHA0KeZ+KDo5EF2Q=", + "dev": true + }, + "node_modules/d3-polygon": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.3.tgz", + "integrity": "sha1-FoiOkCZGCTPysXllKtN4Ik04LGI=", + "dev": true + }, + "node_modules/d3-quadtree": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.3.tgz", + "integrity": "sha1-rHmH4+I/6AWpkPKOG1DTj8uCJDg=", + "dev": true + }, + "node_modules/d3-queue": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/d3-queue/-/d3-queue-3.0.7.tgz", + "integrity": "sha1-yTouVLQXwJWRKdfXP2z31Ckudhg=", + "dev": true + }, + "node_modules/d3-random": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.0.tgz", + "integrity": "sha1-ZkLlBsb6OmSFldKyRpeIqNElKdM=", + "dev": true + }, + "node_modules/d3-request": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-request/-/d3-request-1.0.6.tgz", + "integrity": "sha512-FJj8ySY6GYuAJHZMaCQ83xEYE4KbkPkmxZ3Hu6zA1xxG2GD+z6P+Lyp+zjdsHf0xEbp2xcluDI50rCS855EQ6w==", + "dev": true, + "dependencies": { + "d3-collection": "1", + "d3-dispatch": "1", + "d3-dsv": "1", + "xmlhttprequest": "1" + } + }, + "node_modules/d3-scale": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz", + "integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==", + "dev": true, + "dependencies": { + "d3-array": "^1.2.0", + "d3-collection": "1", + "d3-color": "1", + "d3-format": "1", + "d3-interpolate": "1", + "d3-time": "1", + "d3-time-format": "2" + } + }, + "node_modules/d3-selection": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.3.0.tgz", + "integrity": "sha512-qgpUOg9tl5CirdqESUAu0t9MU/t3O9klYfGfyKsXEmhyxyzLpzpeh08gaxBUTQw1uXIOkr/30Ut2YRjSSxlmHA==", + "dev": true + }, + "node_modules/d3-shape": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.0.tgz", + "integrity": "sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c=", + "dev": true, + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-time": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.8.tgz", + "integrity": "sha512-YRZkNhphZh3KcnBfitvF3c6E0JOFGikHZ4YqD+Lzv83ZHn1/u6yGenRU1m+KAk9J1GnZMnKcrtfvSktlA1DXNQ==", + "dev": true + }, + "node_modules/d3-time-format": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.1.tgz", + "integrity": "sha512-8kAkymq2WMfzW7e+s/IUNAtN/y3gZXGRrdGfo6R8NKPAA85UBTxZg5E61bR6nLwjPjj4d3zywSQe1CkYLPFyrw==", + "dev": true, + "dependencies": { + "d3-time": "1" + } + }, + "node_modules/d3-timer": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.7.tgz", + "integrity": "sha512-vMZXR88XujmG/L5oB96NNKH5lCWwiLM/S2HyyAQLcjWJCloK5shxta4CwOFYLZoY3AWX73v8Lgv4cCAdWtRmOA==", + "dev": true + }, + "node_modules/d3-transition": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.1.tgz", + "integrity": "sha512-xeg8oggyQ+y5eb4J13iDgKIjUcEfIOZs2BqV/eEmXm2twx80wTzJ4tB4vaZ5BKfz7XsI/DFmQL5me6O27/5ykQ==", + "dev": true, + "dependencies": { + "d3-color": "1", + "d3-dispatch": "1", + "d3-ease": "1", + "d3-interpolate": "1", + "d3-selection": "^1.1.0", + "d3-timer": "1" + } + }, + "node_modules/d3-voronoi": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz", + "integrity": "sha1-Fodmfo8TotFYyAwUgMWinLDYlzw=", + "dev": true + }, + "node_modules/d3-zoom": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.7.1.tgz", + "integrity": "sha512-sZHQ55DGq5BZBFGnRshUT8tm2sfhPHFnOlmPbbwTkAoPeVdRTkB4Xsf9GCY0TSHrTD8PeJPZGmP/TpGicwJDJQ==", + "dev": true, + "dependencies": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, + "node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.3.793", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.793.tgz", + "integrity": "sha512-l9NrGV6Mr4ov5mayYPvIWcwklNw5ROmy6rllzz9dCACw9nKE5y+s5uQk+CBJMetxrWZ6QJFsvEfG6WDcH2IGUg==" + }, + "node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/es-abstract": { + "version": "1.18.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.5.tgz", + "integrity": "sha512-DDggyJLoS91CkJjgauM5c0yZMjiD1uK3KcaCeAmffGwZ+ODWzOkPN4QwRbsK5DOFf06fywmyLci3ZD8jLGhVYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "is-string": "^1.0.6", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fibers": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fibers/-/fibers-5.0.0.tgz", + "integrity": "sha512-UpGv/YAZp7mhKHxDvC1tColrroGRX90sSvh8RMZV9leo+e5+EkRVgCEZPlmXeo3BUNQTZxUaVdLskq1Q2FyCPg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/flat": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", + "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", + "dev": true, + "dependencies": { + "is-buffer": "~2.0.3" + }, + "bin": { + "flat": "cli.js" + } + }, + "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/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "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" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", + "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", + "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-regex": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json5": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + }, + "node_modules/log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "dependencies": { + "chalk": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dependencies": { + "sourcemap-codec": "^1.4.4" + } + }, + "node_modules/meteor-babel-helpers": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/meteor-babel-helpers/-/meteor-babel-helpers-0.0.3.tgz", + "integrity": "sha1-8uXZ+HlvvS6JAQI9dpnlsgLqn7A=" + }, + "node_modules/meteor-promise": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/meteor-promise/-/meteor-promise-0.9.0.tgz", + "integrity": "sha512-O1Fj1Oa5FfyIkAkDtZVnoYYEIC3miy7lvEeIQZVYunGSbOuivSbfAiPPsD+P45WNlcBALhUo94UzlHeIKBYNuQ==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "node_modules/mkdirp": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", + "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mocha": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.3.tgz", + "integrity": "sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg==", + "dev": true, + "dependencies": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "2.2.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.4", + "ms": "2.1.1", + "node-environment-flags": "1.0.5", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "1.6.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "node_modules/mocha/node_modules/object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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-environment-flags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", + "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "dev": true, + "dependencies": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + } + }, + "node_modules/node-environment-flags/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/node-releases": { + "version": "1.1.73", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", + "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==" + }, + "node_modules/object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz", + "integrity": "sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "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/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/periscopic": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-2.0.3.tgz", + "integrity": "sha512-FuCZe61mWxQOJAQFEfmt9FjzebRlcpFz8sFPbyaCKtdusPkMEbA9ey0eARnRav5zAhmXznhaQkKGFAPn7X9NUw==", + "dependencies": { + "estree-walker": "^2.0.2", + "is-reference": "^1.1.4" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/promise": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", + "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", + "dev": true, + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + }, + "node_modules/regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "dependencies": { + "regenerate": "^1.4.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, + "node_modules/regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regexpu-core": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", + "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", + "dependencies": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" + }, + "node_modules/regjsparser": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.9.tgz", + "integrity": "sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ==", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=", + "dev": true + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "engines": { + "node": ">=4" + } + }, + "node_modules/typescript": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/yargs-unparser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", + "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "dev": true, + "dependencies": { + "flat": "^4.1.0", + "lodash": "^4.17.15", + "yargs": "^13.3.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + } + }, "dependencies": { "@ampproject/remapping": { "version": "2.1.1", @@ -1073,7 +4940,8 @@ "acorn-dynamic-import": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", - "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==" + "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==", + "requires": {} }, "ansi-colors": { "version": "3.2.3", @@ -2722,9 +6590,9 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, "typescript": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", - "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==" + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==" }, "unbox-primitive": { "version": "1.0.1", diff --git a/npm-packages/meteor-babel/package.json b/npm-packages/meteor-babel/package.json index 5fbdf188b9..cb7a5fb0ed 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.16.0-beta.7", + "version": "7.18.0-beta.3", "license": "MIT", "description": "Babel wrapper package for use with Meteor", "keywords": [ @@ -47,7 +47,7 @@ "convert-source-map": "^1.6.0", "lodash": "^4.17.21", "meteor-babel-helpers": "0.0.3", - "typescript": "^4.5.4" + "typescript": "~4.6.4" }, "devDependencies": { "@babel/plugin-proposal-decorators": "7.14.5", diff --git a/npm-packages/meteor-babel/plugins/async-await.js b/npm-packages/meteor-babel/plugins/async-await.js index c1745ba2f9..38abbff65f 100644 --- a/npm-packages/meteor-babel/plugins/async-await.js +++ b/npm-packages/meteor-babel/plugins/async-await.js @@ -9,14 +9,10 @@ module.exports = function (babel) { Function: { exit: function (path) { const node = path.node; - if (! node.async) { + if (!node.async) { return; } - // The original function becomes a non-async function that - // returns a Promise. - node.async = false; - // The inner function should inherit lexical environment items // like `this`, `super`, and `arguments` from the outer // function, and arrow functions provide exactly that behavior. @@ -30,6 +26,17 @@ module.exports = function (babel) { !! this.opts.useNativeAsyncAwait ); + if (this.opts.isFiberDisabled && this.opts.overwriteFiberExit) { + if (node.type === "ArrowFunctionExpression") { + node.body = innerFn; + } + return; + } + + // The original function becomes a non-async function that + // returns a Promise. + node.async = false; + const promiseResultExpression = t.callExpression( t.memberExpression( t.identifier("Promise"), diff --git a/npm-packages/meteor-installer/README.md b/npm-packages/meteor-installer/README.md index b240fe71b2..91ad13de8f 100644 --- a/npm-packages/meteor-installer/README.md +++ b/npm-packages/meteor-installer/README.md @@ -14,6 +14,8 @@ npm install -g meteor | NPM Package | Meteor Official Release | |-------------|-------------------------| +| 2.8.2 | 2.8.1 | +| 2.8.1 | 2.8.1 | | 2.8.0 | 2.8.0 | | 2.7.5 | 2.7.3 | | 2.7.4 | 2.7.3 | @@ -61,4 +63,4 @@ npm install -g meteor --ignore-meteor-setup-exec-path ### Proxy configuration Setting the `https_proxy` or `HTTPS_PROXY` environment variable to a valid proxy URL will cause the -downloader to use the configured proxy to retrieve the Meteor files. \ No newline at end of file +downloader to use the configured proxy to retrieve the Meteor files. diff --git a/npm-packages/meteor-installer/config.js b/npm-packages/meteor-installer/config.js index 9b13c21c20..fcae57bcec 100644 --- a/npm-packages/meteor-installer/config.js +++ b/npm-packages/meteor-installer/config.js @@ -1,7 +1,7 @@ const path = require('path'); const os = require('os'); -const METEOR_LATEST_VERSION = '2.8.0'; +const METEOR_LATEST_VERSION = '2.9.0'; const sudoUser = process.env.SUDO_USER || ''; function isRoot() { return process.getuid && process.getuid() === 0; diff --git a/npm-packages/meteor-installer/install.js b/npm-packages/meteor-installer/install.js index 6f52bfb93b..c0d75f0d84 100644 --- a/npm-packages/meteor-installer/install.js +++ b/npm-packages/meteor-installer/install.js @@ -1,5 +1,4 @@ const { DownloaderHelper } = require('node-downloader-helper'); -const HttpsProxyAgent = require('https-proxy-agent'); const cliProgress = require('cli-progress'); const Seven = require('node-7z'); const path = require('path'); @@ -150,6 +149,8 @@ function generateProxyAgent() { return undefined; } + const HttpsProxyAgent = require('https-proxy-agent'); + return new HttpsProxyAgent(proxyUrl); } diff --git a/npm-packages/meteor-installer/package.json b/npm-packages/meteor-installer/package.json index d7a6b90863..53d344ef33 100644 --- a/npm-packages/meteor-installer/package.json +++ b/npm-packages/meteor-installer/package.json @@ -1,6 +1,6 @@ { "name": "meteor", - "version": "2.8.0", + "version": "2.9.0", "description": "Install Meteor", "main": "install.js", "scripts": { @@ -11,6 +11,7 @@ "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": "^1.0.19", "rimraf": "^3.0.2", diff --git a/packages/accounts-base/accounts-base.d.ts b/packages/accounts-base/accounts-base.d.ts new file mode 100644 index 0000000000..923625be79 --- /dev/null +++ b/packages/accounts-base/accounts-base.d.ts @@ -0,0 +1,326 @@ +import { Mongo } from 'meteor/mongo'; +import { Meteor } from 'meteor/meteor'; + +export interface URLS { + resetPassword: (token: string) => string; + verifyEmail: (token: string) => string; + enrollAccount: (token: string) => string; +} + +export interface EmailFields { + from?: ((user: Meteor.User) => string) | undefined; + subject?: ((user: Meteor.User) => string) | undefined; + text?: ((user: Meteor.User, url: string) => string) | undefined; + html?: ((user: Meteor.User, url: string) => string) | undefined; +} + +export namespace Accounts { + var urls: URLS; + + function user(options?: { + fields?: Mongo.FieldSpecifier | undefined; + }): Meteor.User | null; + + function userId(): string | null; + + function createUser( + options: { + username?: string | undefined; + email?: string | undefined; + password?: string | undefined; + profile?: Object | undefined; + }, + callback?: (error?: Error | Meteor.Error | Meteor.TypedError) => void + ): string; + + function config(options: { + sendVerificationEmail?: boolean | undefined; + forbidClientAccountCreation?: boolean | undefined; + restrictCreationByEmailDomain?: string | Function | undefined; + loginExpirationInDays?: number | undefined; + oauthSecretKey?: string | undefined; + passwordResetTokenExpirationInDays?: number | undefined; + passwordEnrollTokenExpirationInDays?: number | undefined; + ambiguousErrorMessages?: boolean | undefined; + defaultFieldSelector?: { [key: string]: 0 | 1 } | undefined; + }): void; + + function onLogin( + func: Function + ): { + stop: () => void; + }; + + function onLoginFailure( + func: Function + ): { + stop: () => void; + }; + + function loginServicesConfigured(): boolean; + + function onPageLoadLogin(func: Function): void; +} + +export namespace Accounts { + function changePassword( + oldPassword: string, + newPassword: string, + callback?: (error?: Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + function forgotPassword( + options: { email?: string | undefined }, + callback?: (error?: Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + function resetPassword( + token: string, + newPassword: string, + callback?: (error?: Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + function verifyEmail( + token: string, + callback?: (error?: Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + function onEmailVerificationLink(callback: Function): void; + + function onEnrollmentLink(callback: Function): void; + + function onResetPasswordLink(callback: Function): void; + + function loggingIn(): boolean; + + function loggingOut(): boolean; + + function logout( + callback?: (error?: Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + function logoutOtherClients( + callback?: (error?: Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + var ui: { + config(options: { + requestPermissions?: Object | undefined; + requestOfflineToken?: Object | undefined; + forceApprovalPrompt?: Object | undefined; + passwordSignupFields?: string | undefined; + }): void; + }; +} + +export interface Header { + [id: string]: string; +} + +export interface EmailTemplates { + from: string; + siteName: string; + headers?: Header | undefined; + resetPassword: EmailFields; + enrollAccount: EmailFields; + verifyEmail: EmailFields; +} + +export namespace Accounts { + var emailTemplates: EmailTemplates; + + function addEmail(userId: string, newEmail: string, verified?: boolean): void; + + function removeEmail(userId: string, email: string): void; + + function onCreateUser( + func: (options: { profile?: {} | undefined }, user: Meteor.User) => void + ): void; + + function findUserByEmail( + email: string, + options?: { fields?: Mongo.FieldSpecifier | undefined } + ): Meteor.User | null | undefined; + + function findUserByUsername( + username: string, + options?: { fields?: Mongo.FieldSpecifier | undefined } + ): Meteor.User | null | undefined; + + function sendEnrollmentEmail( + userId: string, + email?: string, + extraTokenData?: Record, + extraParams?: Record + ): void; + + function sendResetPasswordEmail( + userId: string, + email?: string, + extraTokenData?: Record, + extraParams?: Record + ): void; + + function sendVerificationEmail( + userId: string, + email?: string, + extraTokenData?: Record, + extraParams?: Record + ): void; + + function setUsername(userId: string, newUsername: string): void; + + function setPassword( + userId: string, + newPassword: string, + options?: { logout?: Object | undefined } + ): void; + + function validateNewUser(func: Function): boolean; + + function validateLoginAttempt( + func: Function + ): { + stop: () => void; + }; + + function _hashPassword( + password: string + ): { digest: string; algorithm: string }; + + interface IValidateLoginAttemptCbOpts { + type: string; + allowed: boolean; + error: Meteor.Error; + user: Meteor.User; + connection: Meteor.Connection; + methodName: string; + methodArguments: any[]; + } +} + +export namespace Accounts { + function onLogout(func: Function): void; +} + +export namespace Accounts { + function onLogout( + func: (options: { + user: Meteor.User; + connection: Meteor.Connection; + }) => void + ): void; +} + +export namespace Accounts { + interface LoginMethodOptions { + /** + * The method to call (default 'login') + */ + methodName?: string | undefined; + /** + * The arguments for the method + */ + methodArguments?: any[] | undefined; + /** + * If provided, will be called with the result of the + * method. If it throws, the client will not be logged in (and + * its error will be passed to the callback). + */ + validateResult?: Function | undefined; + /** + * Will be called with no arguments once the user is fully + * logged in, or with the error on error. + */ + userCallback?: ((err?: any) => void) | undefined; + } + + /** + * + * Call a login method on the server. + * + * A login method is a method which on success calls `this.setUserId(id)` and + * `Accounts._setLoginToken` on the server and returns an object with fields + * 'id' (containing the user id), 'token' (containing a resume token), and + * optionally `tokenExpires`. + * + * This function takes care of: + * - Updating the Meteor.loggingIn() reactive data source + * - Calling the method in 'wait' mode + * - On success, saving the resume token to localStorage + * - On success, calling Accounts.connection.setUserId() + * - Setting up an onReconnect handler which logs in with + * the resume token + * + * Options: + * - methodName: The method to call (default 'login') + * - methodArguments: The arguments for the method + * - validateResult: If provided, will be called with the result of the + * method. If it throws, the client will not be logged in (and + * its error will be passed to the callback). + * - userCallback: Will be called with no arguments once the user is fully + * logged in, or with the error on error. + * + * */ + function callLoginMethod(options: LoginMethodOptions): void; + + /** + * + * The main entry point for auth packages to hook in to login. + * + * A login handler is a login method which can return `undefined` to + * indicate that the login request is not handled by this handler. + * + * @param name {String} Optional. The service name, used by default + * if a specific service name isn't returned in the result. + * + * @param handler {Function} A function that receives an options object + * (as passed as an argument to the `login` method) and returns one of: + * - `undefined`, meaning don't handle; + * - a login method result object + **/ + function registerLoginHandler( + name: string, + handler: (options: any) => undefined | Object + ): void; + + type Password = + | string + | { + digest: string; + algorithm: 'sha-256'; + }; + + /** + * + * 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`). + */ + function _checkPassword( + user: Meteor.User, + password: Password + ): { userId: string; error?: any }; +} + +export namespace Accounts { + type StampedLoginToken = { + token: string; + when: Date; + }; + type HashedStampedLoginToken = { + hashedToken: string; + when: Date; + }; + + function _generateStampedLoginToken(): StampedLoginToken; + function _hashStampedToken(token: StampedLoginToken): HashedStampedLoginToken; + function _insertHashedLoginToken( + userId: string, + token: HashedStampedLoginToken, + query?: Mongo.Selector | Mongo.ObjectID | string + ): void; + function _hashLoginToken(token: string): string; +} diff --git a/packages/accounts-base/accounts_client.js b/packages/accounts-base/accounts_client.js index cfded81faa..6f3314ad04 100644 --- a/packages/accounts-base/accounts_client.js +++ b/packages/accounts-base/accounts_client.js @@ -119,18 +119,21 @@ export class AccountsClient extends AccountsCommon { */ logout(callback) { this._loggingOut.set(true); - this.connection.apply('logout', [], { + + this.connection.applyAsync('logout', [], { + // TODO[FIBERS]: Look this { wait: true } later. wait: true - }, (error, result) => { - this._loggingOut.set(false); - this._loginCallbacksCalled = false; - if (error) { - callback && callback(error); - } else { + }) + .then((result) => { + this._loggingOut.set(false); + this._loginCallbacksCalled = false; this.makeClientLoggedOut(); callback && callback(); - } - }); + }) + .catch((e) => { + this._loggingOut.set(false); + callback && callback(e); + }); } /** @@ -798,6 +801,11 @@ if (Package.blaze) { */ Template.registerHelper('currentUser', () => Meteor.user()); + // TODO: the code above needs to be changed to Meteor.userAsync() when we have + // a way to make it reactive using async. + // Template.registerHelper('currentUserAsync', + // async () => await Meteor.userAsync()); + /** * @global * @name loggingIn diff --git a/packages/accounts-base/accounts_client_tests.js b/packages/accounts-base/accounts_client_tests.js index 9ebb7d4b9f..5558884273 100644 --- a/packages/accounts-base/accounts_client_tests.js +++ b/packages/accounts-base/accounts_client_tests.js @@ -36,9 +36,9 @@ const createUserAndLogout = (test, done, nextTests) => { }, }, () => { - Meteor.logout(() => { + Meteor.logout(async () => { // Make sure we're logged out - test.isFalse(Meteor.user()); + test.isFalse(await Meteor.userAsync()); // Handle next tests nextTests(test, done); }); @@ -94,6 +94,20 @@ Tinytest.addAsync( } ); +Tinytest.addAsync( + 'accounts async - Meteor.loggingIn() is false after login has completed', + (test, done) => { + logoutAndCreateUser(test, done, () => { + // Login then verify loggingIn is false after login has completed + Meteor.loginWithPassword(username, password, async () => { + test.isFalse(Meteor.loggingIn()); + test.isTrue(await Meteor.userAsync()); + removeTestUser(done); + }); + }); + } +); + Tinytest.addAsync( 'accounts - Meteor.loggingOut() is true right after a logout call', (test, done) => { @@ -150,7 +164,7 @@ Tinytest.addAsync( ); Tinytest.addAsync( - 'accounts - Meteor.user obeys explicit and default field selectors', + 'accounts - Meteor.user() obeys explicit and default field selectors', (test, done) => { logoutAndCreateUser(test, done, () => { Meteor.loginWithPassword(username, password, () => { @@ -178,6 +192,38 @@ Tinytest.addAsync( } ); +Tinytest.addAsync( + 'accounts async - Meteor.userAsync() obeys explicit and default field selectors', + (test, done) => { + logoutAndCreateUser(test, done, () => { + Meteor.loginWithPassword(username, password, async () => { + // by default, all fields should be returned + let user; + user = await Meteor.userAsync(); + test.equal(user.profile[excludeField], excludeValue); + + // this time we want to exclude the default fields + const options = Accounts._options; + Accounts._options = {}; + Accounts.config({ defaultFieldSelector: { ['profile.' + defaultExcludeField]: 0 } }); + + user = await Meteor.userAsync(); + test.isUndefined(user.profile[defaultExcludeField]); + test.equal(user.profile[excludeField], excludeValue); + test.equal(user.profile.name, username); + + // this time we only want certain fields... + + user = await Meteor.userAsync({ fields: { 'profile.name': 1 } }); + test.isUndefined(user.profile[excludeField]); + test.isUndefined(user.profile[defaultExcludeField]); + test.equal(user.profile.name, username); + Accounts._options = options; + removeTestUser(done); + }); + }); + } +); Tinytest.addAsync( 'accounts-2fa - Meteor.loginWithPasswordAnd2faCode() fails when token is not provided', @@ -199,13 +245,13 @@ Tinytest.addAsync( ); -Tinytest.addAsync( + Tinytest.addAsync( 'accounts-2fa - Meteor.loginWithPasswordAnd2faCode() fails with invalid code', (test, done) => { createUserAndLogout(test, done, () => { forceEnableUser2fa(() => { - Meteor.loginWithPasswordAnd2faCode(username, password, 'ABC', e => { - test.isFalse(Meteor.user()); + Meteor.loginWithPasswordAnd2faCode(username, password, 'ABC', async e => { + test.isFalse(await Meteor.user()); test.equal(e.reason, 'Invalid 2FA code'); removeTestUser(done); }); diff --git a/packages/accounts-base/accounts_common.js b/packages/accounts-base/accounts_common.js index b94e927a2d..edca3cd31b 100644 --- a/packages/accounts-base/accounts_common.js +++ b/packages/accounts-base/accounts_common.js @@ -79,40 +79,6 @@ export class AccountsCommon { // should come up with a more generic way to do this (eg, with some sort of // symbolic error code rather than a number). this.LoginCancelledError.numericError = 0x8acdc2f; - - // loginServiceConfiguration and ConfigError are maintained for backwards compatibility - Meteor.startup(() => { - const { ServiceConfiguration } = Package['service-configuration']; - this.loginServiceConfiguration = ServiceConfiguration.configurations; - this.ConfigError = ServiceConfiguration.ConfigError; - - const settings = Meteor.settings?.packages?.['accounts-base']; - if (settings) { - if (settings.oauthSecretKey) { - if (!Package['oauth-encryption']) { - throw new Error( - 'The oauth-encryption package must be loaded to set oauthSecretKey' - ); - } - Package['oauth-encryption'].OAuthEncryption.loadKey( - settings.oauthSecretKey - ); - delete settings.oauthSecretKey; - } - // Validate config options keys - Object.keys(settings).forEach(key => { - if (!VALID_CONFIG_KEYS.includes(key)) { - // TODO Consider just logging a debug message instead to allow for additional keys in the settings here? - throw new Meteor.Error( - `Accounts configuration: Invalid key: ${key}` - ); - } else { - // set values in Accounts._options - this._options[key] = settings[key]; - } - }); - } - }); } /** @@ -170,6 +136,18 @@ export class AccountsCommon { : null; } + /** + * @summary Get the current user record, or `null` if no user is logged in. + * @locus Anywhere + * @param {Object} [options] + * @param {MongoFieldSpecifier} options.fields Dictionary of fields to return or exclude. + */ + async userAsync(options) { + const userId = this.userId(); + return userId + ? this.users.findOneAsync(userId, this._addDefaultFieldSelector(options)) + : null; + } // Set up config for the accounts system. Call this on both the client // and the server. // @@ -264,6 +242,7 @@ export class AccountsCommon { // Validate config options keys Object.keys(options).forEach(key => { if (!VALID_CONFIG_KEYS.includes(key)) { + // TODO Consider just logging a debug message instead to allow for additional keys in the settings here? throw new Meteor.Error(`Accounts.config: Invalid key: ${key}`); } }); @@ -418,6 +397,15 @@ Meteor.userId = () => Accounts.userId(); */ Meteor.user = options => Accounts.user(options); +/** + * @summary Get the current user record, or `null` if no user is logged in. A reactive data source. + * @locus Anywhere but publish functions + * @importFromPackage meteor + * @param {Object} [options] + * @param {MongoFieldSpecifier} options.fields Dictionary of fields to return or exclude. + */ +Meteor.userAsync = options => Accounts.userAsync(options); + // how long (in days) until a login token expires const DEFAULT_LOGIN_EXPIRATION_DAYS = 90; // how long (in days) until reset password token expires @@ -430,9 +418,6 @@ const DEFAULT_PASSWORD_ENROLL_TOKEN_EXPIRATION_DAYS = 30; const MIN_TOKEN_LIFETIME_CAP_SECS = 3600; // one hour // how often (in milliseconds) we check for expired tokens export const EXPIRE_TOKENS_INTERVAL_MS = 600 * 1000; // 10 minutes -// how long we wait before logging out clients when Meteor.logoutOtherClients is -// called -export const CONNECTION_CLOSE_DELAY_MS = 10 * 1000; // A large number of expiration days (approximately 100 years worth) that is // used when creating unexpiring tokens. const LOGIN_UNEXPIRING_TOKEN_DAYS = 365 * 100; diff --git a/packages/accounts-base/accounts_server.js b/packages/accounts-base/accounts_server.js index 8c472e1b5e..7b28f3bd22 100644 --- a/packages/accounts-base/accounts_server.js +++ b/packages/accounts-base/accounts_server.js @@ -1,4 +1,5 @@ import crypto from 'crypto'; +import { Meteor } from 'meteor/meteor' import { AccountsCommon, EXPIRE_TOKENS_INTERVAL_MS, @@ -13,6 +14,7 @@ const NonEmptyString = Match.Where(x => { return x.length > 0; }); + /** * @summary Constructor for the `Accounts` namespace on the server. * @locus Server @@ -70,8 +72,6 @@ export class AccountsServer extends AccountsCommon { // list of all registered handlers. this._loginHandlers = []; - - setupUsersCollection(this.users); setupDefaultLoginHandlers(this); setExpireTokensInterval(this); @@ -125,6 +125,10 @@ export class AccountsServer extends AccountsCommon { return currentInvocation.userId; } + async init() { + await setupUsersCollection(this.users); + } + /// /// LOGIN HOOKS /// @@ -258,11 +262,11 @@ export class AccountsServer extends AccountsCommon { }); }; - _successfulLogout(connection, userId) { + async _successfulLogout(connection, userId) { // don't fetch the user object unless there are some callbacks registered let user; - this._onLogoutHook.each(callback => { - if (!user && userId) user = this.users.findOne(userId, {fields: this._options.defaultFieldSelector}); + await this._onLogoutHook.forEachAsync(async callback => { + if (!user && userId) user = await this.users.findOne(userId, { fields: this._options.defaultFieldSelector }); callback({ user, connection }); return true; }); @@ -398,10 +402,10 @@ export class AccountsServer extends AccountsCommon { // indicates that the login token has already been inserted into the // database and doesn't need to be inserted again. (It's used by the // "resume" login handler). - _loginUser(methodInvocation, userId, stampedLoginToken) { + async _loginUser(methodInvocation, userId, stampedLoginToken) { if (! stampedLoginToken) { stampedLoginToken = this._generateStampedLoginToken(); - this._insertLoginToken(userId, stampedLoginToken); + await this._insertLoginToken(userId, stampedLoginToken); } // This order (and the avoidance of yields) is important to make @@ -472,12 +476,13 @@ export class AccountsServer extends AccountsCommon { this._validateLogin(methodInvocation.connection, attempt); if (attempt.allowed) { + const o = await this._loginUser( + methodInvocation, + result.userId, + result.stampedLoginToken + ) const ret = { - ...this._loginUser( - methodInvocation, - result.userId, - result.stampedLoginToken - ), + ...o, ...result.options }; ret.type = attempt.type; @@ -614,8 +619,8 @@ export class AccountsServer extends AccountsCommon { // Any connections associated with old-style unhashed tokens will be // in the process of becoming associated with hashed tokens and then // they'll get closed. - destroyToken(userId, loginToken) { - this.users.update(userId, { + async destroyToken(userId, loginToken) { + await this.users.update(userId, { $pull: { "services.resume.loginTokens": { $or: [ @@ -652,13 +657,13 @@ export class AccountsServer extends AccountsCommon { return await accounts._attemptLogin(this, "login", arguments, result); }; - methods.logout = function () { + methods.logout = async function () { const token = accounts._getLoginToken(this.connection.id); accounts._setLoginToken(this.userId, this.connection, null); if (token && this.userId) { - accounts.destroyToken(this.userId, token); + await accounts.destroyToken(this.userId, token); } - accounts._successfulLogout(this.connection, this.userId); + await accounts._successfulLogout(this.connection, this.userId); this.setUserId(null); }; @@ -670,8 +675,8 @@ export class AccountsServer extends AccountsCommon { // @returns Object // If successful, returns { token: , id: , // tokenExpires: }. - methods.getNewToken = function () { - const user = accounts.users.findOne(this.userId, { + methods.getNewToken = async function () { + const user = await accounts.users.findOne(this.userId, { fields: { "services.resume.loginTokens": 1 } }); if (! this.userId || ! user) { @@ -690,8 +695,8 @@ export class AccountsServer extends AccountsCommon { } const newStampedToken = accounts._generateStampedLoginToken(); newStampedToken.when = currentStampedToken.when; - accounts._insertLoginToken(this.userId, newStampedToken); - return accounts._loginUser(this, this.userId, newStampedToken); + await accounts._insertLoginToken(this.userId, newStampedToken); + return await accounts._loginUser(this, this.userId, newStampedToken); }; // Removes all tokens except the token associated with the current @@ -724,14 +729,19 @@ export class AccountsServer extends AccountsCommon { throw new Meteor.Error(403, "Service unknown"); } - const { ServiceConfiguration } = Package['service-configuration']; - if (ServiceConfiguration.configurations.findOne({service: options.service})) - throw new Meteor.Error(403, `Service ${options.service} already configured`); + if (Package['service-configuration']) { + const { ServiceConfiguration } = Package['service-configuration']; + if (ServiceConfiguration.configurations.findOne({service: options.service})) + throw new Meteor.Error(403, `Service ${options.service} already configured`); - if (hasOwn.call(options, 'secret') && usingOAuthEncryption()) - options.secret = OAuthEncryption.seal(options.secret); + if (Package["oauth-encryption"]) { + const { OAuthEncryption } = Package["oauth-encryption"] + if (hasOwn.call(options, 'secret') && OAuthEncryption.keyIsLoaded()) + options.secret = OAuthEncryption.seal(options.secret); + } - ServiceConfiguration.configurations.insert(options); + ServiceConfiguration.configurations.insert(options); + } }; accounts._server.methods(methods); @@ -756,8 +766,10 @@ export class AccountsServer extends AccountsCommon { // Publish all login service configuration fields other than secret. this._server.publish("meteor.loginServiceConfiguration", () => { - const { ServiceConfiguration } = Package['service-configuration']; - return ServiceConfiguration.configurations.find({}, {fields: {secret: 0}}); + if (Package['service-configuration']) { + const { ServiceConfiguration } = Package['service-configuration']; + return ServiceConfiguration.configurations.find({}, {fields: {secret: 0}}); + } }, {is_auto: true}); // not technically autopublish, but stops the warning. // Use Meteor.startup to give other packages a chance to call @@ -888,10 +900,10 @@ export class AccountsServer extends AccountsCommon { // Using $addToSet avoids getting an index error if another client // logging in simultaneously has already inserted the new hashed // token. - _insertHashedLoginToken(userId, hashedToken, query) { + async _insertHashedLoginToken(userId, hashedToken, query) { query = query ? { ...query } : {}; query._id = userId; - this.users.update(query, { + await this.users.update(query, { $addToSet: { "services.resume.loginTokens": hashedToken } @@ -899,14 +911,20 @@ export class AccountsServer extends AccountsCommon { }; // Exported for tests. - _insertLoginToken(userId, stampedToken, query) { - this._insertHashedLoginToken( + async _insertLoginToken(userId, stampedToken, query) { + await this._insertHashedLoginToken( userId, this._hashStampedToken(stampedToken), query ); }; + /** + * + * @param userId + * @private + * @returns {Promise} + */ _clearAllLoginTokens(userId) { this.users.update(userId, { $set: { @@ -964,7 +982,7 @@ export class AccountsServer extends AccountsCommon { // already -- in this case we just clean up the observe that we started). const myObserveNumber = ++this._nextUserObserveNumber; this._userObservesForConnections[connection.id] = myObserveNumber; - Meteor.defer(() => { + Meteor.defer(async () => { // If something else happened on this connection in the meantime (it got // closed, or another call to _setLoginToken happened), just do // nothing. We don't need to start an observe for an old connection or old @@ -977,7 +995,7 @@ export class AccountsServer extends AccountsCommon { // Because we upgrade unhashed login tokens to hashed tokens at // login time, sessions will only be logged in with a hashed // token. Thus we only need to observe hashed tokens here. - const observe = this.users.find({ + const observe = await this.users.find({ _id: userId, 'services.resume.loginTokens.hashedToken': newToken }, { fields: { _id: 1 } }).observeChanges({ @@ -1088,7 +1106,14 @@ export class AccountsServer extends AccountsCommon { // tests. oldestValidDate is simulate expiring tokens without waiting // for them to actually expire. userId is used by tests to only expire // tokens for the test user. - _expireTokens(oldestValidDate, userId) { + /** + * + * @param oldestValidDate + * @param userId + * @private + * @return {Promise} + */ + async _expireTokens(oldestValidDate, userId) { const tokenLifetimeMs = this._getTokenLifetimeMs(); // when calling from a test with extra arguments, you must specify both! @@ -1103,7 +1128,7 @@ export class AccountsServer extends AccountsCommon { // Backwards compatible with older versions of meteor that stored login token // timestamps as numbers. - this.users.update({ ...userFilter, + await this.users.update({ ...userFilter, $or: [ { "services.resume.loginTokens.when": { $lt: oldestValidDate } }, { "services.resume.loginTokens.when": { $lt: +oldestValidDate } } @@ -1140,7 +1165,7 @@ export class AccountsServer extends AccountsCommon { }; // Called by accounts-password - insertUserDoc(options, user) { + async insertUserDoc(options, user) { // - clone user document, to protect from modification // - add createdAt timestamp // - prepare an _id, so that you can modify other collections (eg @@ -1185,7 +1210,7 @@ export class AccountsServer extends AccountsCommon { let userId; try { - userId = this.users.insert(fullUser); + userId = await this.users.insert(fullUser); } catch (e) { // XXX string parsing sucks, maybe // https://jira.mongodb.org/browse/SERVER-3069 will get fixed one day @@ -1215,9 +1240,9 @@ export class AccountsServer extends AccountsCommon { /// CLEAN UP FOR `logoutOtherClients` /// - _deleteSavedTokensForUser(userId, tokensToDelete) { + async _deleteSavedTokensForUser(userId, tokensToDelete) { if (tokensToDelete) { - this.users.update(userId, { + await this.users.update(userId, { $unset: { "services.resume.haveLoginTokensToDelete": 1, "services.resume.loginTokensToDelete": 1 @@ -1236,16 +1261,23 @@ export class AccountsServer extends AccountsCommon { // shouldn't happen very often. We shouldn't put a delay here because // that would give a lot of power to an attacker with a stolen login // token and the ability to crash the server. - Meteor.startup(() => { - this.users.find({ + Meteor.startup(async () => { + await this.users.find({ "services.resume.haveLoginTokensToDelete": true - }, {fields: { + }, { + fields: { "services.resume.loginTokensToDelete": 1 - }}).forEach(user => { + } + }).forEach(user => { this._deleteSavedTokensForUser( user._id, user.services.resume.loginTokensToDelete - ); + ) + // We don't need to wait for this to complete. + .then(_ => _) + .catch(err => { + console.log(err); + }); }); }); }; @@ -1265,7 +1297,7 @@ export class AccountsServer extends AccountsCommon { // @returns {Object} Object with token and id keys, like the result // of the "login" method. // - updateOrCreateUserFromExternalService( + async updateOrCreateUserFromExternalService( serviceName, serviceData, options @@ -1300,13 +1332,11 @@ export class AccountsServer extends AccountsCommon { } else { selector[serviceIdKey] = serviceData.id; } - - let user = this.users.findOne(selector, {fields: this._options.defaultFieldSelector}); - + let user = await this.users.findOne(selector, {fields: this._options.defaultFieldSelector}); // Check to see if the developer has a custom way to find the user outside // of the general selectors above. if (!user && this._additionalFindUserOnExternalLogin) { - user = this._additionalFindUserOnExternalLogin({serviceName, serviceData, options}) + user = await this._additionalFindUserOnExternalLogin({serviceName, serviceData, options}) } // Before continuing, run user hook to see if we should continue @@ -1326,7 +1356,7 @@ export class AccountsServer extends AccountsCommon { } if (user) { - pinEncryptedFieldsToUser(serviceData, user._id); + await pinEncryptedFieldsToUser(serviceData, user._id); let setAttrs = {}; Object.keys(serviceData).forEach(key => @@ -1336,7 +1366,7 @@ export class AccountsServer extends AccountsCommon { // XXX Maybe we should re-use the selector above and notice if the update // touches nothing? setAttrs = { ...setAttrs, ...opts }; - this.users.update(user._id, { + await this.users.update(user._id, { $set: setAttrs }); @@ -1348,9 +1378,10 @@ export class AccountsServer extends AccountsCommon { // Create a new user with the service data. user = {services: {}}; user.services[serviceName] = serviceData; + const userId = await this.insertUserDoc(opts, user); return { type: serviceName, - userId: this.insertUserDoc(opts, user) + userId }; } }; @@ -1532,7 +1563,7 @@ const setupDefaultLoginHandlers = accounts => { }; // Login handler for resume tokens. -const defaultResumeLoginHandler = (accounts, options) => { +const defaultResumeLoginHandler = async (accounts, options) => { if (!options.resume) return undefined; @@ -1543,7 +1574,7 @@ const defaultResumeLoginHandler = (accounts, options) => { // First look for just the new-style hashed login token, to avoid // sending the unhashed token to the database in a query if we don't // need to. - let user = accounts.users.findOne( + let user = await accounts.users.findOne( {"services.resume.loginTokens.hashedToken": hashedToken}, {fields: {"services.resume.loginTokens.$": 1}}); @@ -1553,7 +1584,7 @@ const defaultResumeLoginHandler = (accounts, options) => { // the old-style token OR the new-style token, because another // client connection logging in simultaneously might have already // converted the token. - user = accounts.users.findOne({ + user = await accounts.users.findOne({ $or: [ {"services.resume.loginTokens.hashedToken": hashedToken}, {"services.resume.loginTokens.token": options.resume} @@ -1572,13 +1603,13 @@ const defaultResumeLoginHandler = (accounts, options) => { // {hashedToken, when} for a hashed token or {token, when} for an // unhashed token. let oldUnhashedStyleToken; - let token = user.services.resume.loginTokens.find(token => + let token = await user.services.resume.loginTokens.find(token => token.hashedToken === hashedToken ); if (token) { oldUnhashedStyleToken = false; } else { - token = user.services.resume.loginTokens.find(token => + token = await user.services.resume.loginTokens.find(token => token.token === options.resume ); oldUnhashedStyleToken = true; @@ -1598,7 +1629,7 @@ const defaultResumeLoginHandler = (accounts, options) => { // after we read it). Using $addToSet avoids getting an index // error if another client logging in simultaneously has already // inserted the new hashed token. - accounts.users.update( + await accounts.users.update( { _id: user._id, "services.resume.loginTokens.token": options.resume @@ -1614,7 +1645,7 @@ const defaultResumeLoginHandler = (accounts, options) => { // Remove the old token *after* adding the new, since otherwise // another client trying to login between our removing the old and // adding the new wouldn't find a token to login with. - accounts.users.update(user._id, { + await accounts.users.update(user._id, { $pull: { "services.resume.loginTokens": { "token": options.resume } } @@ -1630,49 +1661,50 @@ const defaultResumeLoginHandler = (accounts, options) => { }; }; -const expirePasswordToken = ( - accounts, - oldestValidDate, - tokenFilter, - userId -) => { - // boolean value used to determine if this method was called from enroll account workflow - let isEnroll = false; - const userFilter = userId ? {_id: userId} : {}; - // check if this method was called from enroll account workflow - if(tokenFilter['services.password.enroll.reason']) { - isEnroll = true; - } - let resetRangeOr = { - $or: [ - { "services.password.reset.when": { $lt: oldestValidDate } }, - { "services.password.reset.when": { $lt: +oldestValidDate } } - ] - }; - if(isEnroll) { - resetRangeOr = { +const expirePasswordToken = + async ( + accounts, + oldestValidDate, + tokenFilter, + userId + ) => { + // boolean value used to determine if this method was called from enroll account workflow + let isEnroll = false; + const userFilter = userId ? { _id: userId } : {}; + // check if this method was called from enroll account workflow + if (tokenFilter['services.password.enroll.reason']) { + isEnroll = true; + } + let resetRangeOr = { $or: [ - { "services.password.enroll.when": { $lt: oldestValidDate } }, - { "services.password.enroll.when": { $lt: +oldestValidDate } } + { "services.password.reset.when": { $lt: oldestValidDate } }, + { "services.password.reset.when": { $lt: +oldestValidDate } } ] }; - } - const expireFilter = { $and: [tokenFilter, resetRangeOr] }; - if(isEnroll) { - accounts.users.update({...userFilter, ...expireFilter}, { - $unset: { - "services.password.enroll": "" - } - }, { multi: true }); - } else { - accounts.users.update({...userFilter, ...expireFilter}, { - $unset: { - "services.password.reset": "" - } - }, { multi: true }); - } + if (isEnroll) { + resetRangeOr = { + $or: [ + { "services.password.enroll.when": { $lt: oldestValidDate } }, + { "services.password.enroll.when": { $lt: +oldestValidDate } } + ] + }; + } + const expireFilter = { $and: [tokenFilter, resetRangeOr] }; + if (isEnroll) { + await accounts.users.update({ ...userFilter, ...expireFilter }, { + $unset: { + "services.password.enroll": "" + } + }, { multi: true }); + } else { + await accounts.users.update({ ...userFilter, ...expireFilter }, { + $unset: { + "services.password.reset": "" + } + }, { multi: true }); + } -}; + }; const setExpireTokensInterval = accounts => { accounts.expireTokenInterval = Meteor.setInterval(() => { @@ -1682,17 +1714,7 @@ const setExpireTokensInterval = accounts => { }, EXPIRE_TOKENS_INTERVAL_MS); }; -/// -/// OAuth Encryption Support -/// - -const OAuthEncryption = - Package["oauth-encryption"] && - Package["oauth-encryption"].OAuthEncryption; - -const usingOAuthEncryption = () => { - return OAuthEncryption && OAuthEncryption.keyIsLoaded(); -}; +const OAuthEncryption = Package["oauth-encryption"]?.OAuthEncryption; // OAuth service data is temporarily stored in the pending credentials // collection during the oauth authentication process. Sensitive data @@ -1704,44 +1726,12 @@ const usingOAuthEncryption = () => { const pinEncryptedFieldsToUser = (serviceData, userId) => { Object.keys(serviceData).forEach(key => { let value = serviceData[key]; - if (OAuthEncryption && OAuthEncryption.isSealed(value)) + if (OAuthEncryption?.isSealed(value)) value = OAuthEncryption.seal(OAuthEncryption.open(value), userId); serviceData[key] = value; }); }; - -// Encrypt unencrypted login service secrets when oauth-encryption is -// added. -// -// XXX For the oauthSecretKey to be available here at startup, the -// developer must call Accounts.config({oauthSecretKey: ...}) at load -// time, instead of in a Meteor.startup block, because the startup -// block in the app code will run after this accounts-base startup -// block. Perhaps we need a post-startup callback? - -Meteor.startup(() => { - if (! usingOAuthEncryption()) { - return; - } - - const { ServiceConfiguration } = Package['service-configuration']; - - ServiceConfiguration.configurations.find({ - $and: [{ - secret: { $exists: true } - }, { - "secret.algorithm": { $exists: false } - }] - }).forEach(config => { - ServiceConfiguration.configurations.update(config._id, { - $set: { - secret: OAuthEncryption.seal(config.secret) - } - }); - }); -}); - // XXX see comment on Accounts.createUser in passwords_server about adding a // second "server options" argument. const defaultCreateUserHook = (options, user) => { @@ -1781,7 +1771,7 @@ function defaultValidateNewUserHook(user) { } } -const setupUsersCollection = users => { +const setupUsersCollection = async users => { /// /// RESTRICTING WRITES TO USER OBJECTS /// @@ -1807,21 +1797,21 @@ const setupUsersCollection = users => { }); /// DEFAULT INDEXES ON USERS - users.createIndex('username', { unique: true, sparse: true }); - users.createIndex('emails.address', { unique: true, sparse: true }); - users.createIndex('services.resume.loginTokens.hashedToken', + await users.createIndex('username', { unique: true, sparse: true }); + await users.createIndex('emails.address', { unique: true, sparse: true }); + await users.createIndex('services.resume.loginTokens.hashedToken', { unique: true, sparse: true }); - users.createIndex('services.resume.loginTokens.token', + await users.createIndex('services.resume.loginTokens.token', { unique: true, sparse: true }); // For taking care of logoutOtherClients calls that crashed before the // tokens were deleted. - users.createIndex('services.resume.haveLoginTokensToDelete', + await users.createIndex('services.resume.haveLoginTokensToDelete', { sparse: true }); // For expiring login tokens - users.createIndex("services.resume.loginTokens.when", { sparse: true }); + await users.createIndex("services.resume.loginTokens.when", { sparse: true }); // For expiring password tokens - users.createIndex('services.password.reset.when', { sparse: true }); - users.createIndex('services.password.enroll.when', { sparse: true }); + await users.createIndex('services.password.reset.when', { sparse: true }); + await users.createIndex('services.password.enroll.when', { sparse: true }); }; diff --git a/packages/accounts-base/accounts_tests.js b/packages/accounts-base/accounts_tests.js index de870e0f81..458c6ce589 100644 --- a/packages/accounts-base/accounts_tests.js +++ b/packages/accounts-base/accounts_tests.js @@ -4,8 +4,8 @@ import { Accounts } from 'meteor/accounts-base'; import { Random } from 'meteor/random'; Meteor.methods({ - getCurrentLoginToken: function () { - return Accounts._getLoginToken(this.connection.id); + getCurrentLoginToken: async function () { + return await Accounts._getLoginToken(this.connection.id); } }); @@ -13,18 +13,18 @@ Meteor.methods({ // *are* validated, but Accounts._options is global state which makes this hard // (impossible?) Tinytest.add( - 'accounts - config validates keys', - test => test.throws(() => Accounts.config({foo: "bar"})) + 'accounts - config - validates keys', + test => test.throws(() => Accounts.config({ foo: "bar" })) ); -Tinytest.add('accounts - config - token lifetime', test => { +Tinytest.addAsync('accounts - config - token lifetime', async test => { const { loginExpirationInDays } = Accounts._options; Accounts._options.loginExpirationInDays = 2; test.equal(Accounts._getTokenLifetimeMs(), 2 * 24 * 60 * 60 * 1000); Accounts._options.loginExpirationInDays = loginExpirationInDays; }); -Tinytest.add('accounts - config - unexpiring tokens', test => { +Tinytest.addAsync('accounts - config - unexpiring tokens', async test => { const { loginExpirationInDays } = Accounts._options; // When setting loginExpirationInDays to null in the global Accounts @@ -52,7 +52,7 @@ Tinytest.add('accounts - config - unexpiring tokens', test => { Accounts._options.loginExpirationInDays = loginExpirationInDays; }); -Tinytest.add('accounts - config - default token lifetime', test => { +Tinytest.addAsync('accounts - config - default token lifetime', async test => { const options = Accounts._options; Accounts._options = {}; test.equal( @@ -62,11 +62,11 @@ Tinytest.add('accounts - config - default token lifetime', test => { Accounts._options = options; }); -Tinytest.add('accounts - config - defaultFieldSelector', test => { +Tinytest.addAsync('accounts - config - defaultFieldSelector', async test => { const options = Accounts._options; Accounts._options = {}; - const setValue = {bigArray: 0}; - Accounts.config({defaultFieldSelector: setValue}); + const setValue = { bigArray: 0 }; + Accounts.config({ defaultFieldSelector: setValue }); test.equal(Accounts._options.defaultFieldSelector, setValue); Accounts._options = options; }); @@ -77,154 +77,51 @@ Accounts.validateNewUser(user => { return true; }); -Tinytest.add('accounts - validateNewUser gets passed user with _id', test => { - const newUserId = Accounts.updateOrCreateUserFromExternalService('foobook', {id: Random.id()}).userId; - test.isTrue(newUserId in idsInValidateNewUser); +Tinytest.addAsync('accounts - validateNewUser gets passed user with _id', async test => { + const { userId } = await Accounts.updateOrCreateUserFromExternalService('foobook', { id: Random.id() }); + test.isTrue(userId in idsInValidateNewUser); }); -Tinytest.add('accounts - updateOrCreateUserFromExternalService - Facebook', test => { - const facebookId = Random.id(); - - // create an account with facebook - const uid1 = Accounts.updateOrCreateUserFromExternalService( - 'facebook', {id: facebookId, monkey: 42}, {profile: {foo: 1}}).id; - const users1 = Meteor.users.find({"services.facebook.id": facebookId}).fetch(); - test.length(users1, 1); - test.equal(users1[0].profile.foo, 1); - test.equal(users1[0].services.facebook.monkey, 42); - - // create again with the same id, see that we get the same user. - // it should update services.facebook but not profile. - const uid2 = Accounts.updateOrCreateUserFromExternalService( - 'facebook', {id: facebookId, llama: 50}, - {profile: {foo: 1000, bar: 2}}).id; - test.equal(uid1, uid2); - const users2 = Meteor.users.find({"services.facebook.id": facebookId}).fetch(); - test.length(users2, 1); - test.equal(users2[0].profile.foo, 1); - test.equal(users2[0].profile.bar, undefined); - test.equal(users2[0].services.facebook.llama, 50); - // make sure we *don't* lose values not passed this call to - // updateOrCreateUserFromExternalService - test.equal(users2[0].services.facebook.monkey, 42); - - // cleanup - Meteor.users.remove(uid1); -}); - -Tinytest.add('accounts - updateOrCreateUserFromExternalService - Meteor Developer', test => { - const developerId = Random.id(); - const uid1 = Accounts.updateOrCreateUserFromExternalService( - 'meteor-developer', - { id: developerId, username: 'meteor-developer' }, - { profile: { name: 'meteor-developer' } } - ).id; - const users1 = Meteor.users.find({ 'services.meteor-developer.id': developerId }).fetch(); - test.length(users1, 1); - test.equal(users1[0].profile.name, 'meteor-developer'); - - const uid2 = Accounts.updateOrCreateUserFromExternalService( - 'meteor-developer', - { id: developerId, username: 'meteor-developer' }, - { profile: { name: 'meteor-developer', username: 'developer' } } - ).id; - test.equal(uid1, uid2); - const users2 = Meteor.users.find({ 'services.meteor-developer.id': developerId }).fetch(); - test.length(users2, 1); - test.equal(users1[0].profile.name, 'meteor-developer'); - test.equal(users1[0].profile.username, undefined); - - // cleanup - Meteor.users.remove(uid1); -}); - -Tinytest.add('accounts - updateOrCreateUserFromExternalService - Weibo', test => { - const weiboId1 = Random.id(); - const weiboId2 = Random.id(); - - // users that have different service ids get different users - const uid1 = Accounts.updateOrCreateUserFromExternalService( - 'weibo', {id: weiboId1}, {profile: {foo: 1}}).id; - const uid2 = Accounts.updateOrCreateUserFromExternalService( - 'weibo', {id: weiboId2}, {profile: {bar: 2}}).id; - test.equal(Meteor.users.find({"services.weibo.id": {$in: [weiboId1, weiboId2]}}).count(), 2); - test.equal(Meteor.users.findOne({"services.weibo.id": weiboId1}).profile.foo, 1); - test.equal(Meteor.users.findOne({"services.weibo.id": weiboId1}).emails, undefined); - test.equal(Meteor.users.findOne({"services.weibo.id": weiboId2}).profile.bar, 2); - test.equal(Meteor.users.findOne({"services.weibo.id": weiboId2}).emails, undefined); - - // cleanup - Meteor.users.remove(uid1); - Meteor.users.remove(uid2); -}); - -Tinytest.add('accounts - updateOrCreateUserFromExternalService - Twitter', test => { - const twitterIdOld = parseInt(Random.hexString(4), 16); - const twitterIdNew = ''+twitterIdOld; - - // create an account with twitter using the old ID format of integer - const uid1 = Accounts.updateOrCreateUserFromExternalService( - 'twitter', {id: twitterIdOld, monkey: 42}, {profile: {foo: 1}}).id; - const users1 = Meteor.users.find({"services.twitter.id": twitterIdOld}).fetch(); - test.length(users1, 1); - test.equal(users1[0].profile.foo, 1); - test.equal(users1[0].services.twitter.monkey, 42); - - // Update the account with the new ID format of string - // test that the existing user is found, and that the ID - // gets updated to a string value - const uid2 = Accounts.updateOrCreateUserFromExternalService( - 'twitter', {id: twitterIdNew, monkey: 42}, {profile: {foo: 1}}).id; - test.equal(uid1, uid2); - const users2 = Meteor.users.find({"services.twitter.id": twitterIdNew}).fetch(); - test.length(users2, 1); - - // cleanup - Meteor.users.remove(uid1); -}); - - -Tinytest.add('accounts - insertUserDoc username', test => { +Tinytest.addAsync('accounts - insertUserDoc username', async test => { const userIn = { username: Random.id() }; // user does not already exist. create a user object with fields set. - const userId = Accounts.insertUserDoc( - {profile: {name: 'Foo Bar'}}, + const userId = await Accounts.insertUserDoc( + { profile: { name: 'Foo Bar' } }, userIn ); - const userOut = Meteor.users.findOne(userId); - + const userOut = await Meteor.users.findOne(userId); test.equal(typeof userOut.createdAt, 'object'); test.equal(userOut.profile.name, 'Foo Bar'); test.equal(userOut.username, userIn.username); // run the hook again. now the user exists, so it throws an error. - test.throws( - () => Accounts.insertUserDoc({profile: {name: 'Foo Bar'}}, userIn), + await test.throwsAsync( + async () => await Accounts.insertUserDoc({ profile: { name: 'Foo Bar' } }, userIn), 'Username already exists.' ); // cleanup - Meteor.users.remove(userId); + await Meteor.users.remove(userId); }); -Tinytest.add('accounts - insertUserDoc email', test => { +Tinytest.addAsync('accounts - insertUserDoc email', async test => { const email1 = Random.id(); const email2 = Random.id(); const email3 = Random.id(); const userIn = { - emails: [{address: email1, verified: false}, - {address: email2, verified: true}] + emails: [{ address: email1, verified: false }, + { address: email2, verified: true }] }; // user does not already exist. create a user object with fields set. - const userId = Accounts.insertUserDoc( - {profile: {name: 'Foo Bar'}}, + const userId = await Accounts.insertUserDoc( + { profile: { name: 'Foo Bar' } }, userIn ); - const userOut = Meteor.users.findOne(userId); + const userOut = await Meteor.users.findOne(userId); test.equal(typeof userOut.createdAt, 'object'); test.equal(userOut.profile.name, 'Foo Bar'); @@ -232,43 +129,47 @@ Tinytest.add('accounts - insertUserDoc email', test => { // run the hook again with the exact same emails. // run the hook again. now the user exists, so it throws an error. - test.throws( - () => Accounts.insertUserDoc({profile: {name: 'Foo Bar'}}, userIn), + await test.throwsAsync( + async () => await Accounts.insertUserDoc({ profile: { name: 'Foo Bar' } }, userIn), 'Email already exists.' ); // now with only one of them. - test.throws(() => - Accounts.insertUserDoc({}, {emails: [{address: email1}]}), + await test.throwsAsync( + async () => + await Accounts.insertUserDoc({}, { emails: [{ address: email1 }] }), 'Email already exists.' ); - test.throws(() => - Accounts.insertUserDoc({}, {emails: [{address: email2}]}), + await test.throwsAsync( + async () => + await Accounts.insertUserDoc({}, { emails: [{ address: email2 }] }), 'Email already exists.' ); // a third email works. - const userId3 = Accounts.insertUserDoc( - {}, {emails: [{address: email3}]} + const userId3 = await Accounts.insertUserDoc( + {}, { emails: [{ address: email3 }] } ); - const user3 = Meteor.users.findOne(userId3); + const user3 = await Meteor.users.findOne(userId3); test.equal(typeof user3.createdAt, 'object'); // cleanup - Meteor.users.remove(userId); - Meteor.users.remove(userId3); + await Meteor.users.remove(userId); + await Meteor.users.remove(userId3); }); // More token expiration tests are in accounts-password -Tinytest.addAsync('accounts - expire numeric token', (test, onComplete) => { +Tinytest.addAsync('accounts - expire numeric token', async (test, onComplete) => { const userIn = { username: Random.id() }; - const userId = Accounts.insertUserDoc({ profile: { - name: 'Foo Bar' - } }, userIn); + const userId = await Accounts.insertUserDoc({ + profile: { + name: 'Foo Bar' + } + }, userIn); const date = new Date(new Date() - 5000); - Meteor.users.update(userId, { + await Meteor.users.update(userId, { $set: { "services.resume.loginTokens": [{ hashedToken: Random.id(), @@ -279,59 +180,64 @@ Tinytest.addAsync('accounts - expire numeric token', (test, onComplete) => { }] } }); - const observe = Meteor.users.find(userId).observe({ + const observe = await Meteor.users.find(userId).observe({ changed: newUser => { if (newUser.services && newUser.services.resume && - (!newUser.services.resume.loginTokens || + (!newUser.services.resume.loginTokens || newUser.services.resume.loginTokens.length === 0)) { observe.stop(); onComplete(); } } }); - Accounts._expireTokens(new Date(), userId); + await Accounts._expireTokens(new Date(), userId); }); // Login tokens used to be stored unhashed in the database. We want // to make sure users can still login after upgrading. -const insertUnhashedLoginToken = (userId, stampedToken) => { - Meteor.users.update( +const insertUnhashedLoginToken = async (userId, stampedToken) => { + await Meteor.users.update( userId, - {$push: {'services.resume.loginTokens': stampedToken}} + { $push: { 'services.resume.loginTokens': stampedToken } } ); }; -Tinytest.addAsync('accounts - login token', (test, onComplete) => { +Tinytest.addAsync('accounts - login token', async (test) => { // Test that we can login when the database contains a leftover // old style unhashed login token. - const userId1 = Accounts.insertUserDoc({}, {username: Random.id()}); + const userId1 = + await Accounts.insertUserDoc({}, { username: Random.id() }); const stampedToken1 = Accounts._generateStampedLoginToken(); - insertUnhashedLoginToken(userId1, stampedToken1); + await insertUnhashedLoginToken(userId1, stampedToken1); let connection = DDP.connect(Meteor.absoluteUrl()); - connection.call('login', {resume: stampedToken1.token}); + await connection.callAsync('login', { resume: stampedToken1.token }); connection.disconnect(); // Steal the unhashed token from the database and use it to login. // This is a sanity check so that when we *can't* login with a // stolen *hashed* token, we know it's not a problem with the test. - const userId2 = Accounts.insertUserDoc({}, {username: Random.id()}); - insertUnhashedLoginToken(userId2, Accounts._generateStampedLoginToken()); - const stolenToken1 = Meteor.users.findOne(userId2).services.resume.loginTokens[0].token; + const userId2 = + await Accounts.insertUserDoc({}, { username: Random.id() }); + await insertUnhashedLoginToken(userId2, Accounts._generateStampedLoginToken()); + const user2 = await Meteor.users.findOne(userId2); + const stolenToken1 = user2.services.resume.loginTokens[0].token; test.isTrue(stolenToken1); connection = DDP.connect(Meteor.absoluteUrl()); - connection.call('login', {resume: stolenToken1}); + await connection.callAsync('login', { resume: stolenToken1 }); connection.disconnect(); // Now do the same thing, this time with a stolen hashed token. - const userId3 = Accounts.insertUserDoc({}, {username: Random.id()}); - Accounts._insertLoginToken(userId3, Accounts._generateStampedLoginToken()); - const stolenToken2 = Meteor.users.findOne(userId3).services.resume.loginTokens[0].hashedToken; + const userId3 = + await Accounts.insertUserDoc({}, { username: Random.id() }); + await Accounts._insertLoginToken(userId3, Accounts._generateStampedLoginToken()); + const user3 = await Meteor.users.findOne(userId3); + const stolenToken2 = user3.services.resume.loginTokens[0].hashedToken; test.isTrue(stolenToken2); connection = DDP.connect(Meteor.absoluteUrl()); // evil plan foiled - test.throws( - () => connection.call('login', {resume: stolenToken2}), + await test.throwsAsync( + async () => await connection.callAsync('login', { resume: stolenToken2 }), /You\'ve been logged out by the server/ ); connection.disconnect(); @@ -339,24 +245,25 @@ Tinytest.addAsync('accounts - login token', (test, onComplete) => { // Old style unhashed tokens are replaced by hashed tokens when // encountered. This means that after someone logins once, the // old unhashed token is no longer available to be stolen. - const userId4 = Accounts.insertUserDoc({}, {username: Random.id()}); + const userId4 = + await Accounts.insertUserDoc({}, { username: Random.id() }); const stampedToken2 = Accounts._generateStampedLoginToken(); - insertUnhashedLoginToken(userId4, stampedToken2); + await insertUnhashedLoginToken(userId4, stampedToken2); connection = DDP.connect(Meteor.absoluteUrl()); - connection.call('login', {resume: stampedToken2.token}); + await connection.callAsync('login', { resume: stampedToken2.token }); connection.disconnect(); // The token is no longer available to be stolen. - const stolenToken3 = Meteor.users.findOne(userId4).services.resume.loginTokens[0].token; + const user4 = await Meteor.users.findOne(userId4); + const stolenToken3 = user4.services.resume.loginTokens[0].token; test.isFalse(stolenToken3); // After the upgrade, the client can still login with their original // unhashed login token. connection = DDP.connect(Meteor.absoluteUrl()); - connection.call('login', {resume: stampedToken2.token}); + await connection.callAsync('login', { resume: stampedToken2.token }); connection.disconnect(); - onComplete(); }); Tinytest.addAsync( @@ -380,59 +287,63 @@ Tinytest.addAsync( } ); -Tinytest.add('accounts - get new token', test => { +Tinytest.addAsync('accounts - get new token', async test => { // Test that the `getNewToken` method returns us a valid token, with // the same expiration as our original token. - const userId = Accounts.insertUserDoc({}, { username: Random.id() }); + const userId = await Accounts.insertUserDoc({}, { username: Random.id() }); const stampedToken = Accounts._generateStampedLoginToken(); - Accounts._insertLoginToken(userId, stampedToken); + await Accounts._insertLoginToken(userId, stampedToken); const conn = DDP.connect(Meteor.absoluteUrl()); - conn.call('login', { resume: stampedToken.token }); - test.equal(conn.call('getCurrentLoginToken'), - Accounts._hashLoginToken(stampedToken.token)); + await conn.callAsync('login', { resume: stampedToken.token }); + test.equal(await conn.callAsync('getCurrentLoginToken'), + Accounts._hashLoginToken(stampedToken.token)); - const newTokenResult = conn.call('getNewToken'); + const newTokenResult = await conn.callAsync('getNewToken'); test.equal(newTokenResult.tokenExpires, - Accounts._tokenExpiration(stampedToken.when)); - test.equal(conn.call('getCurrentLoginToken'), - Accounts._hashLoginToken(newTokenResult.token)); + Accounts._tokenExpiration(stampedToken.when)); + const token = await conn.callAsync('getCurrentLoginToken'); + test.equal(await conn.callAsync('getCurrentLoginToken'), + Accounts._hashLoginToken(newTokenResult.token)); conn.disconnect(); // A second connection should be able to log in with the new token // we got. const secondConn = DDP.connect(Meteor.absoluteUrl()); - secondConn.call('login', { resume: newTokenResult.token }); + await secondConn.callAsync('login', { resume: newTokenResult.token }); secondConn.disconnect(); } ); -Tinytest.addAsync('accounts - remove other tokens', (test, onComplete) => { +Tinytest.addAsync('accounts - remove other tokens', async (test) => { // Test that the `removeOtherTokens` method removes all tokens other // than the caller's token, thereby logging out and closing other // connections. - const userId = Accounts.insertUserDoc({}, { username: Random.id() }); + const userId = await Accounts.insertUserDoc({}, { username: Random.id() }); const stampedTokens = []; const conns = []; - for(let i = 0; i < 2; i++) { + for (let i = 0; i < 2; i++) { stampedTokens.push(Accounts._generateStampedLoginToken()); - Accounts._insertLoginToken(userId, stampedTokens[i]); + await Accounts._insertLoginToken(userId, stampedTokens[i]); const conn = DDP.connect(Meteor.absoluteUrl()); - conn.call('login', { resume: stampedTokens[i].token }); - test.equal(conn.call('getCurrentLoginToken'), - Accounts._hashLoginToken(stampedTokens[i].token)); + await conn.callAsync('login', { resume: stampedTokens[i].token }); + test.equal(await conn.callAsync('getCurrentLoginToken'), + Accounts._hashLoginToken(stampedTokens[i].token)); conns.push(conn); - }; + } + ; - conns[0].call('removeOtherTokens'); - simplePoll(() => { - const tokens = conns.map(conn => conn.call('getCurrentLoginToken')); - return ! tokens[1] && + await conns[0].callAsync('removeOtherTokens'); + simplePoll(async () => { + let tokens = []; + for (const conn of conns) { + tokens.push(await conn.callAsync('getCurrentLoginToken')); + } + return !tokens[1] && tokens[0] === Accounts._hashLoginToken(stampedTokens[0].token); }, () => { // success conns.forEach(conn => conn.disconnect()); - onComplete(); }, () => { // timed out throw new Error("accounts - remove other tokens timed out"); @@ -441,12 +352,12 @@ Tinytest.addAsync('accounts - remove other tokens', (test, onComplete) => { } ); -Tinytest.add( +Tinytest.addAsync( 'accounts - hook callbacks can access Meteor.userId()', - test => { - const userId = Accounts.insertUserDoc({}, { username: Random.id() }); + async test => { + const userId = await Accounts.insertUserDoc({}, { username: Random.id() }); const stampedToken = Accounts._generateStampedLoginToken(); - Accounts._insertLoginToken(userId, stampedToken); + await Accounts._insertLoginToken(userId, stampedToken); const validateStopper = Accounts.validateLoginAttempt(attempt => { test.equal(Meteor.userId(), validateAttemptExpectedUserId, "validateLoginAttempt"); @@ -468,20 +379,22 @@ Tinytest.add( // On a new connection, Meteor.userId() should be null until logged in. let validateAttemptExpectedUserId = null; const onLoginExpectedUserId = userId; - conn.call('login', { resume: stampedToken.token }); + await conn.callAsync('login', { resume: stampedToken.token }); // Now that the user is logged in on the connection, Meteor.userId() should // return that user. validateAttemptExpectedUserId = userId; - conn.call('login', { resume: stampedToken.token }); + await conn.callAsync('login', { resume: stampedToken.token }); // Trigger onLoginFailure callbacks const onLoginFailureExpectedUserId = userId; - test.throws(() => conn.call('login', { resume: "bogus" }), '403'); + await test.throwsAsync( + async () => + await conn.callAsync('login', { resume: "bogus" }), '403'); // Trigger onLogout callbacks const onLogoutExpectedUserId = userId; - conn.call('logout'); + await conn.callAsync('logout'); conn.disconnect(); validateStopper.stop(); @@ -491,17 +404,18 @@ Tinytest.add( } ); -Tinytest.add( +Tinytest.addAsync( 'accounts - hook callbacks obey options.defaultFieldSelector', - test => { + async test => { const ignoreFieldName = "bigArray"; - const userId = Accounts.insertUserDoc({}, { username: Random.id(), [ignoreFieldName]: [1] }); + const userId = + await Accounts.insertUserDoc({}, { username: Random.id(), [ignoreFieldName]: [1] }); const stampedToken = Accounts._generateStampedLoginToken(); - Accounts._insertLoginToken(userId, stampedToken); + await Accounts._insertLoginToken(userId, stampedToken); const options = Accounts._options; Accounts._options = {}; - Accounts.config({defaultFieldSelector: {[ignoreFieldName]: 0}}); - test.equal(Accounts._options.defaultFieldSelector, {[ignoreFieldName]: 0}, 'defaultFieldSelector'); + Accounts.config({ defaultFieldSelector: { [ignoreFieldName]: 0 } }); + test.equal(Accounts._options.defaultFieldSelector, { [ignoreFieldName]: 0 }, 'defaultFieldSelector'); const validateStopper = Accounts.validateLoginAttempt(attempt => { test.isUndefined(allowLogin != 'bogus' ? attempt.user[ignoreFieldName] : attempt.user, "validateLoginAttempt") @@ -521,23 +435,27 @@ Tinytest.add( // test a new connection let allowLogin = true; - conn.call('login', { resume: stampedToken.token }); + await conn.callAsync('login', { resume: stampedToken.token }); // Now that the user is logged in on the connection, Meteor.userId() should // return that user. - conn.call('login', { resume: stampedToken.token }); + await conn.callAsync('login', { resume: stampedToken.token }); // Trigger onLoginFailure callbacks, this will not include the user object allowLogin = 'bogus'; - test.throws(() => conn.call('login', { resume: "bogus" }), '403'); + await test.throwsAsync( + async () => + await conn.callAsync('login', { resume: "bogus" }), '403'); // test a forced login fail which WILL include the user object allowLogin = false; - test.throws(() => conn.call('login', { resume: stampedToken.token }), '403'); + await test.throwsAsync( + async () => + await conn.callAsync('login', { resume: stampedToken.token }), '403'); // Trigger onLogout callbacks const onLogoutExpectedUserId = userId; - conn.call('logout'); + await conn.callAsync('logout'); Accounts._options = options; conn.disconnect(); @@ -548,53 +466,55 @@ Tinytest.add( } ); -Tinytest.add( +Tinytest.addAsync( 'accounts - Meteor.user() obeys options.defaultFieldSelector', - test => { + async test => { const ignoreFieldName = "bigArray"; const customField = "customField"; - const userId = Accounts.insertUserDoc({}, { username: Random.id(), [ignoreFieldName]: [1], [customField]: 'test' }); + const userId = + await Accounts.insertUserDoc({}, { username: Random.id(), [ignoreFieldName]: [1], [customField]: 'test' }); const stampedToken = Accounts._generateStampedLoginToken(); - Accounts._insertLoginToken(userId, stampedToken); + await Accounts._insertLoginToken(userId, stampedToken); const options = Accounts._options; // stub Meteor.userId() so it works outside methods and returns the correct user: const origAccountsUserId = Accounts.userId; - Accounts.userId = () => userId; + Accounts.userId = + () => userId; Accounts._options = {}; // test the field is included by default - let user = Meteor.user(); + let user = await Meteor.user(); test.isNotUndefined(user[ignoreFieldName], 'included by default'); // test the field is excluded - Accounts.config({defaultFieldSelector: {[ignoreFieldName]: 0}}); - user = Meteor.user(); + Accounts.config({ defaultFieldSelector: { [ignoreFieldName]: 0 } }); + user = await Meteor.user(); test.isUndefined(user[ignoreFieldName], 'excluded'); - user = Meteor.user({}); + user = await Meteor.user({}); test.isUndefined(user[ignoreFieldName], 'excluded {}'); // test the field can still be retrieved if required - user = Meteor.user({fields: {[ignoreFieldName]: 1}}); + user = await Meteor.user({ fields: { [ignoreFieldName]: 1 } }); test.isNotUndefined(user[ignoreFieldName], 'field can be retrieved'); test.isUndefined(user.username, 'field can be retrieved username'); // test a combined negative field specifier - user = Meteor.user({fields: {username: 0}}); + user = await Meteor.user({ fields: { username: 0 } }); test.isUndefined(user[ignoreFieldName], 'combined field selector'); test.isUndefined(user.username, 'combined field selector username'); // test an explicit request for the full user object - user = Meteor.user({fields: {}}); + user = await Meteor.user({ fields: {} }); test.isNotUndefined(user[ignoreFieldName], 'full selector'); test.isNotUndefined(user.username, 'full selector username'); Accounts._options = {}; // Test that a custom field gets retrieved properly - Accounts.config({defaultFieldSelector: {[customField]: 1}}); - user = Meteor.user() + Accounts.config({ defaultFieldSelector: { [customField]: 1 } }); + user = await Meteor.user() test.isNotUndefined(user[customField]); test.isUndefined(user.username); test.isUndefined(user[ignoreFieldName]); @@ -604,21 +524,83 @@ Tinytest.add( } ); -Tinytest.add( + +Tinytest.addAsync( + 'accounts async - Meteor.userAsync() obeys options.defaultFieldSelector', + async test => { + const ignoreFieldName = "bigArray"; + const customField = "customField"; + const userId = + await Accounts.insertUserDoc({}, { username: Random.id(), [ignoreFieldName]: [1], [customField]: 'test' }); + const stampedToken = Accounts._generateStampedLoginToken(); + await Accounts._insertLoginToken(userId, stampedToken); + const options = Accounts._options; + + // stub Meteor.userId() so it works outside methods and returns the correct user: + const origAccountsUserId = Accounts.userId; + Accounts.userId = + () => userId; + + Accounts._options = {}; + + // test the field is included by default + let user = await Meteor.userAsync(); + test.isNotUndefined(user[ignoreFieldName], 'included by default'); + + // test the field is excluded + Accounts.config({ defaultFieldSelector: { [ignoreFieldName]: 0 } }); + user = await Meteor.userAsync(); + test.isUndefined(user[ignoreFieldName], 'excluded'); + user = await Meteor.userAsync({}); + test.isUndefined(user[ignoreFieldName], 'excluded {}'); + + // test the field can still be retrieved if required + user = await Meteor.userAsync({ fields: { [ignoreFieldName]: 1 } }); + test.isNotUndefined(user[ignoreFieldName], 'field can be retrieved'); + test.isUndefined(user.username, 'field can be retrieved username'); + + // test a combined negative field specifier + user = await Meteor.userAsync({ fields: { username: 0 } }); + test.isUndefined(user[ignoreFieldName], 'combined field selector'); + test.isUndefined(user.username, 'combined field selector username'); + + // test an explicit request for the full user object + user = await Meteor.userAsync({ fields: {} }); + test.isNotUndefined(user[ignoreFieldName], 'full selector'); + test.isNotUndefined(user.username, 'full selector username'); + + Accounts._options = {}; + + // Test that a custom field gets retrieved properly + Accounts.config({ defaultFieldSelector: { [customField]: 1 } }); + user = await Meteor.userAsync(); + test.isNotUndefined(user[customField]); + test.isUndefined(user.username); + test.isUndefined(user[ignoreFieldName]); + + Accounts._options = options; + Accounts.userId = origAccountsUserId; + } +); +Tinytest.addAsync( 'accounts - verify onExternalLogin hook can update oauth user profiles', - test => { + async test => { // Verify user profile data is saved properly when not using the // onExternalLogin hook. let facebookId = Random.id(); - const uid1 = Accounts.updateOrCreateUserFromExternalService( + const u1 = await Accounts.updateOrCreateUserFromExternalService( 'facebook', { id: facebookId }, { profile: { foo: 1 } }, - ).userId; + ); const ignoreFieldName = "bigArray"; - const c = Meteor.users.update(uid1, {$set: {[ignoreFieldName]: [1]}}); + + const c = + await Meteor.users.update(u1.userId, { $set: { [ignoreFieldName]: [1] } }); + let users = - Meteor.users.find({ 'services.facebook.id': facebookId }).fetch(); + await Meteor.users.find({ 'services.facebook.id': facebookId }).fetch(); + test.length(users, 1); test.equal(users[0].profile.foo, 1); test.isNotUndefined(users[0][ignoreFieldName], 'ignoreField - before limit fields'); @@ -628,19 +610,19 @@ Tinytest.add( // Also verify that the user object is filtered by _options.defaultFieldSelector const accountsOptions = Accounts._options; Accounts._options = {}; - Accounts.config({defaultFieldSelector: {[ignoreFieldName]: 0}}); + Accounts.config({ defaultFieldSelector: { [ignoreFieldName]: 0 } }); Accounts.onExternalLogin((options, user) => { options.profile.foo = 2; test.isUndefined(users[ignoreFieldName], 'ignoreField - after limit fields'); return options; }); - Accounts.updateOrCreateUserFromExternalService( + await Accounts.updateOrCreateUserFromExternalService( 'facebook', { id: facebookId }, { profile: { foo: 1 } }, ); // test.isUndefined(users[0][ignoreFieldName], 'ignoreField - fields limited'); - users = Meteor.users.find({ 'services.facebook.id': facebookId }).fetch(); + users = await Meteor.users.find({ 'services.facebook.id': facebookId }).fetch(); test.length(users, 1); test.equal(users[0].profile.foo, 2); test.isNotUndefined(users[0][ignoreFieldName], 'ignoreField - still there'); @@ -648,106 +630,116 @@ Tinytest.add( // Verify user profile data can be modified using the onExternalLogin // hook, for new users. facebookId = Random.id(); - const uid2 = Accounts.updateOrCreateUserFromExternalService( + const u2 = await Accounts.updateOrCreateUserFromExternalService( 'facebook', { id: facebookId }, { profile: { foo: 3 } }, - ).userId; - users = Meteor.users.find({ 'services.facebook.id': facebookId }).fetch(); + ); + users = await Meteor.users.find({ 'services.facebook.id': facebookId }).fetch(); test.length(users, 1); test.equal(users[0].profile.foo, 2); // Cleanup - Meteor.users.remove(uid1); - Meteor.users.remove(uid2); + await Meteor.users.remove(u1); + await Meteor.users.remove(u2.userId); Accounts._onExternalLoginHook = null; Accounts._options = accountsOptions; } ); -Tinytest.add( - 'accounts - verify beforeExternalLogin hook can stop user login', - test => { - // Verify user data is saved properly when not using the - // beforeExternalLogin hook. - let facebookId = Random.id(); - const uid1 = Accounts.updateOrCreateUserFromExternalService( - 'facebook', - { id: facebookId }, - { profile: { foo: 1 } }, - ).userId; - const ignoreFieldName = "bigArray"; - const c = Meteor.users.update(uid1, {$set: {[ignoreFieldName]: [1]}}); - let users = - Meteor.users.find({ 'services.facebook.id': facebookId }).fetch(); - test.length(users, 1); - test.equal(users[0].profile.foo, 1); - test.isNotUndefined(users[0][ignoreFieldName], 'ignoreField - before limit fields'); +Tinytest.addAsync( + 'accounts - verify beforeExternalLogin hook can stop user login', + async test => { + // Verify user data is saved properly when not using the + // beforeExternalLogin hook. + let facebookId = Random.id(); - // Verify that when beforeExternalLogin returns false - // that an error throws and user is not saved - Accounts.beforeExternalLogin((serviceName, serviceData, user) => { - // Check that we get the correct data - test.equal(serviceName, 'facebook'); - test.equal(serviceData, { id: facebookId }); - test.equal(user._id, uid1); - return false - }); + const u = + await Accounts.updateOrCreateUserFromExternalService( + 'facebook', + { id: facebookId }, + { profile: { foo: 1 } }, + ); - test.throws(() => Accounts.updateOrCreateUserFromExternalService( - 'facebook', - { id: facebookId }, - { profile: { foo: 1 } }, + const ignoreFieldName = "bigArray"; + + const c = + await Meteor.users.update(u.userId, { $set: { [ignoreFieldName]: [1] } }); + + let users = + await Meteor.users.find({ 'services.facebook.id': facebookId }).fetch(); + + test.length(users, 1); + test.equal(users[0].profile.foo, 1); + test.isNotUndefined(users[0][ignoreFieldName], 'ignoreField - before limit fields'); + + // Verify that when beforeExternalLogin returns false + // that an error throws and user is not saved + Accounts.beforeExternalLogin((serviceName, serviceData, user) => { + // Check that we get the correct data + test.equal(serviceName, 'facebook'); + test.equal(serviceData, { id: facebookId }); + test.equal(user._id, u.userId); + return false + }); + + await test.throwsAsync( + async () => + await Accounts.updateOrCreateUserFromExternalService( + 'facebook', + { id: facebookId }, + { profile: { foo: 1 } }, )); - // Cleanup - Meteor.users.remove(uid1); - Accounts._beforeExternalLoginHook = null; - } -); - -Tinytest.add( - 'accounts - verify setAdditionalFindUserOnExternalLogin hook can provide user', - test => { - // create test user, without a google service - const testEmail = "test@testdomain.com" - const uid0 = Accounts.createUser({email: testEmail}) - - // Verify that user is found from email and service merged - Accounts.setAdditionalFindUserOnExternalLogin(({serviceName, serviceData}) => { - if (serviceName === "google") { - return Accounts.findUserByEmail(serviceData.email) - } - }) - - let googleId = Random.id(); - const uid1 = Accounts.updateOrCreateUserFromExternalService( - 'google', - { id: googleId, email: testEmail }, - { profile: { foo: 1 } }, - ).userId; - - test.equal(uid0, uid1) - - // Cleanup - if (uid1 !== uid0) { - Meteor.users.remove(uid0) - } - Meteor.users.remove(uid1); - Accounts.selectCustomUserOnExternalLogin = null; + // Cleanup + await Meteor.users.remove(u.userId); + Accounts._beforeExternalLoginHook = null; } ); -if(Meteor.isServer) { - Tinytest.add( +Tinytest.addAsync( + 'accounts - verify setAdditionalFindUserOnExternalLogin hook can provide user', + async test => { + // create test user, without a google service + const testEmail = "test@testdomain.com" + // being sure that the user is not already in the database + await Meteor.users.remove({ "emails.address": testEmail }); + const uid0 = await Accounts.createUser({ email: testEmail }) + + // Verify that user is found from email and service merged + Accounts.setAdditionalFindUserOnExternalLogin(async ({ serviceName, serviceData }) => { + if (serviceName === "google") { + return await Accounts.findUserByEmail(serviceData.email) + } + }) + + let googleId = Random.id(); + const u1 = await Accounts.updateOrCreateUserFromExternalService( + 'google', + { id: googleId, email: testEmail }, + { profile: { foo: 1 } }, + ); + test.equal(uid0, u1.userId) + + // Cleanup + if (u1.userId !== uid0) { + await Meteor.users.remove(uid0) + } + await Meteor.users.remove(u1.userId); + Accounts.selectCustomUserOnExternalLogin = null; + } +); + +if (Meteor.isServer) { + Tinytest.addAsync( 'accounts - make sure that extra params to accounts urls are added', - test => { + async test => { // No extra params const verifyEmailURL = new URL(Accounts.urls.verifyEmail('test')); test.equal(verifyEmailURL.searchParams.toString(), ""); // Extra params - const extraParams = { test: 'success'}; + const extraParams = { test: 'success' }; const resetPasswordURL = new URL(Accounts.urls.resetPassword('test', extraParams)); test.equal(resetPasswordURL.searchParams.get('test'), extraParams.test); const enrollAccountURL = new URL(Accounts.urls.enrollAccount('test', extraParams)); @@ -755,3 +747,127 @@ if(Meteor.isServer) { } ); } + +Tinytest.addAsync('accounts - updateOrCreateUserFromExternalService - Facebook', async test => { + const facebookId = Random.id(); + + // create an account with facebook + const u1 = + await Accounts.updateOrCreateUserFromExternalService( + 'facebook', { id: facebookId, monkey: 42 }, { profile: { foo: 1 } }); + const users1 = + await Meteor.users.find({ "services.facebook.id": facebookId }).fetch(); + test.length(users1, 1); + test.equal(users1[0].profile.foo, 1); + test.equal(users1[0].services.facebook.monkey, 42); + + // create again with the same id, see that we get the same user. + // it should update services.facebook but not profile. + const u2 = + await Accounts.updateOrCreateUserFromExternalService( + 'facebook', { id: facebookId, llama: 50 }, + { profile: { foo: 1000, bar: 2 } }); + test.equal(u1.id, u2.id); + const users2 = + await Meteor.users.find({ "services.facebook.id": facebookId }).fetch(); + test.length(users2, 1); + test.equal(users2[0].profile.foo, 1); + test.equal(users2[0].profile.bar, undefined); + test.equal(users2[0].services.facebook.llama, 50); + // make sure we *don't* lose values not passed this call to + // updateOrCreateUserFromExternalService + test.equal(users2[0].services.facebook.monkey, 42); + + // cleanup + await Meteor.users.remove(u1.id); +}); + +Tinytest.addAsync('accounts - updateOrCreateUserFromExternalService - Meteor Developer', async test => { + const developerId = + Random.id(); + const u1 = + await Accounts.updateOrCreateUserFromExternalService( + 'meteor-developer', + { id: developerId, username: 'meteor-developer' }, + { profile: { name: 'meteor-developer' } } + ); + const users1 = + await Meteor.users.find({ 'services.meteor-developer.id': developerId }).fetch(); + test.length(users1, 1); + test.equal(users1[0].profile.name, 'meteor-developer'); + + const u2 = + await Accounts.updateOrCreateUserFromExternalService( + 'meteor-developer', + { id: developerId, username: 'meteor-developer' }, + { profile: { name: 'meteor-developer', username: 'developer' } } + ); + test.equal(u1.id, u2.id); + const users2 = + await Meteor.users.find({ 'services.meteor-developer.id': developerId }).fetch(); + test.length(users2, 1); + test.equal(users1[0].profile.name, 'meteor-developer'); + test.equal(users1[0].profile.username, undefined); + + // cleanup + await Meteor.users.remove(u1); +}); + +Tinytest.addAsync('accounts - updateOrCreateUserFromExternalService - Weibo', async test => { + const weiboId1 = + Random.id(); + const weiboId2 = + Random.id(); + + // users that have different service ids get different users + const u1 = + await Accounts.updateOrCreateUserFromExternalService( + 'weibo', { id: weiboId1 }, { profile: { foo: 1 } }); + const u2 = + await Accounts.updateOrCreateUserFromExternalService( + 'weibo', { id: weiboId2 }, { profile: { bar: 2 } }); + test.equal(await Meteor.users.find({ "services.weibo.id": { $in: [weiboId1, weiboId2] } }).count(), 2); + + const user1 = + await Meteor.users.findOne({ "services.weibo.id": weiboId1 }); + const user2 = + await Meteor.users.findOne({ "services.weibo.id": weiboId2 }); + test.equal(user1.profile.foo, 1); + test.equal(user1.emails, undefined); + test.equal(user2.profile.bar, 2); + test.equal(user2.emails, undefined); + + // cleanup + Meteor.users.remove(u1.id); + Meteor.users.remove(u2.id); +}); + +Tinytest.addAsync('accounts - updateOrCreateUserFromExternalService - Twitter', async test => { + const twitterIdOld = parseInt(Random.hexString(4), 16); + const twitterIdNew = '' + twitterIdOld; + + // create an account with twitter using the old ID format of integer + const u1 = + await Accounts.updateOrCreateUserFromExternalService( + 'twitter', { id: twitterIdOld, monkey: 42 }, { profile: { foo: 1 } }); + const users1 = + await Meteor.users.find({ "services.twitter.id": twitterIdOld }).fetch(); + test.length(users1, 1); + test.equal(users1[0].profile.foo, 1); + test.equal(users1[0].services.twitter.monkey, 42); + + // Update the account with the new ID format of string + // test that the existing user is found, and that the ID + // gets updated to a string value + const u2 = + await Accounts.updateOrCreateUserFromExternalService( + 'twitter', { id: twitterIdNew, monkey: 42 }, { profile: { foo: 1 } }); + test.equal(u1.id, u2.id); + const users2 = + await Meteor.users.find({ "services.twitter.id": twitterIdNew }).fetch(); + test.length(users2, 1); + + // cleanup + await Meteor.users.remove(u1.id); +}); + diff --git a/packages/accounts-base/accounts_tests_setup.js b/packages/accounts-base/accounts_tests_setup.js index bd79562fe0..c83ce5677b 100644 --- a/packages/accounts-base/accounts_tests_setup.js +++ b/packages/accounts-base/accounts_tests_setup.js @@ -1,25 +1,25 @@ -const getTokenFromSecret = ({ selector, secret: secretParam }) => { +const getTokenFromSecret = async ({ selector, secret: secretParam }) => { let secret = secretParam; if (!secret) { const { services: { twoFactorAuthentication } = {} } = - Meteor.users.findOne(selector) || {}; + await Meteor.users.findOne(selector) || {}; if (!twoFactorAuthentication) { throw new Meteor.Error(500, 'twoFactorAuthentication not set.'); } secret = twoFactorAuthentication.secret; } - const { token } = Accounts._generate2faToken(secret); + const { token } = await Accounts._generate2faToken(secret); return token; }; Meteor.methods({ - removeAccountsTestUser(username) { - Meteor.users.remove({ username }); + async removeAccountsTestUser(username) { + await Meteor.users.remove({ username }); }, - forceEnableUser2fa(selector, secret) { - Meteor.users.update( + async forceEnableUser2fa(selector, secret) { + await Meteor.users.update( selector, { $set: { @@ -30,7 +30,7 @@ Meteor.methods({ }, } ); - return getTokenFromSecret({ selector, secret }); + return await getTokenFromSecret({ selector, secret }); }, getTokenFromSecret, }); diff --git a/packages/accounts-base/package-types.json b/packages/accounts-base/package-types.json new file mode 100644 index 0000000000..033f6cab29 --- /dev/null +++ b/packages/accounts-base/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "accounts-base.d.ts" +} diff --git a/packages/accounts-base/package.js b/packages/accounts-base/package.js index 32a6df946c..61a19fd4ba 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: '2.2.4', + version: '2.2.6', }); Package.onUse(api => { @@ -15,10 +15,6 @@ Package.onUse(api => { api.use('reactive-var', 'client'); api.use('url', ['client', 'server']); - // use unordered to work around a circular dependency - // (service-configuration needs Accounts.connection) - api.use('service-configuration', ['client', 'server'], { unordered: true }); - // needed for getting the currently logged-in user and handling reconnects api.use('ddp', ['client', 'server']); @@ -48,6 +44,8 @@ Package.onUse(api => { // modules that import the accounts-base package. api.mainModule('server_main.js', 'server'); api.mainModule('client_main.js', 'client'); + + api.addAssets('accounts-base.d.ts', 'server'); }); Package.onTest(api => { diff --git a/packages/accounts-base/server_main.js b/packages/accounts-base/server_main.js index db5020fed5..05ec621117 100644 --- a/packages/accounts-base/server_main.js +++ b/packages/accounts-base/server_main.js @@ -5,7 +5,8 @@ import { AccountsServer } from "./accounts_server.js"; * @summary The namespace for all server-side accounts-related methods. */ Accounts = new AccountsServer(Meteor.server); - +// TODO[FIBERS]: I need TLA +Accounts.init().then() // Users table. Don't use the normal autopublish, since we want to hide // some fields. Code to autopublish this is in accounts_server.js. // XXX Allow users to configure this collection name. @@ -15,7 +16,7 @@ Accounts = new AccountsServer(Meteor.server); * @locus Anywhere * @type {Mongo.Collection} * @importFromPackage meteor -*/ + */ Meteor.users = Accounts.users; export { diff --git a/packages/accounts-oauth/oauth_common.js b/packages/accounts-oauth/oauth_common.js index e0e1a1ad48..bfb99b0a5d 100644 --- a/packages/accounts-oauth/oauth_common.js +++ b/packages/accounts-oauth/oauth_common.js @@ -1,3 +1,24 @@ +import { Meteor } from 'meteor/meteor'; + +// TODO get from account-base +// config option keys +const VALID_CONFIG_KEYS = [ + 'sendVerificationEmail', + 'forbidClientAccountCreation', + 'passwordEnrollTokenExpiration', + 'passwordEnrollTokenExpirationInDays', + 'restrictCreationByEmailDomain', + 'loginExpirationInDays', + 'loginExpiration', + 'passwordResetTokenExpirationInDays', + 'passwordResetTokenExpiration', + 'ambiguousErrorMessages', + 'bcryptRounds', + 'defaultFieldSelector', + 'loginTokenExpirationHours', + 'tokenSequenceLength', +]; + Accounts.oauth = {}; const services = {}; @@ -31,3 +52,37 @@ Accounts.oauth.unregisterService = name => { }; Accounts.oauth.serviceNames = () => Object.keys(services); + +// loginServiceConfiguration and ConfigError are maintained for backwards compatibility +Meteor.startup(() => { + const { ServiceConfiguration } = Package['service-configuration']; + Accounts.loginServiceConfiguration = ServiceConfiguration.configurations; + Accounts.ConfigError = ServiceConfiguration.ConfigError; + + const settings = Meteor.settings?.packages?.['accounts-base']; + if (settings) { + if (settings.oauthSecretKey) { + if (!Package['oauth-encryption']) { + throw new Error( + 'The oauth-encryption package must be loaded to set oauthSecretKey' + ); + } + Package['oauth-encryption'].OAuthEncryption.loadKey( + settings.oauthSecretKey + ); + delete settings.oauthSecretKey; + } + // Validate config options keys + Object.keys(settings).forEach(key => { + if (!VALID_CONFIG_KEYS.includes(key)) { + // TODO Consider just logging a debug message instead to allow for additional keys in the settings here? + throw new Meteor.Error( + `Accounts configuration: Invalid key: ${key}` + ); + } else { + // set values in Accounts._options + Accounts._options[key] = settings[key]; + } + }); + } +}); diff --git a/packages/accounts-oauth/oauth_server.js b/packages/accounts-oauth/oauth_server.js index c76b2e439b..f8d67eff25 100644 --- a/packages/accounts-oauth/oauth_server.js +++ b/packages/accounts-oauth/oauth_server.js @@ -1,3 +1,5 @@ +import { Meteor } from 'meteor/meteor'; + // Listen to calls to `login` with an oauth option set. This is where // users actually get logged in to meteor via oauth. Accounts.registerLoginHandler(options => { @@ -55,3 +57,44 @@ Accounts.registerLoginHandler(options => { return Accounts.updateOrCreateUserFromExternalService(result.serviceName, result.serviceData, result.options); } }); + +/// +/// OAuth Encryption Support +/// + +const OAuthEncryption = Package["oauth-encryption"]?.OAuthEncryption; + +const usingOAuthEncryption = () => { + return OAuthEncryption?.keyIsLoaded(); +}; + +// Encrypt unencrypted login service secrets when oauth-encryption is +// added. +// +// XXX For the oauthSecretKey to be available here at startup, the +// developer must call Accounts.config({oauthSecretKey: ...}) at load +// time, instead of in a Meteor.startup block, because the startup +// block in the app code will run after this accounts-base startup +// block. Perhaps we need a post-startup callback? + +Meteor.startup(() => { + if (! usingOAuthEncryption()) { + return; + } + + const { ServiceConfiguration } = Package['service-configuration']; + + ServiceConfiguration.configurations.find({ + $and: [{ + secret: { $exists: true } + }, { + "secret.algorithm": { $exists: false } + }] + }).forEach(config => { + ServiceConfiguration.configurations.update(config._id, { + $set: { + secret: OAuthEncryption.seal(config.secret) + } + }); + }); +}); diff --git a/packages/accounts-oauth/package.js b/packages/accounts-oauth/package.js index f20513769d..d26a1ff571 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.1", + version: "1.4.2", }); Package.onUse(api => { @@ -9,6 +9,11 @@ Package.onUse(api => { api.use(['accounts-base', 'ecmascript'], ['client', 'server']); // Export Accounts (etc) to packages using this one. api.imply('accounts-base', ['client', 'server']); + + // use unordered to work around a circular dependency + // (service-configuration needs Accounts.connection) + api.use('service-configuration', ['client', 'server'], { unordered: true }); + api.use('oauth'); api.addFiles('oauth_common.js'); diff --git a/packages/accounts-password/package.js b/packages/accounts-password/package.js index c4f9cadbd3..719191d8dc 100644 --- a/packages/accounts-password/package.js +++ b/packages/accounts-password/package.js @@ -5,7 +5,7 @@ Package.describe({ // 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: '2.3.1', + version: '2.3.2', }); Npm.depends({ diff --git a/packages/accounts-password/password_client.js b/packages/accounts-password/password_client.js index 5d1279782b..30d3b49450 100644 --- a/packages/accounts-password/password_client.js +++ b/packages/accounts-password/password_client.js @@ -7,12 +7,10 @@ const reportError = (error, callback) => { } }; - const internalLoginWithPassword = ({ selector, password, code, callback }) => { if (typeof selector === 'string') if (!selector.includes('@')) selector = { username: selector }; else selector = { email: selector }; - Accounts.callLoginMethod({ methodArguments: [ { diff --git a/packages/accounts-password/password_server.js b/packages/accounts-password/password_server.js index 2e6d57c075..5127342209 100644 --- a/packages/accounts-password/password_server.js +++ b/packages/accounts-password/password_server.js @@ -98,7 +98,7 @@ const checkPasswordAsync = async (user, password) => { return result; }; -const checkPassword = async (user, password) => { +const checkPassword = (user, password) => { return Promise.await(checkPasswordAsync(user, password)); }; diff --git a/packages/babel-compiler/package.js b/packages/babel-compiler/package.js index 32515319fb..a3ecdbed82 100644 --- a/packages/babel-compiler/package.js +++ b/packages/babel-compiler/package.js @@ -1,11 +1,11 @@ Package.describe({ name: "babel-compiler", summary: "Parser/transpiler for ECMAScript 2015+ syntax", - version: '7.9.0' + version: '7.10.1' }); Npm.depends({ - '@meteorjs/babel': '7.16.0-beta.7', + '@meteorjs/babel': '7.17.2-beta.0', 'json5': '2.1.1' }); diff --git a/packages/browser-policy-common/browser-policy-common.d.ts b/packages/browser-policy-common/browser-policy-common.d.ts new file mode 100644 index 0000000000..0cfa8678a3 --- /dev/null +++ b/packages/browser-policy-common/browser-policy-common.d.ts @@ -0,0 +1,38 @@ +export namespace BrowserPolicy { + var framing: { + disallow(): void; + restrictToOrigin(origin: string): void; + allowAll(): void; + }; + + var content: { + allowEval(): void; + allowInlineStyles(): void; + allowInlineScripts(): void; + allowSameOriginForAll(): void; + allowDataUrlForAll(): void; + allowOriginForAll(origin: string): void; + allowImageOrigin(origin: string): void; + allowMediaOrigin(origin: string): void; + allowFontOrigin(origin: string): void; + allowStyleOrigin(origin: string): void; + allowScriptOrigin(origin: string): void; + allowFrameOrigin(origin: string): void; + allowFrameAncestorsOrigin(origin: string): void; + allowContentTypeSniffing(): void; + allowAllContentOrigin(): void; + allowAllContentDataUrl(): void; + allowAllContentSameOrigin(): void; + allowConnectOrigin(origin: string): void; + allowObjectOrigin(origin: string): void; + + disallowAll(): void; + disallowInlineStyles(): void; + disallowEval(): void; + disallowInlineScripts(): void; + disallowFont(): void; + disallowObject(): void; + disallowAllContent(): void; + disallowConnect(): void; + }; +} diff --git a/packages/browser-policy-common/package-types.json b/packages/browser-policy-common/package-types.json new file mode 100644 index 0000000000..1b2482b244 --- /dev/null +++ b/packages/browser-policy-common/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "browser-policy-common.d.ts" +} diff --git a/packages/browser-policy-common/package.js b/packages/browser-policy-common/package.js index 85c2554c3e..d3a1888984 100644 --- a/packages/browser-policy-common/package.js +++ b/packages/browser-policy-common/package.js @@ -1,10 +1,11 @@ Package.describe({ summary: "Common code for browser-policy packages", - version: "1.0.11" + version: "1.0.12" }); Package.onUse(function (api) { api.use('webapp', 'server'); api.addFiles('browser-policy-common.js', 'server'); api.export('BrowserPolicy', 'server'); + api.addAssets('browser-policy-common.d.ts', 'server'); }); diff --git a/packages/browser-policy-framing/package.js b/packages/browser-policy-framing/package.js index 7dc9041568..1135346dd5 100644 --- a/packages/browser-policy-framing/package.js +++ b/packages/browser-policy-framing/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Restrict which websites can frame your app", - version: "1.1.0" + version: '1.1.1' }); Package.onUse(function (api) { diff --git a/packages/browser-policy/package.js b/packages/browser-policy/package.js index a44f8ba6b4..bbab788e76 100644 --- a/packages/browser-policy/package.js +++ b/packages/browser-policy/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Configure security policies enforced by the browser", - version: "1.1.0" + version: '1.1.1' }); Package.onUse(function (api) { diff --git a/packages/check/check.d.ts b/packages/check/check.d.ts new file mode 100644 index 0000000000..0322aab9e9 --- /dev/null +++ b/packages/check/check.d.ts @@ -0,0 +1,92 @@ +/** + * The namespace for all Match types and methods. + */ +export namespace Match { + interface Matcher { + _meteorCheckMatcherBrand: void; + } + // prettier-ignore + export type Pattern = + typeof String | + typeof Number | + typeof Boolean | + typeof Object | + typeof Function | + (new (...args: any[]) => any) | + undefined | null | string | number | boolean | + [Pattern] | + {[key: string]: Pattern} | + Matcher; + // prettier-ignore + export type PatternMatch = + T extends Matcher ? U : + T extends typeof String ? string : + T extends typeof Number ? number : + T extends typeof Boolean ? boolean : + T extends typeof Object ? object : + T extends typeof Function ? Function : + T extends undefined | null | string | number | boolean ? T : + T extends new (...args: any[]) => infer U ? U : + T extends [Pattern] ? PatternMatch[] : + T extends {[key: string]: Pattern} ? {[K in keyof T]: PatternMatch} : + unknown; + + /** Matches any value. */ + var Any: Matcher; + /** Matches a signed 32-bit integer. Doesn’t match `Infinity`, `-Infinity`, or `NaN`. */ + var Integer: Matcher; + + /** + * Matches either `undefined`, `null`, or pattern. If used in an object, matches only if the key is not set as opposed to the value being set to `undefined` or `null`. This set of conditions + * was chosen because `undefined` arguments to Meteor Methods are converted to `null` when sent over the wire. + */ + function Maybe( + pattern: T + ): Matcher | undefined | null>; + + /** Behaves like `Match.Maybe` except it doesn’t accept `null`. If used in an object, the behavior is identical to `Match.Maybe`. */ + function Optional( + pattern: T + ): Matcher | undefined>; + + /** Matches an Object with the given keys; the value may also have other keys with arbitrary values. */ + function ObjectIncluding( + dico: T + ): Matcher>; + + /** Matches any value that matches at least one of the provided patterns. */ + function OneOf( + ...patterns: T + ): Matcher>; + + /** + * Calls the function condition with the value as the argument. If condition returns true, this matches. If condition throws a `Match.Error` or returns false, this fails. If condition throws + * any other error, that error is thrown from the call to `check` or `Match.test`. + */ + function Where(condition: (val: any) => val is T): Matcher; + function Where(condition: (val: any) => boolean): Matcher; + + /** + * Returns true if the value matches the pattern. + * @param value The value to check + * @param pattern The pattern to match `value` against + */ + function test( + value: any, + pattern: T + ): value is PatternMatch; +} + +/** + * Check that a value matches a pattern. + * If the value does not match the pattern, throw a `Match.Error`. + * + * Particularly useful to assert that arguments to a function have the right + * types and structure. + * @param value The value to check + * @param pattern The pattern to match `value` against + */ +export declare function check( + value: any, + pattern: T +): asserts value is Match.PatternMatch; diff --git a/packages/check/package-types.json b/packages/check/package-types.json new file mode 100644 index 0000000000..19e3d36ba9 --- /dev/null +++ b/packages/check/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "check.d.ts" +} diff --git a/packages/check/package.js b/packages/check/package.js index ee260f6f24..7505e58b51 100644 --- a/packages/check/package.js +++ b/packages/check/package.js @@ -1,12 +1,14 @@ Package.describe({ summary: 'Check whether a value matches a pattern', - version: '1.3.1', + version: '1.3.2', }); Package.onUse(api => { api.use('ecmascript'); api.use('ejson'); + api.addAssets('check.d.ts', 'server'); + api.mainModule('match.js'); api.export('check'); diff --git a/packages/ddp-client-async/.npm/package/.gitignore b/packages/ddp-client-async/.npm/package/.gitignore deleted file mode 100644 index 3c3629e647..0000000000 --- a/packages/ddp-client-async/.npm/package/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/packages/ddp-client-async/.npm/package/README b/packages/ddp-client-async/.npm/package/README deleted file mode 100644 index 3d492553a4..0000000000 --- a/packages/ddp-client-async/.npm/package/README +++ /dev/null @@ -1,7 +0,0 @@ -This directory and the files immediately inside it are automatically generated -when you change this package's NPM dependencies. Commit the files in this -directory (npm-shrinkwrap.json, .gitignore, and this README) to source control -so that others run the same versions of sub-dependencies. - -You should NOT check in the node_modules directory that Meteor automatically -creates; if you are using git, the .gitignore file tells git to ignore it. diff --git a/packages/ddp-client-async/.npm/package/npm-shrinkwrap.json b/packages/ddp-client-async/.npm/package/npm-shrinkwrap.json deleted file mode 100644 index 28feb731a6..0000000000 --- a/packages/ddp-client-async/.npm/package/npm-shrinkwrap.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "lockfileVersion": 1, - "dependencies": { - "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==" - }, - "@sinonjs/fake-timers": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.0.5.tgz", - "integrity": "sha512-fUt6b15bjV/VW93UP5opNXJxdwZSbK1EdiwnhN7XrQrcpaOhMJpZ/CjwFpM3THpxwA+YviBUJKSuEqKlCK5alw==" - }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" - } - } -} diff --git a/packages/ddp-client-async/README.md b/packages/ddp-client-legacy/README.md similarity index 100% rename from packages/ddp-client-async/README.md rename to packages/ddp-client-legacy/README.md diff --git a/packages/ddp-client-async/client/client.js b/packages/ddp-client-legacy/client/client.js similarity index 100% rename from packages/ddp-client-async/client/client.js rename to packages/ddp-client-legacy/client/client.js diff --git a/packages/ddp-client-async/client/client_convenience.js b/packages/ddp-client-legacy/client/client_convenience.js similarity index 100% rename from packages/ddp-client-async/client/client_convenience.js rename to packages/ddp-client-legacy/client/client_convenience.js diff --git a/packages/ddp-client-async/common/MethodInvoker.js b/packages/ddp-client-legacy/common/MethodInvoker.js similarity index 100% rename from packages/ddp-client-async/common/MethodInvoker.js rename to packages/ddp-client-legacy/common/MethodInvoker.js diff --git a/packages/ddp-client-async/common/livedata_connection.js b/packages/ddp-client-legacy/common/livedata_connection.js similarity index 99% rename from packages/ddp-client-async/common/livedata_connection.js rename to packages/ddp-client-legacy/common/livedata_connection.js index 1ea402ec98..bd5645baba 100644 --- a/packages/ddp-client-async/common/livedata_connection.js +++ b/packages/ddp-client-legacy/common/livedata_connection.js @@ -696,7 +696,14 @@ export class Connection { invocation ); try { - stubOptions.stubReturnValue = await stubInvocation(); + const resultOrThenable = stubInvocation(); + const isThenable = + resultOrThenable && typeof resultOrThenable.then === 'function'; + if (isThenable) { + stubOptions.stubReturnValue = await resultOrThenable; + } else { + stubOptions.stubReturnValue = resultOrThenable; + } } finally { DDP._CurrentMethodInvocation._set(currentContext); } diff --git a/packages/ddp-client-async/common/namespace.js b/packages/ddp-client-legacy/common/namespace.js similarity index 100% rename from packages/ddp-client-async/common/namespace.js rename to packages/ddp-client-legacy/common/namespace.js diff --git a/packages/ddp-client-async/package.js b/packages/ddp-client-legacy/package.js similarity index 98% rename from packages/ddp-client-async/package.js rename to packages/ddp-client-legacy/package.js index 0cdc77a953..5a0b31737f 100644 --- a/packages/ddp-client-async/package.js +++ b/packages/ddp-client-legacy/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Meteor's latency-compensated distributed data client", - version: '2.6.0', + version: '2.6.1', documentation: null }); diff --git a/packages/ddp-client-async/server/server.js b/packages/ddp-client-legacy/server/server.js similarity index 100% rename from packages/ddp-client-async/server/server.js rename to packages/ddp-client-legacy/server/server.js diff --git a/packages/ddp-client-async/test/livedata_connection_tests.js b/packages/ddp-client-legacy/test/livedata_connection_tests.js similarity index 100% rename from packages/ddp-client-async/test/livedata_connection_tests.js rename to packages/ddp-client-legacy/test/livedata_connection_tests.js diff --git a/packages/ddp-client-async/test/livedata_test_service.js b/packages/ddp-client-legacy/test/livedata_test_service.js similarity index 100% rename from packages/ddp-client-async/test/livedata_test_service.js rename to packages/ddp-client-legacy/test/livedata_test_service.js diff --git a/packages/ddp-client-async/test/livedata_tests.js b/packages/ddp-client-legacy/test/livedata_tests.js similarity index 100% rename from packages/ddp-client-async/test/livedata_tests.js rename to packages/ddp-client-legacy/test/livedata_tests.js diff --git a/packages/ddp-client-async/test/random_stream_tests.js b/packages/ddp-client-legacy/test/random_stream_tests.js similarity index 100% rename from packages/ddp-client-async/test/random_stream_tests.js rename to packages/ddp-client-legacy/test/random_stream_tests.js diff --git a/packages/ddp-client-async/test/stub_stream.js b/packages/ddp-client-legacy/test/stub_stream.js similarity index 100% rename from packages/ddp-client-async/test/stub_stream.js rename to packages/ddp-client-legacy/test/stub_stream.js diff --git a/packages/ddp-client/test/livedata_connection_tests.js b/packages/ddp-client/test/livedata_connection_tests.js index 32e014ccbf..58bc55bd59 100644 --- a/packages/ddp-client/test/livedata_connection_tests.js +++ b/packages/ddp-client/test/livedata_connection_tests.js @@ -63,27 +63,27 @@ const testGotMessage = function(test, stream, expected) { return got; }; -const startAndConnect = function(test, stream) { - stream.reset(); // initial connection start. +const startAndConnect = async function(test, stream) { + await stream.reset(); // initial connection start. testGotMessage(test, stream, makeConnectMessage()); test.length(stream.sent, 0); - stream.receive({ msg: 'connected', session: SESSION_ID }); + await stream.receive({ msg: 'connected', session: SESSION_ID }); test.length(stream.sent, 0); }; const SESSION_ID = '17'; -Tinytest.add('livedata stub - receive data', function(test) { +Tinytest.addAsync('livedata stub - receive data', async function(test) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); // data comes in for unknown collection. const coll_name = Random.id(); - stream.receive({ + await stream.receive({ msg: 'added', collection: coll_name, id: '1234', @@ -101,7 +101,7 @@ Tinytest.add('livedata stub - receive data', function(test) { test.equal(coll.find({}).fetch(), [{ _id: '1234', a: 1 }]); // second message. applied directly to the db. - stream.receive({ + await stream.receive({ msg: 'changed', collection: coll_name, id: '1234', @@ -111,7 +111,7 @@ Tinytest.add('livedata stub - receive data', function(test) { test.isUndefined(conn._updatesForUnknownStores[coll_name]); }); -Tinytest.add('livedata stub - buffering data', function(test) { +Tinytest.addAsync('livedata stub - buffering data', async function(test) { // Install special setTimeout that allows tick-by-tick control in tests using sinonjs 'lolex' // This needs to be before the connection is instantiated. const clock = FakeTimers.install(); @@ -123,15 +123,15 @@ Tinytest.add('livedata stub - buffering data', function(test) { bufferedWritesMaxAge: 40 }); - startAndConnect(test, stream); + await startAndConnect(test, stream); const coll_name = Random.id(); const coll = new Mongo.Collection(coll_name, conn); const testDocCount = count => test.equal(coll.find({}).count(), count); - const addDoc = () => { - stream.receive({ + const addDoc = async () => { + await stream.receive({ msg: 'added', collection: coll_name, id: Random.id(), @@ -141,31 +141,31 @@ Tinytest.add('livedata stub - buffering data', function(test) { // Starting at 0 ticks. At this point we haven't advanced the fake clock at all. - addDoc(); // 1st Doc + await addDoc(); // 1st Doc testDocCount(0); // No doc been recognized yet because it's buffered, waiting for more. tick(6); // 6 total ticks testDocCount(0); // Ensure that the doc still hasn't shown up, despite the clock moving forward. tick(4); // 10 total ticks, 1st buffer interval testDocCount(1); // No other docs have arrived, so we 'see' the 1st doc. - addDoc(); // 2nd doc + await addDoc(); // 2nd doc tick(1); // 11 total ticks (1 since last flush) testDocCount(1); // Again, second doc hasn't arrived because we're waiting for more... tick(9); // 20 total ticks (10 ticks since last flush & the 2nd 10-tick interval) testDocCount(2); // Now we're here and got the second document. // Add several docs, frequently enough that we buffer multiple times before the next flush. - addDoc(); // 3 docs + await addDoc(); // 3 docs tick(6); // 26 ticks (6 since last flush) - addDoc(); // 4 docs + await addDoc(); // 4 docs tick(6); // 32 ticks (12 since last flush) - addDoc(); // 5 docs + await addDoc(); // 5 docs tick(6); // 38 ticks (18 since last flush) - addDoc(); // 6 docs + await addDoc(); // 6 docs tick(6); // 44 ticks (24 since last flush) - addDoc(); // 7 docs + await addDoc(); // 7 docs tick(9); // 53 ticks (33 since last flush) - addDoc(); // 8 docs + await addDoc(); // 8 docs tick(9); // 62 ticks! (42 ticks since last flush, over max-age - next interval triggers flush) testDocCount(2); // Still at 2 from before! (Just making sure) tick(1); // Ok, 63 ticks (10 since last doc, so this should cause the flush of all the docs) @@ -175,11 +175,11 @@ Tinytest.add('livedata stub - buffering data', function(test) { clock.uninstall(); }); -Tinytest.add('livedata stub - subscribe', function(test) { +Tinytest.addAsync('livedata stub - subscribe', async function(test) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); // subscribe let callback_fired = false; @@ -201,7 +201,7 @@ Tinytest.add('livedata stub - subscribe', function(test) { test.isFalse(reactivelyReady); // get the sub satisfied. callback fires. - stream.receive({ msg: 'ready', subs: [id] }); + await stream.receive({ msg: 'ready', subs: [id] }); test.isTrue(callback_fired); Tracker.flush(); test.isTrue(reactivelyReady); @@ -224,11 +224,11 @@ Tinytest.add('livedata stub - subscribe', function(test) { test.equal(message, { msg: 'sub', name: 'my_data', params: [] }); }); -Tinytest.add('livedata stub - reactive subscribe', function(test) { +Tinytest.addAsync('livedata stub - reactive subscribe', async function(test) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); const rFoo = new ReactiveVar('foo1'); const rBar = new ReactiveVar('bar1'); @@ -283,7 +283,7 @@ Tinytest.add('livedata stub - reactive subscribe', function(test) { test.isFalse(completerReady); // "completer" gets ready now. its callback should fire. - stream.receive({ msg: 'ready', subs: [idCompleter] }); + await stream.receive({ msg: 'ready', subs: [idCompleter] }); test.equal(onReadyCount, { completer: 1 }); test.length(stream.sent, 0); Tracker.flush(); @@ -331,7 +331,7 @@ Tinytest.add('livedata stub - reactive subscribe', function(test) { // the client; completing bar should call the onReady from the new // subscription because we always call onReady for a given reactively-saved // subscription. - stream.receive({ msg: 'ready', subs: [idStopperAgain, idBar1] }); + await stream.receive({ msg: 'ready', subs: [idStopperAgain, idBar1] }); test.equal(onReadyCount, { completer: 2, bar1: 1, stopper: 1 }); // Shut down the autorun. This should unsub us from all current subs at flush @@ -353,13 +353,13 @@ Tinytest.add('livedata stub - reactive subscribe', function(test) { test.equal(actualIds, expectedIds); }); -Tinytest.add('livedata stub - reactive subscribe handle correct', function( +Tinytest.addAsync('livedata stub - reactive subscribe handle correct', async function( test ) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); const rFoo = new ReactiveVar('foo1'); @@ -402,7 +402,7 @@ Tinytest.add('livedata stub - reactive subscribe handle correct', function( test.isFalse(fooReady); // "foo" gets ready now. The handle should be ready and the autorun rerun - stream.receive({ msg: 'ready', subs: [idFoo2] }); + await stream.receive({ msg: 'ready', subs: [idFoo2] }); test.length(stream.sent, 0); Tracker.flush(); test.isTrue(fooHandle.ready()); @@ -427,7 +427,7 @@ Tinytest.add('livedata stub - reactive subscribe handle correct', function( test.isFalse(fooReady); // "foo" gets ready again - stream.receive({ msg: 'ready', subs: [idFoo3] }); + await stream.receive({ msg: 'ready', subs: [idFoo3] }); test.length(stream.sent, 0); Tracker.flush(); test.isTrue(fooHandle.ready()); @@ -436,11 +436,11 @@ Tinytest.add('livedata stub - reactive subscribe handle correct', function( autorunHandle.stop(); }); -Tinytest.add('livedata stub - this', function(test) { +Tinytest.addAsync('livedata stub - this', async function(test) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); conn.methods({ test_this: function() { test.isTrue(this.isSimulation); @@ -461,16 +461,16 @@ Tinytest.add('livedata stub - this', function(test) { }); test.length(stream.sent, 0); - stream.receive({ msg: 'result', id: message.id, result: null }); - stream.receive({ msg: 'updated', methods: [message.id] }); + await stream.receive({ msg: 'result', id: message.id, result: null }); + await stream.receive({ msg: 'updated', methods: [message.id] }); }); if (Meteor.isClient) { - Tinytest.add('livedata stub - methods', function(test) { + Tinytest.addAsync('livedata stub - methods', async function(test) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); const collName = Random.id(); const coll = new Mongo.Collection(collName, { connection: conn }); @@ -484,7 +484,7 @@ if (Meteor.isClient) { // setup observers const counts = { added: 0, removed: 0, changed: 0, moved: 0 }; - const handle = coll.find({}).observe({ + const handle = await coll.find({}).observe({ addedAt: function() { counts.added += 1; }, @@ -520,29 +520,29 @@ if (Meteor.isClient) { randomSeed: '*' }); - test.equal(coll.find({}).count(), 1); - test.equal(coll.find({ value: 'friday!' }).count(), 1); - const docId = coll.findOne({ value: 'friday!' })._id; + test.equal(await coll.find({}).count(), 1); + test.equal(await coll.find({ value: 'friday!' }).count(), 1); + const docId = (await coll.findOneAsync({ value: 'friday!' }))._id; // results does not yet result in callback, because data is not // ready. - stream.receive({ msg: 'result', id: message.id, result: '1234' }); + await stream.receive({ msg: 'result', id: message.id, result: '1234' }); test.isFalse(callback1Fired); // result message doesn't affect data - test.equal(coll.find({}).count(), 1); - test.equal(coll.find({ value: 'friday!' }).count(), 1); + test.equal(await coll.find({}).count(), 1); + test.equal(await coll.find({ value: 'friday!' }).count(), 1); test.equal(counts, { added: 1, removed: 0, changed: 0, moved: 0 }); // data methods do not show up (not quiescent yet) - stream.receive({ + await stream.receive({ msg: 'added', collection: collName, id: MongoID.idStringify(docId), fields: { value: 'tuesday' } }); - test.equal(coll.find({}).count(), 1); - test.equal(coll.find({ value: 'friday!' }).count(), 1); + test.equal(await coll.find({}).count(), 1); + test.equal(await coll.find({ value: 'friday!' }).count(), 1); test.equal(counts, { added: 1, removed: 0, changed: 0, moved: 0 }); // send another methods (unknown on client) @@ -566,35 +566,35 @@ if (Meteor.isClient) { // get the first data satisfied message. changes are applied to database even // though another method is outstanding, because the other method didn't have // a stub. and its callback is called. - stream.receive({ msg: 'updated', methods: [message.id] }); + await stream.receive({ msg: 'updated', methods: [message.id] }); test.isTrue(callback1Fired); test.isFalse(callback2Fired); - test.equal(coll.find({}).count(), 1); - test.equal(coll.find({ value: 'tuesday' }).count(), 1); + test.equal(await coll.find({}).count(), 1); + test.equal(await coll.find({ value: 'tuesday' }).count(), 1); test.equal(counts, { added: 1, removed: 0, changed: 1, moved: 0 }); // second result - stream.receive({ msg: 'result', id: message2.id, result: 'bupkis' }); + await stream.receive({ msg: 'result', id: message2.id, result: 'bupkis' }); test.isFalse(callback2Fired); // get second satisfied; no new changes are applied. - stream.receive({ msg: 'updated', methods: [message2.id] }); + await stream.receive({ msg: 'updated', methods: [message2.id] }); test.isTrue(callback2Fired); - test.equal(coll.find({}).count(), 1); - test.equal(coll.find({ value: 'tuesday', _id: docId }).count(), 1); + test.equal(await coll.find({}).count(), 1); + test.equal(await coll.find({ value: 'tuesday', _id: docId }).count(), 1); test.equal(counts, { added: 1, removed: 0, changed: 1, moved: 0 }); handle.stop(); }); } -Tinytest.add('livedata stub - mutating method args', function(test) { +Tinytest.addAsync('livedata stub - mutating method args', async function(test) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); conn.methods({ mutateArgs: function(arg) { @@ -616,10 +616,10 @@ Tinytest.add('livedata stub - mutating method args', function(test) { test.length(stream.sent, 0); }); -const observeCursor = function(test, cursor) { +const observeCursor = async function(test, cursor) { const counts = { added: 0, removed: 0, changed: 0, moved: 0 }; const expectedCounts = _.clone(counts); - const handle = cursor.observe({ + const handle = await cursor.observe({ addedAt: function() { counts.added += 1; }, @@ -646,11 +646,11 @@ const observeCursor = function(test, cursor) { // method calls another method in simulation. see not sent. if (Meteor.isClient) { - Tinytest.add('livedata stub - methods calling methods', function(test) { + Tinytest.addAsync('livedata stub - methods calling methods', async function(test) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); const coll_name = Random.id(); const coll = new Mongo.Collection(coll_name, { connection: conn }); @@ -665,7 +665,7 @@ if (Meteor.isClient) { } }); - const o = observeCursor(test, coll.find()); + const o = await observeCursor(test, coll.find()); // call method. conn.call('do_something', _.identity); @@ -683,40 +683,40 @@ if (Meteor.isClient) { // but inner method runs locally. o.expectCallbacks({ added: 1 }); test.equal(coll.find().count(), 1); - const docId = coll.findOne()._id; - test.equal(coll.findOne(), { _id: docId, a: 1 }); + const docId = (await coll.findOneAsync())._id; + test.equal(await coll.findOneAsync(), { _id: docId, a: 1 }); // we get the results - stream.receive({ msg: 'result', id: message.id, result: '1234' }); + await stream.receive({ msg: 'result', id: message.id, result: '1234' }); // get data from the method. data from this doc does not show up yet, but data // from another doc does. - stream.receive({ + await stream.receive({ msg: 'added', collection: coll_name, id: MongoID.idStringify(docId), fields: { value: 'tuesday' } }); o.expectCallbacks(); - test.equal(coll.findOne(docId), { _id: docId, a: 1 }); - stream.receive({ + test.equal(await coll.findOneAsync(docId), { _id: docId, a: 1 }); + await stream.receive({ msg: 'added', collection: coll_name, id: 'monkey', fields: { value: 'bla' } }); o.expectCallbacks({ added: 1 }); - test.equal(coll.findOne(docId), { _id: docId, a: 1 }); - const newDoc = coll.findOne({ value: 'bla' }); + test.equal(await coll.findOneAsync(docId), { _id: docId, a: 1 }); + const newDoc = await coll.findOneAsync({ value: 'bla' }); test.isTrue(newDoc); test.equal(newDoc, { _id: newDoc._id, value: 'bla' }); // get method satisfied. all data shows up. the 'a' field is reverted and // 'value' field is set. - stream.receive({ msg: 'updated', methods: [message.id] }); + await stream.receive({ msg: 'updated', methods: [message.id] }); o.expectCallbacks({ changed: 1 }); - test.equal(coll.findOne(docId), { _id: docId, value: 'tuesday' }); - test.equal(coll.findOne(newDoc._id), { _id: newDoc._id, value: 'bla' }); + test.equal(await coll.findOneAsync(docId), { _id: docId, value: 'tuesday' }); + test.equal(await coll.findOneAsync(newDoc._id), { _id: newDoc._id, value: 'bla' }); o.stop(); }); @@ -746,7 +746,7 @@ Tinytest.add('livedata stub - method call before connect', function(test) { }); }); -Tinytest.add('livedata stub - reconnect', function(test) { +Tinytest.addAsync('livedata stub - reconnect', async function(test, onComplete) { const stream = new StubStream(); const conn = newConnection(stream); @@ -755,7 +755,7 @@ Tinytest.add('livedata stub - reconnect', function(test) { const collName = Random.id(); const coll = new Mongo.Collection(collName, { connection: conn }); - const o = observeCursor(test, coll.find()); + const o = await observeCursor(test, coll.find()); // subscribe let subCallbackFired = false; @@ -773,31 +773,31 @@ Tinytest.add('livedata stub - reconnect', function(test) { }); // get some data. it shows up. - stream.receive({ + await stream.receive({ msg: 'added', collection: collName, id: '1234', fields: { a: 1 } }); - test.equal(coll.find({}).count(), 1); + test.equal(await coll.find({}).count(), 1); o.expectCallbacks({ added: 1 }); test.isFalse(subCallbackFired); - stream.receive({ + await stream.receive({ msg: 'changed', collection: collName, id: '1234', fields: { b: 2 } }); - stream.receive({ + await stream.receive({ msg: 'ready', subs: [subMessage.id] // satisfy sub }); test.isTrue(subCallbackFired); subCallbackFired = false; // re-arm for test that it doesn't fire again. - test.equal(coll.find({ a: 1, b: 2 }).count(), 1); + test.equal(await coll.find({ a: 1, b: 2 }).count(), 1); o.expectCallbacks({ changed: 1 }); // call method. @@ -823,69 +823,69 @@ Tinytest.add('livedata stub - reconnect', function(test) { test.equal(stream.sent.length, 0); // more data. shows up immediately because there was no relevant method stub. - stream.receive({ + await stream.receive({ msg: 'changed', collection: collName, id: '1234', fields: { c: 3 } }); - test.equal(coll.findOne('1234'), { _id: '1234', a: 1, b: 2, c: 3 }); + test.equal(await coll.findOneAsync('1234'), { _id: '1234', a: 1, b: 2, c: 3 }); o.expectCallbacks({ changed: 1 }); // stream reset. reconnect! we send a connect, our pending method, and our // sub. The wait method still is blocked. - stream.reset(); + await stream.reset(); testGotMessage(test, stream, makeConnectMessage(SESSION_ID)); testGotMessage(test, stream, methodMessage); testGotMessage(test, stream, subMessage); // reconnect with different session id - stream.receive({ msg: 'connected', session: SESSION_ID + 1 }); + await stream.receive({ msg: 'connected', session: SESSION_ID + 1 }); // resend data. doesn't show up: we're in reconnect quiescence. - stream.receive({ + await stream.receive({ msg: 'added', collection: collName, id: '1234', fields: { a: 1, b: 2, c: 3, d: 4 } }); - stream.receive({ + await stream.receive({ msg: 'added', collection: collName, id: '2345', fields: { e: 5 } }); - test.equal(coll.findOne('1234'), { _id: '1234', a: 1, b: 2, c: 3 }); - test.isFalse(coll.findOne('2345')); + test.equal(await await coll.findOneAsync('1234'), { _id: '1234', a: 1, b: 2, c: 3 }); + test.isFalse(await coll.findOneAsync('2345')); o.expectCallbacks(); // satisfy and return the method - stream.receive({ + await stream.receive({ msg: 'updated', methods: [methodMessage.id] }); test.isFalse(methodCallbackFired); - stream.receive({ msg: 'result', id: methodMessage.id, result: 'bupkis' }); + await stream.receive({ msg: 'result', id: methodMessage.id, result: 'bupkis' }); // The callback still doesn't fire (and we don't send the wait method): we're // still in global quiescence test.isFalse(methodCallbackFired); test.equal(stream.sent.length, 0); // still no update. - test.equal(coll.findOne('1234'), { _id: '1234', a: 1, b: 2, c: 3 }); - test.isFalse(coll.findOne('2345')); + test.equal(await coll.findOneAsync('1234'), { _id: '1234', a: 1, b: 2, c: 3 }); + test.isFalse(await coll.findOneAsync('2345')); o.expectCallbacks(); // re-satisfy sub - stream.receive({ msg: 'ready', subs: [subMessage.id] }); + await stream.receive({ msg: 'ready', subs: [subMessage.id] }); // now the doc changes and method callback is called, and the wait method is // sent. the sub callback isn't re-called. test.isTrue(methodCallbackFired); test.isFalse(subCallbackFired); - test.equal(coll.findOne('1234'), { _id: '1234', a: 1, b: 2, c: 3, d: 4 }); - test.equal(coll.findOne('2345'), { _id: '2345', e: 5 }); + test.equal(await coll.findOneAsync('1234'), { _id: '1234', a: 1, b: 2, c: 3, d: 4 }); + test.equal(await coll.findOneAsync('2345'), { _id: '2345', e: 5 }); o.expectCallbacks({ added: 1, changed: 1 }); let waitMethodMessage = JSON.parse(stream.sent.shift()); @@ -897,9 +897,9 @@ Tinytest.add('livedata stub - reconnect', function(test) { id: waitMethodMessage.id }); test.equal(stream.sent.length, 0); - stream.receive({ msg: 'result', id: waitMethodMessage.id, result: 'bupkis' }); + await stream.receive({ msg: 'result', id: waitMethodMessage.id, result: 'bupkis' }); test.equal(stream.sent.length, 0); - stream.receive({ msg: 'updated', methods: [waitMethodMessage.id] }); + await stream.receive({ msg: 'updated', methods: [waitMethodMessage.id] }); // wait method done means we can send the third method test.equal(stream.sent.length, 1); @@ -916,14 +916,14 @@ Tinytest.add('livedata stub - reconnect', function(test) { }); if (Meteor.isClient) { - Tinytest.add('livedata stub - reconnect non-idempotent method', function( + Tinytest.addAsync('livedata stub - reconnect non-idempotent method', async function( test ) { // This test is for https://github.com/meteor/meteor/issues/6108 const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); let firstMethodCallbackFired = false; let firstMethodCallbackErrored = false; @@ -954,12 +954,12 @@ if (Meteor.isClient) { stream.sent.shift(); stream.sent.shift(); // reconnect - stream.reset(); + await stream.reset(); // verify that a reconnect message was sent. testGotMessage(test, stream, makeConnectMessage(SESSION_ID)); // Make sure that the stream triggers connection. - stream.receive({ msg: 'connected', session: SESSION_ID + 1 }); + await stream.receive({ msg: 'connected', session: SESSION_ID + 1 }); //The method callback should fire even though the stream has not sent a response. //the callback should have been fired with an error. @@ -974,14 +974,14 @@ if (Meteor.isClient) { } function addReconnectTests(name, testFunc) { - Tinytest.add(name + ' (deprecated)', function(test) { + Tinytest.addAsync(name + ' (deprecated)', async function(test) { function deprecatedSetOnReconnect(conn, handler) { conn.onReconnect = handler; } - testFunc.call(this, test, deprecatedSetOnReconnect); + await testFunc.call(this, test, deprecatedSetOnReconnect); }); - Tinytest.add(name, function(test) { + Tinytest.addAsync(name, async function(test) { let stopper; function setOnReconnect(conn, handler) { stopper && stopper.stop(); @@ -991,7 +991,7 @@ function addReconnectTests(name, testFunc) { } }); } - testFunc.call(this, test, setOnReconnect); + await testFunc.call(this, test, setOnReconnect); stopper && stopper.stop(); }); } @@ -999,14 +999,14 @@ function addReconnectTests(name, testFunc) { if (Meteor.isClient) { addReconnectTests( 'livedata stub - reconnect method which only got result', - function(test, setOnReconnect) { + async function(test, setOnReconnect) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); const collName = Random.id(); const coll = new Mongo.Collection(collName, { connection: conn }); - const o = observeCursor(test, coll.find()); + const o = await observeCursor(test, coll.find()); conn.methods({ writeSomething: function() { @@ -1034,7 +1034,7 @@ if (Meteor.isClient) { ); // Stub write is visible. test.equal(coll.find({ foo: 'bar' }).count(), 1); - const stubWrittenId = coll.findOne({ foo: 'bar' })._id; + const stubWrittenId = (await coll.findOneAsync({ foo: 'bar' }))._id; o.expectCallbacks({ added: 1 }); // Callback not called. test.equal(callbackOutput, []); @@ -1050,7 +1050,7 @@ if (Meteor.isClient) { test.equal(stream.sent.length, 0); // Get some data. - stream.receive({ + await stream.receive({ msg: 'added', collection: collName, id: MongoID.idStringify(stubWrittenId), @@ -1058,17 +1058,17 @@ if (Meteor.isClient) { }); // It doesn't show up yet. test.equal(coll.find().count(), 1); - test.equal(coll.findOne(stubWrittenId), { + test.equal(await coll.findOneAsync(stubWrittenId), { _id: stubWrittenId, foo: 'bar' }); o.expectCallbacks(); // Get the result. - stream.receive({ msg: 'result', id: methodId, result: 'bla' }); + await stream.receive({ msg: 'result', id: methodId, result: 'bla' }); // Data unaffected. test.equal(coll.find().count(), 1); - test.equal(coll.findOne(stubWrittenId), { + test.equal(await coll.findOneAsync(stubWrittenId), { _id: stubWrittenId, foo: 'bar' }); @@ -1080,11 +1080,11 @@ if (Meteor.isClient) { // Reset stream. Method does NOT get resent, because its result is already // in. Reconnect quiescence happens as soon as 'connected' is received because // there are no pending methods or subs in need of revival. - stream.reset(); + await stream.reset(); testGotMessage(test, stream, makeConnectMessage(SESSION_ID)); // Still holding out hope for session resumption, so nothing updated yet. test.equal(coll.find().count(), 1); - test.equal(coll.findOne(stubWrittenId), { + test.equal(await coll.findOneAsync(stubWrittenId), { _id: stubWrittenId, foo: 'bar' }); @@ -1093,18 +1093,18 @@ if (Meteor.isClient) { // Receive 'connected': time for reconnect quiescence! Data gets updated // locally (ie, data is reset) and callback gets called. - stream.receive({ msg: 'connected', session: SESSION_ID + 1 }); + await stream.receive({ msg: 'connected', session: SESSION_ID + 1 }); test.equal(coll.find().count(), 0); o.expectCallbacks({ removed: 1 }); test.equal(callbackOutput, ['bla']); test.equal(onResultReceivedOutput, ['bla']); - stream.receive({ + await stream.receive({ msg: 'added', collection: collName, id: MongoID.idStringify(stubWrittenId), fields: { baz: 42 } }); - test.equal(coll.findOne(stubWrittenId), { _id: stubWrittenId, baz: 42 }); + test.equal(await coll.findOneAsync(stubWrittenId), { _id: stubWrittenId, baz: 42 }); o.expectCallbacks({ added: 1 }); // Run method again. We're going to do the same thing this time, except we're @@ -1124,7 +1124,7 @@ if (Meteor.isClient) { ); // Stub write is visible. test.equal(coll.find({ foo: 'bar' }).count(), 1); - const stubWrittenId2 = coll.findOne({ foo: 'bar' })._id; + const stubWrittenId2 = (await coll.findOneAsync({ foo: 'bar' }))._id; o.expectCallbacks({ added: 1 }); // Callback not called. test.equal(callbackOutput, ['bla']); @@ -1140,7 +1140,7 @@ if (Meteor.isClient) { test.equal(stream.sent.length, 0); // Get some data. - stream.receive({ + await stream.receive({ msg: 'added', collection: collName, id: MongoID.idStringify(stubWrittenId2), @@ -1148,17 +1148,17 @@ if (Meteor.isClient) { }); // It doesn't show up yet. test.equal(coll.find().count(), 2); - test.equal(coll.findOne(stubWrittenId2), { + test.equal(await coll.findOneAsync(stubWrittenId2), { _id: stubWrittenId2, foo: 'bar' }); o.expectCallbacks(); // Get the result. - stream.receive({ msg: 'result', id: methodId2, result: 'blab' }); + await stream.receive({ msg: 'result', id: methodId2, result: 'blab' }); // Data unaffected. test.equal(coll.find().count(), 2); - test.equal(coll.findOne(stubWrittenId2), { + test.equal(await coll.findOneAsync(stubWrittenId2), { _id: stubWrittenId2, foo: 'bar' }); @@ -1175,7 +1175,7 @@ if (Meteor.isClient) { // Reset stream. Method does NOT get resent, because its result is already in, // but slowMethod gets called via onReconnect. Reconnect quiescence is now // blocking on slowMethod. - stream.reset(); + await stream.reset(); testGotMessage(test, stream, makeConnectMessage(SESSION_ID + 1)); const slowMethodId = testGotMessage(test, stream, { msg: 'method', @@ -1185,7 +1185,7 @@ if (Meteor.isClient) { }).id; // Still holding out hope for session resumption, so nothing updated yet. test.equal(coll.find().count(), 2); - test.equal(coll.findOne(stubWrittenId2), { + test.equal(await coll.findOneAsync(stubWrittenId2), { _id: stubWrittenId2, foo: 'bar' }); @@ -1193,9 +1193,9 @@ if (Meteor.isClient) { test.equal(callbackOutput, ['bla']); // Receive 'connected'... but no reconnect quiescence yet due to slowMethod. - stream.receive({ msg: 'connected', session: SESSION_ID + 2 }); + await stream.receive({ msg: 'connected', session: SESSION_ID + 2 }); test.equal(coll.find().count(), 2); - test.equal(coll.findOne(stubWrittenId2), { + test.equal(await coll.findOneAsync(stubWrittenId2), { _id: stubWrittenId2, foo: 'bar' }); @@ -1203,7 +1203,7 @@ if (Meteor.isClient) { test.equal(callbackOutput, ['bla']); // Receive data matching our stub. It doesn't take effect yet. - stream.receive({ + await stream.receive({ msg: 'added', collection: collName, id: MongoID.idStringify(stubWrittenId2), @@ -1215,9 +1215,9 @@ if (Meteor.isClient) { // slowMethod callback)... ie, a reset followed by applying the data we just // got, as well as calling the callback from the method that half-finished // before reset. The net effect is deleting doc 'stubWrittenId'. - stream.receive({ msg: 'updated', methods: [slowMethodId] }); + await stream.receive({ msg: 'updated', methods: [slowMethodId] }); test.equal(coll.find().count(), 1); - test.equal(coll.findOne(stubWrittenId2), { + test.equal(await coll.findOneAsync(stubWrittenId2), { _id: stubWrittenId2, foo: 'bar' }); @@ -1225,7 +1225,7 @@ if (Meteor.isClient) { test.equal(callbackOutput, ['bla', 'blab']); // slowMethod returns a value now. - stream.receive({ msg: 'result', id: slowMethodId, result: 'slow' }); + await stream.receive({ msg: 'result', id: slowMethodId, result: 'slow' }); o.expectCallbacks(); test.equal(callbackOutput, ['bla', 'blab', 'slow']); @@ -1233,16 +1233,16 @@ if (Meteor.isClient) { } ); } -Tinytest.add('livedata stub - reconnect method which only got data', function( +Tinytest.addAsync('livedata stub - reconnect method which only got data', async function( test ) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); const collName = Random.id(); const coll = new Mongo.Collection(collName, { connection: conn }); - const o = observeCursor(test, coll.find()); + const o = await observeCursor(test, coll.find()); // Call a method. We'll get the data-done message but not the result before // reconnect. @@ -1273,7 +1273,7 @@ Tinytest.add('livedata stub - reconnect method which only got data', function( test.equal(stream.sent.length, 0); // Get some data. - stream.receive({ + await stream.receive({ msg: 'added', collection: collName, id: 'photo', @@ -1281,14 +1281,14 @@ Tinytest.add('livedata stub - reconnect method which only got data', function( }); // It shows up instantly because the stub didn't write anything. test.equal(coll.find().count(), 1); - test.equal(coll.findOne('photo'), { _id: 'photo', baz: 42 }); + test.equal(await coll.findOneAsync('photo'), { _id: 'photo', baz: 42 }); o.expectCallbacks({ added: 1 }); // Get the data-done message. - stream.receive({ msg: 'updated', methods: [methodId] }); + await stream.receive({ msg: 'updated', methods: [methodId] }); // Data still here. test.equal(coll.find().count(), 1); - test.equal(coll.findOne('photo'), { _id: 'photo', baz: 42 }); + test.equal(await coll.findOneAsync('photo'), { _id: 'photo', baz: 42 }); o.expectCallbacks(); // Method callback not called yet (no result yet). test.equal(callbackOutput, []); @@ -1296,7 +1296,7 @@ Tinytest.add('livedata stub - reconnect method which only got data', function( // Reset stream. Method gets resent (with same ID), and blocks reconnect // quiescence. - stream.reset(); + await stream.reset(); testGotMessage(test, stream, makeConnectMessage(SESSION_ID)); testGotMessage(test, stream, { msg: 'method', @@ -1306,15 +1306,15 @@ Tinytest.add('livedata stub - reconnect method which only got data', function( }); // Still holding out hope for session resumption, so nothing updated yet. test.equal(coll.find().count(), 1); - test.equal(coll.findOne('photo'), { _id: 'photo', baz: 42 }); + test.equal(await coll.findOneAsync('photo'), { _id: 'photo', baz: 42 }); o.expectCallbacks(); test.equal(callbackOutput, []); test.equal(onResultReceivedOutput, []); // Receive 'connected'. Still blocking on reconnect quiescence. - stream.receive({ msg: 'connected', session: SESSION_ID + 1 }); + await stream.receive({ msg: 'connected', session: SESSION_ID + 1 }); test.equal(coll.find().count(), 1); - test.equal(coll.findOne('photo'), { _id: 'photo', baz: 42 }); + test.equal(await coll.findOneAsync('photo'), { _id: 'photo', baz: 42 }); o.expectCallbacks(); test.equal(callbackOutput, []); test.equal(onResultReceivedOutput, []); @@ -1322,12 +1322,12 @@ Tinytest.add('livedata stub - reconnect method which only got data', function( // Receive method result. onResultReceived is called but the main callback // isn't (ie, we don't get confused by the fact that we got data-done the // *FIRST* time through). - stream.receive({ msg: 'result', id: methodId, result: 'res' }); + await stream.receive({ msg: 'result', id: methodId, result: 'res' }); test.equal(callbackOutput, []); test.equal(onResultReceivedOutput, ['res']); // Now we get data-done. Collection is reset and callback is called. - stream.receive({ msg: 'updated', methods: [methodId] }); + await stream.receive({ msg: 'updated', methods: [methodId] }); test.equal(coll.find().count(), 0); o.expectCallbacks({ removed: 1 }); test.equal(callbackOutput, ['res']); @@ -1336,14 +1336,14 @@ Tinytest.add('livedata stub - reconnect method which only got data', function( o.stop(); }); if (Meteor.isClient) { - Tinytest.add('livedata stub - multiple stubs same doc', function(test) { + Tinytest.addAsync('livedata stub - multiple stubs same doc', async function(test) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); const collName = Random.id(); const coll = new Mongo.Collection(collName, { connection: conn }); - const o = observeCursor(test, coll.find()); + const o = await observeCursor(test, coll.find()); conn.methods({ insertSomething: function() { @@ -1361,7 +1361,7 @@ if (Meteor.isClient) { conn.call('insertSomething', _.identity); // Stub write is visible. test.equal(coll.find({ foo: 'bar' }).count(), 1); - const stubWrittenId = coll.findOne({ foo: 'bar' })._id; + const stubWrittenId = (await coll.findOneAsync({ foo: 'bar' }))._id; o.expectCallbacks({ added: 1 }); // Method sent. const insertMethodId = testGotMessage(test, stream, { @@ -1377,7 +1377,7 @@ if (Meteor.isClient) { conn.call('updateIt', stubWrittenId, _.identity); // This stub write is visible too. test.equal(coll.find().count(), 1); - test.equal(coll.findOne(stubWrittenId), { + test.equal(await coll.findOneAsync(stubWrittenId), { _id: stubWrittenId, foo: 'bar', baz: 42 @@ -1393,7 +1393,7 @@ if (Meteor.isClient) { test.equal(stream.sent.length, 0); // Get some data... slightly different than what we wrote. - stream.receive({ + await stream.receive({ msg: 'added', collection: collName, id: MongoID.idStringify(stubWrittenId), @@ -1405,7 +1405,7 @@ if (Meteor.isClient) { }); // It doesn't show up yet. test.equal(coll.find().count(), 1); - test.equal(coll.findOne(stubWrittenId), { + test.equal(await coll.findOneAsync(stubWrittenId), { _id: stubWrittenId, foo: 'bar', baz: 42 @@ -1414,9 +1414,9 @@ if (Meteor.isClient) { // And get the first method-done. Still no updates to minimongo: we can't // quiesce the doc until the second method is done. - stream.receive({ msg: 'updated', methods: [insertMethodId] }); + await stream.receive({ msg: 'updated', methods: [insertMethodId] }); test.equal(coll.find().count(), 1); - test.equal(coll.findOne(stubWrittenId), { + test.equal(await coll.findOneAsync(stubWrittenId), { _id: stubWrittenId, foo: 'bar', baz: 42 @@ -1424,7 +1424,7 @@ if (Meteor.isClient) { o.expectCallbacks(); // More data. Not quite what we wrote. Also ignored for now. - stream.receive({ + await stream.receive({ msg: 'changed', collection: collName, id: MongoID.idStringify(stubWrittenId), @@ -1432,7 +1432,7 @@ if (Meteor.isClient) { cleared: ['other'] }); test.equal(coll.find().count(), 1); - test.equal(coll.findOne(stubWrittenId), { + test.equal(await coll.findOneAsync(stubWrittenId), { _id: stubWrittenId, foo: 'bar', baz: 42 @@ -1440,9 +1440,9 @@ if (Meteor.isClient) { o.expectCallbacks(); // Second data-ready. Now everything takes effect! - stream.receive({ msg: 'updated', methods: [updateMethodId] }); + await stream.receive({ msg: 'updated', methods: [updateMethodId] }); test.equal(coll.find().count(), 1); - test.equal(coll.findOne(stubWrittenId), { + test.equal(await coll.findOneAsync(stubWrittenId), { _id: stubWrittenId, foo: 'barb', other2: 'bla', @@ -1455,14 +1455,14 @@ if (Meteor.isClient) { } if (Meteor.isClient) { - Tinytest.add( + Tinytest.addAsync( "livedata stub - unsent methods don't block quiescence", - function(test) { + async function(test) { // This test is for https://github.com/meteor/meteor/issues/555 const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); const collName = Random.id(); const coll = new Mongo.Collection(collName, { connection: conn }); @@ -1485,7 +1485,7 @@ if (Meteor.isClient) { // Stub write is visible. test.equal(coll.find({ foo: 'bar' }).count(), 1); - const stubWrittenId = coll.findOne({ foo: 'bar' })._id; + const stubWrittenId = await coll.findOneAsync({ foo: 'bar' })._id; // first method sent const firstMethodId = testGotMessage(test, stream, { @@ -1497,8 +1497,8 @@ if (Meteor.isClient) { test.equal(stream.sent.length, 0); // ack the first method - stream.receive({ msg: 'updated', methods: [firstMethodId] }); - stream.receive({ msg: 'result', id: firstMethodId }); + await stream.receive({ msg: 'updated', methods: [firstMethodId] }); + await stream.receive({ msg: 'result', id: firstMethodId }); // Wait method sent. const waitMethodId = testGotMessage(test, stream, { @@ -1510,8 +1510,8 @@ if (Meteor.isClient) { test.equal(stream.sent.length, 0); // ack the wait method - stream.receive({ msg: 'updated', methods: [waitMethodId] }); - stream.receive({ msg: 'result', id: waitMethodId }); + await stream.receive({ msg: 'updated', methods: [waitMethodId] }); + await stream.receive({ msg: 'result', id: waitMethodId }); // insert method sent. const insertMethodId = testGotMessage(test, stream, { @@ -1524,19 +1524,19 @@ if (Meteor.isClient) { test.equal(stream.sent.length, 0); // ack the insert method - stream.receive({ msg: 'updated', methods: [insertMethodId] }); - stream.receive({ msg: 'result', id: insertMethodId }); + await stream.receive({ msg: 'updated', methods: [insertMethodId] }); + await stream.receive({ msg: 'result', id: insertMethodId }); // simulation reverted. test.equal(coll.find({ foo: 'bar' }).count(), 0); } ); } -Tinytest.add('livedata stub - reactive resub', function(test) { +Tinytest.addAsync('livedata stub - reactive resub', async function(test) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); const readiedSubs = {}; const markAllReady = function() { @@ -1627,10 +1627,10 @@ Tinytest.add('livedata connection - reactive userId', function(test) { test.equal(conn.userId(), 1337); }); -Tinytest.add('livedata connection - two wait methods', function(test) { +Tinytest.addAsync('livedata connection - two wait methods', async function(test) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); const collName = Random.id(); const coll = new Mongo.Collection(collName, { connection: conn }); @@ -1673,19 +1673,19 @@ Tinytest.add('livedata connection - two wait methods', function(test) { // Receive some data. "one" is not a wait method and there are no stubs, so it // gets applied immediately. test.equal(coll.find().count(), 0); - stream.receive({ + await stream.receive({ msg: 'added', collection: collName, id: 'foo', fields: { x: 1 } }); test.equal(coll.find().count(), 1); - test.equal(coll.findOne('foo'), { _id: 'foo', x: 1 }); + test.equal(await coll.findOneAsync('foo'), { _id: 'foo', x: 1 }); // Let "one!" finish. Both messages are required to fire the callback. - stream.receive({ msg: 'result', id: one_message.id }); + await stream.receive({ msg: 'result', id: one_message.id }); test.equal(responses, []); - stream.receive({ msg: 'updated', methods: [one_message.id] }); + await stream.receive({ msg: 'updated', methods: [one_message.id] }); test.equal(responses, ['one']); // Now we've send out "two!". @@ -1697,23 +1697,23 @@ Tinytest.add('livedata connection - two wait methods', function(test) { // Receive more data. "two" is a wait method, so the data doesn't get applied // yet. - stream.receive({ + await stream.receive({ msg: 'changed', collection: collName, id: 'foo', fields: { y: 3 } }); test.equal(coll.find().count(), 1); - test.equal(coll.findOne('foo'), { _id: 'foo', x: 1 }); + test.equal(await coll.findOneAsync('foo'), { _id: 'foo', x: 1 }); // Let "two!" finish, with its end messages in the opposite order to "one!". - stream.receive({ msg: 'updated', methods: [two_message.id] }); + await stream.receive({ msg: 'updated', methods: [two_message.id] }); test.equal(responses, ['one']); test.equal(stream.sent.length, 0); // data-done message is enough to allow data to be written. test.equal(coll.find().count(), 1); - test.equal(coll.findOne('foo'), { _id: 'foo', x: 1, y: 3 }); - stream.receive({ msg: 'result', id: two_message.id }); + test.equal(await coll.findOneAsync('foo'), { _id: 'foo', x: 1, y: 3 }); + await stream.receive({ msg: 'result', id: two_message.id }); test.equal(responses, ['one', 'two']); // Verify that we just sent "three!" and "four!" now that we got @@ -1725,14 +1725,14 @@ Tinytest.add('livedata connection - two wait methods', function(test) { test.equal(four_message.params, ['four!']); // Out of order response is OK for non-wait methods. - stream.receive({ msg: 'result', id: three_message.id }); - stream.receive({ msg: 'result', id: four_message.id }); - stream.receive({ msg: 'updated', methods: [four_message.id] }); + await stream.receive({ msg: 'result', id: three_message.id }); + await stream.receive({ msg: 'result', id: four_message.id }); + await stream.receive({ msg: 'updated', methods: [four_message.id] }); test.equal(responses, ['one', 'two', 'four']); test.equal(stream.sent.length, 0); // Let three finish too. - stream.receive({ msg: 'updated', methods: [three_message.id] }); + await stream.receive({ msg: 'updated', methods: [three_message.id] }); test.equal(responses, ['one', 'two', 'four', 'three']); // Verify that we just sent "five!" (the next wait method). @@ -1742,8 +1742,8 @@ Tinytest.add('livedata connection - two wait methods', function(test) { test.equal(responses, ['one', 'two', 'four', 'three']); // Let five finish. - stream.receive({ msg: 'result', id: five_message.id }); - stream.receive({ msg: 'updated', methods: [five_message.id] }); + await stream.receive({ msg: 'result', id: five_message.id }); + await stream.receive({ msg: 'updated', methods: [five_message.id] }); test.equal(responses, ['one', 'two', 'four', 'three', 'five']); let six_message = JSON.parse(stream.sent.shift()); @@ -1752,10 +1752,10 @@ Tinytest.add('livedata connection - two wait methods', function(test) { addReconnectTests( 'livedata connection - onReconnect prepends messages correctly with a wait method', - function(test, setOnReconnect) { + async function(test, setOnReconnect) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); // setup method conn.methods({ do_something: function(x) {} }); @@ -1773,7 +1773,7 @@ addReconnectTests( // reconnect stream.sent = []; - stream.reset(); + await stream.reset(); testGotMessage(test, stream, makeConnectMessage(conn._lastSessionId)); // Test that we sent what we expect to send, and we're blocked on @@ -1807,22 +1807,22 @@ addReconnectTests( } ); -Tinytest.add('livedata connection - ping without id', function(test) { +Tinytest.addAsync('livedata connection - ping without id', async function(test) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); - stream.receive({ msg: 'ping' }); + await stream.receive({ msg: 'ping' }); testGotMessage(test, stream, { msg: 'pong' }); }); -Tinytest.add('livedata connection - ping with id', function(test) { +Tinytest.addAsync('livedata connection - ping with id', async function(test) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); const id = Random.id(); - stream.receive({ msg: 'ping', id: id }); + await stream.receive({ msg: 'ping', id: id }); testGotMessage(test, stream, { msg: 'pong', id: id }); }); @@ -1978,10 +1978,10 @@ Tinytest.addAsync('livedata connection - version negotiation error', function( addReconnectTests( 'livedata connection - onReconnect prepends messages correctly without a wait method', - function(test, setOnReconnect) { + async function(test, setOnReconnect) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); // setup method conn.methods({ do_something: function(x) {} }); @@ -1999,7 +1999,7 @@ addReconnectTests( // reconnect stream.sent = []; - stream.reset(); + await stream.reset(); testGotMessage(test, stream, makeConnectMessage(conn._lastSessionId)); // Test that we sent what we expect to send, and we're blocked on @@ -2034,10 +2034,10 @@ addReconnectTests( addReconnectTests( 'livedata connection - onReconnect with sent messages', - function(test, setOnReconnect) { + async function(test, setOnReconnect) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); // setup method conn.methods({ do_something: function(x) {} }); @@ -2050,7 +2050,7 @@ addReconnectTests( // initial connect stream.sent = []; - stream.reset(); + await stream.reset(); testGotMessage(test, stream, makeConnectMessage(conn._lastSessionId)); // Test that we sent just the login message. @@ -2062,15 +2062,15 @@ addReconnectTests( }).id; // we connect. - stream.receive({ msg: 'connected', session: Random.id() }); + await stream.receive({ msg: 'connected', session: Random.id() }); test.length(stream.sent, 0); // login got result (but not yet data) - stream.receive({ msg: 'result', id: loginId, result: 'foo' }); + await stream.receive({ msg: 'result', id: loginId, result: 'foo' }); test.length(stream.sent, 0); // login got data. now we send next method. - stream.receive({ msg: 'updated', methods: [loginId] }); + await stream.receive({ msg: 'updated', methods: [loginId] }); testGotMessage(test, stream, { msg: 'method', @@ -2081,13 +2081,13 @@ addReconnectTests( } ); -addReconnectTests('livedata stub - reconnect double wait method', function( +addReconnectTests('livedata stub - reconnect double wait method', async function( test, setOnReconnect ) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); const output = []; setOnReconnect(conn, function() { @@ -2111,13 +2111,13 @@ addReconnectTests('livedata stub - reconnect double wait method', function( test.equal(stream.sent.length, 0); // Get the result. This means it will not be resent. - stream.receive({ msg: 'result', id: halfwayId, result: 'bla' }); + await stream.receive({ msg: 'result', id: halfwayId, result: 'bla' }); // Callback not called. test.equal(output, []); // Reset stream. halfwayMethod does NOT get resent, but reconnectMethod does! // Reconnect quiescence happens when reconnectMethod is done. - stream.reset(); + await stream.reset(); testGotMessage(test, stream, makeConnectMessage(SESSION_ID)); const reconnectId = testGotMessage(test, stream, { msg: 'method', @@ -2131,18 +2131,18 @@ addReconnectTests('livedata stub - reconnect double wait method', function( // Receive 'connected', but reconnect quiescence is blocking on // reconnectMethod. - stream.receive({ msg: 'connected', session: SESSION_ID + 1 }); + await stream.receive({ msg: 'connected', session: SESSION_ID + 1 }); test.equal(output, []); // Data-done for reconnectMethod. This gets us to reconnect quiescence, so // halfwayMethod's callback fires. reconnectMethod's is still waiting on its // result. - stream.receive({ msg: 'updated', methods: [reconnectId] }); + await stream.receive({ msg: 'updated', methods: [reconnectId] }); test.equal(output.shift(), 'halfway'); test.equal(output, []); // Get result of reconnectMethod. Its callback fires. - stream.receive({ msg: 'result', id: reconnectId, result: 'foo' }); + await stream.receive({ msg: 'result', id: reconnectId, result: 'foo' }); test.equal(output.shift(), 'reconnect'); test.equal(output, []); @@ -2158,11 +2158,11 @@ addReconnectTests('livedata stub - reconnect double wait method', function( }); }); -Tinytest.add('livedata stub - subscribe errors', function(test) { +Tinytest.addAsync('livedata stub - subscribe errors', async function(test) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); // subscribe let onReadyFired = false; @@ -2202,7 +2202,7 @@ Tinytest.add('livedata stub - subscribe errors', function(test) { }); // Reject the sub. - stream.receive({ + await stream.receive({ msg: 'nosub', id: subMessage.id, error: new Meteor.Error(404, 'Subscription not found') @@ -2221,7 +2221,7 @@ Tinytest.add('livedata stub - subscribe errors', function(test) { test.equal(subErrorInError.reason, 'Subscription not found'); // stream reset: reconnect! - stream.reset(); + await stream.reset(); // We send a connect. testGotMessage(test, stream, makeConnectMessage(SESSION_ID)); // We should NOT re-sub to the sub, because we processed the error. @@ -2229,11 +2229,11 @@ Tinytest.add('livedata stub - subscribe errors', function(test) { test.isFalse(onReadyFired); }); -Tinytest.add('livedata stub - subscribe stop', function(test) { +Tinytest.addAsync('livedata stub - subscribe stop', async function(test) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); // subscribe let onReadyFired = false; @@ -2256,7 +2256,7 @@ Tinytest.add('livedata stub - subscribe stop', function(test) { }); if (Meteor.isClient) { - Tinytest.add('livedata stub - stubs before connected', function(test) { + Tinytest.addAsync('livedata stub - stubs before connected', async function(test) { const stream = new StubStream(); const conn = newConnection(stream); @@ -2264,7 +2264,7 @@ if (Meteor.isClient) { const coll = new Mongo.Collection(collName, { connection: conn }); // Start and send "connect", but DON'T get 'connected' quite yet. - stream.reset(); // initial connection start. + await stream.reset(); // initial connection start. testGotMessage(test, stream, makeConnectMessage()); test.length(stream.sent, 0); @@ -2272,7 +2272,7 @@ if (Meteor.isClient) { // Insert a document. The stub updates "conn" directly. coll.insert({ _id: 'foo', bar: 42 }, _.identity); test.equal(coll.find().count(), 1); - test.equal(coll.findOne(), { _id: 'foo', bar: 42 }); + test.equal(await coll.findOneAsync(), { _id: 'foo', bar: 42 }); // It also sends the method message. let methodMessage = JSON.parse(stream.sent.shift()); test.isUndefined(methodMessage.randomSeed); @@ -2286,33 +2286,33 @@ if (Meteor.isClient) { // Now receive a connected message. This should not clear the // _documentsWrittenByStub state! - stream.receive({ msg: 'connected', session: SESSION_ID }); + await stream.receive({ msg: 'connected', session: SESSION_ID }); test.length(stream.sent, 0); test.equal(coll.find().count(), 1); // Now receive the "updated" message for the method. This should revert the // insert. - stream.receive({ msg: 'updated', methods: [methodMessage.id] }); + await stream.receive({ msg: 'updated', methods: [methodMessage.id] }); test.length(stream.sent, 0); test.equal(coll.find().count(), 0); }); } if (Meteor.isClient) { - Tinytest.add( + Tinytest.addAsync ( 'livedata stub - method call between reset and quiescence', - function(test) { + async function(test) { const stream = new StubStream(); const conn = newConnection(stream); - startAndConnect(test, stream); + await startAndConnect(test, stream); const collName = Random.id(); const coll = new Mongo.Collection(collName, { connection: conn }); conn.methods({ - update_value: function() { - coll.update('aaa', { value: 222 }); + update_value: async function() { + await coll.updateAsync('aaa', { value: 222 }); } }); @@ -2336,23 +2336,23 @@ if (Meteor.isClient) { const subReadyMessage = { msg: 'ready', subs: [subMessage.id] }; - stream.receive(subDocMessage); - stream.receive(subReadyMessage); - test.isTrue(coll.findOne('aaa').value == 111); + await stream.receive(subDocMessage); + await stream.receive(subReadyMessage); + test.isTrue((await coll.findOneAsync('aaa')).value == 111); // Initiate reconnect. - stream.reset(); + await stream.reset(); testGotMessage(test, stream, makeConnectMessage(SESSION_ID)); testGotMessage(test, stream, subMessage); - stream.receive({ msg: 'connected', session: SESSION_ID + 1 }); + await stream.receive({ msg: 'connected', session: SESSION_ID + 1 }); // Now in reconnect, can still see the document. - test.isTrue(coll.findOne('aaa').value == 111); + test.isTrue((await coll.findOneAsync('aaa')).value == 111); - conn.call('update_value'); + await conn.callAsync('update_value'); // Observe the stub-written value. - test.isTrue(coll.findOne('aaa').value == 222); + test.isTrue((await coll.findOneAsync('aaa')).value == 222); let methodMessage = JSON.parse(stream.sent.shift()); test.equal(methodMessage, { @@ -2363,29 +2363,29 @@ if (Meteor.isClient) { }); test.length(stream.sent, 0); - stream.receive(subDocMessage); - stream.receive(subReadyMessage); + await stream.receive(subDocMessage); + await stream.receive(subReadyMessage); // By this point quiescence is reached and stores have been reset. // The stub-written value is still there. - test.isTrue(coll.findOne('aaa').value == 222); + test.isTrue((await coll.findOneAsync('aaa')).value == 222); - stream.receive({ + await stream.receive({ msg: 'changed', collection: collName, id: 'aaa', fields: { value: 333 } }); - stream.receive({ msg: 'updated', methods: [methodMessage.id] }); - stream.receive({ msg: 'result', id: methodMessage.id, result: null }); + await stream.receive({ msg: 'updated', methods: [methodMessage.id] }); + await stream.receive({ msg: 'result', id: methodMessage.id, result: null }); // Server wrote a different value, make sure it's visible now. - test.isTrue(coll.findOne('aaa').value == 333); + test.isTrue((await coll.findOneAsync('aaa')).value == 333); } ); - Tinytest.add('livedata stub - buffering and methods interaction', function( + Tinytest.addAsync('livedata stub - buffering and methods interaction', async function( test ) { const stream = new StubStream(); @@ -2395,14 +2395,14 @@ if (Meteor.isClient) { bufferedWritesMaxAge: 10000 }); - startAndConnect(test, stream); + await startAndConnect(test, stream); const collName = Random.id(); const coll = new Mongo.Collection(collName, { connection: conn }); conn.methods({ - update_value: function() { - const value = coll.findOne('aaa').subscription; + update_value: async function() { + const value = (await coll.findOneAsync('aaa')).subscription; // Method should have access to the latest value of the collection. coll.update('aaa', { $set: { method: value + 110 } }); } @@ -2428,9 +2428,9 @@ if (Meteor.isClient) { const subReadyMessage = { msg: 'ready', subs: [subMessage.id] }; - stream.receive(subDocMessage); - stream.receive(subReadyMessage); - test.equal(coll.findOne('aaa').subscription, 111); + await stream.receive(subDocMessage); + await stream.receive(subReadyMessage); + test.equal((await coll.findOneAsync('aaa')).subscription, 111); const subDocChangeMessage = { msg: 'changed', @@ -2439,18 +2439,18 @@ if (Meteor.isClient) { fields: { subscription: 112 } }; - stream.receive(subDocChangeMessage); + await stream.receive(subDocChangeMessage); // Still 111 because buffer has not been flushed. - test.equal(coll.findOne('aaa').subscription, 111); + test.equal((await coll.findOneAsync('aaa')).subscription, 111); // Call updates the stub. - conn.call('update_value'); + await conn.callAsync('update_value'); // Observe the stub-written value. - test.equal(coll.findOne('aaa').method, 222); + test.equal((await coll.findOneAsync('aaa')).method, 222); // subscription field is updated to the latest value // because of the method call. - test.equal(coll.findOne('aaa').subscription, 112); + test.equal((await coll.findOneAsync('aaa')).subscription, 112); let methodMessage = JSON.parse(stream.sent.shift()); test.equal(methodMessage, { @@ -2464,23 +2464,23 @@ if (Meteor.isClient) { // "Server-side" change from the method arrives and method returns. // With potentially fixed value for method field, if stub didn't // use 112 as the subscription field value. - stream.receive({ + await stream.receive({ msg: 'changed', collection: collName, id: 'aaa', fields: { method: 222 } }); - stream.receive({ msg: 'updated', methods: [methodMessage.id] }); - stream.receive({ msg: 'result', id: methodMessage.id, result: null }); + await stream.receive({ msg: 'updated', methods: [methodMessage.id] }); + await stream.receive({ msg: 'result', id: methodMessage.id, result: null }); - test.equal(coll.findOne('aaa').method, 222); - test.equal(coll.findOne('aaa').subscription, 112); + test.equal((await coll.findOneAsync('aaa')).method, 222); + test.equal((await coll.findOneAsync('aaa')).subscription, 112); // Buffer should already be flushed because of a non-update message. // And after a flush we really want subscription field to be 112. conn._flushBufferedWrites(); - test.equal(coll.findOne('aaa').method, 222); - test.equal(coll.findOne('aaa').subscription, 112); + test.equal((await coll.findOneAsync('aaa')).method, 222); + test.equal((await coll.findOneAsync('aaa')).subscription, 112); }); } diff --git a/packages/ddp-client/test/livedata_test_service.js b/packages/ddp-client/test/livedata_test_service.js index db32cf3cc4..57c47b1e8d 100644 --- a/packages/ddp-client/test/livedata_test_service.js +++ b/packages/ddp-client/test/livedata_test_service.js @@ -59,28 +59,29 @@ if (Meteor.isServer) { // other. const waiters = Object.create(null); - const Future = Npm.require('fibers/future'); - const returnThroughFuture = function(token, returnValue) { // Make sure that when we call return, the fields are already cleared. const record = waiters[token]; if (!record) return; delete waiters[token]; - record.future['return'](returnValue); + record.future(returnValue); }; Meteor.methods({ delayedTrue: function(token) { check(token, String); - const record = (waiters[token] = { - future: new Future(), + + let resolver; + const promise = new Promise(res => resolver = res); + waiters[token] = { + future: resolver, timer: Meteor.setTimeout(function() { returnThroughFuture(token, true); }, 1000) - }); + }; this.unblock(); - return record.future.wait(); + return promise; }, makeDelayedTrueImmediatelyReturnFalse: function(token) { check(token, String); @@ -108,8 +109,8 @@ Ledger.allow({ fetch: [] }); -Meteor.startup(function() { - if (Meteor.isServer) Ledger.remove({}); // XXX can this please be Ledger.remove()? +Meteor.startup(async function() { + if (Meteor.isServer) await Ledger.removeAsync({}); }); if (Meteor.isServer) @@ -119,14 +120,14 @@ if (Meteor.isServer) }); Meteor.methods({ - 'ledger/transfer': function(world, from_name, to_name, amount, cheat) { + 'ledger/transfer': async function(world, from_name, to_name, amount, cheat) { check(world, String); check(from_name, String); check(to_name, String); check(amount, Number); check(cheat, Match.Optional(Boolean)); - const from = Ledger.findOne({ name: from_name, world: world }); - const to = Ledger.findOne({ name: to_name, world: world }); + const from = await Ledger.findOneAsync({ name: from_name, world: world }); + const to = await Ledger.findOneAsync({ name: to_name, world: world }); if (Meteor.isServer) cheat = false; @@ -145,8 +146,8 @@ Meteor.methods({ if (from.balance < amount && !cheat) throw new Meteor.Error(409, 'Insufficient funds'); - Ledger.update(from._id, { $inc: { balance: -amount } }); - Ledger.update(to._id, { $inc: { balance: amount } }); + await Ledger.updateAsync({_id: from._id}, { $inc: { balance: -amount } }); + await Ledger.updateAsync({_id: to._id, }, { $inc: { balance: amount } }); } }); @@ -156,56 +157,57 @@ Meteor.methods({ objectsWithUsers = new Mongo.Collection('objectsWithUsers'); -if (Meteor.isServer) { - objectsWithUsers.remove({}); - objectsWithUsers.insert({ name: 'owned by none', ownerUserIds: [null] }); - objectsWithUsers.insert({ name: 'owned by one - a', ownerUserIds: ['1'] }); - objectsWithUsers.insert({ - name: 'owned by one/two - a', - ownerUserIds: ['1', '2'] - }); - objectsWithUsers.insert({ - name: 'owned by one/two - b', - ownerUserIds: ['1', '2'] - }); - objectsWithUsers.insert({ name: 'owned by two - a', ownerUserIds: ['2'] }); - objectsWithUsers.insert({ name: 'owned by two - b', ownerUserIds: ['2'] }); +Meteor.startup(async function() { + if (Meteor.isServer) { + await objectsWithUsers.removeAsync({}); + await objectsWithUsers.insertAsync({name: 'owned by none', ownerUserIds: [null]}); + await objectsWithUsers.insertAsync({name: 'owned by one - a', ownerUserIds: ['1']}); + await objectsWithUsers.insertAsync({ + name: 'owned by one/two - a', + ownerUserIds: ['1', '2'] + }); + await objectsWithUsers.insertAsync({ + name: 'owned by one/two - b', + ownerUserIds: ['1', '2'] + }); + await objectsWithUsers.insertAsync({name: 'owned by two - a', ownerUserIds: ['2']}); + await objectsWithUsers.insertAsync({name: 'owned by two - b', ownerUserIds: ['2']}); - Meteor.publish('objectsWithUsers', function() { - return objectsWithUsers.find( - { ownerUserIds: this.userId }, - { fields: { ownerUserIds: 0 } } - ); - }); - - (function() { - const userIdWhenStopped = Object.create(null); - Meteor.publish('recordUserIdOnStop', function(key) { - check(key, String); - const self = this; - self.onStop(function() { - userIdWhenStopped[key] = self.userId; - }); + Meteor.publish('objectsWithUsers', function () { + return objectsWithUsers.find( + {ownerUserIds: this.userId}, + {fields: {ownerUserIds: 0}} + ); }); - Meteor.methods({ - userIdWhenStopped: function(key) { + (function () { + const userIdWhenStopped = Object.create(null); + Meteor.publish('recordUserIdOnStop', function (key) { check(key, String); - return userIdWhenStopped[key]; - } - }); - })(); -} + const self = this; + self.onStop(function () { + userIdWhenStopped[key] = self.userId; + }); + }); + Meteor.methods({ + userIdWhenStopped: function (key) { + check(key, String); + return userIdWhenStopped[key]; + } + }); + })(); + } +}); /*****/ /// Helper for "livedata - setUserId fails when called on server" if (Meteor.isServer) { - Meteor.startup(function() { + Meteor.startup(async function() { errorThrownWhenCallingSetUserIdDirectlyOnServer = null; try { - Meteor.call('setUserId', '1000'); + await Meteor.callAsync('setUserId', '1000'); } catch (e) { errorThrownWhenCallingSetUserIdDirectlyOnServer = e; } @@ -331,36 +333,38 @@ if (Meteor.isServer) { One = new Mongo.Collection('collectionOne'); Two = new Mongo.Collection('collectionTwo'); -if (Meteor.isServer) { - One.remove({}); - One.insert({ name: 'value1' }); - One.insert({ name: 'value2' }); +Meteor.startup(async () => { + if (Meteor.isServer) { + await One.removeAsync({}); + await One.insertAsync({ name: 'value1' }); + await One.insertAsync({ name: 'value2' }); - Two.remove({}); - Two.insert({ name: 'value3' }); - Two.insert({ name: 'value4' }); - Two.insert({ name: 'value5' }); + await Two.removeAsync({}); + await Two.insertAsync({ name: 'value3' }); + await Two.insertAsync({ name: 'value4' }); + await Two.insertAsync({ name: 'value5' }); - Meteor.publish('multiPublish', function(options) { - // See below to see what options are accepted. - check(options, Object); - if (options.normal) { - return [One.find(), Two.find()]; - } else if (options.dup) { - // Suppress the log of the expected internal error. - Meteor._suppress_log(1); - return [ - One.find(), - One.find({ name: 'value2' }), // multiple cursors for one collection - error - Two.find() - ]; - } else if (options.notCursor) { - // Suppress the log of the expected internal error. - Meteor._suppress_log(1); - return [One.find(), 'not a cursor', Two.find()]; - } else throw 'unexpected options'; - }); -} + Meteor.publish('multiPublish', function(options) { + // See below to see what options are accepted. + check(options, Object); + if (options.normal) { + return [One.find(), Two.find()]; + } else if (options.dup) { + // Suppress the log of the expected internal error. + Meteor._suppress_log(1); + return [ + One.find(), + One.find({ name: 'value2' }), // multiple cursors for one collection - error + Two.find(), + ]; + } else if (options.notCursor) { + // Suppress the log of the expected internal error. + Meteor._suppress_log(1); + return [One.find(), 'not a cursor', Two.find()]; + } else throw 'unexpected options'; + }); + } +}); /// Helper for "livedata - result by value" const resultByValueArrays = Object.create(null); diff --git a/packages/ddp-client/test/livedata_tests.js b/packages/ddp-client/test/livedata_tests.js index a5fae51937..3c181410eb 100644 --- a/packages/ddp-client/test/livedata_tests.js +++ b/packages/ddp-client/test/livedata_tests.js @@ -1,6 +1,24 @@ import { DDP } from '../common/namespace.js'; import { Connection } from '../common/livedata_connection.js'; +const _sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); + +const callWhenSubReady = async (subName, handle, cb = () => {}) => { + let control = 0; + + while (!handle.ready()) { + if (!handle.ready()) { + // Just in case something happens with the subscription, we have this control + if (control++ === 1000) { + throw new Error(`Subscribe to ${subName} is taking too long!`); + } + await _sleep(0); + return; + } + await cb(); + } +}; + // XXX should check error codes const failure = function(test, code, reason) { return function(error, result) { @@ -79,10 +97,10 @@ Tinytest.add('livedata - non-function method', function(test) { }); const echoTest = function(item) { - return function(test, expect) { + return async function(test, expect) { if (Meteor.isServer) { - test.equal(Meteor.call('echo', item), [item]); - test.equal(Meteor.call('echoOne', item), item); + test.equal(await Meteor.callAsync('echo', item), [item]); + test.equal(await Meteor.callAsync('echoOne', item), item); } if (Meteor.isClient) test.equal(Meteor.call('echo', item), undefined); @@ -96,13 +114,13 @@ const echoTest = function(item) { testAsyncMulti('livedata - basic method invocation', [ // Unknown methods - function(test, expect) { + async function(test, expect) { if (Meteor.isServer) { // On server, with no callback, throws exception let ret; let threw; try { - ret = Meteor.call('unknown method'); + ret = await Meteor.callAsync('unknown method'); } catch (e) { test.equal(e.error, 404); threw = true; @@ -125,18 +143,19 @@ testAsyncMulti('livedata - basic method invocation', [ test.equal(ret, undefined); }, - function(test, expect) { + async function(test, expect) { // make sure 'undefined' is preserved as such, instead of turning // into null (JSON does not have 'undefined' so there is special // code for this) - if (Meteor.isServer) test.equal(Meteor.call('nothing'), undefined); + if (Meteor.isServer) + test.equal(await Meteor.callAsync('nothing'), undefined); if (Meteor.isClient) test.equal(Meteor.call('nothing'), undefined); test.equal(Meteor.call('nothing', expect(undefined, undefined)), undefined); }, - function(test, expect) { - if (Meteor.isServer) test.equal(Meteor.call('echo'), []); + async function(test, expect) { + if (Meteor.isServer) test.equal(await Meteor.callAsync('echo'), []); if (Meteor.isClient) test.equal(Meteor.call('echo'), undefined); test.equal(Meteor.call('echo', expect(undefined, [])), undefined); @@ -153,9 +172,12 @@ testAsyncMulti('livedata - basic method invocation', [ echoTest(Infinity), echoTest(-Infinity), - function(test, expect) { + async function(test, expect) { if (Meteor.isServer) - test.equal(Meteor.call('echo', 12, { x: 13 }), [12, { x: 13 }]); + test.equal(await Meteor.callAsync('echo', 12, { x: 13 }), [ + 12, + { x: 13 }, + ]); if (Meteor.isClient) test.equal(Meteor.call('echo', 12, { x: 13 }), undefined); @@ -198,7 +220,7 @@ testAsyncMulti('livedata - basic method invocation', [ } }, - function(test, expect) { + async function(test, expect) { // No callback if (Meteor.isServer) { @@ -209,7 +231,7 @@ testAsyncMulti('livedata - basic method invocation', [ Meteor.call('exception', 'server'); }); // No exception, because no code will run on the client - test.equal(Meteor.call('exception', 'client'), undefined); + test.equal(await Meteor.callAsync('exception', 'client'), undefined); } if (Meteor.isClient) { @@ -273,15 +295,15 @@ testAsyncMulti('livedata - basic method invocation', [ ), undefined ); - test.equal(Meteor.call('exception', 'client'), undefined); + test.equal(await Meteor.callAsync('exception', 'client'), undefined); } }, - function(test, expect) { + async function(test, expect) { if (Meteor.isServer) { let threw = false; try { - Meteor.call('exception', 'both', { intended: true }); + await Meteor.callAsync('exception', 'both', { intended: true }); } catch (e) { threw = true; test.equal(e.error, 999); @@ -290,9 +312,9 @@ testAsyncMulti('livedata - basic method invocation', [ test.isTrue(threw); threw = false; try { - Meteor.call('exception', 'both', { + await Meteor.callAsync('exception', 'both', { intended: true, - throwThroughFuture: true + throwThroughFuture: true, }); } catch (e) { threw = true; @@ -327,74 +349,81 @@ testAsyncMulti('livedata - basic method invocation', [ 'server', { intended: true, - throwThroughFuture: true + throwThroughFuture: true, }, expect(failure(test, 999, 'Client-visible test exception')) ), undefined ); } - } + }, ]); -const checkBalances = function(test, a, b) { - const alice = Ledger.findOne({ name: 'alice', world: test.runId() }); - const bob = Ledger.findOne({ name: 'bob', world: test.runId() }); +const checkBalances = async function(test, a, b) { + const alice = await Ledger.findOneAsync({ + name: 'alice', + world: test.runId(), + }); + const bob = await Ledger.findOneAsync({ name: 'bob', world: test.runId() }); + test.equal(alice.balance, a); test.equal(bob.balance, b); }; +const subscribeBeforeRun = async (subName, testId, cb) => { + if (Meteor.isClient) { + const handle = Meteor.subscribe(subName, testId); + await callWhenSubReady(subName, handle); + handle.stop(); + } + await cb(); +}; + // would be nice to have a database-aware test harness of some kind -- // this is a big hack (and XXX pollutes the global test namespace) testAsyncMulti('livedata - compound methods', [ - function(test, expect) { - if (Meteor.isClient) Meteor.subscribe('ledger', test.runId(), expect()); - - Ledger.insert( - { name: 'alice', balance: 100, world: test.runId() }, - expect(function() {}) - ); - Ledger.insert( - { name: 'bob', balance: 50, world: test.runId() }, - expect(function() {}) - ); + async function(test) { + await Ledger.insertAsync({ + name: 'alice', + balance: 100, + world: test.runId(), + }); + await Ledger.insertAsync({ name: 'bob', balance: 50, world: test.runId() }); }, - function(test, expect) { - Meteor.call( - 'ledger/transfer', - test.runId(), - 'alice', - 'bob', - 10, - expect(function(err, result) { - test.equal(err, undefined); - test.equal(result, undefined); - checkBalances(test, 90, 60); - }) - ); - checkBalances(test, 90, 60); + async function(test) { + await subscribeBeforeRun('ledger', test.runId(), async () => { + await Meteor.callAsync( + 'ledger/transfer', + test.runId(), + 'alice', + 'bob', + 10 + ); + await checkBalances(test, 90, 60); + }); }, - function(test, expect) { - Meteor.call( - 'ledger/transfer', - test.runId(), - 'alice', - 'bob', - 100, - true, - expect(function(err, result) { - failure(test, 409)(err, result); - // Balances are reverted back to pre-stub values. - checkBalances(test, 90, 60); - }) - ); + async function(test) { + await subscribeBeforeRun('ledger', test.runId(), async () => { + try { + await Meteor.callAsync( + 'ledger/transfer', + test.runId(), + 'alice', + 'bob', + 100, + true + ); + } catch (e) {} - if (Meteor.isClient) - // client can fool itself by cheating, but only until the sync - // finishes - checkBalances(test, -10, 160); - else checkBalances(test, 90, 60); - } + if (Meteor.isClient) { + // client can fool itself by cheating, but only until the sync + // finishes + await checkBalances(test, -10, 160); + } else { + await checkBalances(test, 90, 60); + } + }); + }, ]); // Replaces the Connection's `_livedata_data` method to push incoming @@ -435,7 +464,7 @@ if (Meteor.isClient) { testAsyncMulti( 'livedata - changing userid reruns subscriptions without flapping data on the wire', [ - function(test, expect) { + async function(test, expect) { const messages = []; const undoEavesdrop = eavesdropOnCollection( Meteor.connection, @@ -484,53 +513,53 @@ if (Meteor.isClient) { let afterSecondSetUserId; let afterThirdSetUserId; - Meteor.subscribe( - 'objectsWithUsers', - expect(function() { - expectMessages(1, 0, ['owned by none']); + const handle = Meteor.subscribe('objectsWithUsers'); + + // Just make sure the subscription is ready before running the tests + // As everything now runs async, the tests were running before the data fully came in + await callWhenSubReady('objectsWithUsers', handle, () => { + expectMessages(1, 0, ['owned by none']); + Meteor.apply('setUserId', ['1'], { wait: true }, afterFirstSetUserId); + afterFirstSetUserId = expect(function() { + expectMessages(3, 1, [ + 'owned by one - a', + 'owned by one/two - a', + 'owned by one/two - b', + ]); Meteor.apply( 'setUserId', - ['1'], + ['2'], { wait: true }, - afterFirstSetUserId + afterSecondSetUserId ); - }) - ); + }); - afterFirstSetUserId = expect(function() { - expectMessages(3, 1, [ - 'owned by one - a', - 'owned by one/two - a', - 'owned by one/two - b' - ]); - Meteor.apply( - 'setUserId', - ['2'], - { wait: true }, - afterSecondSetUserId - ); - }); + afterSecondSetUserId = expect(function() { + expectMessages(2, 1, [ + 'owned by one/two - a', + 'owned by one/two - b', + 'owned by two - a', + 'owned by two - b', + ]); + Meteor.apply( + 'setUserId', + ['2'], + { wait: true }, + afterThirdSetUserId + ); + }); - afterSecondSetUserId = expect(function() { - expectMessages(2, 1, [ - 'owned by one/two - a', - 'owned by one/two - b', - 'owned by two - a', - 'owned by two - b' - ]); - Meteor.apply('setUserId', ['2'], { wait: true }, afterThirdSetUserId); - }); - - afterThirdSetUserId = expect(function() { - // Nothing should have been sent since the results of the - // query are the same ("don't flap data on the wire") - expectMessages(0, 0, [ - 'owned by one/two - a', - 'owned by one/two - b', - 'owned by two - a', - 'owned by two - b' - ]); - undoEavesdrop(); + afterThirdSetUserId = expect(function() { + // Nothing should have been sent since the results of the + // query are the same ("don't flap data on the wire") + expectMessages(0, 0, [ + 'owned by one/two - a', + 'owned by one/two - b', + 'owned by two - a', + 'owned by two - b', + ]); + undoEavesdrop(); + }); }); }, function(test, expect) { @@ -563,7 +592,7 @@ if (Meteor.isClient) { { wait: true }, expect(function() {}) ); - } + }, ] ); } @@ -614,7 +643,7 @@ Meteor.methods({ return 2; } return 0; - } + }, }); if (Meteor.isClient) { @@ -623,12 +652,22 @@ if (Meteor.isClient) { const id = Random.id(); testAsyncMulti('livedata - added from two different subs', [ function(test, expect) { - Meteor.call('livedata/setup', id, expect(function() {})); + Meteor.call( + 'livedata/setup', + id, + expect(function() {}) + ); }, function(test, expect) { MultiPub = new Mongo.Collection('MultiPubCollection' + id); - const sub1 = Meteor.subscribe('pub1' + id, expect(function() {})); - const sub2 = Meteor.subscribe('pub2' + id, expect(function() {})); + const sub1 = Meteor.subscribe( + 'pub1' + id, + expect(function() {}) + ); + const sub2 = Meteor.subscribe( + 'pub2' + id, + expect(function() {}) + ); }, function(test, expect) { Meteor.call( @@ -653,7 +692,7 @@ if (Meteor.isClient) { }, function(test, expect) { test.equal(MultiPub.findOne('foo'), { _id: 'foo', a: 'aa', b: 'bb' }); - } + }, ]); })(); } @@ -672,7 +711,7 @@ if (Meteor.isClient) { test.isTrue(coll.findOne(token)); }) ); - } + }, ]); testAsyncMulti('livedata - runtime universal sub creation', [ @@ -688,7 +727,7 @@ if (Meteor.isClient) { test.isTrue(coll.findOne(token)); }) ); - } + }, ]); testAsyncMulti('livedata - no setUserId after unblock', [ @@ -700,7 +739,7 @@ if (Meteor.isClient) { test.isTrue(result); }) ); - } + }, ]); testAsyncMulti( @@ -714,7 +753,7 @@ if (Meteor.isClient) { // Use a separate connection so that we can safely check to see if // conn._subscriptions is empty. conn = new Connection('/', { - reloadWithOutstanding: true + reloadWithOutstanding: true, }); collName = Random.id(); coll = new Mongo.Collection(collName, { connection: conn }); @@ -730,7 +769,7 @@ if (Meteor.isClient) { ? 'Internal server error' : 'Explicit error' ) - ) + ), }); }; testSubError({ throwInHandler: true }); @@ -752,7 +791,7 @@ if (Meteor.isClient) { onReady: expect(), onError: function(error) { errorFromRerun = error; - } + }, } ); }, @@ -761,7 +800,11 @@ if (Meteor.isClient) { test.equal(coll.find().count(), 1); test.isFalse(errorFromRerun); test.equal(_.size(conn._subscriptions), 1); // white-box test - conn.call('setUserId', 'bla', expect(function() {})); + conn.call( + 'setUserId', + 'bla', + expect(function() {}) + ); }, function(test, expect) { // Now that we've re-run, we should have stopped the subscription, @@ -780,13 +823,16 @@ if (Meteor.isClient) { { onError: function() { gotErrorFromStopper = true; - } + }, } ); // Call a method. This method won't be processed until the publisher's // function returns, so blocking on it being done ensures that we've // gotten the removed/nosub/etc. - conn.call('nothing', expect(function() {})); + conn.call( + 'nothing', + expect(function() {}) + ); }, function(test, expect) { test.equal(coll.find().count(), 0); @@ -794,7 +840,7 @@ if (Meteor.isClient) { test.isFalse(gotErrorFromStopper); test.equal(_.size(conn._subscriptions), 0); // white-box test conn._stream.disconnect({ _permanent: true }); - } + }, ]; })() ); @@ -810,7 +856,7 @@ if (Meteor.isClient) { // Use a separate connection so that we can safely check to see if // conn._subscriptions is empty. conn = new Connection('/', { - reloadWithOutstanding: true + reloadWithOutstanding: true, }); collName = Random.id(); coll = new Mongo.Collection(collName, { connection: conn }); @@ -826,7 +872,7 @@ if (Meteor.isClient) { ? 'Internal server error' : 'Explicit error' ) - ) + ), }); }; testSubError({ throwInHandler: true }); @@ -848,7 +894,7 @@ if (Meteor.isClient) { onReady: expect(), onStop: function(error) { errorFromRerun = error; - } + }, } ); }, @@ -857,7 +903,11 @@ if (Meteor.isClient) { test.equal(coll.find().count(), 1); test.isFalse(errorFromRerun); test.equal(_.size(conn._subscriptions), 1); // white-box test - conn.call('setUserId', 'bla', expect(function() {})); + conn.call( + 'setUserId', + 'bla', + expect(function() {}) + ); }, function(test, expect) { // Now that we've re-run, we should have stopped the subscription, @@ -878,13 +928,16 @@ if (Meteor.isClient) { if (error) { gotErrorFromStopper = true; } - } + }, } ); // Call a method. This method won't be processed until the publisher's // function returns, so blocking on it being done ensures that we've // gotten the removed/nosub/etc. - conn.call('nothing', expect(function() {})); + conn.call( + 'nothing', + expect(function() {}) + ); }, function(test, expect) { test.equal(coll.find().count(), 0); @@ -892,7 +945,7 @@ if (Meteor.isClient) { test.isFalse(gotErrorFromStopper); test.equal(_.size(conn._subscriptions), 0); // white-box test conn._stream.disconnect({ _permanent: true }); - } + }, ]; })() ); @@ -908,7 +961,7 @@ if (Meteor.isClient) { test.equal(One.find().count(), 2); test.equal(Two.find().count(), 3); }), - onError: failure() + onError: failure(), } ); }, @@ -918,7 +971,7 @@ if (Meteor.isClient) { { dup: 1 }, { onReady: failure(), - onError: expect(failure(test, 500, 'Internal server error')) + onError: expect(failure(test, 500, 'Internal server error')), } ); }, @@ -928,10 +981,10 @@ if (Meteor.isClient) { { notCursor: 1 }, { onReady: failure(), - onError: expect(failure(test, 500, 'Internal server error')) + onError: expect(failure(test, 500, 'Internal server error')), } ); - } + }, ]); } @@ -944,7 +997,7 @@ if (Meteor.isServer) { s2s: function(arg) { check(arg, String); return 's2s ' + arg; - } + }, }); } (function() { @@ -973,7 +1026,7 @@ if (Meteor.isServer) { }) ); } - } + }, ]); })(); @@ -992,12 +1045,12 @@ if (Meteor.isServer) { ); }, - function(test, expect) { + async function(test, expect) { const self = this; if (self.conn.status().connected) { - test.equal(self.conn.call('s2s', 'foo'), 's2s foo'); + test.equal(await self.conn.callAsync('s2s', 'foo'), 's2s foo'); } - } + }, ]); })(); } @@ -1014,7 +1067,7 @@ if (Meteor.isServer) { }), 500 ); - } + }, ]); })(); @@ -1038,13 +1091,13 @@ if (Meteor.isServer) { onReady: expect(function() { test.equal(PublisherCloningCollection.findOne(), { _id: 'a', - x: { y: 43 } + x: { y: 43 }, }); }), - onError: failure() + onError: failure(), } ); - } + }, ]); } @@ -1083,7 +1136,7 @@ testAsyncMulti('livedata - result by value', [ test.equal(self.firstResult.length + 1, secondResult.length); }) ); - } + }, ]); // XXX some things to test in greater detail: diff --git a/packages/ddp-client/test/random_stream_tests.js b/packages/ddp-client/test/random_stream_tests.js index 03b1ce05ec..b2a44281fe 100644 --- a/packages/ddp-client/test/random_stream_tests.js +++ b/packages/ddp-client/test/random_stream_tests.js @@ -1,8 +1,8 @@ -Tinytest.add('livedata - DDP.randomStream', function(test) { +Tinytest.addAsync('livedata - DDP.randomStream', async function(test) { const randomSeed = Random.id(); const context = { randomSeed: randomSeed }; - let sequence = DDP._CurrentMethodInvocation.withValue(context, function() { + let sequence = await DDP._CurrentMethodInvocation.withValue(context, function() { return DDP.randomStream('1'); }); @@ -21,7 +21,7 @@ Tinytest.add('livedata - DDP.randomStream', function(test) { test.equal(id1, id1Cloned); // We should get the same sequence when we use the same key - sequence = DDP._CurrentMethodInvocation.withValue(context, function() { + sequence = await DDP._CurrentMethodInvocation.withValue(context, function() { return DDP.randomStream('1'); }); seeds = sequence.alea.args; diff --git a/packages/ddp-client/test/stub_stream.js b/packages/ddp-client/test/stub_stream.js index 1b0186a348..d65990055d 100644 --- a/packages/ddp-client/test/stub_stream.js +++ b/packages/ddp-client/test/stub_stream.js @@ -32,23 +32,23 @@ _.extend(StubStream.prototype, { }, // Methods for tests - receive: function(data) { + receive: async function(data) { const self = this; if (typeof data === 'object') { data = EJSON.stringify(data); } - _.each(self.callbacks['message'], function(cb) { - cb(data); - }); + for (const cb of self.callbacks['message']) { + await cb(data); + } }, - reset: function() { + reset: async function() { const self = this; - _.each(self.callbacks['reset'], function(cb) { - cb(); - }); + for (const cb of self.callbacks['reset']) { + await cb(); + } }, // Provide a tag to detect stub streams. diff --git a/packages/ddp-rate-limiter/ddp-rate-limiter.d.ts b/packages/ddp-rate-limiter/ddp-rate-limiter.d.ts new file mode 100644 index 0000000000..fbce221f5a --- /dev/null +++ b/packages/ddp-rate-limiter/ddp-rate-limiter.d.ts @@ -0,0 +1,17 @@ +export namespace DDPRateLimiter { + interface Matcher { + type?: string | ((type: string) => boolean) | undefined; + name?: string | ((name: string) => boolean) | undefined; + userId?: string | ((userId: string) => boolean) | undefined; + connectionId?: string | ((connectionId: string) => boolean) | undefined; + clientAddress?: string | ((clientAddress: string) => boolean) | undefined; + } + + function addRule( + matcher: Matcher, + numRequests: number, + timeInterval: number + ): string; + + function removeRule(ruleId: string): boolean; +} diff --git a/packages/ddp-rate-limiter/package-types.json b/packages/ddp-rate-limiter/package-types.json new file mode 100644 index 0000000000..34869b7735 --- /dev/null +++ b/packages/ddp-rate-limiter/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "ddp-rate-limiter.d.ts" +} diff --git a/packages/ddp-rate-limiter/package.js b/packages/ddp-rate-limiter/package.js index a38d55936e..c39e26c462 100644 --- a/packages/ddp-rate-limiter/package.js +++ b/packages/ddp-rate-limiter/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'ddp-rate-limiter', - version: '1.1.0', + version: '1.1.1', // Brief, one-line summary of the package. summary: 'The DDPRateLimiter allows users to add rate limits to DDP' + ' methods and subscriptions.', @@ -14,6 +14,7 @@ Package.describe({ Package.onUse(function(api) { api.use('rate-limit', 'server'); api.use('ecmascript'); + api.addAssets('ddp-rate-limiter.d.ts', 'server'); api.export('DDPRateLimiter', 'server'); api.mainModule('ddp-rate-limiter.js', 'server'); }); diff --git a/packages/ddp-server-async/.npm/package/.gitignore b/packages/ddp-server-async/.npm/package/.gitignore deleted file mode 100644 index 3c3629e647..0000000000 --- a/packages/ddp-server-async/.npm/package/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/packages/ddp-server-async/.npm/package/README b/packages/ddp-server-async/.npm/package/README deleted file mode 100644 index 3d492553a4..0000000000 --- a/packages/ddp-server-async/.npm/package/README +++ /dev/null @@ -1,7 +0,0 @@ -This directory and the files immediately inside it are automatically generated -when you change this package's NPM dependencies. Commit the files in this -directory (npm-shrinkwrap.json, .gitignore, and this README) to source control -so that others run the same versions of sub-dependencies. - -You should NOT check in the node_modules directory that Meteor automatically -creates; if you are using git, the .gitignore file tells git to ignore it. diff --git a/packages/ddp-server-async/.npm/package/npm-shrinkwrap.json b/packages/ddp-server-async/.npm/package/npm-shrinkwrap.json deleted file mode 100644 index b9b024674c..0000000000 --- a/packages/ddp-server-async/.npm/package/npm-shrinkwrap.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "lockfileVersion": 1, - "dependencies": { - "faye-websocket": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", - "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==" - }, - "http-parser-js": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", - "integrity": "sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg==" - }, - "permessage-deflate": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/permessage-deflate/-/permessage-deflate-0.1.7.tgz", - "integrity": "sha512-EUNi/RIsyJ1P1u9QHFwMOUWMYetqlE22ZgGbad7YP856WF4BFF0B7DuNy6vEGsgNNud6c/SkdWzkne71hH8MjA==" - }, - "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==" - }, - "sockjs": { - "version": "0.3.21", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.21.tgz", - "integrity": "sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==" - }, - "websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" - } - } -} diff --git a/packages/ddp-server-async/writefence.js b/packages/ddp-server-async/writefence.js deleted file mode 100644 index d85f028ff8..0000000000 --- a/packages/ddp-server-async/writefence.js +++ /dev/null @@ -1,125 +0,0 @@ -// A write fence collects a group of writes, and provides a callback -// when all of the writes are fully committed and propagated (all -// observers have been notified of the write and acknowledged it.) -// -DDPServer._WriteFence = class { - constructor() { - this.armed = false; - this.fired = false; - this.retired = false; - this.outstanding_writes = 0; - this.before_fire_callbacks = []; - this.completion_callbacks = []; - } - - // Start tracking a write, and return an object to represent it. The - // object has a single method, committed(). This method should be - // called when the write is fully committed and propagated. You can - // continue to add writes to the WriteFence up until it is triggered - // (calls its callbacks because all writes have committed.) - beginWrite() { - if (this.retired) - return { committed: function () {} }; - - if (this.fired) - throw new Error("fence has already activated -- too late to add writes"); - - this.outstanding_writes++; - let committed = false; - const _committedFn = async () => { - if (committed) - throw new Error("committed called twice on the same write"); - committed = true; - this.outstanding_writes--; - await this._maybeFire(); - }; - - const self = this; - return { - committed: Meteor._isFibersEnabled ? () => Promise.await(_committedFn.apply(self)) : _committedFn, - }; - } - - // Arm the fence. Once the fence is armed, and there are no more - // uncommitted writes, it will activate. - arm() { - if (this === DDPServer._CurrentWriteFence.get()) - throw Error("Can't arm the current fence"); - this.armed = true; - return Meteor._isFibersEnabled ? Promise.await(this._maybeFire()) : this._maybeFire(); - } - - // Register a function to be called once before firing the fence. - // Callback function can add new writes to the fence, in which case - // it won't fire until those writes are done as well. - onBeforeFire(func) { - if (this.fired) - throw new Error("fence has already activated -- too late to " + - "add a callback"); - this.before_fire_callbacks.push(func); - } - - // Register a function to be called when the fence fires. - onAllCommitted(func) { - if (this.fired) - throw new Error("fence has already activated -- too late to " + - "add a callback"); - this.completion_callbacks.push(func); - } - - _armAndWait() { - let resolver; - const returnValue = new Promise(r => resolver = r); - this.onAllCommitted(resolver); - this.arm(); - - return returnValue; - } - // Convenience function. Arms the fence, then blocks until it fires. - armAndWait() { - return Meteor._isFibersEnabled ? Promise.await(this._armAndWait()) : this._armAndWait(); - } - - async _maybeFire() { - if (this.fired) - throw new Error("write fence already activated?"); - if (this.armed && !this.outstanding_writes) { - const invokeCallback = async (func) => { - try { - await func(this); - } catch (err) { - Meteor._debug("exception in write fence callback:", err); - } - }; - - this.outstanding_writes++; - while (this.before_fire_callbacks.length > 0) { - const cb = this.before_fire_callbacks.shift(); - await invokeCallback(cb); - } - this.outstanding_writes--; - - if (!this.outstanding_writes) { - this.fired = true; - while (this.completion_callbacks.length > 0) { - const cb = this.completion_callbacks.shift(); - await invokeCallback(cb); - } - } - } - } - - // Deactivate this fence so that adding more writes has no effect. - // The fence must have already fired. - retire() { - if (!this.fired) - throw new Error("Can't retire a fence that hasn't fired."); - this.retired = true; - } -}; - -// The current write fence. When there is a current write fence, code -// that writes to databases should register their writes with it using -// beginWrite(). -// -DDPServer._CurrentWriteFence = new Meteor.EnvironmentVariable; diff --git a/packages/ddp-server-async/README.md b/packages/ddp-server-legacy/README.md similarity index 100% rename from packages/ddp-server-async/README.md rename to packages/ddp-server-legacy/README.md diff --git a/packages/ddp-server-async/crossbar.js b/packages/ddp-server-legacy/crossbar.js similarity index 100% rename from packages/ddp-server-async/crossbar.js rename to packages/ddp-server-legacy/crossbar.js diff --git a/packages/ddp-server-async/crossbar_tests.js b/packages/ddp-server-legacy/crossbar_tests.js similarity index 100% rename from packages/ddp-server-async/crossbar_tests.js rename to packages/ddp-server-legacy/crossbar_tests.js diff --git a/packages/ddp-server-async/livedata_server.js b/packages/ddp-server-legacy/livedata_server.js similarity index 98% rename from packages/ddp-server-async/livedata_server.js rename to packages/ddp-server-legacy/livedata_server.js index 533e01b701..3b4853c39e 100644 --- a/packages/ddp-server-async/livedata_server.js +++ b/packages/ddp-server-legacy/livedata_server.js @@ -1,5 +1,7 @@ DDPServer = {}; +var Fiber = Npm.require('fibers'); + // Publication strategies define how we handle data from published cursors at the collection level // This allows someone to: // - Choose a trade-off between client-server bandwidth and server memory usage @@ -328,9 +330,9 @@ var Session = function (server, version, socket, options) { self.send({ msg: 'connected', session: self.id }); // On initial connect, spin up all the universal publishers. - Meteor._runAsync(function() { + Fiber(function () { self.startUniversalSubs(); - }); + }).run(); if (version !== 'pre1' && options.heartbeatInterval !== 0) { // We no longer need the low level timeout because we have heartbeats. @@ -555,10 +557,10 @@ Object.assign(Session.prototype, { // Any message counts as receiving a pong, as it demonstrates that // the client is still alive. if (self.heartbeat) { - Meteor._runAsync(function() { + Fiber(function () { self.heartbeat.messageReceived(); - }); - }; + }).run(); + } if (self.version !== 'pre1' && msg_in.msg === 'ping') { if (self._respondToPings) @@ -582,7 +584,7 @@ Object.assign(Session.prototype, { return; } - function runHandlers() { + Fiber(function () { var blocked = true; var unblock = function () { @@ -602,9 +604,7 @@ Object.assign(Session.prototype, { else self.sendError('Bad request', msg); unblock(); // in case the handler didn't already do it - } - - Meteor._runAsync(runHandlers); + }).run(); }; processNext(); @@ -778,7 +778,7 @@ Object.assign(Session.prototype, { const isThenable = resultOrThenable && typeof resultOrThenable.then === 'function'; if (isThenable) { - result = Meteor._isFibersEnabled ? Promise.await(resultOrThenable) : resultOrThenable; + result = Promise.await(resultOrThenable); } else { result = resultOrThenable; } @@ -1177,21 +1177,15 @@ Object.assign(Subscription.prototype, { return c && c._publishCursor; }; if (isCursor(res)) { - if (Meteor._isFibersEnabled) { - try { - res._publishCursor(self); - } catch (e) { - self.error(e); - return; - } - // _publishCursor only returns after the initial added callbacks have run. - // mark subscription as ready. - self.ready(); - } else { - res._publishCursor(self).then(() => { - self.ready(); - }).catch((e) => self.error(e)); + try { + res._publishCursor(self); + } catch (e) { + self.error(e); + return; } + // _publishCursor only returns after the initial added callbacks have run. + // mark subscription as ready. + self.ready(); } else if (_.isArray(res)) { // Check all the elements are cursors if (! _.all(res, isCursor)) { @@ -1213,21 +1207,15 @@ Object.assign(Subscription.prototype, { collectionNames[collectionName] = true; }; - if (Meteor._isFibersEnabled) { - try { - _.each(res, function (cur) { - cur._publishCursor(self); - }); - } catch (e) { - self.error(e); - return; - } - self.ready(); - } else { - Promise.all(res.map((c) => c._publishCursor(self))).then(() => { - self.ready(); - }).catch((e) => self.error(e)); + try { + _.each(res, function (cur) { + cur._publishCursor(self); + }); + } catch (e) { + self.error(e); + return; } + self.ready(); } else if (res) { // Truthy values other than cursors or arrays are probably a // user mistake (possible returning a Mongo document via, say, @@ -1504,11 +1492,9 @@ Server = function (options = {}) { sendError("Already connected", msg); return; } - - Meteor._runAsync(function() { + Fiber(function () { self._handleConnect(socket, msg); - }) - + }).run(); return; } @@ -1525,9 +1511,9 @@ Server = function (options = {}) { socket.on('close', function () { if (socket._meteorSession) { - Meteor._runAsync(function() { + Fiber(function () { socket._meteorSession.close(); - }); + }).run(); } }); }); @@ -1705,9 +1691,9 @@ Object.assign(Server.prototype, { // self.sessions to change while we're running this loop. self.sessions.forEach(function (session) { if (!session._dontStartNewUniversalSubs) { - Meteor._runAsync(function() { + Fiber(function() { session._startSubscription(handler); - }); + }).run(); } }); } diff --git a/packages/ddp-server-async/livedata_server_async_tests.js b/packages/ddp-server-legacy/livedata_server_async_tests.js similarity index 100% rename from packages/ddp-server-async/livedata_server_async_tests.js rename to packages/ddp-server-legacy/livedata_server_async_tests.js diff --git a/packages/ddp-server-async/livedata_server_tests.js b/packages/ddp-server-legacy/livedata_server_tests.js similarity index 100% rename from packages/ddp-server-async/livedata_server_tests.js rename to packages/ddp-server-legacy/livedata_server_tests.js diff --git a/packages/ddp-server-async/package.js b/packages/ddp-server-legacy/package.js similarity index 100% rename from packages/ddp-server-async/package.js rename to packages/ddp-server-legacy/package.js diff --git a/packages/ddp-server-async/server_convenience.js b/packages/ddp-server-legacy/server_convenience.js similarity index 100% rename from packages/ddp-server-async/server_convenience.js rename to packages/ddp-server-legacy/server_convenience.js diff --git a/packages/ddp-server-async/session_view_tests.js b/packages/ddp-server-legacy/session_view_tests.js similarity index 100% rename from packages/ddp-server-async/session_view_tests.js rename to packages/ddp-server-legacy/session_view_tests.js diff --git a/packages/ddp-server-async/stream_server.js b/packages/ddp-server-legacy/stream_server.js similarity index 100% rename from packages/ddp-server-async/stream_server.js rename to packages/ddp-server-legacy/stream_server.js diff --git a/packages/ddp-server-legacy/writefence.js b/packages/ddp-server-legacy/writefence.js new file mode 100644 index 0000000000..e9310c9f7f --- /dev/null +++ b/packages/ddp-server-legacy/writefence.js @@ -0,0 +1,131 @@ +var Future = Npm.require('fibers/future'); + +// A write fence collects a group of writes, and provides a callback +// when all of the writes are fully committed and propagated (all +// observers have been notified of the write and acknowledged it.) +// +DDPServer._WriteFence = function () { + var self = this; + + self.armed = false; + self.fired = false; + self.retired = false; + self.outstanding_writes = 0; + self.before_fire_callbacks = []; + self.completion_callbacks = []; +}; + +// The current write fence. When there is a current write fence, code +// that writes to databases should register their writes with it using +// beginWrite(). +// +DDPServer._CurrentWriteFence = new Meteor.EnvironmentVariable; + +_.extend(DDPServer._WriteFence.prototype, { + // Start tracking a write, and return an object to represent it. The + // object has a single method, committed(). This method should be + // called when the write is fully committed and propagated. You can + // continue to add writes to the WriteFence up until it is triggered + // (calls its callbacks because all writes have committed.) + beginWrite: function () { + var self = this; + + if (self.retired) + return { committed: function () {} }; + + if (self.fired) + throw new Error("fence has already activated -- too late to add writes"); + + self.outstanding_writes++; + var committed = false; + return { + committed: function () { + if (committed) + throw new Error("committed called twice on the same write"); + committed = true; + self.outstanding_writes--; + self._maybeFire(); + } + }; + }, + + // Arm the fence. Once the fence is armed, and there are no more + // uncommitted writes, it will activate. + arm: function () { + var self = this; + if (self === DDPServer._CurrentWriteFence.get()) + throw Error("Can't arm the current fence"); + self.armed = true; + self._maybeFire(); + }, + + // Register a function to be called once before firing the fence. + // Callback function can add new writes to the fence, in which case + // it won't fire until those writes are done as well. + onBeforeFire: function (func) { + var self = this; + if (self.fired) + throw new Error("fence has already activated -- too late to " + + "add a callback"); + self.before_fire_callbacks.push(func); + }, + + // Register a function to be called when the fence fires. + onAllCommitted: function (func) { + var self = this; + if (self.fired) + throw new Error("fence has already activated -- too late to " + + "add a callback"); + self.completion_callbacks.push(func); + }, + + // Convenience function. Arms the fence, then blocks until it fires. + armAndWait: function () { + var self = this; + var future = new Future; + self.onAllCommitted(function () { + future['return'](); + }); + self.arm(); + future.wait(); + }, + + _maybeFire: function () { + var self = this; + if (self.fired) + throw new Error("write fence already activated?"); + if (self.armed && !self.outstanding_writes) { + function invokeCallback (func) { + try { + func(self); + } catch (err) { + Meteor._debug("exception in write fence callback", err); + } + } + + self.outstanding_writes++; + while (self.before_fire_callbacks.length > 0) { + var callbacks = self.before_fire_callbacks; + self.before_fire_callbacks = []; + _.each(callbacks, invokeCallback); + } + self.outstanding_writes--; + + if (!self.outstanding_writes) { + self.fired = true; + var callbacks = self.completion_callbacks; + self.completion_callbacks = []; + _.each(callbacks, invokeCallback); + } + } + }, + + // Deactivate this fence so that adding more writes has no effect. + // The fence must have already fired. + retire: function () { + var self = this; + if (! self.fired) + throw new Error("Can't retire a fence that hasn't fired."); + self.retired = true; + } +}); diff --git a/packages/ddp-server/livedata_server.js b/packages/ddp-server/livedata_server.js index 3b4853c39e..9cadd4d4ee 100644 --- a/packages/ddp-server/livedata_server.js +++ b/packages/ddp-server/livedata_server.js @@ -1,7 +1,5 @@ DDPServer = {}; -var Fiber = Npm.require('fibers'); - // Publication strategies define how we handle data from published cursors at the collection level // This allows someone to: // - Choose a trade-off between client-server bandwidth and server memory usage @@ -330,9 +328,9 @@ var Session = function (server, version, socket, options) { self.send({ msg: 'connected', session: self.id }); // On initial connect, spin up all the universal publishers. - Fiber(function () { + Meteor._runAsync(function() { self.startUniversalSubs(); - }).run(); + }); if (version !== 'pre1' && options.heartbeatInterval !== 0) { // We no longer need the low level timeout because we have heartbeats. @@ -557,10 +555,10 @@ Object.assign(Session.prototype, { // Any message counts as receiving a pong, as it demonstrates that // the client is still alive. if (self.heartbeat) { - Fiber(function () { + Meteor._runAsync(function() { self.heartbeat.messageReceived(); - }).run(); - } + }); + }; if (self.version !== 'pre1' && msg_in.msg === 'ping') { if (self._respondToPings) @@ -584,7 +582,7 @@ Object.assign(Session.prototype, { return; } - Fiber(function () { + function runHandlers() { var blocked = true; var unblock = function () { @@ -604,7 +602,9 @@ Object.assign(Session.prototype, { else self.sendError('Bad request', msg); unblock(); // in case the handler didn't already do it - }).run(); + } + + Meteor._runAsync(runHandlers); }; processNext(); @@ -762,33 +762,32 @@ Object.assign(Session.prototype, { } } - const getCurrentMethodInvocationResult = () => { - const currentContext = DDP._CurrentMethodInvocation._setNewContextAndGetCurrent( - invocation + const getCurrentMethodInvocationResult = () => + DDP._CurrentPublicationInvocation.withValue( + invocation, + () => + maybeAuditArgumentChecks( + handler, + invocation, + msg.params, + "call to '" + msg.method + "'" + ), + { + name: 'getCurrentMethodInvocationResult', + keyName: 'getCurrentMethodInvocationResult', + } ); - try { - let result; - const resultOrThenable = maybeAuditArgumentChecks( - handler, - invocation, - msg.params, - "call to '" + msg.method + "'" - ); - const isThenable = - resultOrThenable && typeof resultOrThenable.then === 'function'; - if (isThenable) { - result = Promise.await(resultOrThenable); - } else { - result = resultOrThenable; + resolve( + DDPServer._CurrentWriteFence.withValue( + fence, + getCurrentMethodInvocationResult, + { + name: 'DDPServer._CurrentWriteFence', + keyName: '_CurrentWriteFence', } - return result; - } finally { - DDP._CurrentMethodInvocation._set(currentContext); - } - }; - - resolve(DDPServer._CurrentWriteFence.withValue(fence, getCurrentMethodInvocationResult)); + ) + ); }); function finish() { @@ -899,7 +898,7 @@ Object.assign(Session.prototype, { // subs. self._dontStartNewUniversalSubs = false; self.startUniversalSubs(); - }); + }, { name: '_setUserId' }); // Start sending messages again, beginning with the diff from the previous // state of the world to the current state. No yields are allowed during @@ -1120,16 +1119,19 @@ Object.assign(Subscription.prototype, { const self = this; let resultOrThenable = null; try { - resultOrThenable = DDP._CurrentPublicationInvocation.withValue(self, () => - maybeAuditArgumentChecks( - self._handler, - self, - EJSON.clone(self._params), - // It's OK that this would look weird for universal subscriptions, - // because they have no arguments so there can never be an - // audit-argument-checks failure. - "publisher '" + self._name + "'" - ) + resultOrThenable = DDP._CurrentPublicationInvocation.withValue( + self, + () => + maybeAuditArgumentChecks( + self._handler, + self, + EJSON.clone(self._params), + // It's OK that this would look weird for universal subscriptions, + // because they have no arguments so there can never be an + // audit-argument-checks failure. + "publisher '" + self._name + "'" + ), + { name: self._name } ); } catch (e) { self.error(e); @@ -1152,6 +1154,7 @@ Object.assign(Subscription.prototype, { } else { self._publishHandlerResult(resultOrThenable); } + }, _publishHandlerResult: function (res) { @@ -1177,15 +1180,21 @@ Object.assign(Subscription.prototype, { return c && c._publishCursor; }; if (isCursor(res)) { - try { - res._publishCursor(self); - } catch (e) { - self.error(e); - return; + if (Meteor._isFibersEnabled) { + try { + res._publishCursor(self); + } catch (e) { + self.error(e); + return; + } + // _publishCursor only returns after the initial added callbacks have run. + // mark subscription as ready. + self.ready(); + } else { + res._publishCursor(self).then(() => { + self.ready(); + }).catch((e) => self.error(e)); } - // _publishCursor only returns after the initial added callbacks have run. - // mark subscription as ready. - self.ready(); } else if (_.isArray(res)) { // Check all the elements are cursors if (! _.all(res, isCursor)) { @@ -1207,15 +1216,21 @@ Object.assign(Subscription.prototype, { collectionNames[collectionName] = true; }; - try { - _.each(res, function (cur) { - cur._publishCursor(self); - }); - } catch (e) { - self.error(e); - return; + if (Meteor._isFibersEnabled) { + try { + _.each(res, function (cur) { + cur._publishCursor(self); + }); + } catch (e) { + self.error(e); + return; + } + self.ready(); + } else { + Promise.all(res.map((c) => c._publishCursor(self))).then(() => { + self.ready(); + }).catch((e) => self.error(e)); } - self.ready(); } else if (res) { // Truthy values other than cursors or arrays are probably a // user mistake (possible returning a Mongo document via, say, @@ -1492,9 +1507,11 @@ Server = function (options = {}) { sendError("Already connected", msg); return; } - Fiber(function () { + + Meteor._runAsync(function() { self._handleConnect(socket, msg); - }).run(); + }) + return; } @@ -1511,9 +1528,9 @@ Server = function (options = {}) { socket.on('close', function () { if (socket._meteorSession) { - Fiber(function () { + Meteor._runAsync(function() { socket._meteorSession.close(); - }).run(); + }); } }); }); @@ -1691,9 +1708,9 @@ Object.assign(Server.prototype, { // self.sessions to change while we're running this loop. self.sessions.forEach(function (session) { if (!session._dontStartNewUniversalSubs) { - Fiber(function() { + Meteor._runAsync(function() { session._startSubscription(handler); - }).run(); + }); } }); } diff --git a/packages/ddp-server/livedata_server_async_tests.js b/packages/ddp-server/livedata_server_async_tests.js index d145aeee92..e326a59a0a 100644 --- a/packages/ddp-server/livedata_server_async_tests.js +++ b/packages/ddp-server/livedata_server_async_tests.js @@ -1,4 +1,3 @@ -var Fiber = Npm.require('fibers'); function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); @@ -23,6 +22,8 @@ Meteor.publish('livedata_server_test_sub_context_async', async function( var methodInvocation = DDP._CurrentMethodInvocation.get(); var publicationInvocation = DDP._CurrentPublicationInvocation.get(); +// console.log('methodInvocation', methodInvocation); +// console.log('publicationInvocation', !!publicationInvocation); // Check the publish function's environment variables and context. if (callback) { callback.call(this, methodInvocation, publicationInvocation); @@ -33,6 +34,7 @@ Meteor.publish('livedata_server_test_sub_context_async', async function( this.onStop(function() { var onStopMethodInvocation = DDP._CurrentMethodInvocation.get(); var onStopPublicationInvocation = DDP._CurrentPublicationInvocation.get(); + callback.call( this, onStopMethodInvocation, @@ -45,7 +47,7 @@ Meteor.publish('livedata_server_test_sub_context_async', async function( this.stop(); } else { this.ready(); - Meteor.call('livedata_server_test_setuserid', userId); + await Meteor.callAsync('livedata_server_test_setuserid', userId); } }); @@ -164,8 +166,8 @@ Tinytest.addAsync('livedata server - async publish cursor', function( const remoteCollection = new Mongo.Collection('names', { connection: clientConn, }); - clientConn.subscribe('asyncPublishCursor', () => { - const actual = remoteCollection.find().fetch(); + clientConn.subscribe('asyncPublishCursor', async () => { + const actual = await remoteCollection.find().fetch(); test.equal(actual[0].name, 'async'); onComplete(); }); diff --git a/packages/ddp-server/livedata_server_tests.js b/packages/ddp-server/livedata_server_tests.js index cde56b6196..aa4cc6f845 100644 --- a/packages/ddp-server/livedata_server_tests.js +++ b/packages/ddp-server/livedata_server_tests.js @@ -1,6 +1,3 @@ -var Fiber = Npm.require('fibers'); - - Tinytest.addAsync( "livedata server - connectionHandle.onClose()", function (test, onComplete) { @@ -87,8 +84,8 @@ Meteor.methods({ return this.connection && this.connection.id; }, - livedata_server_test_outer: function () { - return Meteor.call('livedata_server_test_inner'); + livedata_server_test_outer: async function () { + return await Meteor.callAsync('livedata_server_test_inner'); }, livedata_server_test_setuserid: function (userId) { @@ -108,12 +105,17 @@ Tinytest.addAsync( }); makeTestConnection( - test, - function (clientConn, serverConn) { - clientConn.call('livedata_server_test_inner'); - clientConn.disconnect(); - }, - onComplete + test, + function(clientConn, serverConn) { + clientConn + .callAsync('livedata_server_test_inner') + .then(() => clientConn.disconnect()) + .catch(e => { + onComplete(); + throw new Meteor.Error(e); + }); + }, + onComplete ); } ); @@ -125,10 +127,11 @@ Tinytest.addAsync( makeTestConnection( test, function (clientConn, serverConn) { - var res = clientConn.call('livedata_server_test_inner'); - test.equal(res, serverConn.id); - clientConn.disconnect(); - onComplete(); + clientConn.callAsync('livedata_server_test_inner').then(res => { + test.equal(res, serverConn.id); + clientConn.disconnect(); + onComplete(); + }); }, onComplete ); @@ -142,10 +145,11 @@ Tinytest.addAsync( makeTestConnection( test, function (clientConn, serverConn) { - var res = clientConn.call('livedata_server_test_outer'); - test.equal(res, serverConn.id); - clientConn.disconnect(); - onComplete(); + clientConn.callAsync('livedata_server_test_outer').then(res => { + test.equal(res, serverConn.id); + clientConn.disconnect(); + onComplete(); + }); }, onComplete ); @@ -163,16 +167,16 @@ Meteor.publish("livedata_server_test_sub", function (connectionId) { this.stop(); }); -Meteor.publish("livedata_server_test_sub_method", function (connectionId) { +Meteor.publish("livedata_server_test_sub_method", async function (connectionId) { var callback = onSubscription[connectionId]; if (callback) { - var id = Meteor.call('livedata_server_test_inner'); + var id = await Meteor.callAsync('livedata_server_test_inner'); callback(id); } this.stop(); }); -Meteor.publish("livedata_server_test_sub_context", function (connectionId, userId) { +Meteor.publish("livedata_server_test_sub_context", async function (connectionId, userId) { var callback = onSubscription[connectionId]; var methodInvocation = DDP._CurrentMethodInvocation.get(); var publicationInvocation = DDP._CurrentPublicationInvocation.get(); @@ -194,7 +198,7 @@ Meteor.publish("livedata_server_test_sub_context", function (connectionId, userI this.stop(); } else { this.ready(); - Meteor.call('livedata_server_test_setuserid', userId); + await Meteor.callAsync('livedata_server_test_setuserid', userId); } }); @@ -311,13 +315,13 @@ Tinytest.addAsync( ); Meteor.methods({ - testResolvedPromise(arg) { - const invocation1 = DDP._CurrentMethodInvocation.get(); + async testResolvedPromise(arg) { + const invocationRunningFromCallAsync1 = DDP._CurrentMethodInvocation._isCallAsyncMethodRunning(); return Promise.resolve(arg).then(result => { - const invocation2 = DDP._CurrentMethodInvocation.get(); - // This equality holds because Promise callbacks are bound to the - // dynamic environment where .then was called. - if (invocation1 !== invocation2) { + const invocationRunningFromCallAsync2 = DDP._CurrentMethodInvocation._isCallAsyncMethodRunning(); + // What matters here is that both invocations are coming from the same call, + // so both of them can be considered a simulation. + if (invocationRunningFromCallAsync1 !== invocationRunningFromCallAsync2) { throw new Meteor.Error("invocation mismatch"); } return result + " after waiting"; @@ -344,9 +348,10 @@ Meteor.methods({ Tinytest.addAsync( "livedata server - waiting for Promise", - (test, onComplete) => makeTestConnection(test, (clientConn, serverConn) => { + (test, onComplete) => makeTestConnection(test, async (clientConn, serverConn) => { + const testResolvedPromiseResult = await clientConn.callAsync("testResolvedPromise", "clientConn.call"); test.equal( - clientConn.call("testResolvedPromise", "clientConn.call"), + testResolvedPromiseResult, "clientConn.call after waiting" ); diff --git a/packages/ddp-server/package.js b/packages/ddp-server/package.js index 4077518df0..da413690b0 100644 --- a/packages/ddp-server/package.js +++ b/packages/ddp-server/package.js @@ -10,11 +10,6 @@ Npm.depends({ }); Package.onUse(function (api) { - if (process.env.DISABLE_FIBERS) { - api.use('ddp-server-async', 'server'); - api.export('DDPServer', 'server'); - return; - } api.use(['check', 'random', 'ejson', 'underscore', 'retry', 'mongo-id', 'diff-sequence', 'ecmascript'], 'server'); diff --git a/packages/ddp/ddp.d.ts b/packages/ddp/ddp.d.ts new file mode 100644 index 0000000000..199adb3d55 --- /dev/null +++ b/packages/ddp/ddp.d.ts @@ -0,0 +1,65 @@ +import { Meteor } from 'meteor/meteor'; + +export namespace DDP { + interface DDPStatic { + subscribe(name: string, ...rest: any[]): Meteor.SubscriptionHandle; + call(method: string, ...parameters: any[]): any; + apply(method: string, ...parameters: any[]): any; + methods(IMeteorMethodsDictionary: any): any; + status(): DDPStatus; + reconnect(): void; + disconnect(): void; + onReconnect(): void; + } + + function _allSubscriptionsReady(): boolean; + + type Status = 'connected' | 'connecting' | 'failed' | 'waiting' | 'offline'; + + interface DDPStatus { + connected: boolean; + status: Status; + retryCount: number; + retryTime?: number | undefined; + reason?: string | undefined; + } + + function connect(url: string): DDPStatic; +} + +export namespace DDPCommon { + interface MethodInvocationOptions { + userId: string | null; + setUserId?: ((newUserId: string) => void) | undefined; + isSimulation: boolean; + connection: Meteor.Connection; + randomSeed: string; + } + + /** The state for a single invocation of a method, referenced by this inside a method definition. */ + interface MethodInvocation { + new (options: MethodInvocationOptions): MethodInvocation; + /** + * Call inside a method invocation. Allow subsequent method from this client to begin running in a new fiber. + */ + unblock(): void; + /** + * Set the logged in user. + * @param userId The value that should be returned by `userId` on this connection. + */ + setUserId(userId: string | null): void; + /** + * The id of the user that made this method call, or `null` if no user was logged in. + */ + userId: string | null; + /** + * Access inside a method invocation. Boolean value, true if this invocation is a stub. + */ + isSimulation: boolean; + /** + * Access inside a method invocation. The [connection](#meteor_onconnection) that this method was received on. `null` if the method is not associated with a connection, eg. a server + * initiated method call. Calls to methods made from a server method which was in turn initiated from the client share the same `connection`. + */ + connection: Meteor.Connection; + } +} diff --git a/packages/ddp/package-types.json b/packages/ddp/package-types.json new file mode 100644 index 0000000000..31646e57c4 --- /dev/null +++ b/packages/ddp/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "ddp.d.ts" +} diff --git a/packages/ddp/package.js b/packages/ddp/package.js index 49cace9e05..bf92eed179 100644 --- a/packages/ddp/package.js +++ b/packages/ddp/package.js @@ -1,12 +1,14 @@ Package.describe({ summary: "Meteor's latency-compensated distributed data framework", - version: '1.4.0' + version: '1.4.1' }); Package.onUse(function (api) { api.use(['ddp-client'], ['client', 'server']); api.use(['ddp-server'], 'server'); + api.addAssets('ddp.d.ts', 'server'); + api.export('DDP'); api.export('DDPServer', 'server'); diff --git a/packages/deprecated/jshint/.versions b/packages/deprecated/jshint/.versions index c5612a10eb..2560991c2d 100644 --- a/packages/deprecated/jshint/.versions +++ b/packages/deprecated/jshint/.versions @@ -5,7 +5,7 @@ base64@1.0.12 binary-heap@1.0.11 boilerplate-generator@1.7.1 callback-hook@1.3.0 -check@1.3.1 +check@1.3.2 ddp@1.4.0 ddp-client@2.4.1 ddp-common@1.4.0 @@ -36,14 +36,14 @@ mongo-id@1.0.8 npm-mongo@3.9.0 ordered-dict@1.1.0 promise@0.11.2 -random@1.2.0 +random@1.2.1 react-fast-refresh@0.1.1 reload@1.3.1 retry@1.1.0 routepolicy@1.1.0 socket-stream-client@0.3.3 tinytest@1.1.0 -tracker@1.2.0 -underscore@1.0.10 +tracker@1.2.1 +underscore@1.0.11 webapp@1.10.1 webapp-hashing@1.1.0 diff --git a/packages/diff-sequence/package.js b/packages/diff-sequence/package.js index ff143f3e8a..8c63436265 100644 --- a/packages/diff-sequence/package.js +++ b/packages/diff-sequence/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "An implementation of a diff algorithm on arrays and objects.", - version: '1.1.1', + version: '1.1.2', documentation: null }); diff --git a/packages/ecmascript/package.js b/packages/ecmascript/package.js index 37378b5163..a43b8dec7e 100644 --- a/packages/ecmascript/package.js +++ b/packages/ecmascript/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'ecmascript', - version: '0.16.2', + version: '0.16.4', summary: 'Compiler plugin that supports ES2015+ in all .js files', documentation: 'README.md', }); diff --git a/packages/ejson/package.js b/packages/ejson/package.js index 654f16c568..8003d75acf 100644 --- a/packages/ejson/package.js +++ b/packages/ejson/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: 'Extended and Extensible JSON library', - version: '1.1.2' + version: '1.1.3' }); Package.onUse(function onUse(api) { diff --git a/packages/email/email.d.ts b/packages/email/email.d.ts new file mode 100644 index 0000000000..71380d328e --- /dev/null +++ b/packages/email/email.d.ts @@ -0,0 +1,44 @@ +export namespace Email { + interface EmailOptions { + from?: string | undefined; + to?: string | string[] | undefined; + cc?: string | string[] | undefined; + bcc?: string | string[] | undefined; + replyTo?: string | string[] | undefined; + subject?: string | undefined; + text?: string | undefined; + html?: string | undefined; + headers?: Object | undefined; + attachments?: Object[] | undefined; + mailComposer?: MailComposer | undefined; + } + + interface CustomEmailOptions extends EmailOptions { + packageSettings?: unknown; + } + + function send(options: EmailOptions): void; + function hookSend(fn: (options: EmailOptions) => boolean): void; + function customTransport(fn: (options: CustomEmailOptions) => void): void; +} + +export interface MailComposerOptions { + escapeSMTP: boolean; + encoding: string; + charset: string; + keepBcc: boolean; + forceEmbeddedImages: boolean; +} + +export declare var MailComposer: MailComposerStatic; + +export interface MailComposerStatic { + new (options: MailComposerOptions): MailComposer; +} + +export interface MailComposer { + addHeader(name: string, value: string): void; + setMessageOption(from: string, to: string, body: string, html: string): void; + streamMessage(): void; + pipe(stream: any /** fs.WriteStream **/): void; +} diff --git a/packages/email/package-types.json b/packages/email/package-types.json new file mode 100644 index 0000000000..de228d2fc5 --- /dev/null +++ b/packages/email/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "email.d.ts" +} diff --git a/packages/email/package.js b/packages/email/package.js index 3fb07dae92..cc02138f6d 100644 --- a/packages/email/package.js +++ b/packages/email/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: 'Send email messages', - version: '2.2.1', + version: '2.2.3', }); Npm.depends({ @@ -10,6 +10,7 @@ Npm.depends({ Package.onUse(function(api) { api.use(['ecmascript', 'logging', 'callback-hook'], 'server'); + api.addAssets('email.d.ts', 'server'); api.mainModule('email.js', 'server'); api.export(['Email', 'EmailInternals'], 'server'); api.export('EmailTest', 'server', { testOnly: true }); diff --git a/packages/facebook-oauth/package.js b/packages/facebook-oauth/package.js index de073e9b0c..98b393d2a9 100644 --- a/packages/facebook-oauth/package.js +++ b/packages/facebook-oauth/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Facebook OAuth flow", - version: "1.11.0" + version: '1.11.2' }); Package.onUse(api => { diff --git a/packages/facts-ui/package.js b/packages/facts-ui/package.js index 5f367eb09d..4a40b24089 100644 --- a/packages/facts-ui/package.js +++ b/packages/facts-ui/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Display internal app statistics", - version: '1.0.0' + version: '1.0.1' }); Package.onUse(function (api) { diff --git a/packages/fetch/package.js b/packages/fetch/package.js index dcba22f913..5648235dac 100644 --- a/packages/fetch/package.js +++ b/packages/fetch/package.js @@ -1,6 +1,6 @@ Package.describe({ name: "fetch", - version: "0.1.1", + version: '0.1.2', summary: "Isomorphic modern/legacy/Node polyfill for WHATWG fetch()", documentation: "README.md" }); diff --git a/packages/geojson-utils/package.js b/packages/geojson-utils/package.js index 052cdc64bc..4dc3e82de0 100644 --- a/packages/geojson-utils/package.js +++ b/packages/geojson-utils/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: 'GeoJSON utility functions (from https://github.com/maxogden/geojson-js-utils)', - version: '1.0.10' + version: '1.0.11' }); Package.onUse(function (api) { diff --git a/packages/github-oauth/package.js b/packages/github-oauth/package.js index de8e9415cb..2316e275a2 100644 --- a/packages/github-oauth/package.js +++ b/packages/github-oauth/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: 'GitHub OAuth flow', - version: '1.4.0' + version: '1.4.1' }); Package.onUse(api => { diff --git a/packages/google-oauth/package.js b/packages/google-oauth/package.js index 102f60b0ac..141c79e6c6 100644 --- a/packages/google-oauth/package.js +++ b/packages/google-oauth/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Google OAuth flow", - version: "1.4.2", + version: "1.4.3", }); Cordova.depends({ diff --git a/packages/hot-module-replacement/hot-module-replacement.d.ts b/packages/hot-module-replacement/hot-module-replacement.d.ts new file mode 100644 index 0000000000..767262f3ac --- /dev/null +++ b/packages/hot-module-replacement/hot-module-replacement.d.ts @@ -0,0 +1,14 @@ +export interface Module { + readonly hot?: { + accept(): void; + decline(): void; + dispose(callback: (data: object) => void): void; + data: object | null; + onRequire(callbacks: { + before?(requiredModule: Module, parentId: string): T; + after?(requiredModule: Module, data: T): void; + }): void; + }; +} + +export declare var module: NodeJS.Module; diff --git a/packages/hot-module-replacement/package-types.json b/packages/hot-module-replacement/package-types.json new file mode 100644 index 0000000000..b11c5dadfe --- /dev/null +++ b/packages/hot-module-replacement/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "hot-module-replacement.d.ts" +} diff --git a/packages/hot-module-replacement/package.js b/packages/hot-module-replacement/package.js index 2d92f97b09..c3f3e3c3af 100644 --- a/packages/hot-module-replacement/package.js +++ b/packages/hot-module-replacement/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'hot-module-replacement', - version: '0.5.1', + version: '0.5.2', summary: 'Update code in development without reloading the page', documentation: 'README.md', debugOnly: true, @@ -11,6 +11,8 @@ Package.onUse(function(api) { api.use('meteor'); api.use('hot-code-push', { unordered: true }); + api.addAssets('hot-module-replacement.d.ts', 'server'); + // Provides polyfills needed by Meteor.absoluteUrl in legacy browsers api.use('ecmascript-runtime-client', { weak: true }); diff --git a/packages/meetup-oauth/package.js b/packages/meetup-oauth/package.js index 9691528e9f..e5049f19cf 100644 --- a/packages/meetup-oauth/package.js +++ b/packages/meetup-oauth/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: 'Meetup OAuth flow', - version: '1.1.1' + version: '1.1.2' }); Package.onUse(api => { diff --git a/packages/meteor-developer-oauth/package.js b/packages/meteor-developer-oauth/package.js index db38232d52..36e4dbb76c 100644 --- a/packages/meteor-developer-oauth/package.js +++ b/packages/meteor-developer-oauth/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: 'Meteor developer accounts OAuth flow', - version: '1.3.1' + version: '1.3.2' }); Package.onUse(api => { diff --git a/packages/meteor-tool/package.js b/packages/meteor-tool/package.js index 374140976c..bafb59a62e 100644 --- a/packages/meteor-tool/package.js +++ b/packages/meteor-tool/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: 'The Meteor command-line tool', - version: '2.8.0', + version: '2.9.0', }); Package.includeTool(); diff --git a/packages/meteor/asl-helpers.js b/packages/meteor/asl-helpers.js index 0d4ab7688a..24e460385e 100644 --- a/packages/meteor/asl-helpers.js +++ b/packages/meteor/asl-helpers.js @@ -7,18 +7,21 @@ Meteor._getAslStore = getAslStore; Meteor._getValueFromAslStore = getValueFromAslStore; Meteor._updateAslStore = updateAslStore; -Meteor._runAsync = (fn, ctx) => { +Meteor._runAsync = (fn, ctx, store = {}) => { if (Meteor._isFibersEnabled) { - const Fiber = Npm.require('fibers'); + const Fiber = Npm.require('fibers'); - return Fiber(() => { - fn.call(ctx); - }).run(); + return Fiber(() => { + fn.call(ctx); + }).run(); } - return global.asyncLocalStorage.run(Meteor._getAslStore(), () => { - fn.call(ctx); - }); + return global.asyncLocalStorage.run( + store || Meteor._getAslStore(), + () => { + return fn.call(ctx); + } + ); }; Meteor._isPromise = (r) => { diff --git a/packages/meteor/async_helpers.js b/packages/meteor/async_helpers.js index e8862322f3..3e1e5023ac 100644 --- a/packages/meteor/async_helpers.js +++ b/packages/meteor/async_helpers.js @@ -33,36 +33,22 @@ class AsynchronousQueue { this._draining = false; } - queueTask(task) { + async queueTask(task) { this._taskHandles.push({ task: task, name: task.name }); - return this._scheduleRun(); + await this._scheduleRun(); } - _scheduleRun() { + async _scheduleRun() { // Already running or scheduled? Do nothing. if (this._runningOrRunScheduled) return; this._runningOrRunScheduled = true; - let resolver; - const returnValue = new Promise(r => resolver = r); - setImmediate(() => { - Meteor._runAsync(async () => { - await this._run(); - - if (!resolver) { - throw new Error("Resolver not found for task"); - } - - resolver(); - }); - }); - - return returnValue; + await this._run(); } async _run() { @@ -89,7 +75,7 @@ class AsynchronousQueue { await this._scheduleRun(); } - runTask(task) { + async runTask(task) { const handle = { task: Meteor.bindEnvironment(task, function(e) { Meteor._debug('Exception from task', e); @@ -98,7 +84,7 @@ class AsynchronousQueue { name: task.name }; this._taskHandles.push(handle); - return this._scheduleRun(); + await this._scheduleRun(); } flush() { diff --git a/packages/meteor/dynamics_nodejs.js b/packages/meteor/dynamics_nodejs.js index a39ce7ffb0..a2528efefa 100644 --- a/packages/meteor/dynamics_nodejs.js +++ b/packages/meteor/dynamics_nodejs.js @@ -3,12 +3,18 @@ var Fiber = Meteor._isFibersEnabled && Npm.require('fibers'); let nextSlot = 0; +let callAsyncMethodRunning = false; -Meteor._nodeCodeMustBeInFiber = function () { +const CURRENT_VALUE_KEY_NAME = 'currentValue'; +const SLOT_CALL_KEY = 'slotCall'; + +Meteor._nodeCodeMustBeInFiber = function() { if (!Fiber.current) { - throw new Error("Meteor code must always run within a Fiber. " + - "Try wrapping callbacks that you pass to non-Meteor " + - "libraries with Meteor.bindEnvironment."); + throw new Error( + 'Meteor code must always run within a Fiber. ' + + 'Try wrapping callbacks that you pass to non-Meteor ' + + 'libraries with Meteor.bindEnvironment.' + ); } }; @@ -20,21 +26,23 @@ class EnvironmentVariableFibers { get() { Meteor._nodeCodeMustBeInFiber(); - return Fiber.current._meteor_dynamics && - Fiber.current._meteor_dynamics[this.slot]; + return ( + Fiber.current._meteor_dynamics && + Fiber.current._meteor_dynamics[this.slot] + ); } getOrNullIfOutsideFiber() { - if (!Fiber.current) - return null; + if (!Fiber.current) return null; return this.get(); } withValue(value, func) { Meteor._nodeCodeMustBeInFiber(); - if (!Fiber.current._meteor_dynamics) + if (!Fiber.current._meteor_dynamics) { Fiber.current._meteor_dynamics = []; + } var currentValues = Fiber.current._meteor_dynamics; var saved = currentValues[this.slot]; @@ -68,41 +76,45 @@ class EnvironmentVariableAsync { } get() { - const currentValue = Meteor._getValueFromAslStore("_meteor_dynamics"); - return currentValue && currentValue[this.slot]; + if (this.slot !== Meteor._getValueFromAslStore(SLOT_CALL_KEY)) { + return; + } + return Meteor._getValueFromAslStore(CURRENT_VALUE_KEY_NAME); } getOrNullIfOutsideFiber() { return this.get(); } - async withValue(value, func) { - let currentValues = Meteor._getValueFromAslStore("_meteor_dynamics"); - if (!currentValues) { - currentValues = []; - } - - const saved = currentValues[this.slot]; - let ret; - try { - currentValues[this.slot] = value; - Meteor._updateAslStore("_meteor_dynamics", currentValues); - ret = await func(); - } finally { - currentValues[this.slot] = saved; - Meteor._updateAslStore("_meteor_dynamics", currentValues); - } - - return ret; + async withValue(value, func, options = {}) { + return Meteor._runAsync( + async () => { + let ret; + try { + Meteor._updateAslStore(CURRENT_VALUE_KEY_NAME, value); + ret = await func(); + } finally { + Meteor._updateAslStore(CURRENT_VALUE_KEY_NAME, undefined); + } + return ret; + }, + this, + { + callId: `${this.slot}-${Math.random()}`, + [SLOT_CALL_KEY]: this.slot, + ...options, + } + ); } _set(context) { - const _meteor_dynamics = Meteor._getValueFromAslStore("_meteor_dynamics") || []; + const _meteor_dynamics = + Meteor._getValueFromAslStore('_meteor_dynamics') || []; _meteor_dynamics[this.slot] = context; } _setNewContextAndGetCurrent(value) { - let _meteor_dynamics = Meteor._getValueFromAslStore("_meteor_dynamics"); + let _meteor_dynamics = Meteor._getValueFromAslStore('_meteor_dynamics'); if (!_meteor_dynamics) { _meteor_dynamics = []; } @@ -111,6 +123,14 @@ class EnvironmentVariableAsync { this._set(value); return saved; } + + _isCallAsyncMethodRunning() { + return callAsyncMethodRunning; + } + + _setCallAsyncMethodRunning(value) { + callAsyncMethodRunning = value; + } } /** @@ -119,7 +139,9 @@ class EnvironmentVariableAsync { * @locus Anywhere * @class */ -Meteor.EnvironmentVariable = Meteor._isFibersEnabled ? EnvironmentVariableFibers : EnvironmentVariableAsync; +Meteor.EnvironmentVariable = Meteor._isFibersEnabled + ? EnvironmentVariableFibers + : EnvironmentVariableAsync; // Meteor application code is always supposed to be run inside a // fiber. bindEnvironment ensures that the function it wraps is run from @@ -151,8 +173,10 @@ Meteor.EnvironmentVariable = Meteor._isFibersEnabled ? EnvironmentVariableFibers * @param {Object} _this Optional `this` object against which the original function will be invoked * @return {Function} The wrapped function */ -Meteor.bindEnvironment = function (func, onException, _this) { - return Meteor._isFibersEnabled ? bindEnvironmentFibers(func, onException, _this) : bindEnvironmentAsync(func, onException, _this); +Meteor.bindEnvironment = function(func, onException, _this) { + return Meteor._isFibersEnabled + ? bindEnvironmentFibers(func, onException, _this) + : bindEnvironmentAsync(func, onException, _this); }; const bindEnvironmentFibers = (func, onException, _this) => { @@ -161,22 +185,21 @@ const bindEnvironmentFibers = (func, onException, _this) => { var dynamics = Fiber.current._meteor_dynamics; var boundValues = dynamics ? dynamics.slice() : []; - if (!onException || typeof(onException) === 'string') { - var description = onException || "callback of async function"; - onException = function (error) { - Meteor._debug( - "Exception in " + description + ":", - error - ); + if (!onException || typeof onException === 'string') { + var description = onException || 'callback of async function'; + onException = function(error) { + Meteor._debug('Exception in ' + description + ':', error); }; - } else if (typeof(onException) !== 'function') { - throw new Error('onException argument must be a function, string or undefined for Meteor.bindEnvironment().'); + } else if (typeof onException !== 'function') { + throw new Error( + 'onException argument must be a function, string or undefined for Meteor.bindEnvironment().' + ); } - return function (/* arguments */) { + return function(/* arguments */) { var args = Array.prototype.slice.call(arguments); - var runWithEnvironment = function () { + var runWithEnvironment = function() { var savedValues = Fiber.current._meteor_dynamics; try { // Need to clone boundValues in case two fibers invoke this @@ -194,46 +217,49 @@ const bindEnvironmentFibers = (func, onException, _this) => { return ret; }; - if (Fiber.current) - return runWithEnvironment(); + if (Fiber.current) return runWithEnvironment(); Fiber(runWithEnvironment).run(); }; }; const bindEnvironmentAsync = (func, onException, _this) => { - var dynamics = Meteor._getValueFromAslStore("_meteor_dynamics"); - var boundValues = Array.isArray(dynamics) ? dynamics.slice() : []; + const dynamics = Meteor._getValueFromAslStore(CURRENT_VALUE_KEY_NAME); + const currentSlot = Meteor._getValueFromAslStore(SLOT_CALL_KEY); - if (!onException || typeof(onException) === 'string') { - var description = onException || "callback of async function"; - onException = function (error) { - Meteor._debug( - "Exception in " + description + ":", - error - ); + if (!onException || typeof onException === 'string') { + var description = onException || 'callback of async function'; + onException = function(error) { + Meteor._debug('Exception in ' + description + ':', error); }; - } else if (typeof(onException) !== 'function') { - throw new Error('onException argument must be a function, string or undefined for Meteor.bindEnvironment().'); + } else if (typeof onException !== 'function') { + throw new Error( + 'onException argument must be a function, string or undefined for Meteor.bindEnvironment().' + ); } - return function (/* arguments */) { + return function(/* arguments */) { var args = Array.prototype.slice.call(arguments); - var runWithEnvironment = async function () { - const savedValues = Meteor._getValueFromAslStore("_meteor_dynamics"); - let ret; - try { - // Need to clone boundValues in case two fibers invoke this - // function at the same time - // TODO -> Probably not needed - Meteor._updateAslStore("_meteor_dynamics", boundValues.slice()); - ret = await func.apply(_this, args); - } catch (e) { - onException(e); - } finally { - Meteor._updateAslStore("_meteor_dynamics", savedValues); - } - return ret; + var runWithEnvironment = function() { + return Meteor._runAsync( + async () => { + let ret; + try { + if (currentSlot) { + Meteor._updateAslStore(CURRENT_VALUE_KEY_NAME, dynamics); + } + ret = await func.apply(_this, args); + } catch (e) { + onException(e); + } + return ret; + }, + _this, + { + callId: `bindEnvironment-${Math.random()}`, + [SLOT_CALL_KEY]: currentSlot, + } + ); }; if (Meteor._getAslStore()) { diff --git a/packages/meteor/meteor.d.ts b/packages/meteor/meteor.d.ts new file mode 100644 index 0000000000..eb08d994bd --- /dev/null +++ b/packages/meteor/meteor.d.ts @@ -0,0 +1,518 @@ +import { Mongo } from 'meteor/mongo'; +import { EJSONable, EJSONableProperty } from 'meteor/ejson'; +import { Blaze } from 'meteor/blaze'; +import { DDP } from 'meteor/ddp'; + +export type global_Error = Error; + +export namespace Meteor { + /** Global props **/ + /** True if running in client environment. */ + var isClient: boolean; + /** True if running in a Cordova mobile environment. */ + var isCordova: boolean; + /** True if running in server environment. */ + var isServer: boolean; + /** True if running in production environment. */ + var isProduction: boolean; + /** + * `Meteor.release` is a string containing the name of the release with which the project was built (for example, `"1.2.3"`). It is `undefined` if the project was built using a git checkout + * of Meteor. + */ + var release: string; + /** Global props **/ + + /** Settings **/ + interface Settings { + public: { [id: string]: any }; + [id: string]: any; + } + /** + * `Meteor.settings` contains deployment-specific configuration options. You can initialize settings by passing the `--settings` option (which takes the name of a file containing JSON data) + * to `meteor run` or `meteor deploy`. When running your server directly (e.g. from a bundle), you instead specify settings by putting the JSON directly into the `METEOR_SETTINGS` environment + * variable. If the settings object contains a key named `public`, then `Meteor.settings.public` will be available on the client as well as the server. All other properties of + * `Meteor.settings` are only defined on the server. You can rely on `Meteor.settings` and `Meteor.settings.public` being defined objects (not undefined) on both client and server even if + * there are no settings specified. Changes to `Meteor.settings.public` at runtime will be picked up by new client connections. + */ + var settings: Settings; + /** Settings **/ + + /** User **/ + interface UserEmail { + address: string; + verified: boolean; + } + /** + * UserProfile is left intentionally underspecified here, to allow you + * to override it in your application (but keep in mind that the default + * Meteor configuration allows users to write directly to their user + * record's profile field) + */ + interface UserProfile {} + interface User { + _id: string; + username?: string | undefined; + emails?: UserEmail[] | undefined; + createdAt?: Date | undefined; + profile?: UserProfile; + services?: any; + } + + function user(options?: { + fields?: Mongo.FieldSpecifier | undefined; + }): User | null; + + function userId(): string | null; + var users: Mongo.Collection; + /** User **/ + + /** Error **/ + /** + * This class represents a symbolic error thrown by a method. + */ + var Error: ErrorStatic; + interface ErrorStatic { + /** + * @param error A string code uniquely identifying this kind of error. + * This string should be used by callers of the method to determine the + * appropriate action to take, instead of attempting to parse the reason + * or details fields. For example: + * + * ``` + * // on the server, pick a code unique to this error + * // the reason field should be a useful debug message + * throw new Meteor.Error("logged-out", + * "The user must be logged in to post a comment."); + * + * // on the client + * Meteor.call("methodName", function (error) { + * // identify the error + * if (error && error.error === "logged-out") { + * // show a nice error message + * Session.set("errorMessage", "Please log in to post a comment."); + * } + * }); + * ``` + * + * For legacy reasons, some built-in Meteor functions such as `check` throw + * errors with a number in this field. + * + * @param reason Optional. A short human-readable summary of the + * error, like 'Not Found'. + * @param details Optional. Additional information about the error, + * like a textual stack trace. + */ + new (error: string | number, reason?: string, details?: string): Error; + } + interface Error extends global_Error { + error: string | number; + reason?: string | undefined; + details?: string | undefined; + } + var TypedError: TypedErrorStatic; + interface TypedErrorStatic { + new (message: string, errorType: string): TypedError; + } + interface TypedError extends global_Error { + message: string; + errorType: string; + } + /** Error **/ + + /** Method **/ + interface MethodThisType { + /** Access inside a method invocation. Boolean value, true if this invocation is a stub. */ + isSimulation: boolean; + /** The id of the user that made this method call, or `null` if no user was logged in. */ + userId: string | null; + /** + * Access inside a method invocation. The connection that this method was received on. `null` if the method is not associated with a connection, eg. a server initiated method call. Calls + * to methods made from a server method which was in turn initiated from the client share the same `connection`. */ + connection: Connection | null; + /** + * Set the logged in user. + * @param userId The value that should be returned by `userId` on this connection. + */ + setUserId(userId: string | null): void; + /** Call inside a method invocation. Allow subsequent method from this client to begin running in a new fiber. */ + unblock(): void; + } + + /** + * Defines functions that can be invoked over the network by clients. + * @param methods Dictionary whose keys are method names and values are functions. + */ + function methods(methods: { + [key: string]: (this: MethodThisType, ...args: any[]) => any; + }): void; + + /** + * Invokes a method with a sync stub, passing any number of arguments. + * @param name Name of method to invoke + * @param args Optional method arguments + */ + function call(name: string, ...args: any[]): any; + + /** + * Invokes a method with an async stub, passing any number of arguments. + * @param name Name of method to invoke + * @param args Optional method arguments + */ + function callAsync(name: string, ...args: any[]): Promise; + + function apply< + Result extends + | EJSONable + | EJSONable[] + | EJSONableProperty + | EJSONableProperty[] + >( + name: string, + args: ReadonlyArray, + options?: { + wait?: boolean | undefined; + onResultReceived?: + | (( + error: global_Error | Meteor.Error | undefined, + result?: Result + ) => void) + | undefined; + /** + * (Client only) if true, don't send this method again on reload, simply call the callback an error with the error code 'invocation-failed'. + */ + noRetry?: boolean | undefined; + returnStubValue?: boolean | undefined; + throwStubExceptions?: boolean | undefined; + }, + asyncCallback?: ( + error: global_Error | Meteor.Error | undefined, + result?: Result + ) => void + ): any; + /** Method **/ + + /** Url **/ + /** + * Generate an absolute URL pointing to the application. The server reads from the `ROOT_URL` environment variable to determine where it is running. This is taken care of automatically for + * apps deployed to Galaxy, but must be provided when using `meteor build`. + */ + var absoluteUrl: { + /** + * @param path A path to append to the root URL. Do not include a leading "`/`". + */ + (path?: string, options?: absoluteUrlOptions): string; + defaultOptions: absoluteUrlOptions; + }; + + interface absoluteUrlOptions { + /** Create an HTTPS URL. */ + secure?: boolean | undefined; + /** Replace localhost with 127.0.0.1. Useful for services that don't recognize localhost as a domain name. */ + replaceLocalhost?: boolean | undefined; + /** Override the default ROOT_URL from the server environment. For example: "`http://foo.example.com`" */ + rootUrl?: string | undefined; + } + /** Url **/ + + /** Timeout **/ + /** + * Call a function repeatedly, with a time delay between calls. + * @param func The function to run + * @param delay Number of milliseconds to wait between each function call. + */ + function setInterval(func: Function, delay: number): number; + + /** + * Call a function in the future after waiting for a specified delay. + * @param func The function to run + * @param delay Number of milliseconds to wait before calling function + */ + function setTimeout(func: Function, delay: number): number; + /** + * Cancel a repeating function call scheduled by `Meteor.setInterval`. + * @param id The handle returned by `Meteor.setInterval` + */ + function clearInterval(id: number): void; + + /** + * Cancel a function call scheduled by `Meteor.setTimeout`. + * @param id The handle returned by `Meteor.setTimeout` + */ + function clearTimeout(id: number): void; + /** + * Defer execution of a function to run asynchronously in the background (similar to `Meteor.setTimeout(func, 0)`. + * @param func The function to run + */ + function defer(func: Function): void; + /** Timeout **/ + + /** utils **/ + /** + * Run code when a client or a server starts. + * @param func A function to run on startup. + */ + function startup(func: Function): void; + + /** + * Wrap a function that takes a callback function as its final parameter. + * The signature of the callback of the wrapped function should be `function(error, result){}`. + * On the server, the wrapped function can be used either synchronously (without passing a callback) or asynchronously + * (when a callback is passed). On the client, a callback is always required; errors will be logged if there is no callback. + * If a callback is provided, the environment captured when the original function was called will be restored in the callback. + * The parameters of the wrapped function must not contain any optional parameters or be undefined, as the callback function is expected to be the final, non-undefined parameter. + * @param func A function that takes a callback as its final parameter + * @param context Optional `this` object against which the original function will be invoked + */ + function wrapAsync(func: Function, context?: Object): any; + + function bindEnvironment(func: TFunc): TFunc; + + class EnvironmentVariable { + readonly slot: number; + constructor(); + get(): T; + getOrNullIfOutsideFiber(): T | null; + withValue(value: T, fn: () => U): U; + } + /** utils **/ + + /** Pub/Sub **/ + interface SubscriptionHandle { + /** Cancel the subscription. This will typically result in the server directing the client to remove the subscription’s data from the client’s cache. */ + stop(): void; + /** True if the server has marked the subscription as ready. A reactive data source. */ + ready(): boolean; + } + interface LiveQueryHandle { + stop(): void; + } + /** Pub/Sub **/ +} + +export namespace Meteor { + /** Login **/ + interface LoginWithExternalServiceOptions { + requestPermissions?: ReadonlyArray | undefined; + requestOfflineToken?: Boolean | undefined; + forceApprovalPrompt?: Boolean | undefined; + loginUrlParameters?: Object | undefined; + redirectUrl?: string | undefined; + loginHint?: string | undefined; + loginStyle?: string | undefined; + } + + function loginWithMeteorDeveloperAccount( + options?: Meteor.LoginWithExternalServiceOptions, + callback?: (error?: global_Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + function loginWithFacebook( + options?: Meteor.LoginWithExternalServiceOptions, + callback?: (error?: global_Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + function loginWithGithub( + options?: Meteor.LoginWithExternalServiceOptions, + callback?: (error?: global_Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + function loginWithGoogle( + options?: Meteor.LoginWithExternalServiceOptions, + callback?: (error?: global_Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + function loginWithMeetup( + options?: Meteor.LoginWithExternalServiceOptions, + callback?: (error?: global_Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + function loginWithTwitter( + options?: Meteor.LoginWithExternalServiceOptions, + callback?: (error?: global_Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + function loginWithWeibo( + options?: Meteor.LoginWithExternalServiceOptions, + callback?: (error?: global_Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + function loginWith( + options?: { + requestPermissions?: ReadonlyArray | undefined; + requestOfflineToken?: boolean | undefined; + loginUrlParameters?: Object | undefined; + userEmail?: string | undefined; + loginStyle?: string | undefined; + redirectUrl?: string | undefined; + }, + callback?: (error?: global_Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + function loginWithPassword( + user: Object | string, + password: string, + callback?: (error?: global_Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + function loginWithToken( + token: string, + callback?: (error?: global_Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + function loggingIn(): boolean; + + function loggingOut(): boolean; + + function logout( + callback?: (error?: global_Error | Meteor.Error | Meteor.TypedError) => void + ): void; + + function logoutOtherClients( + callback?: (error?: global_Error | Meteor.Error | Meteor.TypedError) => void + ): void; + /** Login **/ + + /** Event **/ + interface Event { + type: string; + target: HTMLElement; + currentTarget: HTMLElement; + which: number; + stopPropagation(): void; + stopImmediatePropagation(): void; + preventDefault(): void; + isPropagationStopped(): boolean; + isImmediatePropagationStopped(): boolean; + isDefaultPrevented(): boolean; + } + interface EventHandlerFunction extends Function { + (event?: Meteor.Event, templateInstance?: Blaze.TemplateInstance): void; + } + interface EventMap { + [id: string]: Meteor.EventHandlerFunction; + } + /** Event **/ + + /** Connection **/ + function reconnect(): void; + + function disconnect(): void; + /** Connection **/ + + /** Status **/ + function status(): DDP.DDPStatus; + /** Status **/ + + /** Pub/Sub **/ + /** + * Subscribe to a record set. Returns a handle that provides + * `stop()` and `ready()` methods. + * @param name Name of the subscription. Matches the name of the + * server's `publish()` call. + * @param args Optional arguments passed to publisher + * function on server. + * @param callbacks Optional. May include `onStop` + * and `onReady` callbacks. If there is an error, it is passed as an + * argument to `onStop`. If a function is passed instead of an object, it + * is interpreted as an `onReady` callback. + */ + function subscribe(name: string, ...args: any[]): Meteor.SubscriptionHandle; + /** Pub/Sub **/ +} + +export namespace Meteor { + /** Connection **/ + interface Connection { + id: string; + close: () => void; + onClose: (callback: () => void) => void; + clientAddress: string; + httpHeaders: Object; + } + + function onConnection(callback: (connection: Connection) => void): void; + /** Connection **/ + /** + * Publish a record set. + * @param name If String, name of the record set. If Object, publications Dictionary of publish functions by name. If `null`, the set has no name, and the record set is automatically sent to + * all connected clients. + * @param func Function called on the server each time a client subscribes. Inside the function, `this` is the publish handler object, described below. If the client passed arguments to + * `subscribe`, the function is called with the same arguments. + */ + function publish( + name: string | null, + func: ( + this: Subscription, + ...args: any[] + ) => + | void + | Mongo.Cursor + | Mongo.Cursor[] + | Promise | Mongo.Cursor[]>, + options?: { is_auto: boolean } + ): void; + + function _debug(...args: any[]): void; +} + +export interface Subscription { + /** + * Call inside the publish function. Informs the subscriber that a document has been added to the record set. + * @param collection The name of the collection that contains the new document. + * @param id The new document's ID. + * @param fields The fields in the new document. If `_id` is present it is ignored. + */ + added(collection: string, id: string, fields: Object): void; + /** + * Call inside the publish function. Informs the subscriber that a document in the record set has been modified. + * @param collection The name of the collection that contains the changed document. + * @param id The changed document's ID. + * @param fields The fields in the document that have changed, together with their new values. If a field is not present in `fields` it was left unchanged; if it is present in `fields` and + * has a value of `undefined` it was removed from the document. If `_id` is present it is ignored. + */ + changed(collection: string, id: string, fields: Object): void; + /** Access inside the publish function. The incoming connection for this subscription. */ + connection: Meteor.Connection; + /** + * Call inside the publish function. Stops this client's subscription, triggering a call on the client to the `onStop` callback passed to `Meteor.subscribe`, if any. If `error` is not a + * `Meteor.Error`, it will be sanitized. + * @param error The error to pass to the client. + */ + error(error: Error): void; + /** + * Call inside the publish function. Registers a callback function to run when the subscription is stopped. + * @param func The callback function + */ + onStop(func: Function): void; + /** + * Call inside the publish function. Informs the subscriber that an initial, complete snapshot of the record set has been sent. This will trigger a call on the client to the `onReady` + * callback passed to `Meteor.subscribe`, if any. + */ + ready(): void; + /** + * Call inside the publish function. Informs the subscriber that a document has been removed from the record set. + * @param collection The name of the collection that the document has been removed from. + * @param id The ID of the document that has been removed. + */ + removed(collection: string, id: string): void; + /** + * Access inside the publish function. The incoming connection for this subscription. + */ + stop(): void; + /** + * Call inside the publish function. Allows subsequent methods or subscriptions for the client of this subscription + * to begin running without waiting for the publishing to become ready. + */ + unblock(): void; + /** Access inside the publish function. The id of the logged-in user, or `null` if no user is logged in. */ + userId: string | null; +} + +export namespace Meteor { + /** Global props **/ + /** True if running in development environment. */ + var isDevelopment: boolean; + var isTest: boolean; + var isAppTest: boolean; + /** Global props **/ +} diff --git a/packages/meteor/package-types.json b/packages/meteor/package-types.json new file mode 100644 index 0000000000..bc20149ad0 --- /dev/null +++ b/packages/meteor/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "meteor.d.ts" +} diff --git a/packages/meteor/package.js b/packages/meteor/package.js index 7effcd9675..a05aed1339 100644 --- a/packages/meteor/package.js +++ b/packages/meteor/package.js @@ -2,7 +2,7 @@ Package.describe({ summary: "Core Meteor environment", - version: '1.10.1-beta280.2' + version: '1.10.3' }); Package.registerBuildPlugin({ diff --git a/packages/minifier-css/package.js b/packages/minifier-css/package.js index 057a6f2104..373e5ae579 100644 --- a/packages/minifier-css/package.js +++ b/packages/minifier-css/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: 'CSS minifier', - version: '1.6.1' + version: '1.6.2' }); Npm.depends({ diff --git a/packages/minimongo/cursor.js b/packages/minimongo/cursor.js index 72a51cd67b..0c119a8f81 100644 --- a/packages/minimongo/cursor.js +++ b/packages/minimongo/cursor.js @@ -39,7 +39,11 @@ export default class Cursor { } /** - * @summary Returns the number of documents that match a query. + * @deprecated in 2.9 + * @summary Returns the number of documents that match a query. This method is + * [deprecated since MongoDB 4.0](https://www.mongodb.com/docs/v4.4/reference/command/count/); + * see `Collection.countDocuments` and + * `Collection.estimatedDocumentCount` for a replacement. * @memberOf Mongo.Cursor * @method count * @instance diff --git a/packages/minimongo/local_collection.js b/packages/minimongo/local_collection.js index 9b4946f4ea..6e48060501 100644 --- a/packages/minimongo/local_collection.js +++ b/packages/minimongo/local_collection.js @@ -39,6 +39,14 @@ export default class LocalCollection { this.paused = false; } + countDocuments(selector, options) { + return this.find(selector ?? {}, options).countAsync(); + } + + estimatedDocumentCount(options) { + return this.find({}, options).countAsync(); + } + // options may include sort, skip, limit, reactive // sort may be any of these forms: // {a: 1, b: -1} diff --git a/packages/minimongo/package.js b/packages/minimongo/package.js index 3b8e47fb2f..2353ea1305 100644 --- a/packages/minimongo/package.js +++ b/packages/minimongo/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Meteor's client-side datastore: a port of MongoDB to Javascript", - version: '1.9.0' + version: '1.9.1' }); Package.onUse(api => { diff --git a/packages/modern-browsers/modern.d.ts b/packages/modern-browsers/modern.d.ts new file mode 100644 index 0000000000..69f1268f74 --- /dev/null +++ b/packages/modern-browsers/modern.d.ts @@ -0,0 +1,4 @@ +export declare function setMinimumBrowserVersions( + versions: Record, + source: string +): void; diff --git a/packages/modern-browsers/package-types.json b/packages/modern-browsers/package-types.json new file mode 100644 index 0000000000..ee517974a2 --- /dev/null +++ b/packages/modern-browsers/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "modern.d.ts" +} diff --git a/packages/modern-browsers/package.js b/packages/modern-browsers/package.js index 40e95833a3..7ca4b6d41c 100644 --- a/packages/modern-browsers/package.js +++ b/packages/modern-browsers/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'modern-browsers', - version: '0.1.8', + version: '0.1.9', summary: 'API for defining the boundary between modern and legacy ' + 'JavaScript clients', @@ -10,6 +10,7 @@ Package.describe({ Package.onUse(function(api) { api.use('modules'); api.mainModule('modern.js', 'server'); + api.addAssets('modern.d.ts', 'server'); }); Package.onTest(function(api) { diff --git a/packages/modules-runtime-hot/package.js b/packages/modules-runtime-hot/package.js index 6a932eb21b..2ba683b96d 100644 --- a/packages/modules-runtime-hot/package.js +++ b/packages/modules-runtime-hot/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'modules-runtime-hot', - version: '0.14.0', + version: '0.14.1', summary: 'Patches modules-runtime to support Hot Module Replacement', git: 'https://github.com/benjamn/install', documentation: 'README.md', diff --git a/packages/modules-runtime/package.js b/packages/modules-runtime/package.js index f7ba69f134..4e124aaacb 100644 --- a/packages/modules-runtime/package.js +++ b/packages/modules-runtime/package.js @@ -1,6 +1,6 @@ Package.describe({ name: "modules-runtime", - version: "0.13.0", + version: '0.13.1', summary: "CommonJS module system", git: "https://github.com/benjamn/install", documentation: "README.md" diff --git a/packages/modules/module-system.png b/packages/modules/module-system.png new file mode 100644 index 0000000000..e8b82ac455 Binary files /dev/null and b/packages/modules/module-system.png differ diff --git a/packages/mongo-id/id.js b/packages/mongo-id/id.js index b0bf43dd37..142a761a0d 100644 --- a/packages/mongo-id/id.js +++ b/packages/mongo-id/id.js @@ -3,7 +3,7 @@ import { Random } from 'meteor/random'; const MongoID = {}; -MongoID._looksLikeObjectID = str => str.length === 24 && str.match(/^[0-9a-f]*$/); +MongoID._looksLikeObjectID = str => str.length === 24 && /^[0-9a-f]*$/.test(str); MongoID.ObjectID = class ObjectID { constructor (hexString) { diff --git a/packages/mongo/collection.js b/packages/mongo/collection.js index bdc9333e70..c1fdffe144 100644 --- a/packages/mongo/collection.js +++ b/packages/mongo/collection.js @@ -320,6 +320,33 @@ Object.assign(Mongo.Collection.prototype, { /// /// Main collection API /// + /** + * @summary Gets the number of documents matching the filter. For a fast count of the total documents in a collection see `estimatedDocumentCount`. + * @locus Anywhere + * @method countDocuments + * @memberof Mongo.Collection + * @instance + * @param {MongoSelector} [selector] A query describing the documents to count + * @param {Object} [options] All options are listed in [MongoDB documentation](https://mongodb.github.io/node-mongodb-native/4.11/interfaces/CountDocumentsOptions.html). Please note that not all of them are available on the client. + * @returns {Promise} + */ + countDocuments(...args) { + return this._collection.countDocuments(...args); + }, + + /** + * @summary Gets an estimate of the count of documents in a collection using collection metadata. For an exact count of the documents in a collection see `countDocuments`. + * @locus Anywhere + * @method estimatedDocumentCount + * @memberof Mongo.Collection + * @instance + * @param {MongoSelector} [selector] A query describing the documents to count + * @param {Object} [options] All options are listed in [MongoDB documentation](https://mongodb.github.io/node-mongodb-native/4.11/interfaces/EstimatedDocumentCountOptions.html). Please note that not all of them are available on the client. + * @returns {Promise} + */ + estimatedDocumentCount(...args) { + return this._collection.estimatedDocumentCount(...args); + }, _getFindSelector(args) { if (args.length == 0) return {}; diff --git a/packages/mongo/collection_async_tests.js b/packages/mongo/collection_async_tests.js index 5d3a277fa0..d709cee26c 100644 --- a/packages/mongo/collection_async_tests.js +++ b/packages/mongo/collection_async_tests.js @@ -19,3 +19,14 @@ Tinytest.add('async collection - check for methods presence', function (test) { isFunction(cursor.mapAsync); isFunction(cursor[Symbol.asyncIterator]); }); + +['countDocuments', 'estimatedDocumentCount'].forEach(method => { + Tinytest.addAsync(`async collection - ${method}`, async test => { + const collection = new Mongo.Collection(method + test.id); + for (let index = 0; index < 10; ++index) { + test.instanceOf(collection[method](), Promise); + test.equal(await collection[method](), index); + collection.insert({}); + } + }); +}); diff --git a/packages/mongo/collection_tests.js b/packages/mongo/collection_tests.js index 78da9a1f18..fb92fb8b79 100644 --- a/packages/mongo/collection_tests.js +++ b/packages/mongo/collection_tests.js @@ -170,8 +170,8 @@ Tinytest.add('collection - calling find with a valid readPreference', ); // Trigger the creation of _synchronousCursor - defaultCursor.count(); - customCursor.count(); + defaultCursor.fetch(); + customCursor.fetch(); // defaultCursor._synchronousCursor._dbCursor.operation is not an option anymore // as the cursor options are now private @@ -384,3 +384,33 @@ Tinytest.add('collection - finding with a query with a binary field should retur } } ); + + +Tinytest.add('collection - count should release the session', + function(test) { + const client = MongoInternals.defaultRemoteCollectionDriver().mongo.client; + var collectionName = 'count' + test.id; + var collection = new Mongo.Collection(collectionName); + collection.insert({ _id: '1' }); + collection.insert({ _id: '2' }); + collection.insert({ _id: '3' }); + const preCount = client.s.activeSessions.size; + + test.equal(collection.find().count(), 3); + + // options and selector still work + test.equal(collection.find({ _id: { $ne: '1' } }, { skip: 1 }).count(), 1); + + // cursor reuse + const cursor1 = collection.find({ _id: { $ne: '1' } }, { skip: 1 }); + test.equal(cursor1.count(), 1); + test.equal(cursor1.fetch().length, 1); + + const cursor2 = collection.find({ _id: { $ne: '1' } }, { skip: 1 }); + test.equal(cursor2.fetch().length, 1); + test.equal(cursor2.count(), 1); + + const postCount = client.s.activeSessions.size; + test.equal(preCount, postCount); + } +); diff --git a/packages/mongo/mongo.d.ts b/packages/mongo/mongo.d.ts new file mode 100644 index 0000000000..43e8e9c3e7 --- /dev/null +++ b/packages/mongo/mongo.d.ts @@ -0,0 +1,598 @@ +import * as MongoNpmModule from 'mongodb'; +import { + Collection as MongoCollection, + CreateIndexesOptions, + Db as MongoDb, + Hint, + IndexSpecification, + MongoClient, +} from 'mongodb'; +import { Meteor } from 'meteor/meteor'; + +// Based on https://github.com/microsoft/TypeScript/issues/28791#issuecomment-443520161 +export type UnionOmit = T extends T + ? Pick> + : never; + +export namespace Mongo { + // prettier-ignore + type BsonType = 1 | "double" | + 2 | "string" | + 3 | "object" | + 4 | "array" | + 5 | "binData" | + 6 | "undefined" | + 7 | "objectId" | + 8 | "bool" | + 9 | "date" | + 10 | "null" | + 11 | "regex" | + 12 | "dbPointer" | + 13 | "javascript" | + 14 | "symbol" | + 15 | "javascriptWithScope" | + 16 | "int" | + 17 | "timestamp" | + 18 | "long" | + 19 | "decimal" | + -1 | "minKey" | + 127 | "maxKey" | "number"; + + type FieldExpression = { + $eq?: T | undefined; + $gt?: T | undefined; + $gte?: T | undefined; + $lt?: T | undefined; + $lte?: T | undefined; + $in?: T[] | undefined; + $nin?: T[] | undefined; + $ne?: T | undefined; + $exists?: boolean | undefined; + $type?: BsonType[] | BsonType | undefined; + $not?: FieldExpression | undefined; + $expr?: FieldExpression | undefined; + $jsonSchema?: any; + $mod?: number[] | undefined; + $regex?: RegExp | string | undefined; + $options?: string | undefined; + $text?: + | { + $search: string; + $language?: string | undefined; + $caseSensitive?: boolean | undefined; + $diacriticSensitive?: boolean | undefined; + } + | undefined; + $where?: string | Function | undefined; + $geoIntersects?: any; + $geoWithin?: any; + $near?: any; + $nearSphere?: any; + $all?: T[] | undefined; + $elemMatch?: T extends {} ? Query : FieldExpression | undefined; + $size?: number | undefined; + $bitsAllClear?: any; + $bitsAllSet?: any; + $bitsAnyClear?: any; + $bitsAnySet?: any; + $comment?: string | undefined; + }; + + type Flatten = T extends any[] ? T[0] : T; + + type Query = { + [P in keyof T]?: Flatten | RegExp | FieldExpression>; + } & { + $or?: Query[] | undefined; + $and?: Query[] | undefined; + $nor?: Query[] | undefined; + } & Dictionary; + + type QueryWithModifiers = { + $query: Query; + $comment?: string | undefined; + $explain?: any; + $hint?: Hint; + $maxScan?: any; + $max?: any; + $maxTimeMS?: any; + $min?: any; + $orderby?: any; + $returnKey?: any; + $showDiskLoc?: any; + $natural?: any; + }; + + type Selector = Query | QueryWithModifiers; + + type Dictionary = { [key: string]: T }; + type PartialMapTo = Partial>; + type OnlyArrays = T extends any[] ? T : never; + type OnlyElementsOfArrays = T extends any[] ? Partial : never; + type ElementsOf = { + [P in keyof T]?: OnlyElementsOfArrays; + }; + type PushModifier = { + [P in keyof T]?: + | OnlyElementsOfArrays + | { + $each?: T[P] | undefined; + $position?: number | undefined; + $slice?: number | undefined; + $sort?: 1 | -1 | Dictionary | undefined; + }; + }; + type ArraysOrEach = { + [P in keyof T]?: OnlyElementsOfArrays | { $each: T[P] }; + }; + type CurrentDateModifier = { $type: 'timestamp' | 'date' } | true; + type Modifier = + | T + | { + $currentDate?: + | (Partial> & + Dictionary) + | undefined; + $inc?: (PartialMapTo & Dictionary) | undefined; + $min?: + | (PartialMapTo & Dictionary) + | undefined; + $max?: + | (PartialMapTo & Dictionary) + | undefined; + $mul?: (PartialMapTo & Dictionary) | undefined; + $rename?: (PartialMapTo & Dictionary) | undefined; + $set?: (Partial & Dictionary) | undefined; + $setOnInsert?: (Partial & Dictionary) | undefined; + $unset?: + | (PartialMapTo & Dictionary) + | undefined; + $addToSet?: (ArraysOrEach & Dictionary) | undefined; + $push?: (PushModifier & Dictionary) | undefined; + $pull?: (ElementsOf & Dictionary) | undefined; + $pullAll?: (Partial & Dictionary) | undefined; + $pop?: (PartialMapTo & Dictionary<1 | -1>) | undefined; + }; + + type OptionalId = UnionOmit & { _id?: any }; + + interface SortSpecifier {} + interface FieldSpecifier { + [id: string]: Number; + } + + type Transform = ((doc: T) => any) | null | undefined; + + type Options = { + /** Sort order (default: natural order) */ + sort?: SortSpecifier | undefined; + /** Number of results to skip at the beginning */ + skip?: number | undefined; + /** Maximum number of results to return */ + limit?: number | undefined; + /** Dictionary of fields to return or exclude. */ + fields?: FieldSpecifier | undefined; + /** (Server only) Overrides MongoDB's default index selection and query optimization process. Specify an index to force its use, either by its name or index specification. */ + hint?: Hint | undefined; + /** (Client only) Default `true`; pass `false` to disable reactivity */ + reactive?: boolean | undefined; + /** Overrides `transform` on the [`Collection`](#collections) for this cursor. Pass `null` to disable transformation. */ + transform?: Transform | undefined; + }; + + type DispatchTransform = Transform extends ( + ...args: any + ) => any + ? ReturnType + : Transform extends null + ? T + : U; + + var Collection: CollectionStatic; + interface CollectionStatic { + /** + * Constructor for a Collection + * @param name The name of the collection. If null, creates an unmanaged (unsynchronized) local collection. + */ + new ( + name: string | null, + options?: { + /** + * The server connection that will manage this collection. Uses the default connection if not specified. Pass the return value of calling `DDP.connect` to specify a different + * server. Pass `null` to specify no connection. Unmanaged (`name` is null) collections cannot specify a connection. + */ + connection?: Object | null | undefined; + /** The method of generating the `_id` fields of new documents in this collection. Possible values: + * - **`'STRING'`**: random strings + * - **`'MONGO'`**: random [`Mongo.ObjectID`](#mongo_object_id) values + * + * The default id generation technique is `'STRING'`. + */ + idGeneration?: string | undefined; + /** + * An optional transformation function. Documents will be passed through this function before being returned from `fetch` or `findOne`, and before being passed to callbacks of + * `observe`, `map`, `forEach`, `allow`, and `deny`. Transforms are *not* applied for the callbacks of `observeChanges` or to cursors returned from publish functions. + */ + transform?: (doc: T) => U; + /** Set to `false` to skip setting up the mutation methods that enable insert/update/remove from client code. Default `true`. */ + defineMutationMethods?: boolean | undefined; + } + ): Collection; + } + interface Collection { + allow = undefined>(options: { + insert?: + | ((userId: string, doc: DispatchTransform) => boolean) + | undefined; + update?: + | (( + userId: string, + doc: DispatchTransform, + fieldNames: string[], + modifier: any + ) => boolean) + | undefined; + remove?: + | ((userId: string, doc: DispatchTransform) => boolean) + | undefined; + fetch?: string[] | undefined; + transform?: Fn | undefined; + }): boolean; + createCappedCollectionAsync( + byteSize?: number, + maxDocuments?: number + ): Promise; + createIndex( + indexSpec: IndexSpecification, + options?: CreateIndexesOptions + ): void; + createIndexAsync( + indexSpec: IndexSpecification, + options?: CreateIndexesOptions + ): Promise; + deny = undefined>(options: { + insert?: + | ((userId: string, doc: DispatchTransform) => boolean) + | undefined; + update?: + | (( + userId: string, + doc: DispatchTransform, + fieldNames: string[], + modifier: any + ) => boolean) + | undefined; + remove?: + | ((userId: string, doc: DispatchTransform) => boolean) + | undefined; + fetch?: string[] | undefined; + transform?: Fn | undefined; + }): boolean; + dropCollectionAsync(): Promise; + dropIndexAsync(indexName: string): void; + /** + * Find the documents in a collection that match the selector. + * @param selector A query describing the documents to find + */ + find(selector?: Selector | ObjectID | string): Cursor; + /** + * Find the documents in a collection that match the selector. + * @param selector A query describing the documents to find + */ + find>( + selector?: Selector | ObjectID | string, + options?: O + ): Cursor>; + /** + * Finds the first document that matches the selector, as ordered by sort and skip options. Returns `undefined` if no matching document is found. + * @param selector A query describing the documents to find + */ + findOne(selector?: Selector | ObjectID | string): U | undefined; + /** + * Finds the first document that matches the selector, as ordered by sort and skip options. Returns `undefined` if no matching document is found. + * @param selector A query describing the documents to find + */ + findOne, 'limit'>>( + selector?: Selector | ObjectID | string, + options?: O + ): DispatchTransform | undefined; + /** + * Finds the first document that matches the selector, as ordered by sort and skip options. Returns `undefined` if no matching document is found. + * @param selector A query describing the documents to find + */ + findOneAsync( + selector?: Selector | ObjectID | string + ): Promise; + /** + * Finds the first document that matches the selector, as ordered by sort and skip options. Returns `undefined` if no matching document is found. + * @param selector A query describing the documents to find + */ + findOneAsync, 'limit'>>( + selector?: Selector | ObjectID | string, + options?: O + ): Promise | undefined>; + /** + * Insert a document in the collection. Returns its unique _id. + * @param doc The document to insert. May not yet have an _id attribute, in which case Meteor will generate one for you. + * @param callback If present, called with an error object as the first argument and, if no error, the _id as the second. + */ + insert(doc: OptionalId, callback?: Function): string; + /** + * Insert a document in the collection. Returns its unique _id. + * @param doc The document to insert. May not yet have an _id attribute, in which case Meteor will generate one for you. + * @param callback If present, called with an error object as the first argument and, if no error, the _id as the second. + */ + insertAsync(doc: OptionalId, callback?: Function): Promise; + /** + * Returns the [`Collection`](http://mongodb.github.io/node-mongodb-native/3.0/api/Collection.html) object corresponding to this collection from the + * [npm `mongodb` driver module](https://www.npmjs.com/package/mongodb) which is wrapped by `Mongo.Collection`. + */ + rawCollection(): MongoCollection; + /** + * Returns the [`Db`](http://mongodb.github.io/node-mongodb-native/3.0/api/Db.html) object corresponding to this collection's database connection from the + * [npm `mongodb` driver module](https://www.npmjs.com/package/mongodb) which is wrapped by `Mongo.Collection`. + */ + rawDatabase(): MongoDb; + /** + * Remove documents from the collection + * @param selector Specifies which documents to remove + * @param callback If present, called with an error object as its argument. + */ + remove( + selector: Selector | ObjectID | string, + callback?: Function + ): number; + /** + * Remove documents from the collection + * @param selector Specifies which documents to remove + * @param callback If present, called with an error object as its argument. + */ + removeAsync( + selector: Selector | ObjectID | string, + callback?: Function + ): Promise; + /** + * Modify one or more documents in the collection. Returns the number of matched documents. + * @param selector Specifies which documents to modify + * @param modifier Specifies how to modify the documents + * @param callback If present, called with an error object as the first argument and, if no error, the number of affected documents as the second. + */ + update( + selector: Selector | ObjectID | string, + modifier: Modifier, + options?: { + /** True to modify all matching documents; false to only modify one of the matching documents (the default). */ + multi?: boolean | undefined; + /** True to insert a document if no matching documents are found. */ + upsert?: boolean | undefined; + /** + * Used in combination with MongoDB [filtered positional operator](https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/) to specify which elements to + * modify in an array field. + */ + arrayFilters?: { [identifier: string]: any }[] | undefined; + }, + callback?: Function + ): number; + /** + * Modify one or more documents in the collection. Returns the number of matched documents. + * @param selector Specifies which documents to modify + * @param modifier Specifies how to modify the documents + * @param callback If present, called with an error object as the first argument and, if no error, the number of affected documents as the second. + */ + updateAsync( + selector: Selector | ObjectID | string, + modifier: Modifier, + options?: { + /** True to modify all matching documents; false to only modify one of the matching documents (the default). */ + multi?: boolean | undefined; + /** True to insert a document if no matching documents are found. */ + upsert?: boolean | undefined; + /** + * Used in combination with MongoDB [filtered positional operator](https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/) to specify which elements to + * modify in an array field. + */ + arrayFilters?: { [identifier: string]: any }[] | undefined; + }, + callback?: Function + ): Promise; + /** + * Modify one or more documents in the collection, or insert one if no matching documents were found. Returns an object with keys `numberAffected` (the number of documents modified) and + * `insertedId` (the unique _id of the document that was inserted, if any). + * @param selector Specifies which documents to modify + * @param modifier Specifies how to modify the documents + * @param callback If present, called with an error object as the first argument and, if no error, the number of affected documents as the second. + */ + upsert( + selector: Selector | ObjectID | string, + modifier: Modifier, + options?: { + /** True to modify all matching documents; false to only modify one of the matching documents (the default). */ + multi?: boolean | undefined; + }, + callback?: Function + ): { + numberAffected?: number | undefined; + insertedId?: string | undefined; + }; + /** + * Modify one or more documents in the collection, or insert one if no matching documents were found. Returns an object with keys `numberAffected` (the number of documents modified) and + * `insertedId` (the unique _id of the document that was inserted, if any). + * @param selector Specifies which documents to modify + * @param modifier Specifies how to modify the documents + * @param callback If present, called with an error object as the first argument and, if no error, the number of affected documents as the second. + */ + upsertAsync( + selector: Selector | ObjectID | string, + modifier: Modifier, + options?: { + /** True to modify all matching documents; false to only modify one of the matching documents (the default). */ + multi?: boolean | undefined; + }, + callback?: Function + ): Promise<{ + numberAffected?: number | undefined; + insertedId?: string | undefined; + }>; + _createCappedCollection(byteSize?: number, maxDocuments?: number): void; + /** @deprecated */ + _ensureIndex( + indexSpec: IndexSpecification, + options?: CreateIndexesOptions + ): void; + _dropCollection(): Promise; + _dropIndex(indexName: string): void; + } + + var Cursor: CursorStatic; + interface CursorStatic { + /** + * To create a cursor, use find. To access the documents in a cursor, use forEach, map, or fetch. + */ + new (): Cursor; + } + interface ObserveCallbacks { + added?(document: T): void; + addedAt?(document: T, atIndex: number, before: T | null): void; + changed?(newDocument: T, oldDocument: T): void; + changedAt?(newDocument: T, oldDocument: T, indexAt: number): void; + removed?(oldDocument: T): void; + removedAt?(oldDocument: T, atIndex: number): void; + movedTo?( + document: T, + fromIndex: number, + toIndex: number, + before: T | null + ): void; + } + interface ObserveChangesCallbacks { + added?(id: string, fields: Partial): void; + addedBefore?(id: string, fields: Partial, before: T | null): void; + changed?(id: string, fields: Partial): void; + movedBefore?(id: string, before: T | null): void; + removed?(id: string): void; + } + interface Cursor { + /** + * Returns the number of documents that match a query. + * @param applySkipLimit If set to `false`, the value returned will reflect the total number of matching documents, ignoring any value supplied for limit. (Default: true) + */ + count(applySkipLimit?: boolean): number; + /** + * Returns the number of documents that match a query. + * @param applySkipLimit If set to `false`, the value returned will reflect the total number of matching documents, ignoring any value supplied for limit. (Default: true) + */ + countAsync(applySkipLimit?: boolean): Promise; + /** + * Return all matching documents as an Array. + */ + fetch(): Array; + /** + * Return all matching documents as an Array. + */ + fetchAsync(): Promise>; + /** + * Call `callback` once for each matching document, sequentially and + * synchronously. + * @param callback Function to call. It will be called with three arguments: the document, a 0-based index, and cursor itself. + * @param thisArg An object which will be the value of `this` inside `callback`. + */ + forEach( + callback: (doc: U, index: number, cursor: Cursor) => void, + thisArg?: any + ): void; + /** + * Call `callback` once for each matching document, sequentially and + * synchronously. + * @param callback Function to call. It will be called with three arguments: the document, a 0-based index, and cursor itself. + * @param thisArg An object which will be the value of `this` inside `callback`. + */ + forEachAsync( + callback: (doc: U, index: number, cursor: Cursor) => void, + thisArg?: any + ): Promise; + /** + * Map callback over all matching documents. Returns an Array. + * @param callback Function to call. It will be called with three arguments: the document, a 0-based index, and cursor itself. + * @param thisArg An object which will be the value of `this` inside `callback`. + */ + map( + callback: (doc: U, index: number, cursor: Cursor) => M, + thisArg?: any + ): Array; + /** + * Map callback over all matching documents. Returns an Array. + * @param callback Function to call. It will be called with three arguments: the document, a 0-based index, and cursor itself. + * @param thisArg An object which will be the value of `this` inside `callback`. + */ + mapAsync( + callback: (doc: U, index: number, cursor: Cursor) => M, + thisArg?: any + ): Promise>; + /** + * Watch a query. Receive callbacks as the result set changes. + * @param callbacks Functions to call to deliver the result set as it changes + */ + observe(callbacks: ObserveCallbacks): Meteor.LiveQueryHandle; + /** + * Watch a query. Receive callbacks as the result set changes. Only the differences between the old and new documents are passed to the callbacks. + * @param callbacks Functions to call to deliver the result set as it changes + */ + observeChanges( + callbacks: ObserveChangesCallbacks, + options?: { nonMutatingCallbacks?: boolean | undefined } + ): Meteor.LiveQueryHandle; + [Symbol.iterator](): Iterator; + [Symbol.asyncIterator](): AsyncIterator; + } + + var ObjectID: ObjectIDStatic; + interface ObjectIDStatic { + /** + * Create a Mongo-style `ObjectID`. If you don't specify a `hexString`, the `ObjectID` will generated randomly (not using MongoDB's ID construction rules). + + * @param hexString The 24-character hexadecimal contents of the ObjectID to create + */ + new (hexString?: string): ObjectID; + } + interface ObjectID { + toHexString(): string; + equals(otherID: ObjectID): boolean; + } + + function setConnectionOptions(options: any): void; +} + +export namespace Mongo { + interface AllowDenyOptions { + insert?: ((userId: string, doc: any) => boolean) | undefined; + update?: + | (( + userId: string, + doc: any, + fieldNames: string[], + modifier: any + ) => boolean) + | undefined; + remove?: ((userId: string, doc: any) => boolean) | undefined; + fetch?: string[] | undefined; + transform?: Function | null | undefined; + } +} + +export declare module MongoInternals { + interface MongoConnection { + db: MongoDb; + client: MongoClient; + } + + function defaultRemoteCollectionDriver(): { + mongo: MongoConnection; + }; + + var NpmModules: { + mongodb: { + version: string; + module: typeof MongoNpmModule; + }; + }; +} diff --git a/packages/mongo/mongo_driver.js b/packages/mongo/mongo_driver.js index f54e67361e..3b0aa5ebb5 100644 --- a/packages/mongo/mongo_driver.js +++ b/packages/mongo/mongo_driver.js @@ -829,6 +829,18 @@ MongoConnection.prototype.createIndex = function (collectionName, index, future.wait(); }; +MongoConnection.prototype.countDocuments = function (collectionName, ...args) { + args = args.map(arg => replaceTypes(arg, replaceMeteorAtomWithMongo)); + const collection = this.rawCollection(collectionName); + return collection.countDocuments(...args); +}; + +MongoConnection.prototype.estimatedDocumentCount = function (collectionName, ...args) { + args = args.map(arg => replaceTypes(arg, replaceMeteorAtomWithMongo)); + const collection = this.rawCollection(collectionName); + return collection.estimatedDocumentCount(...args); +}; + MongoConnection.prototype._ensureIndex = MongoConnection.prototype.createIndex; MongoConnection.prototype._dropIndex = function (collectionName, index) { @@ -910,11 +922,24 @@ function setupSynchronousCursor(cursor, method) { return cursor._synchronousCursor; } + +Cursor.prototype.count = function () { + const collection = this._mongo.rawCollection(this._cursorDescription.collectionName); + return Promise.await(collection.countDocuments( + replaceTypes(this._cursorDescription.selector, replaceMeteorAtomWithMongo), + replaceTypes(this._cursorDescription.options, replaceMeteorAtomWithMongo), + )); +}; + [...ASYNC_CURSOR_METHODS, Symbol.iterator, Symbol.asyncIterator].forEach(methodName => { - Cursor.prototype[methodName] = function (...args) { - const cursor = setupSynchronousCursor(this, methodName); - return cursor[methodName](...args); - }; + // count is handled specially since we don't want to create a cursor. + // it is still included in ASYNC_CURSOR_METHODS because we still want an async version of it to exist. + if (methodName !== 'count') { + Cursor.prototype[methodName] = function (...args) { + const cursor = setupSynchronousCursor(this, methodName); + return cursor[methodName](...args); + }; + } // These methods are handled separately. if (methodName === Symbol.iterator || methodName === Symbol.asyncIterator) { diff --git a/packages/mongo/oplog_v2_converter.js b/packages/mongo/oplog_v2_converter.js index 952a37478f..43c6e64411 100644 --- a/packages/mongo/oplog_v2_converter.js +++ b/packages/mongo/oplog_v2_converter.js @@ -36,7 +36,7 @@ function join(prefix, key) { return prefix ? `${prefix}.${key}` : key; } -const arrayOperatorKeyRegex = /^(a|u\d+)$/; +const arrayOperatorKeyRegex = /^(a|[su]\d+)$/; function isArrayOperatorKey(field) { return arrayOperatorKeyRegex.test(field); @@ -96,7 +96,9 @@ function convertOplogDiff(oplogEntry, diff, prefix) { } const positionKey = join(join(prefix, key), position.slice(1)); - if (value === null) { + if (position[0] === 's') { + convertOplogDiff(oplogEntry, value, positionKey); + } else if (value === null) { oplogEntry.$unset ??= {}; oplogEntry.$unset[positionKey] = true; } else { diff --git a/packages/mongo/oplog_v2_converter_tests.js b/packages/mongo/oplog_v2_converter_tests.js index f87c8877f3..79bcbada93 100644 --- a/packages/mongo/oplog_v2_converter_tests.js +++ b/packages/mongo/oplog_v2_converter_tests.js @@ -77,6 +77,71 @@ const cases = [ { $v: 2, diff: { u: { params: { e: { _str: '5f953cde8ceca90030bdb86f' } } } } }, { $v: 2, $set: { params: { e: { _str: '5f953cde8ceca90030bdb86f' } } } }, ], + [ + { + $v: 2, + diff: { + sitems: { + a: true, + s0: { + u: { id: 'm57DsX8g8L66bM5JX', name: 'Alice' }, + sbio: { u: { en: 'Just Alice' } }, + slanguages: { + a: true, + s0: { + u: { englishName: 'English', key: 'en', localName: 'English' }, + }, + }, + }, + u1: { + id: 'FJwSQHqwpenCN6RQH', + name: 'Bob', + title: { en: 'Fictional character', sv: '' }, + bio: { en: 'Just Bob', sv: '' }, + avatar: null, + languages: [ + { key: 'sv', englishName: 'Swedish', localName: 'Sverige' }, + ], + }, + u2: null + }, + }, + }, + { + $v: 2, + $set: { + 'items.0.id': 'm57DsX8g8L66bM5JX', + 'items.0.name': 'Alice', + 'items.0.bio.en': 'Just Alice', + 'items.0.languages.0.englishName': 'English', + 'items.0.languages.0.key': 'en', + 'items.0.languages.0.localName': 'English', + 'items.1': { + id: 'FJwSQHqwpenCN6RQH', + name: 'Bob', + title: { + en: 'Fictional character', + sv: '', + }, + bio: { + en: 'Just Bob', + sv: '', + }, + avatar: null, + languages: [ + { + key: 'sv', + englishName: 'Swedish', + localName: 'Sverige', + }, + ], + }, + }, + $unset: { + 'items.2': true + } + }, + ] ]; Tinytest.add('oplog - v2/v1 conversion', function (test) { diff --git a/packages/mongo/package.js b/packages/mongo/package.js index 1718220dc2..a714764d9c 100644 --- a/packages/mongo/package.js +++ b/packages/mongo/package.js @@ -9,7 +9,7 @@ Package.describe({ summary: "Adaptor for using MongoDB and Minimongo over DDP", - version: '1.16.1-beta.0' + version: '1.16.3' }); Npm.depends({ @@ -89,6 +89,7 @@ Package.onUse(function (api) { api.addFiles('remote_collection_driver.js', 'server'); api.addFiles('collection.js', ['client', 'server']); api.addFiles('connection_options.js', 'server'); + api.addAssets('mongo.d.ts', 'server'); }); Package.onTest(function (api) { diff --git a/packages/mongo/package.types.json b/packages/mongo/package.types.json new file mode 100644 index 0000000000..97c9c11e61 --- /dev/null +++ b/packages/mongo/package.types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "mongo.d.ts" +} diff --git a/packages/mongo/remote_collection_driver.js b/packages/mongo/remote_collection_driver.js index f237879de0..035af45157 100644 --- a/packages/mongo/remote_collection_driver.js +++ b/packages/mongo/remote_collection_driver.js @@ -4,13 +4,28 @@ MongoInternals.RemoteCollectionDriver = function ( self.mongo = new MongoConnection(mongo_url, options); }; +const REMOTE_COLLECTION_METHODS = [ + '_createCappedCollection', + '_dropIndex', + '_ensureIndex', + 'createIndex', + 'countDocuments', + 'dropCollection', + 'estimatedDocumentCount', + 'find', + 'findOne', + 'insert', + 'rawCollection', + 'remove', + 'update', + 'upsert', +]; + Object.assign(MongoInternals.RemoteCollectionDriver.prototype, { open: function (name) { var self = this; var ret = {}; - ['find', 'findOne', 'insert', 'update', 'upsert', - 'remove', '_ensureIndex', 'createIndex', '_dropIndex', '_createCappedCollection', - 'dropCollection', 'rawCollection'].forEach( + REMOTE_COLLECTION_METHODS.forEach( function (m) { ret[m] = _.bind(self.mongo[m], self.mongo, name); }); diff --git a/packages/non-core/less/.versions b/packages/non-core/less/.versions index b8dac39441..703024e52f 100644 --- a/packages/non-core/less/.versions +++ b/packages/non-core/less/.versions @@ -7,7 +7,7 @@ blaze@2.3.4 boilerplate-generator@1.7.1 caching-compiler@1.2.2 callback-hook@1.3.1 -check@1.3.1 +check@1.3.2 ddp@1.4.0 ddp-client@2.5.0 ddp-common@1.4.0 @@ -40,16 +40,16 @@ npm-mongo@3.9.0 observe-sequence@1.0.19 ordered-dict@1.1.0 promise@0.12.0 -random@1.2.0 +random@1.2.1 react-fast-refresh@0.1.1 -reactive-var@1.0.11 +reactive-var@1.0.12 reload@1.3.1 retry@1.1.0 routepolicy@1.1.1 socket-stream-client@0.4.0 test-helpers@1.2.0 tinytest@1.1.1 -tracker@1.2.0 -underscore@1.0.10 +tracker@1.2.1 +underscore@1.0.11 webapp@1.11.1 webapp-hashing@1.1.0 diff --git a/packages/npm-mongo/.npm/package/npm-shrinkwrap.json b/packages/npm-mongo/.npm/package/npm-shrinkwrap.json index 11662ebe99..b276e22ce7 100644 --- a/packages/npm-mongo/.npm/package/npm-shrinkwrap.json +++ b/packages/npm-mongo/.npm/package/npm-shrinkwrap.json @@ -62,214 +62,224 @@ } }, "@aws-sdk/abort-controller": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.190.0.tgz", - "integrity": "sha512-M6qo2exTzEfHT5RuW7K090OgesUojhb2JyWiV4ulu7ngY4DWBUBMKUqac696sHRUZvGE5CDzSi0606DMboM+kA==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.215.0.tgz", + "integrity": "sha512-HTvL542nawhVqe0oC1AJchdcomEOmPivJEzYUT1LqiG3e8ikxMNa2KWSqqLPeKi2t0A/cfQy7wDUyg9+BZhDSQ==" }, "@aws-sdk/client-cognito-identity": { - "version": "3.192.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.192.0.tgz", - "integrity": "sha512-nIRmiv5JY8wWGUadhG7yLx8o8aVETj5CAgO8e8UJIwwqfue/Yv9bHi2mvkUphO1pj0TeBatAtvu79neJQtsR5g==" + "version": "3.218.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.218.0.tgz", + "integrity": "sha512-IHzM9jpLqdeqj2w7YA7FrmLCQyKaun7eXtu1OJYMFbJT5XHx6B4jlQ1T/N8xivSSzDfjpJxG6/MMmjec4pI+CA==" }, "@aws-sdk/client-sso": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.190.0.tgz", - "integrity": "sha512-joEKRjJEzgvXnEih/x2UDDCPlvXWCO3MAHmqi44yJ36Ph4YsFS299mOjPdVLuzUtpQ+cST1nRO7hXNFrulW2jQ==" + "version": "3.218.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.218.0.tgz", + "integrity": "sha512-kVMlpjaVblxgb1G8q3wD65mKxO3RzKwnjUjIBmOHpmseXzlSkAdAvYcikaDoJP+CRmys4uXk5DN8c7ZdL0OmgA==" + }, + "@aws-sdk/client-sso-oidc": { + "version": "3.216.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.216.0.tgz", + "integrity": "sha512-O8kmM86BHwiSwyNoIe+iHXuSpUE9PBWl3re8u+/igt/w5W5VmMVz+zQr7gRUDQ1FDgLWNEdAJa0r+JFx3pZdzA==" }, "@aws-sdk/client-sts": { - "version": "3.192.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.192.0.tgz", - "integrity": "sha512-iv72dmRxbZ1cN5jGn4KIVzzu11eduS2fXHbNgd7JsFd5hLBV5TvJaugQzUdXNmy2gN4HiRJr+qa9WkD5b39lsA==" + "version": "3.218.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.218.0.tgz", + "integrity": "sha512-0A81eHvryKFEPq7IeY34Opzh5b9bVhhLlf2fDy5VuZjCFf4R9vD2ceOANvFSJeMsmdlqVDq8U1mHYl0E6FRUug==" }, "@aws-sdk/config-resolver": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.190.0.tgz", - "integrity": "sha512-K+VnDtjTgjpf7yHEdDB0qgGbHToF0pIL0pQMSnmk2yc8BoB3LGG/gg1T0Ki+wRlrFnDCJ6L+8zUdawY2qDsbyw==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.215.0.tgz", + "integrity": "sha512-DxX4R+YYLQOtg0qfceKBrjVD4t1mQBG1eb7IVr2QSlckFCX8ztUNymFMuaSEo3938Jyy/NpgfUDpFqPDaSKnng==" }, "@aws-sdk/credential-provider-cognito-identity": { - "version": "3.192.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.192.0.tgz", - "integrity": "sha512-CWo+KyHCGyYtvjlmDIGtnwBEkdiondergZADiStbFFvie8pPI7IsdTXNVssQQ1VxKIBGGHVebgZGSklHBqthwA==" + "version": "3.218.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.218.0.tgz", + "integrity": "sha512-ndhlPBvnxUgje23TnVw0fkDgTZHh0GVapKSgeEIxmxAy3IVLN15iMs7dCV7LWvb7z1P0cYx9cwvxa0nTrVxjtg==" }, "@aws-sdk/credential-provider-env": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.190.0.tgz", - "integrity": "sha512-GTY7l3SJhTmRGFpWddbdJOihSqoMN8JMo3CsCtIjk4/h3xirBi02T4GSvbrMyP7FP3Fdl4NAdT+mHJ4q2Bvzxw==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.215.0.tgz", + "integrity": "sha512-n5G7I7Pxfsn81+tNsSOzspKp9SYai78oRfImsfFY4JLTcWutv7szMgFUbtEzBfUUINHpOxLiO2Lk5yu5K1C7IQ==" }, "@aws-sdk/credential-provider-imds": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.190.0.tgz", - "integrity": "sha512-gI5pfBqGYCKdmx8igPvq+jLzyE2kuNn9Q5u73pdM/JZxiq7GeWYpE/MqqCubHxPtPcTFgAwxCxCFoXlUTBh/2g==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.215.0.tgz", + "integrity": "sha512-/4FUUR6u9gkNfxB6mEwBr0kk0myIkrDcXbAocWN3fPd/t7otzxpx/JqPZXgM6kcVP7M4T/QT75l1E1RRHLWCCQ==" }, "@aws-sdk/credential-provider-ini": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.190.0.tgz", - "integrity": "sha512-Z7NN/evXJk59hBQlfOSWDfHntwmxwryu6uclgv7ECI6SEVtKt1EKIlPuCLUYgQ4lxb9bomyO5lQAl/1WutNT5w==" + "version": "3.218.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.218.0.tgz", + "integrity": "sha512-tDDrGW+4A+PQThVJ+l9ee03CsDoD0XLpOB5dcf+dr/dCHjcQ7x/CeVFZ8eM+XUtGQnZVvuzXZGwzS8bUWEdJIg==" }, "@aws-sdk/credential-provider-node": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.190.0.tgz", - "integrity": "sha512-ctCG5+TsIK2gVgvvFiFjinPjc5nGpSypU3nQKCaihtPh83wDN6gCx4D0p9M8+fUrlPa5y+o/Y7yHo94ATepM8w==" + "version": "3.218.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.218.0.tgz", + "integrity": "sha512-J9PB6XFA+V0mgxleuY5W6Jjh5WejV8HjMViTJQpp2JN+NWZP3bGvquUSQHRqWGRGg2fSJy6Z/J4zQ8fpPbGsdQ==" }, "@aws-sdk/credential-provider-process": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.190.0.tgz", - "integrity": "sha512-sIJhICR80n5XY1kW/EFHTh5ZzBHb5X+744QCH3StcbKYI44mOZvNKfFdeRL2fQ7yLgV7npte2HJRZzQPWpZUrw==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.215.0.tgz", + "integrity": "sha512-JNvj4L5B7W8byoFdfn/8Y4scoPiwCi+Ha/fRsFCrdSC7C+snDuxM/oQj33HI8DpKY1cjuigzEnpnxiNWaA09EA==" }, "@aws-sdk/credential-provider-sso": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.190.0.tgz", - "integrity": "sha512-uarU9vk471MHHT+GJj3KWFSmaaqLNL5n1KcMer2CCAZfjs+mStAi8+IjZuuKXB4vqVs5DxdH8cy5aLaJcBlXwQ==" + "version": "3.218.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.218.0.tgz", + "integrity": "sha512-HecWvmxD+xffmY8G4SfLRfCOgSoLFki45wOOU8ESgRM9fQp2+3CfRSyiThKZI5PTmE+xhPTRvmR61HUmQjEv8w==" }, "@aws-sdk/credential-provider-web-identity": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.190.0.tgz", - "integrity": "sha512-nlIBeK9hGHKWC874h+ITAfPZ9Eaok+x/ydZQVKsLHiQ9PH3tuQ8AaGqhuCwBSH0hEAHZ/BiKeEx5VyWAE8/x+Q==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.215.0.tgz", + "integrity": "sha512-AWaDDEE3VU1HeLrXvyUrkQ6Wb3PQij5bvvrMil9L0da3b1yrcpoDanQQy7wBFBXcZIVmcmSFe5MMA/nyh2Le4g==" }, "@aws-sdk/credential-providers": { - "version": "3.192.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.192.0.tgz", - "integrity": "sha512-iBTrEPkfOHlfgQyk7EeUCmZnhUKXsGcc/hhxBbc6Z/Xc7Y8LqRVLbEmHq9lruXraFuvs26xV9oZi1s1UMXneQA==" + "version": "3.218.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.218.0.tgz", + "integrity": "sha512-MWpb5k+Oq56NrHA5fYPIDX8QRYUAw4Jp8ErTELBd83kLhTgqTw025YQ05YbhIzAs84+viMeWKif0z/5kNshphw==" }, "@aws-sdk/fetch-http-handler": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.190.0.tgz", - "integrity": "sha512-5riRpKydARXAPLesTZm6eP6QKJ4HJGQ3k0Tepi3nvxHVx3UddkRNoX0pLS3rvbajkykWPNC2qdfRGApWlwOYsA==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.215.0.tgz", + "integrity": "sha512-JfZyrJOE+0ik1PumsIUZd0NfgEx4sZ43VSdPCD9GRhssRWudNsSF1B5fz3xA5v+1y5oQPjXZyaWCzKtnYruiWw==" }, "@aws-sdk/hash-node": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.190.0.tgz", - "integrity": "sha512-DNwVT3O8zc9Jk/bXiXcN0WsD98r+JJWryw9F1/ZZbuzbf6rx2qhI8ZK+nh5X6WMtYPU84luQMcF702fJt/1bzg==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.215.0.tgz", + "integrity": "sha512-MkSRuZvo1RCRmI0VNEmRYCGGD/DkMd9lqnLtOyglMPnSX1mhyD4/DyXmcc3rYa7PsjDRAfykGWJRiMqpoMLjiQ==" }, "@aws-sdk/invalid-dependency": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.190.0.tgz", - "integrity": "sha512-crCh63e8d/Uw9y3dQlVTPja7+IZiXpNXyH6oSuAadTDQwMq6KK87Av1/SDzVf6bAo2KgAOo41MyO2joaCEk0dQ==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.215.0.tgz", + "integrity": "sha512-++bK4BUQe8/CL/YcLZcQB8qPOhiXxhbuhYzfFS7PNVvW1QOLqKRZL/lKs24gzjcOmw7IhAbCybDZwvu2TM4DAg==" }, "@aws-sdk/is-array-buffer": { - "version": "3.188.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.188.0.tgz", - "integrity": "sha512-n69N4zJZCNd87Rf4NzufPzhactUeM877Y0Tp/F3KiHqGeTnVjYUa4Lv1vLBjqtfjYb2HWT3NKlYn5yzrhaEwiQ==" + "version": "3.201.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.201.0.tgz", + "integrity": "sha512-UPez5qLh3dNgt0DYnPD/q0mVJY84rA17QE26hVNOW3fAji8W2wrwrxdacWOxyXvlxWsVRcKmr+lay1MDqpAMfg==" }, "@aws-sdk/middleware-content-length": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.190.0.tgz", - "integrity": "sha512-sSU347SuC6I8kWum1jlJlpAqeV23KP7enG+ToWcEcgFrJhm3AvuqB//NJxDbkKb2DNroRvJjBckBvrwNAjQnBQ==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.215.0.tgz", + "integrity": "sha512-zKJRb6jDLFl9nl/muSFbiQHA4uK3skinuDRcyLbpMvvzhuK/PVodv9QI1+wIUsFdXkaSxAlva1oG4bL8ZFi+sQ==" + }, + "@aws-sdk/middleware-endpoint": { + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.215.0.tgz", + "integrity": "sha512-W0QXL5emcN9IXtMbnWT/abLxBFH2tGIfnre2jPNmZ9M7uVFxUwwv5OTUXxNLGNehJHKhiJPwhfQvMy20IDzVcw==" }, "@aws-sdk/middleware-host-header": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.190.0.tgz", - "integrity": "sha512-cL7Vo/QSpGx/DDmFxjeV0Qlyi1atvHQDPn3MLBBmi1icu+3GKZkCMAJwzsrV3U4+WoVoDYT9FJ9yMQf2HaIjeQ==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.215.0.tgz", + "integrity": "sha512-GOqI7VwoENZwn+6tIMrrJ4SipIqL2JCh+BNvORVcy7CQxn1ViKkna7iaCx+QMjpg/kn9cR6kfY0n1FmgZR1w9A==" }, "@aws-sdk/middleware-logger": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.190.0.tgz", - "integrity": "sha512-rrfLGYSZCBtiXNrIa8pJ2uwUoUMyj6Q82E8zmduTvqKWviCr6ZKes0lttGIkWhjvhql2m4CbjG5MPBnY7RXL4A==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.215.0.tgz", + "integrity": "sha512-0h4GGF0rV3jnY3jxmcAWsOdqHCYf25s0biSjmgTei+l/5S+geOGrovRPCNep0LLg0i9D8bkZsXISojilETbf+g==" }, "@aws-sdk/middleware-recursion-detection": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.190.0.tgz", - "integrity": "sha512-5tc1AIIZe5jDNdyuJW+7vIFmQOxz3q031ZVrEtUEIF7cz2ySho2lkOWziz+v+UGSLhjHGKMz3V26+aN1FLZNxQ==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.215.0.tgz", + "integrity": "sha512-KQ+kiEsaluM4i6opjusUukxY78+UhfR7vzXHDkzZK/GplQ1hY0B+rwVO1eaULmlnmf3FK+Wd6lwrPV7xS2W+EA==" }, "@aws-sdk/middleware-retry": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.190.0.tgz", - "integrity": "sha512-h1bPopkncf2ue/erJdhqvgR2AEh0bIvkNsIHhx93DckWKotZd/GAVDq0gpKj7/f/7B+teHH8Fg5GDOwOOGyKcg==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.215.0.tgz", + "integrity": "sha512-I/dnUPVg2Kp3lW+MywBoPp06EOng8IfuaS9ph4bcJpQKrhNU5ekRgCHH2C4k1A6GcP8uyHxQ5TVV6j+l0QPIsA==" }, "@aws-sdk/middleware-sdk-sts": { - "version": "3.192.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.192.0.tgz", - "integrity": "sha512-xzTV7MyG5ipWYTvekWX1tQc5ExsUvCYsDTBCD3LR5hBrP8assUDPo52zGSe+QMcjgnQv7BcYIzeikTkLEG0dUw==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.215.0.tgz", + "integrity": "sha512-wJRxoDf+2egbRgochaQL8+zzADx8FM/2W0spKNj8x+t/3iqw70QwxCfuEKW/uFQ3ph6eaIrv7gYc8RRjwhD8rg==" }, "@aws-sdk/middleware-serde": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.190.0.tgz", - "integrity": "sha512-S132hEOK4jwbtZ1bGAgSuQ0DMFG4TiD4ulAwbQRBYooC7tiWZbRiR0Pkt2hV8d7WhOHgUpg7rvqlA7/HXXBAsA==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.215.0.tgz", + "integrity": "sha512-+uhLXdKvvQZcRRFc3UmemSr/YUHA4Jc+1YMjHxc3v8vvfztFJBb0wgBx999myOi8PmkYThlRBQDzXy9UCIhIJw==" }, "@aws-sdk/middleware-signing": { - "version": "3.192.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.192.0.tgz", - "integrity": "sha512-qTRIU/TL/dvtTrNj+AkZkgYeTIFslib3Y3XnQNNM6RCm4cMxIgs2K/lnhaUmLdbzHrpOQb4cISkY8yiHo+pNsw==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.215.0.tgz", + "integrity": "sha512-3BqzYqkmdPeOxjI8DVQE7Bm7J5QIvDy30abglXqrDg6npw6KonKI2Q3FIPFf+oLpZTMStwkoQOnwXHTPrSZ6Tg==" }, "@aws-sdk/middleware-stack": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.190.0.tgz", - "integrity": "sha512-h1mqiWNJdi1OTSEY8QovpiHgDQEeRG818v8yShpqSYXJKEqdn54MA3Z1D2fg/Wv/8ZJsFrBCiI7waT1JUYOmCg==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.215.0.tgz", + "integrity": "sha512-rdSVL7LxRgjlvoluqwODD4ypBy2k/YVl6FrDplyCMSi8m2WHZG99FzdmR9bpnWK+0DGzYZSMRYx6ynJ9N9PsSw==" }, "@aws-sdk/middleware-user-agent": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.190.0.tgz", - "integrity": "sha512-y/2cTE1iYHKR0nkb3DvR3G8vt12lcTP95r/iHp8ZO+Uzpc25jM/AyMHWr2ZjqQiHKNlzh8uRw1CmQtgg4sBxXQ==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.215.0.tgz", + "integrity": "sha512-X6GfoMNoEITTw7rGL/gWs8UZ0cmmmezvKcl+KtHsA642R05OR4mY5G7LdbWAw0bcrwKsuKOGmwUrC9lzGqbWUw==" }, "@aws-sdk/node-config-provider": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.190.0.tgz", - "integrity": "sha512-TJPUchyeK5KeEXWrwb6oW5/OkY3STCSGR1QIlbPcaTGkbo4kXAVyQmmZsY4KtRPuDM6/HlfUQV17bD716K65rQ==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.215.0.tgz", + "integrity": "sha512-notckD94QwwxC0GsfpTxB7VH8SREIIlMsUSddqGtpModa0cq/wRb9rqnydZSoznbYpK1ND6h0C9hr/2PNz89zw==" }, "@aws-sdk/node-http-handler": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.190.0.tgz", - "integrity": "sha512-3Klkr73TpZkCzcnSP+gmFF0Baluzk3r7BaWclJHqt2LcFUWfIJzYlnbBQNZ4t3EEq7ZlBJX85rIDHBRlS+rUyA==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.215.0.tgz", + "integrity": "sha512-btKWSR7m0UuWIN3p5MfSIvhqeYik7xri7U6nWuVI5GVzIYjzxEZOMvPAinDLDxL5wipodi0ZvTUNdDJdm7BcGQ==" }, "@aws-sdk/property-provider": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.190.0.tgz", - "integrity": "sha512-uzdKjHE2blbuceTC5zeBgZ0+Uo/hf9pH20CHpJeVNtrrtF3GALtu4Y1Gu5QQVIQBz8gjHnqANx0XhfYzorv69Q==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.215.0.tgz", + "integrity": "sha512-dDPjMCCopkRURAmOJCMSlpIQ5BGWCpYj0+FIfZ5qWQs24fn1PAkQHecOiBhJO0ZSVuQy3xcIyWsAp1NE5e+7ug==" }, "@aws-sdk/protocol-http": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.190.0.tgz", - "integrity": "sha512-s5MVfeONpfZYRzCSbqQ+wJ3GxKED+aSS7+CQoeaYoD6HDTDxaMGNv9aiPxVCzW02sgG7py7f29Q6Vw+5taZXZA==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.215.0.tgz", + "integrity": "sha512-qp6Y6v4S534LAjadiVl9p7ErK7ImphOKq6yhFyQwxko6iITLcz8ib3yU27fs4QJcnNj5ZooqW/YlL/0EikDxCQ==" }, "@aws-sdk/querystring-builder": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.190.0.tgz", - "integrity": "sha512-w9mTKkCsaLIBC8EA4RAHrqethNGbf60CbpPzN/QM7yCV3ZZJAXkppFfjTVVOMbPaI8GUEOptJtzgqV68CRB7ow==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.215.0.tgz", + "integrity": "sha512-eilk8CqG37BVhQklLif00K2dOJgDzacUi8h3KVQ72ry1V3h345i4HsmaFIxvnz8XtNyDvV8qFAzeYg9n2P9RQA==" }, "@aws-sdk/querystring-parser": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.190.0.tgz", - "integrity": "sha512-vCKP0s33VtS47LSYzEWRRr2aTbi3qNkUuQyIrc5LMqBfS5hsy79P1HL4Q7lCVqZB5fe61N8fKzOxDxWRCF0sXg==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.215.0.tgz", + "integrity": "sha512-8h/9H8dWM4fZO27UGzo8W5JXln4yJMugPyUl4qFA437gzPgNFN95+oLJWXtHMlfCHC5T/PDKetY9TarMDgBD0Q==" }, "@aws-sdk/service-error-classification": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.190.0.tgz", - "integrity": "sha512-g+s6xtaMa5fCMA2zJQC4BiFGMP7FN5/L1V/UwxCnKy8skCwaN0K5A1tFffBjjbYiPI7Gu7LVorWD2A0Y4xl01Q==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.215.0.tgz", + "integrity": "sha512-SKBvClGFGzMPsjBBKjneaUazLCNr6bSxe9eFvOr3gCwuwE2jPQwW3VE1mb62howuvm6cLthEDwLQp/FsT1gMsw==" }, "@aws-sdk/shared-ini-file-loader": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.190.0.tgz", - "integrity": "sha512-CZC/xsGReUEl5w+JgfancrxfkaCbEisyIFy6HALUYrioWQe80WMqLAdUMZSXHWjIaNK9mH0J/qvcSV2MuIoMzQ==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.215.0.tgz", + "integrity": "sha512-unzQeLOyUiYHr8WxxandHo0OaCj31gx0wpt8dn2cZcHm/MdCqHcHcsQqOVnQsWQrrxY/XZ27cPyMVQeicNKYwQ==" }, "@aws-sdk/signature-v4": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.190.0.tgz", - "integrity": "sha512-L/R/1X2T+/Kg2k/sjoYyDFulVUGrVcRfyEKKVFIUNg0NwUtw5UKa1/gS7geTKcg4q8M2pd/v+OCBrge2X7phUw==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.215.0.tgz", + "integrity": "sha512-Rc73uUCi3eJneO25DydLTfJYamXeuKS9YIhNMTKlpvcN1UQAmAnUbAmCuEmqvkYOiGD1i4/kd8kBga708iIikQ==" }, "@aws-sdk/smithy-client": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.190.0.tgz", - "integrity": "sha512-f5EoCwjBLXMyuN491u1NmEutbolL0cJegaJbtgK9OJw2BLuRHiBknjDF4OEVuK/WqK0kz2JLMGi9xwVPl4BKCA==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.215.0.tgz", + "integrity": "sha512-PiZfCdZkPohzMPrRmJ46TPOf2Tr/dhKYdwQArRnOOIsJABUGXjlzCUE8vysDN35XZYRx5f9hd+/U7kayhniq2w==" + }, + "@aws-sdk/token-providers": { + "version": "3.216.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.216.0.tgz", + "integrity": "sha512-cEmOfG7njWl0OA5lR65Sp2SW1i8ZLjf7C95TZ1e6t2Oo5aUFeN3aKBxMOV//1yc+BNzcFBnoHP/f29GhWxUOxA==" }, "@aws-sdk/types": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.190.0.tgz", - "integrity": "sha512-mkeZ+vJZzElP6OdRXvuLKWHSlDQxZP9u8BjQB9N0Rw0pCXTzYS0vzIhN1pL0uddWp5fMrIE68snto9xNR6BQuA==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.215.0.tgz", + "integrity": "sha512-eRbCVjwzTYd9C5e2mceScJ6D2kYDDEC3PLkYfJa+1wH9iiF2JlbiYozAokyeYBHQ+AjmD93MK58RBoM8iZfH0Q==" }, "@aws-sdk/url-parser": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.190.0.tgz", - "integrity": "sha512-FKFDtxA9pvHmpfWmNVK5BAVRpDgkWMz3u4Sg9UzB+WAFN6UexRypXXUZCFAo8S04FbPKfYOR3O0uVlw7kzmj9g==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.215.0.tgz", + "integrity": "sha512-r/qIk3TUlV36JvoRjTErFm0LzzgNKLB1YUG8zVZCGAc2TEATi8OVEmsZvi+KfTmsbszulITJVcjZKbHLbGoUzg==" }, - "@aws-sdk/util-base64-browser": { - "version": "3.188.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-browser/-/util-base64-browser-3.188.0.tgz", - "integrity": "sha512-qlH+5NZBLiyKziL335BEPedYxX6j+p7KFRWXvDQox9S+s+gLCayednpK+fteOhBenCcR9fUZOVuAPScy1I8qCg==" - }, - "@aws-sdk/util-base64-node": { - "version": "3.188.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.188.0.tgz", - "integrity": "sha512-r1dccRsRjKq+OhVRUfqFiW3sGgZBjHbMeHLbrAs9jrOjU2PTQ8PSzAXLvX/9lmp7YjmX17Qvlsg0NCr1tbB9OA==" + "@aws-sdk/util-base64": { + "version": "3.208.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.208.0.tgz", + "integrity": "sha512-PQniZph5A6N7uuEOQi+1hnMz/FSOK/8kMFyFO+4DgA1dZ5pcKcn5wiFwHkcTb/BsgVqQa3Jx0VHNnvhlS8JyTg==" }, "@aws-sdk/util-body-length-browser": { "version": "3.188.0", @@ -277,59 +287,64 @@ "integrity": "sha512-8VpnwFWXhnZ/iRSl9mTf+VKOX9wDE8QtN4bj9pBfxwf90H1X7E8T6NkiZD3k+HubYf2J94e7DbeHs7fuCPW5Qg==" }, "@aws-sdk/util-body-length-node": { - "version": "3.188.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.188.0.tgz", - "integrity": "sha512-XwqP3vxk60MKp4YDdvDeCD6BPOiG2e+/Ou4AofZOy5/toB6NKz2pFNibQIUg2+jc7mPMnGnvOW3MQEgSJ+gu/Q==" + "version": "3.208.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.208.0.tgz", + "integrity": "sha512-3zj50e5g7t/MQf53SsuuSf0hEELzMtD8RX8C76f12OSRo2Bca4FLLYHe0TZbxcfQHom8/hOaeZEyTyMogMglqg==" }, "@aws-sdk/util-buffer-from": { - "version": "3.188.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.188.0.tgz", - "integrity": "sha512-NX1WXZ8TH20IZb4jPFT2CnLKSqZWddGxtfiWxD9M47YOtq/SSQeR82fhqqVjJn4P8w2F5E28f+Du4ntg/sGcxA==" + "version": "3.208.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.208.0.tgz", + "integrity": "sha512-7L0XUixNEFcLUGPeBF35enCvB9Xl+K6SQsmbrPk1P3mlV9mguWSDQqbOBwY1Ir0OVbD6H/ZOQU7hI/9RtRI0Zw==" }, "@aws-sdk/util-config-provider": { - "version": "3.188.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.188.0.tgz", - "integrity": "sha512-LBA7tLbi7v4uvbOJhSnjJrxbcRifKK/1ZVK94JTV2MNSCCyNkFotyEI5UWDl10YKriTIUyf7o5cakpiDZ3O4xg==" + "version": "3.208.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.208.0.tgz", + "integrity": "sha512-DSRqwrERUsT34ug+anlMBIFooBEGwM8GejC7q00Y/9IPrQy50KnG5PW2NiTjuLKNi7pdEOlwTSEocJE15eDZIg==" }, "@aws-sdk/util-defaults-mode-browser": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.190.0.tgz", - "integrity": "sha512-FKxTU4tIbFk2pdUbBNneStF++j+/pB4NYJ1HRSEAb/g4D2+kxikR/WKIv3p0JTVvAkwcuX/ausILYEPUyDZ4HQ==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.215.0.tgz", + "integrity": "sha512-MiNfZgB0I4dR8CBxH163W7c9KvE38sgCHNPWopMqSX5ezz7cuCPohCU0XsWd4I7K31PvzuqmKgOiKBAZraQJMA==" }, "@aws-sdk/util-defaults-mode-node": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.190.0.tgz", - "integrity": "sha512-qBiIMjNynqAP7p6urG1+ZattYkFaylhyinofVcLEiDvM9a6zGt6GZsxru2Loq0kRAXXGew9E9BWGt45HcDc20g==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.215.0.tgz", + "integrity": "sha512-mSp3R8GljQ+4UT3QMOksQk9L0cWbFLvR7bBmAlt4+GobgTjpRfzFjBP3uwrCqFa3BKDUR3FeJq3qwo+xeY1Krg==" + }, + "@aws-sdk/util-endpoints": { + "version": "3.216.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.216.0.tgz", + "integrity": "sha512-uHje4H6Qj/z/op8UZoSuvGpEZhz/r+AGY0rCihFo7XjhT4RYVxb2Eb9uHRK/IAeHU4kjHAdpQiWGMSmnT/UacA==" }, "@aws-sdk/util-hex-encoding": { - "version": "3.188.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.188.0.tgz", - "integrity": "sha512-QyWovTtjQ2RYxqVM+STPh65owSqzuXURnfoof778spyX4iQ4z46wOge1YV2ZtwS8w5LWd9eeVvDrLu5POPYOnA==" + "version": "3.201.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.201.0.tgz", + "integrity": "sha512-7t1vR1pVxKx0motd3X9rI3m/xNp78p3sHtP5yo4NP4ARpxyJ0fokBomY8ScaH2D/B+U5o9ARxldJUdMqyBlJcA==" }, "@aws-sdk/util-locate-window": { - "version": "3.188.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.188.0.tgz", - "integrity": "sha512-SxobBVLZkkLSawTCfeQnhVX3Azm9O+C2dngZVe1+BqtF8+retUbVTs7OfYeWBlawVkULKF2e781lTzEHBBjCzw==" + "version": "3.208.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.208.0.tgz", + "integrity": "sha512-iua1A2+P7JJEDHVgvXrRJSvsnzG7stYSGQnBVphIUlemwl6nN5D+QrgbjECtrbxRz8asYFHSzhdhECqN+tFiBg==" }, "@aws-sdk/util-middleware": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.190.0.tgz", - "integrity": "sha512-qzTJ/qhFDzHZS+iXdHydQ/0sWAuNIB5feeLm55Io/I8Utv3l3TKYOhbgGwTsXY+jDk7oD+YnAi7hLN5oEBCwpg==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.215.0.tgz", + "integrity": "sha512-DfHGlFlQCr+T/xhjS36HH8JEThDVB5lg5NZ6x4Cibhyeps9YX/4ovLAIx3B19H34sdWhZi7q6LfslCHLRu2+7Q==" }, "@aws-sdk/util-uri-escape": { - "version": "3.188.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.188.0.tgz", - "integrity": "sha512-4Y6AYZMT483Tiuq8dxz5WHIiPNdSFPGrl6tRTo2Oi2FcwypwmFhqgEGcqxeXDUJktvaCBxeA08DLr/AemVhPCg==" + "version": "3.201.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.201.0.tgz", + "integrity": "sha512-TeTWbGx4LU2c5rx0obHeDFeO9HvwYwQtMh1yniBz00pQb6Qt6YVOETVQikRZ+XRQwEyCg/dA375UplIpiy54mA==" }, "@aws-sdk/util-user-agent-browser": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.190.0.tgz", - "integrity": "sha512-c074wjsD+/u9vT7DVrBLkwVhn28I+OEHuHaqpTVCvAIjpueZ3oms0e99YJLfpdpEgdLavOroAsNFtAuRrrTZZw==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.215.0.tgz", + "integrity": "sha512-uZz6BJWr8sJcA+onveS1lFqnbIXBHwvkyHLgCuuGhAxd5yY6YNLhpJBnhy9Fb8/aSbk6yao3qxlokqw9gthmAw==" }, "@aws-sdk/util-user-agent-node": { - "version": "3.190.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.190.0.tgz", - "integrity": "sha512-R36BMvvPX8frqFhU4lAsrOJ/2PJEHH/Jz1WZzO3GWmVSEAQQdHmo8tVPE3KOM7mZWe5Hj1dZudFAIxWHHFYKJA==" + "version": "3.215.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.215.0.tgz", + "integrity": "sha512-4lrdd1oGRwJEwfvgvg1jcJ2O0bwElsvtiqZfTRHN6MNTFUqsKl0xHlgFChQsz3Hfrc1niWtZCmbqQKGdO5ARpw==" }, "@aws-sdk/util-utf8-browser": { "version": "3.188.0", @@ -337,14 +352,14 @@ "integrity": "sha512-jt627x0+jE+Ydr9NwkFstg3cUvgWh56qdaqAMDsqgRlKD21md/6G226z/Qxl7lb1VEW2LlmCx43ai/37Qwcj2Q==" }, "@aws-sdk/util-utf8-node": { - "version": "3.188.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.188.0.tgz", - "integrity": "sha512-hCgP4+C0Lekjpjt2zFJ2R/iHes5sBGljXa5bScOFAEkRUc0Qw0VNgTv7LpEbIOAwGmqyxBoCwBW0YHPW1DfmYQ==" + "version": "3.208.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.208.0.tgz", + "integrity": "sha512-jKY87Acv0yWBdFxx6bveagy5FYjz+dtV8IPT7ay1E2WPWH1czoIdMAkc8tSInK31T6CRnHWkLZ1qYwCbgRfERQ==" }, "@types/node": { - "version": "18.11.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.2.tgz", - "integrity": "sha512-BWN3M23gLO2jVG8g/XHIRFWiiV4/GckeFIqbU/C4V3xpoBBWSMk4OZomouN0wCkfQFPqgZikyLr7DOYDysIkkw==" + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" }, "@types/webidl-conversions": { "version": "7.0.0", @@ -376,11 +391,6 @@ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==" }, - "denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" - }, "fast-xml-parser": { "version": "4.0.11", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.0.11.tgz", @@ -402,14 +412,14 @@ "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" }, "mongodb": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.11.0.tgz", - "integrity": "sha512-9l9n4Nk2BYZzljW3vHah3Z0rfS5npKw6ktnkmFgTcnzaXH1DRm3pDl6VMHu84EVb1lzmSaJC4OzWZqTkB5i2wg==" + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.12.1.tgz", + "integrity": "sha512-koT87tecZmxPKtxRQD8hCKfn+ockEL2xBiUvx3isQGI6mFmagWt4f4AyCE9J4sKepnLhMacoCTQQA6SLAI2L6w==" }, "mongodb-connection-string-url": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.5.4.tgz", - "integrity": "sha512-SeAxuWs0ez3iI3vvmLk/j2y+zHwigTDKQhtdxTgt5ZCOQQS5+HW4g45/Xw5vzzbn7oQXCNQ24Z40AkJsizEy7w==" + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==" }, "punycode": { "version": "2.1.1", @@ -447,9 +457,9 @@ "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==" }, "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" }, "uuid": { "version": "8.3.2", diff --git a/packages/npm-mongo/package.js b/packages/npm-mongo/package.js index 2222c52f7a..45d1a87a27 100644 --- a/packages/npm-mongo/package.js +++ b/packages/npm-mongo/package.js @@ -3,12 +3,12 @@ Package.describe({ summary: "Wrapper around the mongo npm package", - version: "4.11.0", + version: '4.12.1', documentation: null }); Npm.depends({ - mongodb: "4.11.0" + mongodb: "4.12.1" }); Package.onUse(function (api) { diff --git a/packages/oauth/package.js b/packages/oauth/package.js index 421be1d506..4b56f43d33 100644 --- a/packages/oauth/package.js +++ b/packages/oauth/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Common code for OAuth-based services", - version: "2.1.2" + version: "2.1.3" }); Package.onUse(api => { diff --git a/packages/oauth1/package.js b/packages/oauth1/package.js index bb07c66774..7435caf024 100644 --- a/packages/oauth1/package.js +++ b/packages/oauth1/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Common code for OAuth1-based login services", - version: "1.5.0", + version: "1.5.1", }); Package.onUse(api => { diff --git a/packages/oauth2/package.js b/packages/oauth2/package.js index c5f2fd0917..4ba099aa41 100644 --- a/packages/oauth2/package.js +++ b/packages/oauth2/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Common code for OAuth2-based login services", - version: "1.3.1", + version: "1.3.2", }); Package.onUse(api => { diff --git a/packages/package-version-parser/package-version-parser-tests.js b/packages/package-version-parser/package-version-parser-tests.js index 855dc56057..ff82ae9086 100644 --- a/packages/package-version-parser/package-version-parser-tests.js +++ b/packages/package-version-parser/package-version-parser-tests.js @@ -464,14 +464,14 @@ Tinytest.add("package-version-parser - Invalid in 0.9.2", function (test) { var invalidVersions = ["1.0.0_1", "1.0.0 || 2.0.0", "1.0.0-rc1_1", "3.4.0-rc1 || =1.0.0"]; - _.each(invalidVersions, function (v) { + invalidVersions.forEach(function (v) { test.isTrue(PackageVersion.invalidFirstFormatConstraint(v)); }); // These are all valid in 0.9.2. var validVersions = ["1.0.0", "2.0.0-rc1", "=2.5.0"]; - _.each(validVersions, function (v) { + validVersions.forEach(function (v) { test.isFalse(PackageVersion.invalidFirstFormatConstraint(v)); }); }); diff --git a/packages/package-version-parser/package.js b/packages/package-version-parser/package.js index 4c6ff0dc62..78a084498d 100644 --- a/packages/package-version-parser/package.js +++ b/packages/package-version-parser/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Parses Meteor Smart Package version strings", - version: "3.2.0" + version: "3.2.1" }); Npm.depends({ @@ -14,7 +14,6 @@ Package.onUse(function (api) { }); Package.onTest(function (api) { - api.use('package-version-parser'); - api.use(['tinytest', 'underscore']); + api.use(['package-version-parser', 'tinytest']); api.addFiles('package-version-parser-tests.js', 'server'); }); diff --git a/packages/promise/package.js b/packages/promise/package.js index 05f9c59eca..fcf72881c5 100644 --- a/packages/promise/package.js +++ b/packages/promise/package.js @@ -1,6 +1,6 @@ Package.describe({ name: "promise", - version: "0.12.1", + version: "0.12.2", summary: "ECMAScript 2015 Promise polyfill with Fiber support", git: "https://github.com/meteor/promise", documentation: "README.md" @@ -20,6 +20,7 @@ Package.onUse(function(api) { api.mainModule("client.js", "client"); api.mainModule("server.js", "server"); api.export("Promise"); + api.addAssets("promise.d.ts", ["client", "server"]); }); Package.onTest(function(api) { diff --git a/packages/promise/package.types.json b/packages/promise/package.types.json new file mode 100644 index 0000000000..b939961149 --- /dev/null +++ b/packages/promise/package.types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "promise.d.ts" +} diff --git a/packages/promise/promise.d.ts b/packages/promise/promise.d.ts new file mode 100644 index 0000000000..38698fc7cb --- /dev/null +++ b/packages/promise/promise.d.ts @@ -0,0 +1,23 @@ +export declare class Promise extends globalThis.Promise { + static async< + Fn extends (this: This, ...args: Args) => any, + This, + Args extends any[] + >( + fn: Fn, + allowReuseOfCurrentFiber?: boolean + ): (this: This, ...args: Args) => Promise>; + static asyncApply< + Fn extends (this: This, ...args: Args) => any, + This, + Args extends any[] + >( + fn: Fn, + context: This, + args: Args, + allowReuseOfCurrentFiber?: boolean + ): Promise>; + static await(value: PromiseLike): T; + static awaitAll(values: Iterable>): T[]; + await(): T; +} diff --git a/packages/random/package-types.json b/packages/random/package-types.json new file mode 100644 index 0000000000..71bd4af0cd --- /dev/null +++ b/packages/random/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "random.d.ts" +} diff --git a/packages/random/package.js b/packages/random/package.js index 370077010c..f6d9b6aae9 100644 --- a/packages/random/package.js +++ b/packages/random/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: 'Random number generator and utilities', - version: '1.2.0', + version: '1.2.1', }); Package.onUse(function (api) { @@ -8,6 +8,7 @@ Package.onUse(function (api) { api.export('Random'); api.mainModule('main_client.js', 'client'); api.mainModule('main_server.js', 'server'); + api.addAssets('random.d.ts', 'server'); }); Package.onTest(function (api) { diff --git a/packages/random/random.d.ts b/packages/random/random.d.ts new file mode 100644 index 0000000000..fd529f9a4d --- /dev/null +++ b/packages/random/random.d.ts @@ -0,0 +1,13 @@ +export namespace Random { + function id(numberOfChars?: number): string; + + function secret(numberOfChars?: number): string; + + function fraction(): number; + // @param numberOfDigits, @returns a random hex string of the given length + function hexString(numberOfDigits: number): string; + // @param array, @return a random element in array + function choice(array: T[]): T | undefined; + // @param str, @return a random char in str + function choice(str: string): string; +} diff --git a/packages/reactive-dict/package-types.json b/packages/reactive-dict/package-types.json new file mode 100644 index 0000000000..089231243f --- /dev/null +++ b/packages/reactive-dict/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "reactive-dict.d.ts" +} diff --git a/packages/reactive-dict/package.js b/packages/reactive-dict/package.js index ee7d4e4e9f..9fe4681c54 100644 --- a/packages/reactive-dict/package.js +++ b/packages/reactive-dict/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Reactive dictionary", - version: '1.3.0' + version: '1.3.1' }); Package.onUse(function (api) { @@ -9,6 +9,7 @@ Package.onUse(function (api) { api.use(['mongo', 'reload'], { weak: true }); api.mainModule('migration.js'); api.export('ReactiveDict'); + api.addAssets('reactive-dict.d.ts', 'server'); }); Package.onTest(function (api) { diff --git a/packages/reactive-dict/reactive-dict.d.ts b/packages/reactive-dict/reactive-dict.d.ts new file mode 100644 index 0000000000..b9a32ec4d8 --- /dev/null +++ b/packages/reactive-dict/reactive-dict.d.ts @@ -0,0 +1,94 @@ +import { EJSONable } from 'meteor/ejson'; + +export declare class ReactiveDict { + /** + * Constructor for a ReactiveDict, which represents a reactive dictionary of key/value pairs. + * @param name When a name is passed, preserves contents across Hot Code Pushes + * @param initialValue The default values for the dictionary + */ + constructor(name?: string, initialValue?: Partial); + /** + * Set a value for a key if it hasn't been set before. + * Otherwise works exactly the same as `ReactiveDict.set`. + * @param key The key to set, eg, `selectedItem` + * @param value The new value for `key` + */ + setDefault

(key: P, value?: O[P]): void; + /** + * Set a value for a key if it hasn't been set before. + * Otherwise works exactly the same as `ReactiveDict.set`. + */ + setDefault(object: Partial): void; + /** + * Set a value for a key in the ReactiveDict. Notify any listeners + * that the value has changed (eg: redraw templates, and rerun any + * `Tracker.autorun` computations, that called + * `ReactiveDict.get` on this `key`.) + * @param key The key to set, eg, `selectedItem` + * @param value The new value for `key` + */ + set

(key: P, value?: O[P]): void; + /** + * Set a value for a key in the ReactiveDict. Notify any listeners + * that the value has changed (eg: redraw templates, and rerun any + * `Tracker.autorun` computations, that called + * `ReactiveDict.get` on this `key`.) + */ + set(object: Partial): void; + /** + * Get the value assiciated with a key. If inside a reactive + * computation, invalidate the computation the next time the + * value associated with this key is changed by `ReactiveDict.set`. + * This returns a clone of the value, so if it's an object or an array, + * mutating the returned value has no effect on the value stored in the + * ReactiveDict. + * @param key The key of the element to return + */ + get

(key: P): O[P] | undefined; + /** + * Test if the stored entry for a key is equal to a value. If inside a + * reactive computation, invalidate the computation the next + * time the variable changes to or from the value. + * @param key The name of the session variable to test + * @param value The value to + * test against + */ + equals

( + key: P, + value: string | number | boolean | undefined | null + ): boolean; + /** + * Get all key-value pairs as a plain object. If inside a reactive + * computation, invalidate the computation the next time the + * value associated with any key is changed by `ReactiveDict.set`. + * This returns a clone of each value, so if it's an object or an array, + * mutating the returned value has no effect on the value stored in the + * ReactiveDict. + */ + all(): Partial; + /** + * remove all key-value pairs from the ReactiveDict. Notify any + * listeners that the value has changed (eg: redraw templates, and rerun any + * `Tracker.autorun` computations, that called + * `ReactiveDict.get` on this `key`.) + */ + clear(): void; + + /** + * remove a key-value pair from the ReactiveDict. Notify any listeners + * that the value has changed (eg: redraw templates, and rerun any + * `Tracker.autorun` computations, that called + * `ReactiveDict.get` on this `key`.) + * @param key The key to delete, eg, `selectedItem` + * @return did remove + */ + delete

(key: P): boolean; + /** + * Clear all values from the reactiveDict and prevent it from being + * migrated on a Hot Code Pushes. Notify any listeners + * that the value has changed (eg: redraw templates, and rerun any + * `Tracker.autorun` computations, that called + * `ReactiveDict.get` on this `key`.) + */ + destroy(): void; +} diff --git a/packages/reactive-var/package-types.json b/packages/reactive-var/package-types.json new file mode 100644 index 0000000000..24149d998a --- /dev/null +++ b/packages/reactive-var/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "reactive-var.d.ts" +} diff --git a/packages/reactive-var/package.js b/packages/reactive-var/package.js index 029608a876..05d1b1a194 100644 --- a/packages/reactive-var/package.js +++ b/packages/reactive-var/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Reactive variable", - version: '1.0.11' + version: '1.0.12' }); Package.onUse(function (api) { @@ -9,4 +9,5 @@ Package.onUse(function (api) { api.use('tracker'); api.addFiles('reactive-var.js'); + api.addAssets('reactive-var.d.ts', 'server'); }); diff --git a/packages/reactive-var/reactive-var.d.ts b/packages/reactive-var/reactive-var.d.ts new file mode 100644 index 0000000000..f7d22c7559 --- /dev/null +++ b/packages/reactive-var/reactive-var.d.ts @@ -0,0 +1,25 @@ +export declare var ReactiveVar: ReactiveVarStatic; + +export interface ReactiveVarStatic { + /** + * Constructor for a ReactiveVar, which represents a single reactive variable. + * @param initialValue The initial value to set. `equalsFunc` is ignored when setting the initial value. + * @param equalsFunc A function of two arguments, called on the old value and the new value whenever the ReactiveVar is set. If it returns true, no set is performed. If omitted, the default + * `equalsFunc` returns true if its arguments are `===` and are of type number, boolean, string, undefined, or null. + */ + new ( + initialValue: T, + equalsFunc?: (oldValue: T, newValue: T) => boolean + ): ReactiveVar; +} + +export interface ReactiveVar { + /** + * Returns the current value of the ReactiveVar, establishing a reactive dependency. + */ + get(): T; + /** + * Sets the current value of the ReactiveVar, invalidating the Computations that called `get` if `newValue` is different from the old value. + */ + set(newValue: T): void; +} diff --git a/packages/server-render/package-types.json b/packages/server-render/package-types.json new file mode 100644 index 0000000000..11840961ec --- /dev/null +++ b/packages/server-render/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "server-render.d.ts" +} diff --git a/packages/server-render/package.js b/packages/server-render/package.js index 47d3cadc5c..01be2f4b65 100644 --- a/packages/server-render/package.js +++ b/packages/server-render/package.js @@ -1,6 +1,6 @@ Package.describe({ name: "server-render", - version: "0.4.0", + version: "0.4.1", summary: "Generic support for server-side rendering in Meteor apps", documentation: "README.md" }); @@ -17,6 +17,7 @@ Package.onUse(function(api) { api.use("webapp"); api.mainModule("client.js", "client", { lazy: true }); api.mainModule("server.js", "server"); + api.addAssets('server-render.d.ts', 'server'); }); Package.onTest(function(api) { diff --git a/packages/server-render/server-render.d.ts b/packages/server-render/server-render.d.ts new file mode 100644 index 0000000000..8f55c0fe07 --- /dev/null +++ b/packages/server-render/server-render.d.ts @@ -0,0 +1,36 @@ +import * as http from 'http'; + +// NodeJS.ReadableStream only works on server. +// HTMLElement only works on client. +export type Content = string | Content[] | NodeJS.ReadableStream | HTMLElement; + +export interface ClientSink { + // Client and server. Only client + appendToHead(html: Content): void; + appendToBody(html: Content): void; + appendToElementById(id: string, html: Content): void; + renderIntoElementById(id: string, html: Content): void; + redirect(location: string, code?: number): void; + + // Server-only, but error-raising stubs provided to client: + setStatusCode(code: number): void; + setHeader(key: string, value: number | string | string[]): void; + getHeaders(): http.IncomingHttpHeaders; + getCookies(): { [key: string]: string }; +} + +export interface ServerSink extends ClientSink { + // Server-only: + request: http.IncomingMessage; + arch: string; + head: string; + body: string; + htmlById: { [key: string]: string }; + maybeMadeChanges: boolean; +} + +export type Sink = ClientSink | ServerSink; + +export type Callback = (sink: Sink) => Promise | any; + +export function onPageLoad(callback: T): T; diff --git a/packages/service-configuration/package-types.json b/packages/service-configuration/package-types.json new file mode 100644 index 0000000000..16883a4dac --- /dev/null +++ b/packages/service-configuration/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "service-configuration.d.ts" +} diff --git a/packages/service-configuration/package.js b/packages/service-configuration/package.js index ec496a1fff..a3042eb333 100644 --- a/packages/service-configuration/package.js +++ b/packages/service-configuration/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: 'Manage the configuration for third-party services', - version: '1.3.0', + version: '1.3.1', }); Package.onUse(function(api) { @@ -10,4 +10,5 @@ Package.onUse(function(api) { api.export('ServiceConfiguration'); api.addFiles('service_configuration_common.js', ['client', 'server']); api.addFiles('service_configuration_server.js', 'server'); + api.addAssets('service-configuration.d.ts', 'server'); }); diff --git a/packages/service-configuration/service-configuration.d.ts b/packages/service-configuration/service-configuration.d.ts new file mode 100644 index 0000000000..3c18cfaac3 --- /dev/null +++ b/packages/service-configuration/service-configuration.d.ts @@ -0,0 +1,10 @@ +import { Mongo } from 'meteor/mongo'; + +export interface Configuration { + appId: string; + secret: string; +} + +export declare var ServiceConfiguration: { + configurations: Mongo.Collection; +}; diff --git a/packages/session/package-types.json b/packages/session/package-types.json new file mode 100644 index 0000000000..ee5a93cac4 --- /dev/null +++ b/packages/session/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "session.d.ts" +} diff --git a/packages/session/package.js b/packages/session/package.js index 5515510f25..30f5529638 100644 --- a/packages/session/package.js +++ b/packages/session/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Session variable", - version: '1.2.0' + version: '1.2.1' }); Package.onUse(function (api) { @@ -13,6 +13,7 @@ Package.onUse(function (api) { api.export('Session', 'client'); api.mainModule('session.js', 'client'); + api.addAssets('session.d.ts', 'server'); }); Package.onTest(function (api) { diff --git a/packages/session/session.d.ts b/packages/session/session.d.ts new file mode 100644 index 0000000000..6e5afd4420 --- /dev/null +++ b/packages/session/session.d.ts @@ -0,0 +1,41 @@ +import { EJSONable } from 'meteor/ejson'; + +export namespace Session { + /** + * Test if a session variable is equal to a value. If inside a + * reactive computation, invalidate the computation the next + * time the variable changes to or from the value. + * @param key The name of the session variable to test + * @param value The value to test against + */ + function equals(key: string, value: string | number | boolean | any): boolean; + + /** + * Get the value of a session variable. If inside a reactive + * computation, invalidate the computation the next time the + * value of the variable is changed by `Session.set`. This + * returns a clone of the session value, so if it's an object or an array, + * mutating the returned value has no effect on the value stored in the + * session. + * @param key The name of the session variable to return + */ + function get(key: string): any; + + /** + * Set a variable in the session. Notify any listeners that the value + * has changed (eg: redraw templates, and rerun any + * `Tracker.autorun` computations, that called + * `Session.get` on this `key`.) + * @param key The key to set, eg, `selectedItem` + * @param value The new value for `key` + */ + function set(key: string, value: EJSONable | any): void; + + /** + * Set a variable in the session if it hasn't been set before. + * Otherwise works exactly the same as `Session.set`. + * @param key The key to set, eg, `selectedItem` + * @param value The new value for `key` + */ + function setDefault(key: string, value: EJSONable | any): void; +} diff --git a/packages/standard-minifier-css/package.js b/packages/standard-minifier-css/package.js index 97e0d8f3eb..7d6b2746e9 100644 --- a/packages/standard-minifier-css/package.js +++ b/packages/standard-minifier-css/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'standard-minifier-css', - version: '1.8.2', + version: '1.8.3', summary: 'Standard css minifier used with Meteor apps by default.', documentation: 'README.md' }); diff --git a/packages/test-in-browser/driver.css b/packages/test-in-browser/driver.css index 1a870bc9e9..f58f42fea1 100644 --- a/packages/test-in-browser/driver.css +++ b/packages/test-in-browser/driver.css @@ -1,6 +1,8 @@ body { height: 100vh; width: 100vw; + background-color: black !important; + color: white !important; } .test-in-browser { @@ -71,11 +73,12 @@ body { .test_table .groupname { font-weight: bold; - background: #ddd; + background: #212121; padding-left: 5px; line-height: 24px; vertical-align: middle; font-size: 16px; + color: white; } .test_table .event { @@ -114,11 +117,11 @@ body { line-height: 24px; vertical-align: middle; font-size: 14px; - color: #666; + color: #757575; } -.test_table .succeeded .teststatus { color: #090; /* green */ background: #cfc; } -.test_table .failed .teststatus { color: #900; /* red */ background: #fcc; } +.test_table .succeeded .teststatus { color: #090; /* green */ background: black; } +.test_table .failed .teststatus { color: #900; /* red */ background: black; } .test_table .running .teststatus { color: #ccc; } .test_table .event .expected_fail { color: #600; } diff --git a/packages/test-in-browser/driver.js b/packages/test-in-browser/driver.js index e707f6de2c..b8fb0a9ecf 100644 --- a/packages/test-in-browser/driver.js +++ b/packages/test-in-browser/driver.js @@ -451,7 +451,7 @@ Template.test.helpers({ eventsArray: function() { var events = this.events.filter(function(e) { - return e.type !== "finish"; + return e.type != "finish"; }); var partitionBy = function(seq, func) { diff --git a/packages/test-in-browser/package.js b/packages/test-in-browser/package.js index 7bc88d5cf7..57e3474024 100644 --- a/packages/test-in-browser/package.js +++ b/packages/test-in-browser/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Run tests interactively in the browser", - version: '1.3.0', + version: '1.3.2', documentation: null }); diff --git a/packages/test-in-console/puppeteerRunner.js b/packages/test-in-console/puppeteerRunner.js index a2d07f633f..c6509bd93d 100644 --- a/packages/test-in-console/puppeteerRunner.js +++ b/packages/test-in-console/puppeteerRunner.js @@ -1,12 +1,16 @@ const puppeteer = require('../../dev_bundle/lib/node_modules/puppeteer'); +let testNumber = 0; + async function runNextUrl(browser) { const page = await browser.newPage(); page.on('console', msg => { - if (msg._text !== undefined) { - console.log(msg._text); - } + // this is a way to make sure the travis does not timeout + // if the test is running for too long without any output to the console (10 minutes) + if (msg._text !== undefined) console.log(msg._text); + else console.log(`Test number: ${ testNumber }`); + testNumber++; }); if (!process.env.URL) { @@ -19,11 +23,15 @@ async function runNextUrl(browser) { async function poll() { if (await isDone(page)) { let failCount = await getFailCount(page); - console.log(`Tests complete with ${failCount} failures`); - console.log(`Tests complete with ${await getPassCount(page)} passes`); + console.log(` + The number of tests from Test number may be different because + of the way the test is written. causing the test to fail or + to run more than once. in the console. Test number total: ${ testNumber }`); + console.log(`Tests complete with ${ failCount } failures`); + console.log(`Tests complete with ${ await getPassCount(page) } passes`); if (failCount > 0) { const failed = await getFailed(page); - failed.map( (f) => console.log(`${f.name} failed: ${f.info}`)); + failed.map((f) => console.log(`${ f.name } failed: ${ f.info }`)); await page.close(); await browser.close(); process.exit(1); @@ -46,7 +54,7 @@ async function runNextUrl(browser) { * @return {Promise} */ async function isDone(page) { - return await page.evaluate(function() { + return await page.evaluate(function () { if (typeof TEST_STATUS !== 'undefined') { return TEST_STATUS.DONE; } @@ -61,7 +69,7 @@ async function isDone(page) { * @return {Promise} */ async function getPassCount(page) { - return await page.evaluate(function() { + return await page.evaluate(function () { if (typeof TEST_STATUS !== 'undefined') { return TEST_STATUS.PASSED; } @@ -76,7 +84,7 @@ async function getPassCount(page) { * @return {Promise} */ async function getFailCount(page) { - return await page.evaluate(function() { + return await page.evaluate(function () { if (typeof TEST_STATUS !== 'undefined') { return TEST_STATUS.FAILURES; } @@ -95,7 +103,7 @@ async function getFailCount(page) { * @return {Promise<[{name: string, info: string}]>} */ async function getFailed(page) { - return await page.evaluate(function() { + return await page.evaluate(function () { if (typeof TEST_STATUS !== 'undefined') { return TEST_STATUS.WHERE_FAILED; } @@ -104,11 +112,11 @@ async function getFailed(page) { } async function runTests() { - console.log(`Running test with Puppeteer at ${process.env.URL}`); + console.log(`Running test with Puppeteer at ${ process.env.URL }`); // --no-sandbox and --disable-setuid-sandbox must be disabled for CI compatibility const browser = await puppeteer.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] }); - console.log(`Using version: ${await browser.version()}`); + console.log(`Using version: ${ await browser.version() }`); runNextUrl(browser); } diff --git a/packages/tinytest/package.js b/packages/tinytest/package.js index 862749494b..21a7a053f3 100644 --- a/packages/tinytest/package.js +++ b/packages/tinytest/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Tiny testing framework", - version: '1.2.1' + version: '1.2.2' }); Package.onUse(function (api) { diff --git a/packages/tracker/package-types.json b/packages/tracker/package-types.json new file mode 100644 index 0000000000..7e03462336 --- /dev/null +++ b/packages/tracker/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "tracker.d.ts" +} diff --git a/packages/tracker/package.js b/packages/tracker/package.js index 9475828977..4448d88383 100644 --- a/packages/tracker/package.js +++ b/packages/tracker/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Dependency tracker to allow reactive callbacks", - version: "1.2.0" + version: "1.2.1" }); Package.onUse(function (api) { @@ -8,6 +8,7 @@ Package.onUse(function (api) { api.addFiles("tracker.js"); api.export("Tracker"); api.export("Deps"); + api.addAssets("tracker.d.ts", ["client", "server"]); }); Package.onTest(function (api) { diff --git a/packages/tracker/tracker.d.ts b/packages/tracker/tracker.d.ts new file mode 100644 index 0000000000..9ab6e0bdc5 --- /dev/null +++ b/packages/tracker/tracker.d.ts @@ -0,0 +1,128 @@ +/** + * The namespace for Tracker-related methods. + */ +export namespace Tracker { + function Computation(): void; + /** + * A Computation object represents code that is repeatedly rerun + * in response to + * reactive data changes. Computations don't have return values; they just + * perform actions, such as rerendering a template on the screen. Computations + * are created using Tracker.autorun. Use stop to prevent further rerunning of a + * computation. + */ + interface Computation { + /** + * True during the initial run of the computation at the time `Tracker.autorun` is called, and false on subsequent reruns and at other times. + */ + firstRun: boolean; + /** + * Invalidates this computation so that it will be rerun. + */ + invalidate(): void; + /** + * True if this computation has been invalidated (and not yet rerun), or if it has been stopped. + */ + invalidated: boolean; + /** + * Registers `callback` to run when this computation is next invalidated, or runs it immediately if the computation is already invalidated. The callback is run exactly once and not upon + * future invalidations unless `onInvalidate` is called again after the computation becomes valid again. + * @param callback Function to be called on invalidation. Receives one argument, the computation that was invalidated. + */ + onInvalidate(callback: Function): void; + /** + * Registers `callback` to run when this computation is stopped, or runs it immediately if the computation is already stopped. The callback is run after any `onInvalidate` callbacks. + * @param callback Function to be called on stop. Receives one argument, the computation that was stopped. + */ + onStop(callback: Function): void; + /** + * Prevents this computation from rerunning. + */ + stop(): void; + /** + * True if this computation has been stopped. + */ + stopped: boolean; + } + /** + * The current computation, or `null` if there isn't one. The current computation is the `Tracker.Computation` object created by the innermost active call to + * `Tracker.autorun`, and it's the computation that gains dependencies when reactive data sources are accessed. + */ + var currentComputation: Computation; + + var Dependency: DependencyStatic; + /** + * A Dependency represents an atomic unit of reactive data that a + * computation might depend on. Reactive data sources such as Session or + * Minimongo internally create different Dependency objects for different + * pieces of data, each of which may be depended on by multiple computations. + * When the data changes, the computations are invalidated. + */ + interface DependencyStatic { + new (): Dependency; + } + interface Dependency { + /** + * Invalidate all dependent computations immediately and remove them as dependents. + */ + changed(): void; + /** + * Declares that the current computation (or `fromComputation` if given) depends on `dependency`. The computation will be invalidated the next time `dependency` changes. + * If there is no current computation and `depend()` is called with no arguments, it does nothing and returns false. + * Returns true if the computation is a new dependent of `dependency` rather than an existing one. + * @param fromComputation An optional computation declared to depend on `dependency` instead of the current computation. + */ + depend(fromComputation?: Computation): boolean; + /** + * True if this Dependency has one or more dependent Computations, which would be invalidated if this Dependency were to change. + */ + hasDependents(): boolean; + } + + /** + * True if there is a current computation, meaning that dependencies on reactive data sources will be tracked and potentially cause the current computation to be rerun. + */ + var active: boolean; + + /** + * Schedules a function to be called during the next flush, or later in the current flush if one is in progress, after all invalidated computations have been rerun. The function will be run + * once and not on subsequent flushes unless `afterFlush` is called again. + * @param callback A function to call at flush time. + */ + function afterFlush(callback: Function): void; + + /** + * Run a function now and rerun it later whenever its dependencies + * change. Returns a Computation object that can be used to stop or observe the + * rerunning. + * @param runFunc The function to run. It receives one argument: the Computation object that will be returned. + */ + function autorun( + runFunc: (computation: Computation) => void, + options?: { + /** + * The function to run when an error + * happens in the Computation. The only argument it receives is the Error + * thrown. Defaults to the error being logged to the console. + */ + onError?: Function | undefined; + } + ): Computation; + + /** + * Process all reactive updates immediately and ensure that all invalidated computations are rerun. + */ + function flush(): void; + + /** + * Run a function without tracking dependencies. + * @param func A function to call immediately. + */ + function nonreactive(func: () => T): T; + + /** + * Registers a new `onInvalidate` callback on the current computation (which must exist), to be called immediately when the current computation is invalidated or stopped. + * @param callback A callback function that will be invoked as `func(c)`, where `c` is the computation on which the callback is registered. + */ + function onInvalidate(callback: Function): void; +} diff --git a/packages/twitter-oauth/package.js b/packages/twitter-oauth/package.js index 34e0d8bff8..62d7646ca8 100644 --- a/packages/twitter-oauth/package.js +++ b/packages/twitter-oauth/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Twitter OAuth flow", - version: "1.3.0" + version: '1.3.2' }); Package.onUse(function(api) { diff --git a/packages/typescript/package.js b/packages/typescript/package.js index df071432b9..21db263e8c 100644 --- a/packages/typescript/package.js +++ b/packages/typescript/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'typescript', - version: '4.5.4', + version: '4.6.4', summary: 'Compiler plugin that compiles TypeScript and ECMAScript in .ts and .tsx files', documentation: 'README.md', diff --git a/packages/underscore/package-types.json b/packages/underscore/package-types.json new file mode 100644 index 0000000000..347b86d05e --- /dev/null +++ b/packages/underscore/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "underscore.d.ts" +} diff --git a/packages/underscore/package.js b/packages/underscore/package.js index 483509a727..58525754ed 100644 --- a/packages/underscore/package.js +++ b/packages/underscore/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Collection of small helpers: _.map, _.each, ...", - version: '1.0.10' + version: '1.0.11' }); Package.onUse(function (api) { @@ -27,6 +27,8 @@ Package.onUse(function (api) { // numeric length field whose constructor === Object are still treated as // objects, not as arrays. Search for looksLikeArray. api.addFiles(['pre.js', 'underscore.js', 'post.js']); + + api.addAssets('underscore.d.ts', 'server'); }); diff --git a/packages/underscore/underscore.d.ts b/packages/underscore/underscore.d.ts new file mode 100644 index 0000000000..ffbb281e51 --- /dev/null +++ b/packages/underscore/underscore.d.ts @@ -0,0 +1,2 @@ +import * as _ from 'underscore'; +export { _ }; diff --git a/packages/webapp-hashing/package.js b/packages/webapp-hashing/package.js index 4098a5b937..12e2eac59c 100644 --- a/packages/webapp-hashing/package.js +++ b/packages/webapp-hashing/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Used internally by WebApp. Knows how to hash programs from manifests.", - version: "1.1.0" + version: '1.1.1' }); Package.onUse(function(api) { diff --git a/packages/webapp/package-types.json b/packages/webapp/package-types.json new file mode 100644 index 0000000000..ba477f3ba8 --- /dev/null +++ b/packages/webapp/package-types.json @@ -0,0 +1,3 @@ +{ + "typesEntry": "webapp.d.ts" +} diff --git a/packages/webapp/package.js b/packages/webapp/package.js index 5464ea7dc8..56e920fea2 100644 --- a/packages/webapp/package.js +++ b/packages/webapp/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: 'Serves a Meteor app over HTTP', - version: '1.13.1', + version: '1.13.2', }); Npm.depends({ @@ -57,6 +57,7 @@ Package.onUse(function(api) { api.export('WebApp', 'client'); api.mainModule('webapp_cordova.js', 'web.cordova'); + api.addAssets('webapp.d.ts', 'server'); }); Package.onTest(function(api) { diff --git a/packages/webapp/webapp.d.ts b/packages/webapp/webapp.d.ts new file mode 100644 index 0000000000..e13bb9ccf0 --- /dev/null +++ b/packages/webapp/webapp.d.ts @@ -0,0 +1,87 @@ +import * as http from 'http'; +import * as connect from 'connect'; + +export interface StaticFiles { + [key: string]: { + content?: string | undefined; + absolutePath: string; + cacheable: boolean; + hash: string; + sourceMapUrl?: string | undefined; + type: string; + }; +} + +export declare module WebApp { + var defaultArch: string; + var clientPrograms: { + [key: string]: { + format: string; + manifest: any; + version: string; + cordovaCompatibilityVersions?: any; + PUBLIC_SETTINGS: any; + }; + }; + var connectHandlers: connect.Server; + var rawConnectHandlers: connect.Server; + var httpServer: http.Server; + var connectApp: connect.Server; + function suppressConnectErrors(): void; + function onListening(callback: Function): void; + + type RuntimeConfigHookCallback = (options: { + arch: 'web.browser' | 'web.browser.legacy' | 'web.cordova'; + request: http.IncomingMessage; + encodedCurrentConfig: string; + updated: boolean; + }) => string | undefined | null | false; + function addRuntimeConfigHook(callback: RuntimeConfigHookCallback): void; + function decodeRuntimeConfig(rtimeConfigString: string): unknown; + function encodeRuntimeConfig(rtimeConfig: unknown): string; +} + +export declare module WebAppInternals { + var NpmModules: { + [key: string]: { + version: string; + module: any; + }; + }; + function identifyBrowser( + userAgentString: string + ): { + name: string; + major: string; + minor: string; + patch: string; + }; + function registerBoilerplateDataCallback( + key: string, + callback: Function + ): Function; + function generateBoilerplateInstance( + arch: string, + manifest: any, + additionalOptions: any + ): any; + + function staticFilesMiddleware( + staticFiles: StaticFiles, + req: http.IncomingMessage, + res: http.ServerResponse, + next: Function + ): void; + function parsePort(port: string): number; + function reloadClientPrograms(): void; + function generateBoilerplate(): void; + var staticFiles: StaticFiles; + function inlineScriptsAllowed(): boolean; + function setInlineScriptsAllowed(inlineScriptsAllowed: boolean): void; + + function setBundledJsCssUrlRewriteHook(hookFn: (url: string) => string): void; + function setBundledJsCssPrefix(bundledJsCssPrefix: string): void; + function addStaticJs(): void; + function getBoilerplate(request: http.IncomingMessage, arch: string): string; + var additionalStaticJs: any; +} diff --git a/packages/weibo-oauth/package.js b/packages/weibo-oauth/package.js index 9ce2620c89..e2de8dd3ba 100644 --- a/packages/weibo-oauth/package.js +++ b/packages/weibo-oauth/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Weibo OAuth flow", - version: "1.3.1", + version: "1.3.2", }); Package.onUse(api => { diff --git a/scripts/admin/meteor-release-experimental.json b/scripts/admin/meteor-release-experimental.json index 73b31948b6..acce35a806 100644 --- a/scripts/admin/meteor-release-experimental.json +++ b/scripts/admin/meteor-release-experimental.json @@ -1,6 +1,6 @@ { "track": "METEOR", - "version": "2.8-rc.0", + "version": "2.9.0", "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 41230791aa..27a0c865e4 100644 --- a/scripts/admin/meteor-release-official.json +++ b/scripts/admin/meteor-release-official.json @@ -1,6 +1,6 @@ { "track": "METEOR", - "version": "2.8.0", + "version": "2.9.0", "recommended": false, "official": true, "description": "The Official Meteor Distribution" diff --git a/scripts/admin/update-semver/index.js b/scripts/admin/update-semver/index.js index bc903a43f1..73c4a0fce8 100644 --- a/scripts/admin/update-semver/index.js +++ b/scripts/admin/update-semver/index.js @@ -7,6 +7,7 @@ const semver = require('semver'); const fs = require('fs'); const { exec } = require("child_process"); +const { readdir } = require("fs/promises"); const runCommand = async (command) => { return new Promise((resolve, reject) => { @@ -26,24 +27,64 @@ const runCommand = async (command) => { }) } +/** + * + * @returns {Promise} + */ async function getPackages() { return await runCommand("./get-diff.sh"); } +async function getFile(path) { + try { + const data = await fs.promises.readFile(path, 'utf8'); + return [data, null] + } catch (e) { + console.error(e); + return ['', e]; + } + +} + +const getDirectories = async source => + (await readdir(source, { withFileTypes: true })) + .filter(dirent => dirent.isDirectory()) + .map(dirent => dirent.name); + async function main() { + /** + * @type {string[]} + */ let args = process.argv.slice(2); - if (args[0] === '@auto') { - const packages = await getPackages(); + if (args[0].startsWith('@all')) { + const [_, type] = args[0].split('.'); + const allPackages = await getDirectories('../../../packages'); + args = allPackages.map((packageName) => `${ packageName }.${ type }`); + } + + if (args[0].startsWith('@auto')) { + const [_, type] = args[0].split('.'); + // List of packages that for some reason are not in the diff. + // If there is a change in one of them please do not forget + // to add it to the list. + // List: + // ddp-common + + const p = await getPackages(); + console.log('****************') + console.log('Will be updating the following packages:'); + console.dir(p) + console.log('****************') + const packages = p.concat(`packages/meteor-tool.${ type }`); args = packages .split('/') - .filter((packageName) => packageName !== 'packages' && packageName !== "\npackages" && packageName !== "\n"); + .filter((packageName) => packageName !== 'packages' && packageName !== "\npackages" && packageName !== "\n") + .map((packageName) => `${ packageName }.${ type }`); } + /** - * @type {{ - * name: string, - * version: string, - * }[]} + * @type {{release, name: string|null}[]} */ const packages = args.map(arg => { const [name, release] = arg.split('.'); @@ -51,8 +92,9 @@ async function main() { }); for (const { name, release } of packages) { const filePath = `../../../packages/${ name }/package.js`; - const code = await fs.promises.readFile(filePath, 'utf8'); - + const [code, err] = await getFile(filePath); + // if there is an error reading the file, we will skip it. + if (err) continue; for (const line of code.split(/\n/)) { // should only run on lines that have a version if (!line.includes('version')) continue; @@ -62,8 +104,13 @@ async function main() { // version: '1.2.3' <--- this is the line we want, we assure that it has a version in the previous if //}); const [_, versionValue] = line.split(':'); - const currentVersion = versionValue.trim().replace(',', ''); - const semverVersion = semver.coerce(currentVersion); + if (!versionValue) continue; + const currentVersion = versionValue + .trim() + .replace(',', '') + .replace(/'/g, '') + .replace(/"/g, ''); + /** * @@ -72,17 +119,19 @@ async function main() { */ function incrementNewVersion(release) { if (release.includes('beta') || release.includes('rc')) { - return semver.inc(semverVersion, 'prerelease', release); + return semver.inc(currentVersion, 'prerelease', release); } - return semver.inc(semverVersion, release); + return semver.inc(currentVersion, release); } const newVersion = incrementNewVersion(release); console.log(`Updating ${ name } from ${ currentVersion } to ${ newVersion }`); - const newCode = code.replace(currentVersion, "'" + newVersion + "'"); + const newCode = code.replace(currentVersion, `${ newVersion }`); await fs.promises.writeFile(filePath, newCode); } } + console.log('Done!'); + if (!args[0].startsWith('@auto')) console.log('Do not forget to update meteor-tool'); } main(); diff --git a/scripts/build-dev-bundle-common.sh b/scripts/build-dev-bundle-common.sh index d7b4cfaa10..ef6013c4e5 100644 --- a/scripts/build-dev-bundle-common.sh +++ b/scripts/build-dev-bundle-common.sh @@ -5,7 +5,7 @@ set -u UNAME=$(uname) ARCH=$(uname -m) -NODE_VERSION=14.20.1 +NODE_VERSION=14.21.1 MONGO_VERSION_64BIT=5.0.5 MONGO_VERSION_32BIT=3.2.22 NPM_VERSION=6.14.17 diff --git a/scripts/dev-bundle-tool-package.js b/scripts/dev-bundle-tool-package.js index f593b8b83c..cff64012cf 100644 --- a/scripts/dev-bundle-tool-package.js +++ b/scripts/dev-bundle-tool-package.js @@ -15,7 +15,7 @@ var packageJson = { "node-gyp": "8.0.0", "node-pre-gyp": "0.15.0", typescript: "4.5.4", - "@meteorjs/babel": "7.16.0-beta.7", + "@meteorjs/babel": "7.18.0-beta.3", // Keep the versions of these packages consistent with the versions // found in dev-bundle-server-package.js. "meteor-promise": "0.9.0", diff --git a/scripts/make-release-tarballs.sh b/scripts/make-release-tarballs.sh index 6b52e9c5e7..562b67823c 100755 --- a/scripts/make-release-tarballs.sh +++ b/scripts/make-release-tarballs.sh @@ -12,16 +12,16 @@ done echo "BRANCH_NAME = $BRANCH_NAME" echo "VERSION = $VERSION" -git fetch origin && git checkout release/METEOR@$VERSION && - git reset --hard origin/$BRANCH_NAME && +git fetch origin && git checkout release/METEOR@"$VERSION" && + git reset --hard origin/"$BRANCH_NAME" && git clean -df && - ./meteor admin make-bootstrap-tarballs --target-arch os.windows.x86_64 $VERSION win64 && - ./meteor admin make-bootstrap-tarballs --target-arch os.linux.x86_64 $VERSION linux64 && - ./meteor admin make-bootstrap-tarballs --target-arch os.osx.x86_64 $VERSION osx && - ./meteor admin make-bootstrap-tarballs --target-arch os.osx.arm64 $VERSION osx && - aws s3 mb s3://com.meteor.static/packages-bootstrap/$VERSION/ && - aws s3 cp --acl public-read win64/meteor-bootstrap-os.windows.x86_64.tar.gz s3://com.meteor.static/packages-bootstrap/$VERSION/ && - aws s3 cp --acl public-read linux64/meteor-bootstrap-os.linux.x86_64.tar.gz s3://com.meteor.static/packages-bootstrap/$VERSION/ && - aws s3 cp --acl public-read osx/meteor-bootstrap-os.osx.x86_64.tar.gz s3://com.meteor.static/packages-bootstrap/$VERSION/ && - aws s3 cp --acl public-read osx/meteor-bootstrap-os.osx.arm64.tar.gz s3://com.meteor.static/packages-bootstrap/$VERSION/ && - aws s3 ls s3://com.meteor.static/packages-bootstrap/$VERSION + ./meteor admin make-bootstrap-tarballs --target-arch os.windows.x86_64 "$VERSION" win64 && + aws s3 cp --acl public-read win64/meteor-bootstrap-os.windows.x86_64.tar.gz s3://com.meteor.static/packages-bootstrap/"$VERSION"/ && + ./meteor admin make-bootstrap-tarballs --target-arch os.linux.x86_64 "$VERSION" linux64 && + aws s3 cp --acl public-read linux64/meteor-bootstrap-os.linux.x86_64.tar.gz s3://com.meteor.static/packages-bootstrap/"$VERSION"/ && + ./meteor admin make-bootstrap-tarballs --target-arch os.osx.x86_64 "$VERSION" osx && + aws s3 cp --acl public-read osx/meteor-bootstrap-os.osx.x86_64.tar.gz s3://com.meteor.static/packages-bootstrap/"$VERSION"/ && + ./meteor admin make-bootstrap-tarballs --target-arch os.osx.arm64 "$VERSION" osx && + aws s3 cp --acl public-read osx/meteor-bootstrap-os.osx.arm64.tar.gz s3://com.meteor.static/packages-bootstrap/"$VERSION"/ && + aws s3 mb s3://com.meteor.static/packages-bootstrap/"$VERSION"/ && + aws s3 ls s3://com.meteor.static/packages-bootstrap/"$VERSION" diff --git a/tools/cli/commands-cordova.js b/tools/cli/commands-cordova.js index cc3f570e46..30a449ea46 100644 --- a/tools/cli/commands-cordova.js +++ b/tools/cli/commands-cordova.js @@ -57,7 +57,7 @@ function doAddPlatform(options) { return; } - // Only write the new platform list when we have succesfully synchronized + // Only write the new platform list when we have successfully synchronized. projectContext.platformList.write(installedPlatforms); for (var platform of platformsToAdd) { diff --git a/tools/cli/commands-packages-query.js b/tools/cli/commands-packages-query.js index b8db0b8fca..b058f6d5bb 100644 --- a/tools/cli/commands-packages-query.js +++ b/tools/cli/commands-packages-query.js @@ -278,7 +278,7 @@ var PkgImplies = function (pkgDeps) { var archName = (r.arch === "os") ? "server" : r.arch; architectures.push(archName); }); - // Sort architecures alphabetically. + // Sort architectures alphabetically. architectures.sort(); if (! _.isEmpty(architectures)) { self.data.push({ name: name, architectures: architectures }); diff --git a/tools/cli/commands.js b/tools/cli/commands.js index 39bb517e2c..136090327c 100644 --- a/tools/cli/commands.js +++ b/tools/cli/commands.js @@ -12,6 +12,13 @@ var archinfo = require('../utils/archinfo'); var catalog = require('../packaging/catalog/catalog.js'); var stats = require('../meteor-services/stats.js'); var Console = require('../console/console.js').Console; +const { + blue, + green, + purple, + red, + yellow +} = require('../console/console.js').colors; var projectContextModule = require('../project-context.js'); var release = require('../packaging/release.js'); @@ -535,12 +542,14 @@ main.registerCommand({ blaze: { type: Boolean }, react: { type: Boolean }, vue: { type: Boolean }, + 'vue-2': { type: Boolean }, typescript: { type: Boolean }, apollo: { type: Boolean }, svelte: { type: Boolean }, tailwind: { type: Boolean }, 'chakra-ui': { type: Boolean }, solid: { type: Boolean }, + prototype: { type: Boolean } }, catalogRefresh: new catalog.Refresh.Never() }, async function (options) { @@ -549,7 +558,13 @@ main.registerCommand({ // latest release to create a package if we are inside an app) if (options.package) { var packageName = options.args[0]; - + if (options.prototype) { + Console.error( + `The ${Console.command('--prototype')} option is no longer supported for packages.` + ); + Console.error(); + throw new main.ShowUsage; + } if (options.list || options.example) { Console.error("No package examples exist at this time."); Console.error(); @@ -792,6 +807,22 @@ main.registerCommand({ return transform(f); }, transformContents: function (contents, f) { + + // check if this app is just for prototyping if it is then we need to add autopublish and insecure in the packages file + if ((/packages/).test(f)) { + + const prototypePackages = + () => + 'autopublish # Publish all data to the clients (for prototyping)\n' + + 'insecure # Allow all DB writes from clients (for prototyping)'; + + // XXX: if there is the need to add more options maybe we should have a better abstraction for this if-else + if (options.prototype) { + return Buffer.from(contents.toString().replace(/~prototype~/g, prototypePackages())) + } else { + return Buffer.from(contents.toString().replace(/~prototype~/g, '')) + } + } if ((/(\.html|\.[jt]sx?|\.css)/).test(f)) { return Buffer.from(transform(contents.toString())); } else { @@ -909,7 +940,8 @@ main.registerCommand({ cmd("meteor create --minimal # to create an app with as few Meteor packages as possible"); cmd("meteor create --full # to create a more complete scaffolded app"); cmd("meteor create --react # to create a basic React-based app"); - cmd("meteor create --vue # to create a basic Vue-based app"); + cmd("meteor create --vue # to create a basic Vue3-based app"); + cmd("meteor create --vue-2 # to create a basic Vue2-based app"); cmd("meteor create --apollo # to create a basic Apollo + React app"); cmd("meteor create --svelte # to create a basic Svelte app"); cmd("meteor create --typescript # to create an app using TypeScript and React"); @@ -2512,6 +2544,298 @@ main.registerCommand({ }); +/////////////////////////////////////////////////////////////////////////////// +// generate +/////////////////////////////////////////////////////////////////////////////// + +/** + * + * @param question + * @returns {function(string): Promise} + */ +const createPrompt = () => { + const readline = require('readline') + .createInterface({ input: process.stdin, output: process.stdout }); + return async (question) => new Promise((resolve, reject) => { + readline.question(question, (answer) => { + resolve(answer); + }) + }) +} + +const sanitizeBoolAnswer = (string) => { + if (string === '') return true; + + if (string.toLowerCase() === 'y' || string.toLowerCase() === 'yes') return true; + + if (string.toLowerCase() === 'n' || string.toLowerCase() === 'no' ) return false; + + Console.error(red('You must provide a valid answer')); + Console.error(yellow('it should be either (y)es or (n)o or just press enter to accept the default value')); + throw new main.ExitWithCode(2); +} + +/** + * simple verification for the name + * @param scaffoldName {string} + */ +const checkScaffoldName = (scaffoldName) => { + if (scaffoldName === '') { + Console.error(red('You must provide a name for your model.')); + Console.error(yellow('Model names should not be empty.')); + throw new main.ExitWithCode(2); + } + + if (scaffoldName.includes('/')) { + Console.error(red('You must provide a valid name for your model.')); + Console.error(yellow('Model names should not contain slashes.')); + throw new main.ExitWithCode(2); + } + + const allNonWordRegex = /[^a-zA-Z0-9_-]/g; // all numbers and letters plus _ and - + if (allNonWordRegex.test(scaffoldName)) { + Console.error(red('You must provide a valid name for your model.')); + Console.error(yellow('Model names should not contain special characters except _ and -')); + throw new main.ExitWithCode(2); + } +} + +main.registerCommand({ + name: 'generate', + maxArgs: 1, + minArgs: 0, + options: { + path: { type: String }, + methods: { type: Boolean }, + publications: { type: Boolean }, + templatePath : { type: String }, + replaceFn : { type: String }, + }, + pretty: false, + catalogRefresh: new catalog.Refresh.Never() +}, async function (options) { + const { args, appDir } = options; + + const setup = async (arg0) => { + if (arg0 === undefined) { + const ask = createPrompt(); + // the ANSI color chart is here: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors + const scaffoldName = await ask(`What is the name of your ${yellow('model')}? `); + checkScaffoldName(scaffoldName); + const areMethods = await ask(`There will be methods [${green('Y')}/${red('n')}]? press enter for ${green('yes')} `); + const methods = sanitizeBoolAnswer(areMethods); + const arePublications = await ask(`There will be publications [${green('Y')}/${red('n')}]? press enter for ${green('yes')} `); + const publications = sanitizeBoolAnswer(arePublications); + const path = await ask(`Where it will be placed? press enter for ${yellow('./imports/api/')} `); + return { + isWizard: true, + scaffoldName, + path, + methods, + publications, + } + } + + const { + path, + methods, + publications + } = options; + + return { + isWizard: false, + scaffoldName: arg0, + path, + methods, + publications, + } + } + /** + * @type{string} + */ + const { + isWizard, + scaffoldName, + path, + methods, + publications + } = await setup(args[0]); + + checkScaffoldName(scaffoldName); + // get directory where we will place our files + const scaffoldPath = path ||`${ appDir }/imports/api/${ scaffoldName }`; + + /** + * + * @param appDir + * @returns {string[]} + */ + const getFilesInDir = (appDir) => { + const appPath = files.pathResolve(appDir); + return files.readdir(appPath); + } + + const getExtension = () => { + const rootFiles = getFilesInDir(appDir); + if (rootFiles.includes('tsconfig.json')) return 'ts' + else return 'js' + } + + /** + * + * @returns {string} + */ + const userTransformFilenameFn = (filename) => { + const path = files.pathResolve(files.pathJoin(appDir, options.replaceFn)); + const replaceFn = require(path).transformFilename; + if (typeof replaceFn !== 'function') { + Console.error(red('You must provide a valid function transformFilename.')); + Console.error(yellow('The function should be named transformFilename and should be exported.')); + throw new main.ExitWithCode(2); + } + return replaceFn(scaffoldName, filename); + } + /** + * + * @returns {string} + */ + const userTransformContentsFn = (contents, fileName) => { + const path = files.pathResolve(files.pathJoin(appDir, options.replaceFn)); + const replaceFn = require(path).transformContents; + if (typeof replaceFn !== 'function') { + Console.error(red('You must provide a valid function transformContents.')); + Console.error(yellow('The function should be named transformContents and should be exported.')); + throw new main.ExitWithCode(2); + } + return replaceFn(scaffoldName, contents, fileName); + } + + /** + * if contains - turns into pascal + * @param str{string} + * @returns {string} + */ + const toPascalCase = (str) => { + if(!str.includes('-')) return str.charAt(0).toUpperCase() + str.slice(1); + else return str.split('-').map(toPascalCase).join(''); + } + const toCamelCase = (str) => { + if(!str.includes('-')) return str.charAt(0).toLowerCase() + str.slice(1); + else return str.split('-').map(toPascalCase).join(''); + } + + /** + * + * @param name {string} + */ + const transformName = (name) => { + return name.replace(/\$\$name\$\$|\$\$PascalName\$\$|\$\$camelName\$\$/g, function (substring, args) { + if (substring === '$$name$$') return scaffoldName; + if (substring === '$$PascalName$$') return toPascalCase(scaffoldName); + if (substring === '$$camelName$$') return toCamelCase(scaffoldName); + }) + } + + /** + * + * @param content{string} + * @param fileName{string} + * @returns {string} + */ + const removeUnusedLines = (content, fileName) => { + if (methods && publications) return content; + if (!methods && !publications) return content; + if(!fileName.startsWith('index')) return content; + return content + .split('\n') + .filter(line => { + if (!methods && line.includes('methods')) return false; + if (!publications && line.includes('publications')) return false; + return true; + }) + .join('\n'); + } + /// Program + const rootFiles = getFilesInDir(appDir); + if (!rootFiles.includes('.meteor')) { + Console.error(red('You must be in a Meteor project to run this command')); + Console.error(yellow('You can create a new Meteor project with `meteor create`')); + throw new main.ExitWithCode(2); + } + + const extension = getExtension() + const assetsPath = () => { + if (options.templatePath){ + const templatePath = files.pathJoin(appDir, options.templatePath) + Console.info(`Using template that is in: ${purple(templatePath)}`) + return templatePath; + } + return files.pathJoin( + __dirnameConverted, + '..', + 'static-assets', + `scaffolds-${ extension }`) + } + // create directory + const isOk = files.mkdir_p(scaffoldPath); + if (!isOk) { + Console.error(red('Something went wrong when creating the folder')); + Console.error(yellow('Do you have the correct permissions?')); + throw new main.ExitWithCode(2); + } + + files.cp_r(assetsPath(), files.pathResolve(scaffoldPath), { + transformFilename: function (f) { + if (options.replaceFn) return userTransformFilenameFn(f); + return transformName(f); + }, + transformContents: function (contents, fileName) { + if (options.replaceFn) return userTransformContentsFn(contents.toString(), fileName); + const cleaned = removeUnusedLines(contents.toString(), fileName); + return transformName(cleaned); + } + }) + + const checkAndRemoveFiles = () => { + if (!methods) + files.unlink(files.pathJoin(scaffoldPath, `methods.${ extension }`)); + + if (!publications) + files.unlink(files.pathJoin(scaffoldPath, `publications.${ extension }`)); + } + + const xor = (a, b) => ( a || b ) && !( a && b ); + + if (!isWizard && xor(methods, publications)) { + checkAndRemoveFiles() + } + + if (isWizard) { + checkAndRemoveFiles() + } + + const packageJsonPath = files.pathJoin(appDir, 'package.json'); + const packageJsonFile = files.readFile(packageJsonPath, 'utf8'); + const packageJson = JSON.parse(packageJsonFile); + + const mainJsPath = + packageJson?.meteor?.mainModule?.server + ? files.pathJoin(appDir, packageJson.meteor.mainModule.server) + : files.pathJoin(appDir, 'server', 'main.js'); + const mainJs = files.readFile(mainJsPath); + const mainJsLines = mainJs.toString().split('\n'); + const importLine = path + ? `import '${path}';` + : `import '/imports/api/${ scaffoldName }';` + const mainJsFile = [importLine, ...mainJsLines].join('\n'); + files.writeFile(mainJsPath, mainJsFile); + + Console.info(`Created ${ blue(scaffoldName) } scaffold in ${ yellow(scaffoldPath) }`); + + return 0; +}); + + /////////////////////////////////////////////////////////////////////////////// // admin get-machine /////////////////////////////////////////////////////////////////////////////// diff --git a/tools/cli/default-npm-deps.js b/tools/cli/default-npm-deps.js index 9047a8f8d9..ea92f40695 100644 --- a/tools/cli/default-npm-deps.js +++ b/tools/cli/default-npm-deps.js @@ -16,7 +16,7 @@ export async function install(appDir, options) { // NOTE we need skel-minimal to pull in jQuery which right now is required for Blaze const { dependencies } = require("../static-assets/skel-blaze/package.json"); - // Write a minimial package.json with the same dependencies as the + // Write a minimal package.json with the same dependencies as the // default new-app package.json file. writeFile( packageJsonPath, diff --git a/tools/cli/help.txt b/tools/cli/help.txt index 13c612e9fd..71b63daf23 100644 --- a/tools/cli/help.txt +++ b/tools/cli/help.txt @@ -150,7 +150,7 @@ Options: >>> create Create a new project. -Usage: meteor create [--release ] [--bare|--minimal|--full|--react|--vue|--apollo|--svelte|--blaze|--tailwind|--chakra-ui|--solid] +Usage: meteor create [--release ] [--bare|--minimal|--full|--react|--vue|--vue-2|--apollo|--svelte|--blaze|--tailwind|--chakra-ui|--solid] meteor create [--release ] --example [] meteor create --list meteor create --package [] @@ -183,7 +183,8 @@ Options: --minimal Create an app with as few Meteor packages as possible. --full Create a fully scaffolded app. --react Create a basic react-based app, same as default. - --vue Create a basic vue-based app. + --vue Create a basic vue3-based app. + --vue-2 Create a basic vue2-based app. --apollo Create a basic apollo-based app. --svelte Create a basic svelte-based app. --typescript Create a basic Typescript React-based app. @@ -191,6 +192,7 @@ Options: --tailwind Create a basic react-based app, with tailwind configured. --chakra-ui Create a basic react-based app, with chakra-ui configured. --solid Create a basic solid-based app. + --prototype Create a prototype app with the insecure & autopublish packages. Can be used along with other app commands >>> update @@ -209,10 +211,10 @@ conflicts with other packages in the app. Passing the --patch argument will update to the latest patch, if such exists. Patch releases contain very minor changes, usually bug fixes. Updating to the latest patch is always recommended. This will try to not update non-core -packages unless strictly nessessary. +packages unless strictly necessary. Passing the --release argument will force update to a specific release of -meteor. This will not update non-core packages unless strictly nessessary. It +meteor. This will not update non-core packages unless strictly necessary. It is also possible that some packages cannot be updated to be compatible with the new release. If that happens, the app will not build until dependencies on those packages are removed. @@ -841,6 +843,34 @@ command. To see sites in a region other than us-east-1, set the DEPLOY_HOSTNAME environment variable. For example, `DEPLOY_HOSTNAME=eu-west-1.galaxy-deploy.meteor.com meteor list-sites` +>>> generate + +Generate boilerplate code for a MeteorJS RPC api. +It generates a collection with the name you pass and its methods. +Is JS and TS compatible. No collection name +runs the wizard. + +Usage: meteor generate [] [options] + +By default, generates a collection.ts|js file with the name you pass, +methods(insert, update, remove, find, findOne) in a methods.js|ts file +and publications.js|ts. If you just use the command without collectionName, +it will generate run the wizard, asking you what is necessary. + +We do have as well the templatePath, wich uses the template you pass to generate +the boilerplate code. You can use the default template or create your own. +for replacing the names, we offer $$PascalName$$, $$camelName$$, $$name$$. + +This is a MeteorJS project command. + +Options: + --help Show help. + --path The path to the folder where the files will be generated. Default is the current folder. + --templatePath Path to the template file check https://docs.meteor.com/commandline.html#meteorgenerate-templating for more info. + --replaceFn Replace function to replace the names in the template. Check https://docs.meteor.com/commandline.html#meteorgenerate-templating for more info. + --methods Generate methods. + --publications Generate publications. + >>> publish-release Publish a new meteor release to the package server. diff --git a/tools/console/console.js b/tools/console/console.js index 804dbfd6e8..fa672c120d 100644 --- a/tools/console/console.js +++ b/tools/console/console.js @@ -13,7 +13,7 @@ /// /// Sometimes, there is a phrase that shouldn't be split up over multiple /// lines (for example, 'meteor update'). When applicable, please use the -/// following functions (Some of them add aditional formatting, especially when +/// following functions (Some of them add additional formatting, especially when /// pretty-print is turned on): /// /// - Console.command: things to enter on the command-line, such as @@ -715,7 +715,7 @@ class Console extends ConsoleBase { // Passing in both options will offset the bulletPoint by the indentation, // like so: // " this message is indented by two." - // " => this mesage indented by two and + // " => this message indented by two and // and also starts with an arrow." // options(o) { @@ -1321,4 +1321,19 @@ class Console extends ConsoleBase { } } +const yellow = (text) => `\x1b[33m${ text }\x1b[0m`; +const red = (text) => `\x1b[31m${ text }\x1b[0m`; +const purple = (text) => `\x1b[35m${ text }\x1b[0m`; +const green = (text) => `\x1b[32m${ text }\x1b[0m`; +const blue = (text) => `\x1b[34m${ text }\x1b[0m`; + +const colors = { + yellow, + red, + purple, + green, + blue, +}; + +exports.colors = colors; exports.Console = new Console; diff --git a/tools/cordova/builder.js b/tools/cordova/builder.js index 06fa87760b..3c35e42dbb 100644 --- a/tools/cordova/builder.js +++ b/tools/cordova/builder.js @@ -133,9 +133,9 @@ export class CordovaBuilder { ios: {}, android: { "AndroidXEnabled": true, - // we still use a port based on appId on iOS to avoid conflits on local webserver - // we dont need it on android, but the contentUrl can only be one, and we set this - // here to be able to intercept these calls + // We still use a port based on appId on iOS to avoid conflits on local webserver. + // We don't need it on android, but the contentUrl can only be one, and we set this + // here to be able to intercept these calls. "hostname": `localhost:${cordovaServerPort}`, "AndroidInsecureFileModeEnabled": true } @@ -867,7 +867,7 @@ configuration. The key may be deprecated.`); * [Cordova documentation](http://cordova.apache.org/docs/en/7.x/config_ref/index.html#resource-file). * @param {String} src The project resource path. * @param {String} target Resource destination in build. - * @param {String} [platform] Optional. A platform name (either `ios` or `android`, both if ommited) to add a resource-file entry. + * @param {String} [platform] Optional. A platform name (either `ios` or `android`, both if omitted) to add a resource-file entry. * @memberOf App */ addResourceFile: function (src, target, platform) { diff --git a/tools/fs/files.ts b/tools/fs/files.ts index e0e9fa713d..1f195bf227 100644 --- a/tools/fs/files.ts +++ b/tools/fs/files.ts @@ -991,7 +991,7 @@ Profile("files.writeFileAtomically", function (filename: string, contents: strin rename(tmpFile, filename); }); -// Like fs.symlinkSync, but creates a temporay link and renames it over the +// Like fs.symlinkSync, but creates a temporary link and renames it over the // file; this means it works even if the file already exists. // Do not use this function on Windows, it won't work. export function symlinkOverSync(linkText: string, file: string) { @@ -1016,7 +1016,7 @@ export function symlinkOverSync(linkText: string, file: string) { // files.FancySyntaxError, from which you may read 'message', 'file', // 'line', and 'column' attributes ... v8 is normally reluctant to // reveal this information but will write it to stderr if you pass it -// an undocumented flag. Unforunately though node doesn't have dup2 so +// an undocumented flag. Unfortunately though node doesn't have dup2 so // we can't intercept the write. So instead we use a completely // different parser with a better error handling API. Ah well. The // underlying V8 issue is: diff --git a/tools/fs/watch.ts b/tools/fs/watch.ts index 4d87f26d69..5607349d73 100644 --- a/tools/fs/watch.ts +++ b/tools/fs/watch.ts @@ -726,9 +726,9 @@ export class Watcher { return stat; } - // Iterates over the array, calling handleItem for each item - // When this._async is true, it pauses ocassionally to avoid blocking for too long - // Stops iterating after watcher is stopped + // Iterates over the array, calling handleItem for each item. + // When this._async is true, it pauses occasionally to avoid blocking for too long. + // Stops iterating after watcher is stopped. private processBatches( array: T[], handleItem: (item: T) => any, diff --git a/tools/isobuild/builder.js b/tools/isobuild/builder.js index c8853e3abe..5fb3f0858d 100644 --- a/tools/isobuild/builder.js +++ b/tools/isobuild/builder.js @@ -63,7 +63,7 @@ export default class Builder { outputPath, previousBuilder, // Even though in-place builds are disabled by default on some - // platforms (Windows), they can be forcibly reenabled with this + // platforms (Windows), they can be forcibly re-enabled with this // option, in cases where it's safe and/or necessary to avoid // clobbering existing files. forceInPlaceBuild = false, diff --git a/tools/isobuild/bundler.js b/tools/isobuild/bundler.js index 6071cf061e..8e8f016ac2 100644 --- a/tools/isobuild/bundler.js +++ b/tools/isobuild/bundler.js @@ -473,7 +473,7 @@ class NodeModulesDirectory { if (start >= parts.length) { // If "node_modules" is the final part, then there's nothing - // futher to examine, yet. + // further to examine, yet. return true; } @@ -832,7 +832,7 @@ class Target { // Top-level entry point for building a target. Generally to build a // target, you create with 'new', call make() to specify its sources - // and build options and actually do the work of buliding the + // and build options and actually do the work of building the // target, and finally you retrieve the build product with a // target-type-dependent function such as write() or toJsImage(). // @@ -1041,7 +1041,7 @@ class Target { buildmessage.error( "circular dependency between packages " + unibuild.pkg.name + " and " + usedUnibuild.pkg.name); - // recover by not enforcing one of the depedencies + // recover by not enforcing one of the dependencies return; } onStack[usedUnibuild.id] = true; @@ -2850,7 +2850,7 @@ var writeFile = Profile("bundler writeFile", async function (file, builder, opti // return the provided buffer without modification. function removeSourceMappingURLs(data) { if (Buffer.isBuffer(data)) { - // Unfortuantely there is no way to search a Buffer using a RegExp, so + // Unfortunately there is no way to search a Buffer using a RegExp, so // there's a chance of false positives here, which could lead to // unnecessarily stringifying and re-Buffer.from-ing the data, though // that should not cause any logical problems. @@ -3085,7 +3085,7 @@ Find out more about Meteor at meteor.com. await builder.complete(); // Now, go and "fix up" the outputPath properties of the sub-builders. - // Since the sub-builders originally were targetted at a temporary + // Since the sub-builders originally were targeted at a temporary // buildPath of the main builder, their outputPath properties need to // be adjusted so we can later pass them as previousBuilder's Object.keys(builders).forEach(name => { @@ -3389,7 +3389,7 @@ async function bundle({ // Tell the webapp package to pause responding to requests from // clients that use this arch, because we're about to write a // new version of this bundle to disk. If the message fails - // becuase the child process exited, proceed with writing the + // because the child process exited, proceed with writing the // target anyway. await pauseClient(arch).catch(ignoreHarmlessErrors); diff --git a/tools/isobuild/compiler.js b/tools/isobuild/compiler.js index eb12e945fe..24c8457e92 100644 --- a/tools/isobuild/compiler.js +++ b/tools/isobuild/compiler.js @@ -520,7 +520,7 @@ var compileUnibuild = Profile(function (options) { // addAssets or putting it in the public/private directories in an app. // // This is a backwards-incompatible change, but it doesn't affect - // previously-published packages (because the check is occuring in the + // previously-published packages (because the check is occurring in the // compiler), and it doesn't affect apps (where random files outside of // private/public never end up in the source list anyway). // diff --git a/tools/isobuild/js-analyze.js b/tools/isobuild/js-analyze.js index 1b8abee398..834f8ea809 100644 --- a/tools/isobuild/js-analyze.js +++ b/tools/isobuild/js-analyze.js @@ -297,7 +297,7 @@ export function findAssignedGlobals(source, hash) { programScope.implicit.left.forEach(entry => { if (entry.identifier && entry.identifier.type === "Identifier" && - // Only consider identifers that are assigned a value. + // Only consider identifiers that are assigned a value. entry.writeExpr) { assignedGlobals[entry.identifier.name] = true; } diff --git a/tools/isobuild/package-source.js b/tools/isobuild/package-source.js index 2fd7a885ea..ae0f1388a0 100644 --- a/tools/isobuild/package-source.js +++ b/tools/isobuild/package-source.js @@ -506,7 +506,7 @@ Object.assign(PackageSource.prototype, { var initFromPackageDirOptions = options; // If we know what package we are initializing, we pass in a - // name. Otherwise, we are intializing the base package specified by 'name:' + // name. Otherwise, we are initializing the base package specified by 'name:' // field in Package.Describe. In that case, it is clearly not a test // package. (Though we could be initializing a specific package without it // being a test, for a variety of reasons). @@ -1420,7 +1420,7 @@ Object.assign(PackageSource.prototype, { // subdirectories, so that we know whether we need to descend // further. If sources is still empty after we handle everything // else in dir, then nothing in this node_modules subdir can be - // imported by anthing outside of it, so we can ignore it. + // imported by anything outside of it, so we can ignore it. nodeModulesDir = subdir; // A "local" node_modules directory is one that's managed by the diff --git a/tools/isobuild/resolver.ts b/tools/isobuild/resolver.ts index 0261f90908..12f2477ec6 100644 --- a/tools/isobuild/resolver.ts +++ b/tools/isobuild/resolver.ts @@ -127,7 +127,7 @@ export default class Resolver { // Resolve the given module identifier to an object { path, stat } or // null, relative to an absolute parent path. The _seenDirPaths - // parameter is for internal use only and should be ommitted. + // parameter is for internal use only and should be omitted. public resolve( id: string, absParentPath: string, diff --git a/tools/meteor-services/deploy.js b/tools/meteor-services/deploy.js index 9d03503dd6..65c32ea1af 100644 --- a/tools/meteor-services/deploy.js +++ b/tools/meteor-services/deploy.js @@ -488,7 +488,7 @@ export async function bundleAndDeploy(options) { let preflightPassword = null; if (options.isBuildOnly) { - Console.info('Skipping pre authentication as the option --build-only was provded.'); + Console.info('Skipping pre authentication as the option --build-only was provided.'); } else { site = options.site && canonicalizeSite(options.site) if (! site) { diff --git a/tools/packaging/package-client.js b/tools/packaging/package-client.js index 8c4dbf52be..44d8ab2c57 100644 --- a/tools/packaging/package-client.js +++ b/tools/packaging/package-client.js @@ -809,7 +809,6 @@ exports.publishPackage = function (options) { // XXX If package version already exists, print a nice error message // telling them to try 'meteor publish-for-arch' if they want to // publish a new build. - // Documentation is smaller than the source. Upload it first, to minimize // the chances of PUT URLs expiring. (XXX: in the far future, parallelize this) buildmessage.enterJob("uploading documentation", function () { diff --git a/tools/packaging/warehouse.js b/tools/packaging/warehouse.js index e4142eeaa9..a8e42166e2 100644 --- a/tools/packaging/warehouse.js +++ b/tools/packaging/warehouse.js @@ -6,7 +6,7 @@ // // Because of this, we do have to be careful that the files used by this code // and the files used by tropohouse.js (the modern version of the warehouse) -// don't overlap. tropohouse does not use tools or releases directorys, and +// don't overlap. tropohouse does not use tools or releases directories, and // while they both have packages directories with similar structures, the // version names should not overlap: warehouse versions are SHAs and tropohouse // versions are semvers. Additionally, while they do both use the 'meteor' diff --git a/tools/runners/run-mongo.js b/tools/runners/run-mongo.js index 6fe07107ba..ca25b736c7 100644 --- a/tools/runners/run-mongo.js +++ b/tools/runners/run-mongo.js @@ -8,7 +8,7 @@ var _ = require('underscore'); import { loadIsopackage } from '../tool-env/isopackets.js'; var Console = require('../console/console.js').Console; -// Given a Mongo URL, open an interative Mongo shell on this terminal +// Given a Mongo URL, open an interactive Mongo shell on this terminal // on that database. var runMongoShell = function(url) { var mongoPath = files.pathJoin( diff --git a/tools/static-assets/README.md b/tools/static-assets/README.md index db232e18af..1884abb1d6 100644 --- a/tools/static-assets/README.md +++ b/tools/static-assets/README.md @@ -40,6 +40,14 @@ Similar to `skel`, `skel-chakra-ui` is copied on `meteor create --chakra-ui` com Similar to `skel`, `skel-solid` is copied on `meteor create --solid` command. +## skel-vue - Package Skeleton + +Similar to `skel`, `skel-vue` is copied on `meteor create --vue` command. + +## skel-vue-2 - Package Skeleton + +Similar to `skel`, `skel-vue-2` is copied on `meteor create --vue-2` command. + ## server - Bundled App's Bootstrap The `server` folder is copied by Isobuild when the app is bundled (on diff --git a/tools/static-assets/scaffolds-js/collection.js b/tools/static-assets/scaffolds-js/collection.js new file mode 100644 index 0000000000..a8a92d7cde --- /dev/null +++ b/tools/static-assets/scaffolds-js/collection.js @@ -0,0 +1,3 @@ +import { Mongo } from 'meteor/mongo'; + +export const $$PascalName$$Collection = new Mongo.Collection('$$name$$'); diff --git a/tools/static-assets/scaffolds-js/index.js b/tools/static-assets/scaffolds-js/index.js new file mode 100644 index 0000000000..59951d14bb --- /dev/null +++ b/tools/static-assets/scaffolds-js/index.js @@ -0,0 +1,3 @@ +export * from './collection'; +export * from './methods'; +export * from './publications'; diff --git a/tools/static-assets/scaffolds-js/methods.js b/tools/static-assets/scaffolds-js/methods.js new file mode 100644 index 0000000000..415f0ebb0a --- /dev/null +++ b/tools/static-assets/scaffolds-js/methods.js @@ -0,0 +1,29 @@ +import { Meteor } from 'meteor/meteor'; +import { check } from 'meteor/check'; +import { $$PascalName$$Collection } from './collection'; + +export async function create(data) { + return $$PascalName$$Collection.insertAsync({ ...data }); +} + +export async function update(_id, data) { + check(_id, String); + return $$PascalName$$Collection.updateAsync(_id, { ...data }); +} + +export async function remove(_id) { + check(_id, String); + return $$PascalName$$Collection.removeAsync(_id); +} + +export async function findById(_id) { + check(_id, String); + return $$PascalName$$Collection.findOneAsync(_id); +} + +Meteor.methods({ + '$$PascalName$$.create': create, + '$$PascalName$$.update': update, + '$$PascalName$$.remove': remove, + '$$PascalName$$.find': findById +}); diff --git a/tools/static-assets/scaffolds-js/publications.js b/tools/static-assets/scaffolds-js/publications.js new file mode 100644 index 0000000000..7e3a996634 --- /dev/null +++ b/tools/static-assets/scaffolds-js/publications.js @@ -0,0 +1,6 @@ +import { Meteor } from 'meteor/meteor'; +import { $$PascalName$$Collection } from './collection'; + +Meteor.publish('all$$PascalName$$s', function publish$$PascalName$$s() { + return $$PascalName$$Collection.find({}); +}); diff --git a/tools/static-assets/scaffolds-ts/collection.ts b/tools/static-assets/scaffolds-ts/collection.ts new file mode 100644 index 0000000000..f579cd71a2 --- /dev/null +++ b/tools/static-assets/scaffolds-ts/collection.ts @@ -0,0 +1,9 @@ +import { Mongo } from 'meteor/mongo'; + +export type $$PascalName$$ = { + _id?: string; + name: string; + createdAt: Date; +} + +export const $$PascalName$$Collection = new Mongo.Collection<$$PascalName$$, $$PascalName$$>('$$name$$'); diff --git a/tools/static-assets/scaffolds-ts/index.ts b/tools/static-assets/scaffolds-ts/index.ts new file mode 100644 index 0000000000..59951d14bb --- /dev/null +++ b/tools/static-assets/scaffolds-ts/index.ts @@ -0,0 +1,3 @@ +export * from './collection'; +export * from './methods'; +export * from './publications'; diff --git a/tools/static-assets/scaffolds-ts/methods.ts b/tools/static-assets/scaffolds-ts/methods.ts new file mode 100644 index 0000000000..d36e1cd42c --- /dev/null +++ b/tools/static-assets/scaffolds-ts/methods.ts @@ -0,0 +1,30 @@ +import { Meteor } from 'meteor/meteor'; +import { Mongo } from 'meteor/mongo'; +import { check } from 'meteor/check'; +import { $$PascalName$$, $$PascalName$$Collection } from './collection'; + +export async function create(data: $$PascalName$$) { + return $$PascalName$$Collection.insertAsync({ ...data }); +} + +export async function update(_id: string, data: Mongo.Modifier<$$PascalName$$>) { + check(_id, String); + return $$PascalName$$Collection.updateAsync(_id, { ...data }); +} + +export async function remove(_id: string) { + check(_id, String); + return $$PascalName$$Collection.removeAsync(_id); +} + +export async function findById(_id: string) { + check(_id, String); + return $$PascalName$$Collection.findOneAsync(_id); +} + +Meteor.methods({ + '$$PascalName$$.create': create, + '$$PascalName$$.update': update, + '$$PascalName$$.remove': remove, + '$$PascalName$$.find': findById +}); diff --git a/tools/static-assets/scaffolds-ts/publications.ts b/tools/static-assets/scaffolds-ts/publications.ts new file mode 100644 index 0000000000..818932bba3 --- /dev/null +++ b/tools/static-assets/scaffolds-ts/publications.ts @@ -0,0 +1,6 @@ +import { Meteor, Subscription } from 'meteor/meteor'; +import { $$PascalName$$Collection } from './collection'; + +Meteor.publish('all$$PascalName$$s', function publish$$PascalName$$s() { + return $$PascalName$$Collection.find({}); +}); diff --git a/tools/static-assets/skel-apollo/.meteor/packages b/tools/static-assets/skel-apollo/.meteor/packages index caa775ee6f..0addfea192 100644 --- a/tools/static-assets/skel-apollo/.meteor/packages +++ b/tools/static-assets/skel-apollo/.meteor/packages @@ -16,7 +16,7 @@ ecmascript # Enable ECMAScript2015+ syntax in app code typescript # Enable TypeScript syntax in .ts and .tsx modules shell-server # Server-side component of the `meteor shell` command hot-module-replacement # Update client in development without reloading the page - +~prototype~ static-html # Define static page content in .html files apollo # Basic Apollo integration for Meteor apps swydo:graphql # Import .graphql files diff --git a/tools/static-assets/skel-bare/.meteor/packages b/tools/static-assets/skel-bare/.meteor/packages index 62bedd2c00..294120e852 100644 --- a/tools/static-assets/skel-bare/.meteor/packages +++ b/tools/static-assets/skel-bare/.meteor/packages @@ -10,7 +10,7 @@ mongo # The database Meteor supports right now static-html # Define static page content in .html files reactive-var # Reactive variable for tracker tracker # Meteor's client-side reactive programming library - +~prototype~ standard-minifier-css # CSS minifier run for production mode standard-minifier-js # JS minifier run for production mode es5-shim # ECMAScript 5 compatibility for older browsers diff --git a/tools/static-assets/skel-blaze/.meteor/packages b/tools/static-assets/skel-blaze/.meteor/packages index c2506a81ed..5e929125ff 100644 --- a/tools/static-assets/skel-blaze/.meteor/packages +++ b/tools/static-assets/skel-blaze/.meteor/packages @@ -19,8 +19,7 @@ ecmascript # Enable ECMAScript2015+ syntax in app code typescript # Enable TypeScript syntax in .ts and .tsx modules shell-server # Server-side component of the `meteor shell` command -autopublish # Publish all data to the clients (for prototyping) -insecure # Allow all DB writes from clients (for prototyping) +~prototype~ hot-module-replacement # Update code in development without reloading the page blaze-hot # Update files using Blaze's API with HMR diff --git a/tools/static-assets/skel-chakra-ui/.meteor/packages b/tools/static-assets/skel-chakra-ui/.meteor/packages index 72de92e77b..90ce4b06dd 100644 --- a/tools/static-assets/skel-chakra-ui/.meteor/packages +++ b/tools/static-assets/skel-chakra-ui/.meteor/packages @@ -17,7 +17,6 @@ typescript # Enable TypeScript syntax in .ts and .tsx modules shell-server # Server-side component of the `meteor shell` command hot-module-replacement # Update client in development without reloading the page -autopublish # Publish all data to the clients (for prototyping) -insecure # Allow all DB writes from clients (for prototyping) +~prototype~ static-html # Define static page content in .html files react-meteor-data # React higher-order component for reactively tracking Meteor data diff --git a/tools/static-assets/skel-full/.meteor/packages b/tools/static-assets/skel-full/.meteor/packages index 8f6a2ce1df..42dd3fa370 100644 --- a/tools/static-assets/skel-full/.meteor/packages +++ b/tools/static-assets/skel-full/.meteor/packages @@ -11,6 +11,7 @@ blaze-html-templates # Compile .html files into Meteor Blaze views jquery # Wrapper package for npm-installed jquery reactive-var # Reactive variable for tracker tracker # Meteor's client-side reactive programming library +~prototype~ standard-minifier-css # CSS minifier run for production mode standard-minifier-js # JS minifier run for production mode diff --git a/tools/static-assets/skel-minimal/.meteor/packages b/tools/static-assets/skel-minimal/.meteor/packages index 60ed1976b3..d0998cd7ad 100644 --- a/tools/static-assets/skel-minimal/.meteor/packages +++ b/tools/static-assets/skel-minimal/.meteor/packages @@ -15,3 +15,4 @@ shell-server # Server-side component of the `meteor shell` command webapp # Serves a Meteor app over HTTP server-render # Support for server-side rendering hot-module-replacement # Rebuilds the client if there is a change on the client without restarting the server +~prototype~ diff --git a/tools/static-assets/skel-react/.meteor/packages b/tools/static-assets/skel-react/.meteor/packages index 72de92e77b..90ce4b06dd 100644 --- a/tools/static-assets/skel-react/.meteor/packages +++ b/tools/static-assets/skel-react/.meteor/packages @@ -17,7 +17,6 @@ typescript # Enable TypeScript syntax in .ts and .tsx modules shell-server # Server-side component of the `meteor shell` command hot-module-replacement # Update client in development without reloading the page -autopublish # Publish all data to the clients (for prototyping) -insecure # Allow all DB writes from clients (for prototyping) +~prototype~ static-html # Define static page content in .html files react-meteor-data # React higher-order component for reactively tracking Meteor data diff --git a/tools/static-assets/skel-solid/.meteor/packages b/tools/static-assets/skel-solid/.meteor/packages index d6c05d244b..492b563f76 100644 --- a/tools/static-assets/skel-solid/.meteor/packages +++ b/tools/static-assets/skel-solid/.meteor/packages @@ -17,7 +17,6 @@ typescript # Enable TypeScript syntax in .ts and .tsx modules shell-server # Server-side component of the `meteor shell` command hot-module-replacement # Update client in development without reloading the page -autopublish # Publish all data to the clients (for prototyping) -insecure # Allow all DB writes from clients (for prototyping) +~prototype~ static-html # Define static page content in .html files vite:bundler diff --git a/tools/static-assets/skel-svelte/.meteor/packages b/tools/static-assets/skel-svelte/.meteor/packages index 0e3c38c047..6880ea240a 100644 --- a/tools/static-assets/skel-svelte/.meteor/packages +++ b/tools/static-assets/skel-svelte/.meteor/packages @@ -16,8 +16,7 @@ ecmascript # Enable ECMAScript2015+ syntax in app code typescript # Enable TypeScript syntax in .ts and .tsx modules shell-server # Server-side component of the `meteor shell` command -autopublish # Publish all data to the clients (for prototyping) -insecure # Allow all DB writes from clients (for prototyping) +~prototype~ static-html # Define static page content in .html files zodern:melte # Meteor package to allow us to create files with the .svelte extension rdb:svelte-meteor-data # Meteor package which allows us to consume Meteor's reactive data sources inside of our Svelte components diff --git a/tools/static-assets/skel-tailwind/.meteor/packages b/tools/static-assets/skel-tailwind/.meteor/packages index 72de92e77b..90ce4b06dd 100644 --- a/tools/static-assets/skel-tailwind/.meteor/packages +++ b/tools/static-assets/skel-tailwind/.meteor/packages @@ -17,7 +17,6 @@ typescript # Enable TypeScript syntax in .ts and .tsx modules shell-server # Server-side component of the `meteor shell` command hot-module-replacement # Update client in development without reloading the page -autopublish # Publish all data to the clients (for prototyping) -insecure # Allow all DB writes from clients (for prototyping) +~prototype~ static-html # Define static page content in .html files react-meteor-data # React higher-order component for reactively tracking Meteor data diff --git a/tools/static-assets/skel-typescript/.meteor/packages b/tools/static-assets/skel-typescript/.meteor/packages index 72de92e77b..90ce4b06dd 100644 --- a/tools/static-assets/skel-typescript/.meteor/packages +++ b/tools/static-assets/skel-typescript/.meteor/packages @@ -17,7 +17,6 @@ typescript # Enable TypeScript syntax in .ts and .tsx modules shell-server # Server-side component of the `meteor shell` command hot-module-replacement # Update client in development without reloading the page -autopublish # Publish all data to the clients (for prototyping) -insecure # Allow all DB writes from clients (for prototyping) +~prototype~ static-html # Define static page content in .html files react-meteor-data # React higher-order component for reactively tracking Meteor data diff --git a/tools/static-assets/skel-typescript/package.json b/tools/static-assets/skel-typescript/package.json index c7c54d5cc4..76457880f7 100644 --- a/tools/static-assets/skel-typescript/package.json +++ b/tools/static-assets/skel-typescript/package.json @@ -18,7 +18,7 @@ "@types/mocha": "^8.2.3", "@types/react": "^17.0.43", "@types/react-dom": "^17.0.14", - "typescript": "^4.6.3" + "typescript": "^4.6.4" }, "meteor": { "mainModule": { diff --git a/tools/static-assets/skel-vue-2/.gitignore b/tools/static-assets/skel-vue-2/.gitignore new file mode 100644 index 0000000000..c2658d7d1b --- /dev/null +++ b/tools/static-assets/skel-vue-2/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/tools/static-assets/skel-vue-2/.meteor/.gitignore b/tools/static-assets/skel-vue-2/.meteor/.gitignore new file mode 100644 index 0000000000..4083037423 --- /dev/null +++ b/tools/static-assets/skel-vue-2/.meteor/.gitignore @@ -0,0 +1 @@ +local diff --git a/tools/static-assets/skel-vue-2/.meteor/packages b/tools/static-assets/skel-vue-2/.meteor/packages new file mode 100644 index 0000000000..83be6b3a62 --- /dev/null +++ b/tools/static-assets/skel-vue-2/.meteor/packages @@ -0,0 +1,24 @@ +# Meteor packages used by this project, one per line. +# Check this file (and the other files in this directory) into your repository. +# +# 'meteor add' and 'meteor remove' will edit this file for you, +# but you can also edit it by hand. + +meteor-base # Packages every Meteor app needs to have +mobile-experience # Packages for a great mobile UX +mongo # The database Meteor supports right now +reactive-var # Reactive variable for tracker + +standard-minifier-css # CSS minifier run for production mode +standard-minifier-js # JS minifier run for production mode +es5-shim # ECMAScript 5 compatibility for older browsers +ecmascript # Enable ECMAScript2015+ syntax in app code +typescript # Enable TypeScript syntax in .ts and .tsx modules +shell-server # Server-side component of the `meteor shell` command + +tracker # Dependency tracker to allow reactive callbacks +static-html # Define static page content in .html files +akryum:vue-component # Vue-CLI template to publish components + +meteortesting:mocha # A package for writing and running your meteor app and package tests with mocha +johanbrook:publication-collector # Test a Meteor publication by collecting its output diff --git a/tools/static-assets/skel-vue-2/.meteor/platforms b/tools/static-assets/skel-vue-2/.meteor/platforms new file mode 100644 index 0000000000..efeba1b50c --- /dev/null +++ b/tools/static-assets/skel-vue-2/.meteor/platforms @@ -0,0 +1,2 @@ +server +browser diff --git a/tools/static-assets/skel-vue-2/client/main.html b/tools/static-assets/skel-vue-2/client/main.html new file mode 100644 index 0000000000..99c3dfb74c --- /dev/null +++ b/tools/static-assets/skel-vue-2/client/main.html @@ -0,0 +1,7 @@ + + ~name~ + + + +

+ diff --git a/tools/static-assets/skel-vue-2/client/main.js b/tools/static-assets/skel-vue-2/client/main.js new file mode 100644 index 0000000000..665c6aa1b1 --- /dev/null +++ b/tools/static-assets/skel-vue-2/client/main.js @@ -0,0 +1,12 @@ +import Vue from 'vue' + +import '../imports/ui/plugins' + +import App from '../imports/ui/App.vue' + +Meteor.startup(() => { + new Vue({ + el: '#app', + ...App, + }) +}) diff --git a/tools/static-assets/skel-vue/imports/api/collections/Links.js b/tools/static-assets/skel-vue-2/imports/api/collections/Links.js similarity index 100% rename from tools/static-assets/skel-vue/imports/api/collections/Links.js rename to tools/static-assets/skel-vue-2/imports/api/collections/Links.js diff --git a/tools/static-assets/skel-vue/imports/api/collections/Links.tests.js b/tools/static-assets/skel-vue-2/imports/api/collections/Links.tests.js similarity index 100% rename from tools/static-assets/skel-vue/imports/api/collections/Links.tests.js rename to tools/static-assets/skel-vue-2/imports/api/collections/Links.tests.js diff --git a/tools/static-assets/skel-vue/imports/api/fixtures.js b/tools/static-assets/skel-vue-2/imports/api/fixtures.js similarity index 100% rename from tools/static-assets/skel-vue/imports/api/fixtures.js rename to tools/static-assets/skel-vue-2/imports/api/fixtures.js diff --git a/tools/static-assets/skel-vue/imports/api/methods/createLink.js b/tools/static-assets/skel-vue-2/imports/api/methods/createLink.js similarity index 100% rename from tools/static-assets/skel-vue/imports/api/methods/createLink.js rename to tools/static-assets/skel-vue-2/imports/api/methods/createLink.js diff --git a/tools/static-assets/skel-vue/imports/api/methods/createLink.tests.js b/tools/static-assets/skel-vue-2/imports/api/methods/createLink.tests.js similarity index 100% rename from tools/static-assets/skel-vue/imports/api/methods/createLink.tests.js rename to tools/static-assets/skel-vue-2/imports/api/methods/createLink.tests.js diff --git a/tools/static-assets/skel-vue/imports/api/methods/index.js b/tools/static-assets/skel-vue-2/imports/api/methods/index.js similarity index 100% rename from tools/static-assets/skel-vue/imports/api/methods/index.js rename to tools/static-assets/skel-vue-2/imports/api/methods/index.js diff --git a/tools/static-assets/skel-vue/imports/api/publications/index.js b/tools/static-assets/skel-vue-2/imports/api/publications/index.js similarity index 100% rename from tools/static-assets/skel-vue/imports/api/publications/index.js rename to tools/static-assets/skel-vue-2/imports/api/publications/index.js diff --git a/tools/static-assets/skel-vue/imports/api/publications/links.js b/tools/static-assets/skel-vue-2/imports/api/publications/links.js similarity index 100% rename from tools/static-assets/skel-vue/imports/api/publications/links.js rename to tools/static-assets/skel-vue-2/imports/api/publications/links.js diff --git a/tools/static-assets/skel-vue/imports/api/publications/links.tests.js b/tools/static-assets/skel-vue-2/imports/api/publications/links.tests.js similarity index 100% rename from tools/static-assets/skel-vue/imports/api/publications/links.tests.js rename to tools/static-assets/skel-vue-2/imports/api/publications/links.tests.js diff --git a/tools/static-assets/skel-vue-2/imports/ui/App.vue b/tools/static-assets/skel-vue-2/imports/ui/App.vue new file mode 100644 index 0000000000..e126098ccb --- /dev/null +++ b/tools/static-assets/skel-vue-2/imports/ui/App.vue @@ -0,0 +1,26 @@ + + + + + diff --git a/tools/static-assets/skel-vue/imports/ui/components/Hello.vue b/tools/static-assets/skel-vue-2/imports/ui/components/Hello.vue similarity index 100% rename from tools/static-assets/skel-vue/imports/ui/components/Hello.vue rename to tools/static-assets/skel-vue-2/imports/ui/components/Hello.vue diff --git a/tools/static-assets/skel-vue/imports/ui/components/Info.vue b/tools/static-assets/skel-vue-2/imports/ui/components/Info.vue similarity index 100% rename from tools/static-assets/skel-vue/imports/ui/components/Info.vue rename to tools/static-assets/skel-vue-2/imports/ui/components/Info.vue diff --git a/tools/static-assets/skel-vue/imports/ui/plugins.js b/tools/static-assets/skel-vue-2/imports/ui/plugins.js similarity index 100% rename from tools/static-assets/skel-vue/imports/ui/plugins.js rename to tools/static-assets/skel-vue-2/imports/ui/plugins.js diff --git a/tools/static-assets/skel-vue-2/package.json b/tools/static-assets/skel-vue-2/package.json new file mode 100644 index 0000000000..e8cfe3ee72 --- /dev/null +++ b/tools/static-assets/skel-vue-2/package.json @@ -0,0 +1,23 @@ +{ + "name": "~name~", + "private": true, + "scripts": { + "start": "meteor run", + "test": "meteor test --once --driver-package meteortesting:mocha", + "test-app": "TEST_WATCH=1 meteor test --full-app --driver-package meteortesting:mocha", + "visualize": "meteor --production --extra-packages bundle-visualizer" + }, + "dependencies": { + "@babel/runtime": "^7.17.9", + "meteor-node-stubs": "^1.2.1", + "vue": "^2.6.14", + "vue-meteor-tracker": "^2.0.0-beta.5" + }, + "meteor": { + "mainModule": { + "client": "client/main.js", + "server": "server/main.js" + }, + "testModule": "tests/main.js" + } +} diff --git a/tools/static-assets/skel-vue-2/server/main.js b/tools/static-assets/skel-vue-2/server/main.js new file mode 100644 index 0000000000..42950618b6 --- /dev/null +++ b/tools/static-assets/skel-vue-2/server/main.js @@ -0,0 +1,3 @@ +import '../imports/api/fixtures' +import '../imports/api/methods' +import '../imports/api/publications' diff --git a/tools/static-assets/skel-vue-2/tests/main.js b/tools/static-assets/skel-vue-2/tests/main.js new file mode 100644 index 0000000000..6d2a32e09d --- /dev/null +++ b/tools/static-assets/skel-vue-2/tests/main.js @@ -0,0 +1,20 @@ +import assert from "assert"; + +describe("skel", function () { + it("package.json has correct name", async function () { + const { name } = await import("../package.json"); + assert.strictEqual(name, "skel"); + }); + + if (Meteor.isClient) { + it("client is not server", function () { + assert.strictEqual(Meteor.isServer, false); + }); + } + + if (Meteor.isServer) { + it("server is not client", function () { + assert.strictEqual(Meteor.isClient, false); + }); + } +}); diff --git a/tools/static-assets/skel-vue/.meteor/.finished-upgraders b/tools/static-assets/skel-vue/.meteor/.finished-upgraders new file mode 100644 index 0000000000..c07b6ff75a --- /dev/null +++ b/tools/static-assets/skel-vue/.meteor/.finished-upgraders @@ -0,0 +1,19 @@ +# This file contains information which helps Meteor properly upgrade your +# app when you run 'meteor update'. You should check it into version control +# with your project. + +notices-for-0.9.0 +notices-for-0.9.1 +0.9.4-platform-file +notices-for-facebook-graph-api-2 +1.2.0-standard-minifiers-package +1.2.0-meteor-platform-split +1.2.0-cordova-changes +1.2.0-breaking-changes +1.3.0-split-minifiers-package +1.4.0-remove-old-dev-bundle-link +1.4.1-add-shell-server-package +1.4.3-split-account-service-packages +1.5-add-dynamic-import-package +1.7-split-underscore-from-meteor-base +1.8.3-split-jquery-from-blaze diff --git a/tools/static-assets/skel-vue/.meteor/.id b/tools/static-assets/skel-vue/.meteor/.id new file mode 100644 index 0000000000..dd363b2513 --- /dev/null +++ b/tools/static-assets/skel-vue/.meteor/.id @@ -0,0 +1,7 @@ +# This file contains a token that is unique to your project. +# Check it into your repository along with the rest of this directory. +# It can be used for purposes such as: +# - ensuring you don't accidentally deploy one app on top of another +# - providing package authors with aggregated statistics + +kdvkjcf9nja.gpp7f6ll7w7a diff --git a/tools/static-assets/skel-vue/.meteor/packages b/tools/static-assets/skel-vue/.meteor/packages index 83be6b3a62..2565a5fe32 100644 --- a/tools/static-assets/skel-vue/.meteor/packages +++ b/tools/static-assets/skel-vue/.meteor/packages @@ -4,21 +4,18 @@ # 'meteor add' and 'meteor remove' will edit this file for you, # but you can also edit it by hand. -meteor-base # Packages every Meteor app needs to have -mobile-experience # Packages for a great mobile UX -mongo # The database Meteor supports right now -reactive-var # Reactive variable for tracker +meteor-base@1.5.1 # Packages every Meteor app needs to have +mobile-experience@1.1.0 # Packages for a great mobile UX +mongo@1.16.0 # The database Meteor supports right now +reactive-var@1.0.11 # Reactive variable for tracker -standard-minifier-css # CSS minifier run for production mode -standard-minifier-js # JS minifier run for production mode -es5-shim # ECMAScript 5 compatibility for older browsers -ecmascript # Enable ECMAScript2015+ syntax in app code -typescript # Enable TypeScript syntax in .ts and .tsx modules -shell-server # Server-side component of the `meteor shell` command +standard-minifier-css@1.8.2 # CSS minifier run for production mode +standard-minifier-js@2.8.1 # JS minifier run for production mode +es5-shim@4.8.0 # ECMAScript 5 compatibility for older browsers +ecmascript@0.16.2 # Enable ECMAScript2015+ syntax in app code +typescript@4.5.4 # Enable TypeScript syntax in .ts and .tsx modules +shell-server@0.5.0 # Server-side component of the `meteor shell` command +hot-module-replacement@0.5.1 # Update client in development without reloading the page -tracker # Dependency tracker to allow reactive callbacks -static-html # Define static page content in .html files -akryum:vue-component # Vue-CLI template to publish components - -meteortesting:mocha # A package for writing and running your meteor app and package tests with mocha -johanbrook:publication-collector # Test a Meteor publication by collecting its output +static-html@1.3.2 # Define static page content in .html files +vite:bundler diff --git a/tools/static-assets/skel-vue/.meteor/release b/tools/static-assets/skel-vue/.meteor/release new file mode 100644 index 0000000000..1d2a6d0f79 --- /dev/null +++ b/tools/static-assets/skel-vue/.meteor/release @@ -0,0 +1 @@ +METEOR@2.8.0 diff --git a/tools/static-assets/skel-vue/.meteor/versions b/tools/static-assets/skel-vue/.meteor/versions new file mode 100644 index 0000000000..3b89f7359b --- /dev/null +++ b/tools/static-assets/skel-vue/.meteor/versions @@ -0,0 +1,71 @@ +allow-deny@1.1.1 +autoupdate@1.8.0 +babel-compiler@7.9.2 +babel-runtime@1.5.1 +base64@1.0.12 +binary-heap@1.0.11 +blaze-tools@1.1.3 +boilerplate-generator@1.7.1 +caching-compiler@1.2.2 +caching-html-compiler@1.2.1 +callback-hook@1.4.0 +check@1.3.1 +ddp@1.4.0 +ddp-client@2.6.0 +ddp-common@1.4.0 +ddp-server@2.6.0 +diff-sequence@1.1.1 +dynamic-import@0.7.2 +ecmascript@0.16.2 +ecmascript-runtime@0.8.0 +ecmascript-runtime-client@0.12.1 +ecmascript-runtime-server@0.11.0 +ejson@1.1.2 +es5-shim@4.8.0 +fetch@0.1.1 +geojson-utils@1.0.10 +hot-code-push@1.0.4 +hot-module-replacement@0.5.1 +html-tools@1.1.3 +htmljs@1.1.1 +id-map@1.1.1 +inter-process-messaging@0.1.1 +launch-screen@1.3.0 +logging@1.3.1 +meteor@1.10.1 +meteor-base@1.5.1 +minifier-css@1.6.1 +minifier-js@2.7.5 +minimongo@1.9.0 +mobile-experience@1.1.0 +mobile-status-bar@1.1.0 +modern-browsers@0.1.8 +modules@0.19.0 +modules-runtime@0.13.0 +modules-runtime-hot@0.14.0 +mongo@1.16.0 +mongo-decimal@0.1.3 +mongo-dev-server@1.1.0 +mongo-id@1.0.8 +npm-mongo@4.9.0 +ordered-dict@1.1.0 +promise@0.12.0 +random@1.2.0 +react-fast-refresh@0.2.3 +reactive-var@1.0.11 +reload@1.3.1 +retry@1.1.0 +routepolicy@1.1.1 +shell-server@0.5.0 +socket-stream-client@0.5.0 +spacebars-compiler@1.3.1 +standard-minifier-css@1.8.2 +standard-minifier-js@2.8.1 +static-html@1.3.2 +templating-tools@1.2.2 +tracker@1.2.0 +typescript@4.5.4 +underscore@1.0.10 +vite:bundler@0.1.9 +webapp@1.13.1 +webapp-hashing@1.1.0 diff --git a/tools/static-assets/skel-vue/README.md b/tools/static-assets/skel-vue/README.md new file mode 100644 index 0000000000..7ba6226cb0 --- /dev/null +++ b/tools/static-assets/skel-vue/README.md @@ -0,0 +1,19 @@ +# Meteor + Vue3 + Vite + +This is a simple example of how to use Vue3 with Meteor. + +## How to use + +1. Clone this repo +2. Run `meteor npm install` +3. Run `meteor` +4. Open `http://localhost:3000` in your browser + +## Libraries used + +- [Vue3](https://v3.vuejs.org/) +- [Vite](https://vitejs.dev/) +- [Vue Router](https://next.router.vuejs.org/) +- [Meteor](https://www.meteor.com/) +- [Vue Meteor Tracker](https://github.com/meteor-vue/vue-meteor-tracker) +- [Tailwind CSS](https://tailwindcss.com/) diff --git a/tools/static-assets/skel-vue/client/main.css b/tools/static-assets/skel-vue/client/main.css new file mode 100644 index 0000000000..b5c61c9567 --- /dev/null +++ b/tools/static-assets/skel-vue/client/main.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/tools/static-assets/skel-vue/client/main.html b/tools/static-assets/skel-vue/client/main.html index 99c3dfb74c..9e2393399c 100644 --- a/tools/static-assets/skel-vue/client/main.html +++ b/tools/static-assets/skel-vue/client/main.html @@ -1,7 +1,16 @@ ~name~ + + + + + +
diff --git a/tools/static-assets/skel-vue/client/main.js b/tools/static-assets/skel-vue/client/main.js index 665c6aa1b1..97d382a9bd 100644 --- a/tools/static-assets/skel-vue/client/main.js +++ b/tools/static-assets/skel-vue/client/main.js @@ -1,12 +1 @@ -import Vue from 'vue' - -import '../imports/ui/plugins' - -import App from '../imports/ui/App.vue' - -Meteor.startup(() => { - new Vue({ - el: '#app', - ...App, - }) -}) +// main entry point is in imports/ui/main.jsx diff --git a/tools/static-assets/skel-vue/imports/api/links.js b/tools/static-assets/skel-vue/imports/api/links.js new file mode 100644 index 0000000000..4e98fcca62 --- /dev/null +++ b/tools/static-assets/skel-vue/imports/api/links.js @@ -0,0 +1,10 @@ +import { Meteor } from 'meteor/meteor' +import { Mongo } from 'meteor/mongo' + +export const LinksCollection = new Mongo.Collection('links') + +if (Meteor.isServer) { + Meteor.publish('links', function () { + return LinksCollection.find({}) + }) +} diff --git a/tools/static-assets/skel-vue/imports/ui/About.vue b/tools/static-assets/skel-vue/imports/ui/About.vue new file mode 100644 index 0000000000..d1ba384f1b --- /dev/null +++ b/tools/static-assets/skel-vue/imports/ui/About.vue @@ -0,0 +1,5 @@ + diff --git a/tools/static-assets/skel-vue/imports/ui/App.vue b/tools/static-assets/skel-vue/imports/ui/App.vue index e126098ccb..7a775391cb 100644 --- a/tools/static-assets/skel-vue/imports/ui/App.vue +++ b/tools/static-assets/skel-vue/imports/ui/App.vue @@ -1,26 +1,10 @@ - - - - + diff --git a/tools/static-assets/skel-vue/imports/ui/AppMenu.vue b/tools/static-assets/skel-vue/imports/ui/AppMenu.vue new file mode 100644 index 0000000000..5b1997efec --- /dev/null +++ b/tools/static-assets/skel-vue/imports/ui/AppMenu.vue @@ -0,0 +1,6 @@ + diff --git a/tools/static-assets/skel-vue/imports/ui/Hello.vue b/tools/static-assets/skel-vue/imports/ui/Hello.vue new file mode 100644 index 0000000000..ebe691f4d2 --- /dev/null +++ b/tools/static-assets/skel-vue/imports/ui/Hello.vue @@ -0,0 +1,16 @@ + + + diff --git a/tools/static-assets/skel-vue/imports/ui/Home.vue b/tools/static-assets/skel-vue/imports/ui/Home.vue new file mode 100644 index 0000000000..0473845661 --- /dev/null +++ b/tools/static-assets/skel-vue/imports/ui/Home.vue @@ -0,0 +1,10 @@ + + + diff --git a/tools/static-assets/skel-vue/imports/ui/Info.vue b/tools/static-assets/skel-vue/imports/ui/Info.vue new file mode 100644 index 0000000000..5a17339c53 --- /dev/null +++ b/tools/static-assets/skel-vue/imports/ui/Info.vue @@ -0,0 +1,16 @@ + + + diff --git a/tools/static-assets/skel-vue/imports/ui/main.js b/tools/static-assets/skel-vue/imports/ui/main.js new file mode 100644 index 0000000000..e3500841ea --- /dev/null +++ b/tools/static-assets/skel-vue/imports/ui/main.js @@ -0,0 +1,13 @@ +import { Meteor } from 'meteor/meteor' +import { createApp } from 'vue' +import { VueMeteor } from 'vue-meteor-tracker' + +import App from './App.vue' +import { router } from './router' + +Meteor.startup(() => { + const app = createApp(App) + app.use(router) + app.use(VueMeteor) + app.mount('#app') +}) diff --git a/tools/static-assets/skel-vue/imports/ui/router.js b/tools/static-assets/skel-vue/imports/ui/router.js new file mode 100644 index 0000000000..7768ef4894 --- /dev/null +++ b/tools/static-assets/skel-vue/imports/ui/router.js @@ -0,0 +1,18 @@ +import { createRouter, createWebHistory } from 'vue-router' +import Home from './Home.vue' + +export const router = createRouter({ + history: createWebHistory(), + routes: [ + { + path: '/', + name: 'home', + component: Home, + }, + { + path: '/about', + name: 'about', + component: () => import('./About.vue'), + }, + ], +}) diff --git a/tools/static-assets/skel-vue/package.json b/tools/static-assets/skel-vue/package.json index e8cfe3ee72..f8dc1cace8 100644 --- a/tools/static-assets/skel-vue/package.json +++ b/tools/static-assets/skel-vue/package.json @@ -3,6 +3,7 @@ "private": true, "scripts": { "start": "meteor run", + "build": "meteor build ../output/vue --directory", "test": "meteor test --once --driver-package meteortesting:mocha", "test-app": "TEST_WATCH=1 meteor test --full-app --driver-package meteortesting:mocha", "visualize": "meteor --production --extra-packages bundle-visualizer" @@ -10,8 +11,9 @@ "dependencies": { "@babel/runtime": "^7.17.9", "meteor-node-stubs": "^1.2.1", - "vue": "^2.6.14", - "vue-meteor-tracker": "^2.0.0-beta.5" + "vue": "^3.2.45", + "vue-meteor-tracker": "^3.0.0-beta.7", + "vue-router": "^4.1.6" }, "meteor": { "mainModule": { @@ -19,5 +21,13 @@ "server": "server/main.js" }, "testModule": "tests/main.js" + }, + "devDependencies": { + "@types/meteor": "^2.8.1", + "@vitejs/plugin-vue": "^3.2.0", + "autoprefixer": "^10.4.13", + "postcss": "^8.4.19", + "tailwindcss": "^3.2.4", + "vite": "^3.2.3" } } diff --git a/tools/static-assets/skel-vue/postcss.config.js b/tools/static-assets/skel-vue/postcss.config.js new file mode 100644 index 0000000000..33ad091d26 --- /dev/null +++ b/tools/static-assets/skel-vue/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/tools/static-assets/skel-vue/server/main.js b/tools/static-assets/skel-vue/server/main.js index 42950618b6..44f7bc045b 100644 --- a/tools/static-assets/skel-vue/server/main.js +++ b/tools/static-assets/skel-vue/server/main.js @@ -1,3 +1,31 @@ -import '../imports/api/fixtures' -import '../imports/api/methods' -import '../imports/api/publications' +import { Meteor } from 'meteor/meteor' +import { LinksCollection } from '/imports/api/links' + +async function insertLink({ title, url }) { + await LinksCollection.insertAsync({ title, url, createdAt: new Date() }) +} + +Meteor.startup(async () => { + // If the Links collection is empty, add some data. + if ((await LinksCollection.find().countAsync()) === 0) { + await insertLink({ + title: 'Do the Tutorial', + url: 'https://www.solidjs.com/tutorial/introduction_basics', + }) + + await insertLink({ + title: 'Follow the Guide', + url: 'https://guide.meteor.com', + }) + + await insertLink({ + title: 'Read the Docs', + url: 'https://docs.meteor.com', + }) + + await insertLink({ + title: 'Discussions', + url: 'https://forums.meteor.com', + }) + } +}) diff --git a/tools/static-assets/skel-vue/tailwind.config.js b/tools/static-assets/skel-vue/tailwind.config.js new file mode 100644 index 0000000000..72c950fc84 --- /dev/null +++ b/tools/static-assets/skel-vue/tailwind.config.js @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['./imports/ui/**/*.{vue,js,ts,jsx,tsx}', './client/*.html'], + theme: { + extend: {}, + }, + plugins: [], +} diff --git a/tools/static-assets/skel-vue/tests/main.js b/tools/static-assets/skel-vue/tests/main.js index 6d2a32e09d..086819d896 100644 --- a/tools/static-assets/skel-vue/tests/main.js +++ b/tools/static-assets/skel-vue/tests/main.js @@ -1,20 +1,20 @@ -import assert from "assert"; +import assert from 'assert' -describe("skel", function () { - it("package.json has correct name", async function () { - const { name } = await import("../package.json"); - assert.strictEqual(name, "skel"); - }); +describe('vue-skeleton', function () { + it('package.json has correct name', async function () { + const { name } = await import('../package.json') + assert.strictEqual(name, 'vue-skeleton') + }) if (Meteor.isClient) { - it("client is not server", function () { - assert.strictEqual(Meteor.isServer, false); - }); + it('client is not server', function () { + assert.strictEqual(Meteor.isServer, false) + }) } if (Meteor.isServer) { - it("server is not client", function () { - assert.strictEqual(Meteor.isClient, false); - }); + it('server is not client', function () { + assert.strictEqual(Meteor.isClient, false) + }) } -}); +}) diff --git a/tools/static-assets/skel-vue/vite.config.js b/tools/static-assets/skel-vue/vite.config.js new file mode 100644 index 0000000000..d3aeaa9aba --- /dev/null +++ b/tools/static-assets/skel-vue/vite.config.js @@ -0,0 +1,12 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], + meteor: { + clientEntry: 'imports/ui/main.js', + }, + optimizeDeps: { + exclude: ['vue-meteor-tracker'], + }, +}) diff --git a/tools/tests/apps/app-config/.meteor/versions b/tools/tests/apps/app-config/.meteor/versions index 23b1d17616..e53282cd8f 100644 --- a/tools/tests/apps/app-config/.meteor/versions +++ b/tools/tests/apps/app-config/.meteor/versions @@ -57,8 +57,8 @@ standard-minifier-css@1.4.1 standard-minifier-js@2.3.2 static-html@1.2.2 templating-tools@1.1.2 -tracker@1.2.0 -underscore@1.0.10 +tracker@1.2.1 +underscore@1.0.11 url@1.2.0 webapp@1.5.0 webapp-hashing@1.0.9 diff --git a/tools/tests/apps/client-refresh/.meteor/versions b/tools/tests/apps/client-refresh/.meteor/versions index 087c94fa51..eef8816483 100644 --- a/tools/tests/apps/client-refresh/.meteor/versions +++ b/tools/tests/apps/client-refresh/.meteor/versions @@ -7,7 +7,7 @@ boilerplate-generator@1.6.0 caching-compiler@1.2.1 caching-html-compiler@1.1.3 callback-hook@1.1.0 -check@1.3.1 +check@1.3.2 ddp@1.4.0 ddp-client@2.3.3 ddp-common@1.4.0 @@ -47,8 +47,8 @@ standard-minifier-js@2.4.1 static-html@1.2.2 templating-tools@1.1.2 test-package@0.0.1 -tracker@1.2.0 +tracker@1.2.1-beta.1 typescript@3.5.2-beta182.17 -underscore@1.0.10 +underscore@1.0.11-beta.1 webapp@1.7.4 webapp-hashing@1.0.9 diff --git a/tools/tests/apps/compiler-plugin-static-html-error/.meteor/versions b/tools/tests/apps/compiler-plugin-static-html-error/.meteor/versions index 2112029aa3..ea9c18baf2 100644 --- a/tools/tests/apps/compiler-plugin-static-html-error/.meteor/versions +++ b/tools/tests/apps/compiler-plugin-static-html-error/.meteor/versions @@ -7,7 +7,7 @@ boilerplate-generator@1.6.0 caching-compiler@1.2.1 caching-html-compiler@1.1.3 callback-hook@1.1.0 -check@1.3.1 +check@1.3.2 ddp@1.4.0 ddp-client@2.3.3 ddp-common@1.4.0 @@ -49,7 +49,7 @@ standard-minifier-css@1.5.3 standard-minifier-js@2.4.1 static-html@1.2.2 templating-tools@1.1.2 -tracker@1.2.0 -underscore@1.0.10 +tracker@1.2.1 +underscore@1.0.11 webapp@1.7.4 webapp-hashing@1.0.9 diff --git a/tools/tests/apps/compiler-plugin-static-html/.meteor/versions b/tools/tests/apps/compiler-plugin-static-html/.meteor/versions index 2112029aa3..ea9c18baf2 100644 --- a/tools/tests/apps/compiler-plugin-static-html/.meteor/versions +++ b/tools/tests/apps/compiler-plugin-static-html/.meteor/versions @@ -7,7 +7,7 @@ boilerplate-generator@1.6.0 caching-compiler@1.2.1 caching-html-compiler@1.1.3 callback-hook@1.1.0 -check@1.3.1 +check@1.3.2 ddp@1.4.0 ddp-client@2.3.3 ddp-common@1.4.0 @@ -49,7 +49,7 @@ standard-minifier-css@1.5.3 standard-minifier-js@2.4.1 static-html@1.2.2 templating-tools@1.1.2 -tracker@1.2.0 -underscore@1.0.10 +tracker@1.2.1 +underscore@1.0.11 webapp@1.7.4 webapp-hashing@1.0.9 diff --git a/tools/tests/apps/custom-minifier/.meteor/versions b/tools/tests/apps/custom-minifier/.meteor/versions index 0aacfc62db..99e0fc7d2b 100644 --- a/tools/tests/apps/custom-minifier/.meteor/versions +++ b/tools/tests/apps/custom-minifier/.meteor/versions @@ -7,7 +7,7 @@ boilerplate-generator@1.6.0 caching-compiler@1.2.1 caching-html-compiler@1.1.3 callback-hook@1.2.0 -check@1.3.1 +check@1.3.2 custom-minifier@0.0.1 ddp@1.4.0 ddp-client@2.3.3 @@ -45,7 +45,7 @@ socket-stream-client@0.2.2 spacebars-compiler@1.1.3 static-html@1.2.2 templating-tools@1.1.2 -tracker@1.2.0 -underscore@1.0.10 +tracker@1.2.1 +underscore@1.0.11 webapp@1.7.5 webapp-hashing@1.0.9 diff --git a/tools/tests/apps/dynamic-import/.meteor/packages b/tools/tests/apps/dynamic-import/.meteor/packages index b74a849e52..1570f6c394 100644 --- a/tools/tests/apps/dynamic-import/.meteor/packages +++ b/tools/tests/apps/dynamic-import/.meteor/packages @@ -8,8 +8,8 @@ meteor-base@1.4.0 # Packages every Meteor app needs to have mobile-experience@1.1.0 # Packages for a great mobile UX mongo@1.9.0 # The database Meteor supports right now blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views -reactive-var@1.0.11 # Reactive variable for tracker -tracker@1.2.0 # Meteor's client-side reactive programming library +reactive-var@1.0.12 # Reactive variable for tracker +tracker@1.2.1 # Meteor's client-side reactive programming library standard-minifier-css@1.6.0 # CSS minifier run for production mode standard-minifier-js@2.6.0 # JS minifier run for production mode @@ -23,6 +23,6 @@ dynamic-import@0.5.1 lazy-test-package helper-package user:colon-name -underscore@1.0.10 +underscore@1.0.11 fetch@0.1.1 jquery diff --git a/tools/tests/apps/dynamic-import/.meteor/versions b/tools/tests/apps/dynamic-import/.meteor/versions index a99df3012b..3872664234 100644 --- a/tools/tests/apps/dynamic-import/.meteor/versions +++ b/tools/tests/apps/dynamic-import/.meteor/versions @@ -12,7 +12,7 @@ boilerplate-generator@1.7.0 caching-compiler@1.2.1 caching-html-compiler@1.1.3 callback-hook@1.3.0 -check@1.3.1 +check@1.3.2-beta.1 coffeescript@2.4.1 coffeescript-compiler@2.4.1 ddp@1.4.0 @@ -60,8 +60,8 @@ npm-mongo@3.7.0 observe-sequence@1.0.16 ordered-dict@1.1.0 promise@0.11.2 -random@1.2.0 -reactive-var@1.0.11 +random@1.2.1-beta.1 +reactive-var@1.0.12-beta.1 reload@1.3.0 retry@1.1.0 routepolicy@1.1.0 @@ -75,9 +75,9 @@ templating@1.3.2 templating-compiler@1.3.3 templating-runtime@1.3.2 templating-tools@1.1.2 -tracker@1.2.0 +tracker@1.2.1-beta.1 ui@1.0.13 -underscore@1.0.10 +underscore@1.0.11-beta.1 user:colon-name@0.0.1 webapp@1.9.0 webapp-hashing@1.0.9 diff --git a/tools/tests/apps/ecmascript-regression/.meteor/packages b/tools/tests/apps/ecmascript-regression/.meteor/packages index ab27ff6225..59cfdb5807 100644 --- a/tools/tests/apps/ecmascript-regression/.meteor/packages +++ b/tools/tests/apps/ecmascript-regression/.meteor/packages @@ -7,7 +7,7 @@ meteor-base@1.5.1 # Packages every Meteor app needs to have mobile-experience@1.1.0 # Packages for a great mobile UX mongo@1.13.0 # The database Meteor supports right now -reactive-var@1.0.11 # Reactive variable for tracker +reactive-var@1.0.12 # Reactive variable for tracker standard-minifier-css@1.7.4 # CSS minifier run for production mode standard-minifier-js@2.7.0 # JS minifier run for production mode diff --git a/tools/tests/apps/ecmascript-regression/.meteor/versions b/tools/tests/apps/ecmascript-regression/.meteor/versions index de86973195..d5985967e1 100644 --- a/tools/tests/apps/ecmascript-regression/.meteor/versions +++ b/tools/tests/apps/ecmascript-regression/.meteor/versions @@ -10,7 +10,7 @@ boilerplate-generator@1.7.1 caching-compiler@1.2.2 caching-html-compiler@1.2.1 callback-hook@1.4.0 -check@1.3.1 +check@1.3.2 ddp@1.4.0 ddp-client@2.5.0 ddp-common@1.4.0 @@ -56,10 +56,10 @@ mongo-id@1.0.8 npm-mongo@3.9.1 ordered-dict@1.1.0 promise@0.12.0 -random@1.2.0 +random@1.2.1 react-fast-refresh@0.1.1 react-meteor-data@2.3.3 -reactive-var@1.0.11 +reactive-var@1.0.12 reload@1.3.1 retry@1.1.0 routepolicy@1.1.1 @@ -70,9 +70,9 @@ standard-minifier-css@1.7.4 standard-minifier-js@2.7.1 static-html@1.3.2 templating-tools@1.2.1 -tracker@1.2.0 +tracker@1.2.1 typescript@4.3.5 -underscore@1.0.10 +underscore@1.0.11 url@1.3.2 webapp@1.12.0 webapp-hashing@1.1.0 diff --git a/tools/tests/apps/git-commit-hash/.meteor/versions b/tools/tests/apps/git-commit-hash/.meteor/versions index a10ed1d9c8..91b9af05b0 100644 --- a/tools/tests/apps/git-commit-hash/.meteor/versions +++ b/tools/tests/apps/git-commit-hash/.meteor/versions @@ -33,7 +33,7 @@ standard-minifier-css@1.5.2 standard-minifier-js@2.4.0 static-html@1.2.2 templating-tools@1.1.2 -tracker@1.2.0 -underscore@1.0.10 +tracker@1.2.1 +underscore@1.0.11 webapp@1.7.3-beta181.16 webapp-hashing@1.0.9 diff --git a/tools/tests/apps/link-config-npm-package/.meteor/versions b/tools/tests/apps/link-config-npm-package/.meteor/versions index 91816ff622..38888c802f 100644 --- a/tools/tests/apps/link-config-npm-package/.meteor/versions +++ b/tools/tests/apps/link-config-npm-package/.meteor/versions @@ -33,7 +33,7 @@ standard-minifier-css@1.4.1 standard-minifier-js@2.4.0-rc171.6 static-html@1.2.2 templating-tools@1.1.2 -tracker@1.2.0 -underscore@1.0.10 +tracker@1.2.1 +underscore@1.0.11 webapp@1.7.0-rc171.6 webapp-hashing@1.0.9 diff --git a/tools/tests/apps/linked-external-npm-package/.meteor/versions b/tools/tests/apps/linked-external-npm-package/.meteor/versions index 91816ff622..38888c802f 100644 --- a/tools/tests/apps/linked-external-npm-package/.meteor/versions +++ b/tools/tests/apps/linked-external-npm-package/.meteor/versions @@ -33,7 +33,7 @@ standard-minifier-css@1.4.1 standard-minifier-js@2.4.0-rc171.6 static-html@1.2.2 templating-tools@1.1.2 -tracker@1.2.0 -underscore@1.0.10 +tracker@1.2.1 +underscore@1.0.11 webapp@1.7.0-rc171.6 webapp-hashing@1.0.9 diff --git a/tools/tests/apps/meteor-ignore/.meteor/versions b/tools/tests/apps/meteor-ignore/.meteor/versions index 2a0c9c4a0e..22056f661d 100644 --- a/tools/tests/apps/meteor-ignore/.meteor/versions +++ b/tools/tests/apps/meteor-ignore/.meteor/versions @@ -47,7 +47,7 @@ npm-mongo@2.2.30 ordered-dict@1.0.9 promise@0.9.0 random@1.0.10 -reactive-var@1.0.11 +reactive-var@1.0.12 reload@1.1.11 retry@1.0.9 routepolicy@1.0.12 @@ -58,7 +58,7 @@ standard-minifier-js@2.1.1 static-html@1.2.2 templating-tools@1.1.2 tracker@1.1.3 -underscore@1.0.10 +underscore@1.0.11 url@1.1.0 webapp@1.3.19 webapp-hashing@1.0.9 diff --git a/tools/tests/apps/modules/.meteor/packages b/tools/tests/apps/modules/.meteor/packages index 2f92a7e262..f139c0b7a7 100644 --- a/tools/tests/apps/modules/.meteor/packages +++ b/tools/tests/apps/modules/.meteor/packages @@ -8,9 +8,9 @@ meteor-base@1.4.0 # Packages every Meteor app needs to have mobile-experience@1.1.0 # Packages for a great mobile UX mongo@1.9.0 # The database Meteor supports right now blaze-html-templates # Compile .html files into Meteor Blaze views -session@1.2.0 # Client-side reactive dictionary for your app +session@1.2.1 # Client-side reactive dictionary for your app jquery # Helpful client-side library -tracker@1.2.0 # Meteor's client-side reactive programming library +tracker@1.2.1 # Meteor's client-side reactive programming library es5-shim@4.8.0 # ECMAScript 5 compatibility for older browsers. ecmascript@0.14.2 # Enable ECMAScript2015+ syntax in app code @@ -23,7 +23,7 @@ client-only-ecmascript modules-test-plugin shell-server@0.5.0 dynamic-import@0.5.1 -underscore@1.0.10 +underscore@1.0.11 import-local-json-module akryum:vue-component dummy-compiler diff --git a/tools/tests/apps/standard-app/.meteor/versions b/tools/tests/apps/standard-app/.meteor/versions index a574733bd2..521ac34984 100644 --- a/tools/tests/apps/standard-app/.meteor/versions +++ b/tools/tests/apps/standard-app/.meteor/versions @@ -6,7 +6,7 @@ base64@1.0.11 binary-heap@1.0.11 boilerplate-generator@1.6.0 callback-hook@1.1.0 -check@1.3.1 +check@1.3.2 ddp@1.4.0 ddp-client@2.3.3 ddp-common@1.4.0 @@ -49,7 +49,7 @@ shell-server@0.4.0 socket-stream-client@0.2.2 standard-minifier-css@1.5.2 standard-minifier-js@2.4.0 -tracker@1.2.0 -underscore@1.0.10 +tracker@1.2.1 +underscore@1.0.11 webapp@1.7.2 webapp-hashing@1.0.9 diff --git a/tools/tests/cordova-plugins.js b/tools/tests/cordova-plugins.js index 27de381990..85a9212eac 100644 --- a/tools/tests/cordova-plugins.js +++ b/tools/tests/cordova-plugins.js @@ -264,7 +264,7 @@ selftest.define("add cordova plugins", ["slow", "cordova"], function () { checkUserPlugins(s, ["com.example.plugin"]); - // This should fail beacuse the plugin does not exists at the specified path + // This should fail because the plugin does not exists at the specified path. run = s.run("build", '../a', "--server", "localhost:3000"); run.waitSecs(30); run.expectExit(1); diff --git a/tools/tests/old/app-with-private/.meteor/versions b/tools/tests/old/app-with-private/.meteor/versions index 4a33deb24f..d3261ace0f 100644 --- a/tools/tests/old/app-with-private/.meteor/versions +++ b/tools/tests/old/app-with-private/.meteor/versions @@ -47,7 +47,7 @@ npm-mongo@2.2.33 ordered-dict@1.0.9 promise@0.10.0 random@1.0.10 -reactive-var@1.0.11 +reactive-var@1.0.12-beta.1 reload@1.1.11 retry@1.0.9 routepolicy@1.0.12 @@ -58,7 +58,7 @@ standard-minifier-js@2.2.1 static-html@1.2.2 templating-tools@1.1.2 tracker@1.1.3 -underscore@1.0.10 +underscore@1.0.11-beta.1 url@1.1.0 webapp@1.4.0 webapp-hashing@1.0.9 diff --git a/tools/tool-testing/galaxy-utils.js b/tools/tool-testing/galaxy-utils.js index 14dfc71714..0731bdd6fc 100644 --- a/tools/tool-testing/galaxy-utils.js +++ b/tools/tool-testing/galaxy-utils.js @@ -44,8 +44,8 @@ exports.sanityCheck = selftest.markStack(function () { // Login to Galaxy with environment-variable credentials passed in by the user. // -// Unlike the normal `meteor deploy` Galaxy is not yet publically available, so -// we don't want to use the publically-accessible test account here. +// Unlike the normal `meteor deploy` Galaxy is not yet publicly available, so +// we don't want to use the publicly-accessible test account here. exports.loginToGalaxy = selftest.markStack(function (sandbox) { var user = GALAXY_USERNAME; var pass = GALAXY_PASSWORD; @@ -99,7 +99,7 @@ exports.createAndDeployApp = selftest.markStack(function (sandbox, options) { var settings = options.settings; var appName = options.appName || testUtils.randomAppName(); - // The simple app contains standart app packages and some small bits of code + // The simple app contains standard app packages and some small bits of code // so that we can check that it is being served correctly. Let's use that as // our default. var templateApp = options.templateApp || 'simple-app'; diff --git a/tools/tool-testing/sandbox.js b/tools/tool-testing/sandbox.js index 765db2d2e4..37d7bbc9d4 100644 --- a/tools/tool-testing/sandbox.js +++ b/tools/tool-testing/sandbox.js @@ -283,7 +283,7 @@ export default class Sandbox { } // Write to a file in the sandbox, overwriting its current contents - // if any. 'filename' is a path intepreted relative to the Sandbox's + // if any. 'filename' is a path interpreted relative to the Sandbox's // cwd. 'contents' is a string (utf8 is assumed). write(filename, contents) { files.writeFile(files.pathJoin(this.cwd, filename), contents, 'utf8'); @@ -295,7 +295,7 @@ export default class Sandbox { } // Reads a file in the sandbox as a utf8 string. 'filename' is a - // path intepreted relative to the Sandbox's cwd. Returns null if + // path interpreted relative to the Sandbox's cwd. Returns null if // file does not exist. read(filename) { const file = files.pathJoin(this.cwd, filename); diff --git a/tools/tsconfig.json b/tools/tsconfig.json index 234b36f9bc..88e1ef394b 100644 --- a/tools/tsconfig.json +++ b/tools/tsconfig.json @@ -29,6 +29,7 @@ "exclude": [ "./tests/apps/**", "./tests/packages/**", - "./static-assets/skel*/**" + "./static-assets/skel*/**", + "./static-assets/scaffolds*/**", ] } diff --git a/tools/utils/buildmessage.js b/tools/utils/buildmessage.js index 80bd5e2be6..bbbc194272 100644 --- a/tools/utils/buildmessage.js +++ b/tools/utils/buildmessage.js @@ -7,7 +7,7 @@ var Progress = require('../console/progress').Progress; var debugBuild = !!process.env.METEOR_DEBUG_BUILD; // A job is something like "building package foo". It contains the set -// of messages generated by tha job. A given build run could contain +// of messages generated by the job. A given build run could contain // several jobs. Each job has an (absolute) path associated with // it. Filenames in messages within a job are to be interpreted // relative to that path. diff --git a/tools/utils/parse-stack.ts b/tools/utils/parse-stack.ts index 9a4a970ef6..e513ea1004 100644 --- a/tools/utils/parse-stack.ts +++ b/tools/utils/parse-stack.ts @@ -25,7 +25,7 @@ type ParsedStackFrame = { * More recently called functions appear first. * * Accomplishes this by parsing the text representation of the stack - * with regular expressions. Unlikey to work anywhere but v8. + * with regular expressions. Unlikely to work anywhere but v8. * * If a function on the stack has been marked with mark(), will not * return anything past that function. We call this the "user portion"