Merge branch 'release-1.5' into wip-upgrade-to-node-6

This commit is contained in:
Ben Newman
2017-05-11 16:36:16 -04:00
124 changed files with 2670 additions and 1059 deletions

View File

@@ -2,23 +2,24 @@
We are excited to have your help building Meteor — both the platform and the community behind it. Please read the project overview and guidelines for contributing bug reports and new code, or it might be hard for the community to help you with your issue or pull request.
<h2 id="project-overview">Project overview</h2>
## Project overview
Before we jump into detailed guidelines for opening and triaging issues and submitting pull requests, here is some information about how our project is structured and resources you should refer to as you start contributing.
### Ways to contribute
There are many ways to contribute to the Meteor Project. Heres a list of technical contributions with increasing levels of involvement and required knowledge of Meteors code and operations.
- [Reporting a bug](https://github.com/meteor/meteor/blob/devel/Contributing.md#reporting-a-bug-in-meteor)
- [Triaging issues](https://github.com/meteor/meteor/blob/devel/IssueTriage.md)
- [Contributing to documentation](https://github.com/meteor/docs/blob/master/Contributing.md)
- [Submitting pull requests](https://github.com/meteor/meteor/blob/devel/Contributing.md#making-changes-to-meteor-core) (See "Finding work" below)
- [Reviewing pull requests](https://github.com/meteor/meteor/blob/devel/Contributing.md#reviewer)
- [Maintaining a community package](https://github.com/meteor/meteor/blob/devel/Contributing.md#community-package-maintainer)
- [Reporting a bug](Contributing.md#reporting-a-bug-in-meteor)
- [Triaging issues](IssueTriage.md)
- [Contributing to documentation](https://github.com/meteor/docs/blob/master/Contributing.md)
- [Finding work](Contributing.md#finding-work)
- [Submitting pull requests](Contributing.md#making-changes-to-meteor-core)
- [Reviewing pull requests](Contributing.md#reviewer)
- [Maintaining a community package](Contributing.md#community-package-maintainer)
There are also several ways to contribute to the Meteor Project outside of GitHub, like organizing or speaking at [Meetups](https://www.meetup.com/topics/meteor/) and events and helping to moderate our [forums](https://forums.meteor.com/). Stay tuned for more documentation around non-code contributions.
There are also several ways to contribute to the Meteor Project outside of GitHub, like organizing or speaking at [Meetups](https://www.meetup.com/topics/meteor/) and events and helping to moderate our [forums](https://forums.meteor.com/).
If you can think of any changes to the project, [documentation](https://github.com/meteor/docs), or [guide](https://github.com/meteor/guide) that would improve the contributor experience, let us know by opening an issue!
If you can think of any changes to the project, [documentation](https://github.com/meteor/docs), or [guide](https://github.com/meteor/guide) that would improve the contributor experience, let us know by opening an issue in the correct repository!
### Finding work
@@ -26,7 +27,7 @@ We curate specific issues that would make great pull requests for community cont
Issues which *also* have the [`confirmed` label](https://github.com/meteor/meteor/issues?q=is%3Aissue%20is%3Aopen%20label%3Apull-requests-encouraged%20label%3Aconfirmed) are considered to have their details clear enough to begin working on.
Any issue which does not have the `confirmed` label still requires discussion on implementation details but input and positive commentary is welcome! Any pull-request opened on an issue which is not `confirmed` is still welcome, however the pull-request is more likely to be sent back for reworking than a `confirmed` issue. If in doubt about the best way to implement something, please create additional conversation on the issue.
Any issue which does not have the `confirmed` label still requires discussion on implementation details but input and positive commentary is welcome! Any pull request opened on an issue which is not `confirmed` is still welcome, however the pull-request is more likely to be sent back for reworking than a `confirmed` issue. If in doubt about the best way to implement something, please create additional conversation on the issue.
### Project roles
@@ -72,7 +73,7 @@ Current Documentation Maintainers:
#### Community Package Maintainer:
Community package maintainers are community members who maintain packages outside of Meteor core. This requires code to be extracted from meteor/meteor, and entails a high level of responsibility. For this reason, community maintainers generally (and currently) must first become an advanced contributor to Meteor core and have 4-5 non-trivial pull requests merged that went through the proper contribution workflow. At that point, core contributors may make the case for breaking out a particular core package, and assist in the technical process around doing so.
Community package maintainers are community members who maintain packages outside of Meteor core. This requires code to be extracted from meteor/meteor, and entails a high level of responsibility. For this reason, community maintainers generally (and currently) must first become an advanced contributor to Meteor core and have 4-5 non-trivial pull requests merged that went through the proper contribution work-flow. At that point, core contributors may make the case for breaking out a particular core package, and assist in the technical process around doing so.
Current Community Package Maintainers:
- [@mitar](https://github.com/mitar) for [Blaze](https://github.com/meteor/blaze)
@@ -88,11 +89,12 @@ Current Community Manager:
Right now, the best place to track the work being done on Meteor is to take a look at the latest release milestone [here](https://github.com/meteor/meteor/milestones). Also, the [Meteor Roadmap](Roadmap.md) contains high-level information on the current priorities of the project.
<h2 id="reporting-bug">Reporting a bug in Meteor</h2>
## Reporting a bug in Meteor
<a name="reporting-bug"></a>
We welcome clear bug reports. If you've found a bug in Meteor that
isn't a security risk, please file a report in
[our issue tracker](https://github.com/meteor/meteor/issues). Before you file your issue, look to see if it has already been reported. If so, comment, up-vote or +1 the existing issue to show that it's affecting multiple people.
[our issue tracker](https://github.com/meteor/meteor/issues). Before you file your issue, **search** to see if it has already been reported. If so, up-vote (using GitHub reactions) or add additional helpful details to the existing issue to show that it's affecting multiple people.
> There is a separate procedure for security-related issues. If the
> issue you've found contains sensitive information or raises a security
@@ -135,19 +137,18 @@ A reproduction recipe works like this:
If you want to submit a pull request that fixes your bug, that's even
better. We love getting bugfix pull requests. Just make sure they're
written to the MDG style guide and *come with tests*. Read further down
written with the [correct style](Development.md#code-style) and *come with tests*. Read further down
for more details on proposing changes to core code.
<h2 id="feature-requests">Feature requests</h2>
## Feature requests
As of May 2016, we use GitHub to track feature requests. Feature request issues get the `feature` label, as well as a label
We use GitHub to track feature requests. Feature request issues get the `feature` label, as well as a label
corresponding to the Meteor subproject that they are a part of.
Meteor is a big project with [many subprojects](https://github.com/meteor/meteor/tree/devel/packages).
Right now, the project doesn't have as many
[core developers (we're hiring!)](https://www.meteor.com/jobs/core-developer)
as subprojects, so we're not able to work on every single subproject every
month. We use our [roadmap](Roadmap.md) to communicate the high level features we're prioritizing over the near and medium term.
Meteor is a big project with [many sub-projects](https://github.com/meteor/meteor/tree/devel/packages).
There aren't as many [core developers (we're hiring!)](https://www.meteor.io/jobs/)
as there are sub-projects, so we're not able to work on every single sub-project every
month. We use our [roadmap](Roadmap.md) to communicate the high-level features we're currently prioritizing.
Every additional feature adds a maintenance cost in addition to its value. This
cost starts with the work of writing the feature or reviewing a community pull
@@ -160,7 +161,7 @@ For these reasons, we strongly encourage features to be implemented as [Atmosphe
Feature requests should be well specified and unambiguous to have the greatest chance of being worked on by a contributor.
Finally, you can show your support for features you would like by commenting with a +1 or up-voting the issue.
Finally, you can show your support for (or against!) features by using [GitHub reactions](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments) or by adding meaningful details which help the feature definition become more clear. Please do not comment with "+1" since it creates a lot of noise (e-mails, notifications, etc.).
## Triaging issues
@@ -170,6 +171,10 @@ A great way to contribute to Meteor is by helping keep the issues in the reposit
If you'd like to contribution to Meteor's documentation, head over to https://github.com/meteor/docs and create issues or pull requests there.
## Blaze
Blaze lives in its [own repository](https://github.com/meteor/blaze/) with its own [issue tracker and feature prioritization](https://github.com/meteor/blaze/issues/) and is not tracked within Meteor core.
## Making changes to Meteor core
Eventually you may want to change something in a core Meteor package, or
@@ -189,7 +194,7 @@ any change to a core package:
1. Nothing in Meteor should harm the experience of a new Meteor
developer. That can be a difficult standard to reach, because we're
concerned here with the entire experience of developing and deploying
concerned with the entire experience of developing and deploying
an application. For example, we work hard to make sure that the Meteor
docs don't force new users to understand advanced concepts before they
need them. And we think a great deal about making our APIs as
@@ -206,8 +211,7 @@ any change to a core package:
an expert then we'll probably prefer a different approach.
We have found that writing software to meet both these standards at the
same time is hard but
incredibly rewarding. We hope you come to feel the same way.
same time is hard but incredibly rewarding. We hope you come to feel the same way.
### Understanding the core
@@ -219,19 +223,18 @@ You'll have the best chance of getting a change into core if you can build conse
Help drive discussion and advocate for your feature on the Github ticket (and perhaps the forums). The higher the demand for the feature and the greater the clarity of it's specification will determine the likelihood of a core contributor prioritizing your feature by flagging it with the `pull-requests-encouraged` label.
Split features up into smaller, logically separable chunks. It is unlikely that large and complicated PRs will be merged.
Split features up into smaller, logically separate chunks. It is unlikely that large and complicated PRs will be merged.
Once your feature has been labelled with `pull-requests-encouraged`, leave a comment letting people know you're working on it and you can begin work on the code.
### Submitting pull requests
Once you've hammered out a good design go ahead and submit a pull request. If your PR isn't against a bug with the `confirmed` label or a feature request with the `pull-requests-encouraged` label, don't expect your PR to be merged unless it's a trivial and obvious fix (e.g documentation). When submitting a PR, please follow
these guidelines:
Once you've come up with a good design, go ahead and submit a pull request (PR). If your PR isn't against a bug with the `confirmed` label or a feature request with the `pull-requests-encouraged` label, don't expect your PR to be merged unless it's a trivial and obvious fix (e.g. documentation). When submitting a PR, please follow these guidelines:
* Sign the [contributor's agreement](http://contribute.meteor.com/).
* Base all your work off of the **devel** branch. The **devel** branch
is where active development happens. **We do not merge patches
is where active development happens. **We do not merge pull requests
directly into master.**
* Name your branch to match the feature/bug fix that you are
@@ -251,12 +254,6 @@ these guidelines:
### Need help with your pull request?
Meteor now has groups defined to cover different areas of the codebase. If you need help getting acceptance on certain pull requests with an area of focus listed below, you can address the appropriate people in the pull request:
If you need help with a pull request, you should start by asking questions in the issue which it pertains to. If you feel that your pull request is almost ready or needs feedback which can only be demonstrated with code, go ahead and open a pull-request with as much progress as possible. By including a "[Work in Progress]" note in the subject, project contributors will know you need help!
* Meteor Data Team - This includes DDP, tracker, mongo, accounts, etc. You can mention @data in the PR.
* Blaze - This includes Spacebars, Blaze, etc. You can mention @view-layer in the PR.
* Build tools - This includes modules, build tool changes, etc. You can mention @platform in the PR.
* Mobile integration - This includes Cordova, React Native, etc. You can mention @mobile in the PR.
* Documentation - This includes the Guide, the Docs, and any supporting material. You can mention @guide in the PR.
Including the people above is no guarantee that you will get a response, or ultimately that your pull request will be accepted. This section exists to give some minor guidance on internal Meteor Development Group team structures.
Submitting a pull request is no guarantee it will be accepted, but contributors will do their best to help move your pull request toward release.

View File

@@ -88,6 +88,12 @@ $ ./scripts/generate-dev-bundle.sh
This will generate a new tarball (`dev_bundle_<Platform>_<arch>_<version>.tar.gz`) in the root of the checkout. Assuming you bumped the `BUNDLE_VERSION`, the new version will be extracted automatically when you run `./meteor`. If you are rebuilding the same version (or didn't bump the version number), you should delete the existing `dev_bundle` directory to ensure the new tarball is extracted when you run `./meteor`.
### Submitting "Dev Bundle" Pull Requests
It's important to note that while `dev_bundle` pull requests are accepted/reviewed, a new `dev_bundle` can only be published to MDG's Meteor infrastructure by an MDG staff member. This means that the build tool and package tests of submitted `dev_bundle` pull requests will always initially fail (since the new `dev_bundle` hasn't yet been built/published by MDG, which means it can't be downloaded by Meteor's continuous integration environment).
Pull requests that contain `dev_bundle` changes will be noted by repo collaborators, and a request to have a new `dev_bundle` built/published will be forwarded to MDG.
## Additional documentation
The Meteor core is best documented within the code itself, however, many components also have a `README.md` in their respective directories.

View File

@@ -6,6 +6,8 @@
## v1.5, TBD
* Node has been upgraded to version 4.8.3.
* Running `meteor add dynamic-import` installs support for ECMAScript
[dynamic `import(...)`](https://github.com/tc39/proposal-dynamic-import),
a new language feature which allows for asynchronous module fetching
@@ -15,6 +17,71 @@
information about how dynamic `import(...)` works in Meteor, and how to
use it in your applications.
* The `meteor-babel` npm package has been upgraded to version 0.21.2,
enabling the latest Reify compiler and the transform-class-properties
plugin, among other improvements.
* The `reify` npm package has been upgraded to version 0.11.0, fixing
[issue #8595](https://github.com/meteor/meteor/issues/8595) and
improving compilation and runtime performance.
> 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.
* The `install` npm package has been upgraded to version 0.10.1.
* If you're using the `standard-minifier-js` Meteor package, as most
Meteor developers do, it will now produce a detailed analysis of package
and module sizes within your production `.js` bundle whenever you run
`meteor build` or `meteor run --production`. These data are served by
the application web server at the same URL as the minified `.js` bundle,
except with a `.stats.json` file extension instead of `.js`. If you're
using a different minifier plugin, and would like to support similar
functionality, refer to
[these](https://github.com/meteor/meteor/pull/8327/commits/084801237a8c288d99ec82b0fbc1c76bdf1aab16)
[commits](https://github.com/meteor/meteor/pull/8327/commits/1c8bc7353e9a8d526880634a58c506b423c4a55e)
for inspiration.
## v1.4.4.2, 2017-05-02
* Node has been upgraded to version 4.8.2.
* The `npm` npm package has been upgraded to version 4.5.0.
Note that when using npm `scripts` there has been a change regarding
what happens when `SIGINT` (Ctrl-C) is received. Read more
[here](https://github.com/npm/npm/releases/tag/v4.5.0).
* Fix a regression which prevented us from displaying a helpful banner when
running `meteor debug` because of a change in Node.js.
* Update `node-inspector` npm to 1.1.1, fixing a problem encountered when trying
to press "Enter" in the inspector console.
[Issue #8469](https://github.com/meteor/meteor/issues/8469)
* The `email` package has had its `mailcomposer` npm package swapped with
a Node 4 fork of `nodemailer` due to its ability to support connection pooling
in a similar fashion as the original `mailcomposer`.
[Issue #8591](https://github.com/meteor/meteor/issues/8591)
[PR #8605](https://github.com/meteor/meteor/pull/8605)
> Note: The `MAIL_URL` should be configured with a scheme which matches the
> protocol desired by your e-mail vendor/mail-transport agent. For
> encrypted connections (typically listening on port 465 or 587), this means
> using `smtps://`. Unencrypted connections should continue to use
> `smtp://`.
* A new `Tracker.inFlush()` has been added to provide a global Tracker
"flushing" state.
[PR #8565](https://github.com/meteor/meteor/pull/8565).
* The `meteor-babel` npm package has been upgraded to version 0.20.1, and
the `reify` npm package has been upgraded to version 0.7.4, fixing
[issue #8595](https://github.com/meteor/meteor/issues/8595).
(This was fixed between full Meteor releases, but is being mentioned here.)
## v1.4.4.1, 2017-04-07
* A change in Meteor 1.4.4 to remove "garbage" directories asynchronously

View File

@@ -28,6 +28,9 @@ export class AccountsClient extends AccountsCommon {
// Defined in localstorage_token.js.
this._initLocalStorage();
// This is for .registerClientLoginFunction & .callLoginFunction.
this._loginFuncs = {};
}
///
@@ -62,6 +65,49 @@ export class AccountsClient extends AccountsCommon {
return this._loggingOut.get();
}
/**
* @summary Register a new login function on the client. Intended for OAuth package authors. You can call the login function by using
`Accounts.callLoginFunction` or `Accounts.callLoginFunction`.
* @locus Client
* @param {String} funcName The name of your login function. Used by `Accounts.callLoginFunction` and `Accounts.applyLoginFunction`.
Should be the OAuth provider name accordingly.
* @param {Function} func The actual function you want to call. Just write it in the manner of `loginWithFoo`.
*/
registerClientLoginFunction(funcName, func) {
if (this._loginFuncs[funcName]) {
throw new Error(`${funcName} has been defined already`);
}
this._loginFuncs[funcName] = func;
}
/**
* @summary Call a login function defined using `Accounts.registerClientLoginFunction`. Excluding the first argument, all remaining
arguments are passed to the login function accordingly. Use `applyLoginFunction` if you want to pass in an arguments array that contains
all arguments for the login function.
* @locus Client
* @param {String} funcName The name of the login function you wanted to call.
*/
callLoginFunction(funcName, ...funcArgs) {
if (!this._loginFuncs[funcName]) {
throw new Error(`${funcName} was not defined`);
}
return this._loginFuncs[funcName].apply(this, funcArgs);
}
/**
* @summary Same as ``callLoginFunction` but accept an `arguments` which contains all arguments for the login
function.
* @locus Client
* @param {String} funcName The name of the login function you wanted to call.
* @param {Array} funcArgs The `arguments` for the login function.
*/
applyLoginFunction(funcName, funcArgs) {
if (!this._loginFuncs[funcName]) {
throw new Error(`${funcName} was not defined`);
}
return this._loginFuncs[funcName].apply(this, funcArgs);
}
/**
* @summary Log the user out.
* @locus Client

View File

@@ -1502,6 +1502,8 @@ function setupUsersCollection(users) {
{ sparse: 1 });
// For expiring login tokens
users._ensureIndex("services.resume.loginTokens.when", { sparse: 1 });
// For expiring password tokens
users._ensureIndex('services.password.reset.when', { sparse: 1 });
}
///

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "A user account system",
version: "1.2.16"
version: "1.3.0"
});
Package.onUse(function (api) {

View File

@@ -1,7 +1,7 @@
Accounts.oauth.registerService('facebook');
if (Meteor.isClient) {
Meteor.loginWithFacebook = function(options, callback) {
const loginWithFacebook = function(options, callback) {
// support a callback without options
if (! callback && typeof options === "function") {
callback = options;
@@ -11,6 +11,10 @@ if (Meteor.isClient) {
var credentialRequestCompleteCallback = Accounts.oauth.credentialRequestCompleteHandler(callback);
Facebook.requestCredential(options, credentialRequestCompleteCallback);
};
Accounts.registerClientLoginFunction('facebook', loginWithFacebook);
Meteor.loginWithFacebook = function () {
return Accounts.applyLoginFunction('facebook', arguments);
};
} else {
Accounts.addAutopublishFields({
// publish all fields including access token, which can legitimately

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Login service for Facebook accounts",
version: "1.1.1"
version: "1.2.0"
});
Package.onUse(function(api) {

View File

@@ -1,7 +1,7 @@
Accounts.oauth.registerService('github');
if (Meteor.isClient) {
Meteor.loginWithGithub = function(options, callback) {
const loginWithGithub = function(options, callback) {
// support a callback without options
if (! callback && typeof options === "function") {
callback = options;
@@ -11,6 +11,10 @@ if (Meteor.isClient) {
var credentialRequestCompleteCallback = Accounts.oauth.credentialRequestCompleteHandler(callback);
Github.requestCredential(options, credentialRequestCompleteCallback);
};
Accounts.registerClientLoginFunction('github', loginWithGithub);
Meteor.loginWithGithub = function () {
return Accounts.applyLoginFunction('github', arguments);
};
} else {
Accounts.addAutopublishFields({
// not sure whether the github api can be used from the browser,

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: 'Login service for Github accounts',
version: '1.2.1'
version: '1.3.0'
});
Package.onUse(function (api) {

View File

@@ -1,7 +1,7 @@
Accounts.oauth.registerService('google');
if (Meteor.isClient) {
Meteor.loginWithGoogle = function(options, callback) {
const loginWithGoogle = function(options, callback) {
// support a callback without options
if (! callback && typeof options === "function") {
callback = options;
@@ -30,6 +30,10 @@ if (Meteor.isClient) {
var credentialRequestCompleteCallback = Accounts.oauth.credentialRequestCompleteHandler(callback);
Google.requestCredential(options, credentialRequestCompleteCallback);
};
Accounts.registerClientLoginFunction('google', loginWithGoogle);
Meteor.loginWithGoogle = function () {
return Accounts.applyLoginFunction('google', arguments);
};
} else {
Accounts.addAutopublishFields({
forLoggedInUser: _.map(

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Login service for Google accounts",
version: "1.1.2"
version: "1.2.0"
});
Package.onUse(function(api) {

View File

@@ -1,7 +1,7 @@
Accounts.oauth.registerService('meetup');
if (Meteor.isClient) {
Meteor.loginWithMeetup = function(options, callback) {
const loginWithMeetup = function(options, callback) {
// support a callback without options
if (! callback && typeof options === "function") {
callback = options;
@@ -11,6 +11,10 @@ if (Meteor.isClient) {
var credentialRequestCompleteCallback = Accounts.oauth.credentialRequestCompleteHandler(callback);
Meetup.requestCredential(options, credentialRequestCompleteCallback);
};
Accounts.registerClientLoginFunction('meetup', loginWithMeetup);
Meteor.loginWithMeetup = function () {
return Accounts.applyLoginFunction('meetup', arguments);
};
} else {
Accounts.addAutopublishFields({
// publish all fields including access token, which can legitimately

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: 'Login service for Meetup accounts',
version: '1.2.1'
version: '1.3.0'
});
Package.onUse(function (api) {

View File

@@ -1,7 +1,7 @@
Accounts.oauth.registerService("meteor-developer");
if (Meteor.isClient) {
Meteor.loginWithMeteorDeveloperAccount = function (options, callback) {
const loginWithMeteorDeveloperAccount = function (options, callback) {
// support a callback without options
if (! callback && typeof options === "function") {
callback = options;
@@ -12,6 +12,10 @@ if (Meteor.isClient) {
Accounts.oauth.credentialRequestCompleteHandler(callback);
MeteorDeveloperAccounts.requestCredential(options, credentialRequestCompleteCallback);
};
Accounts.registerClientLoginFunction('meteor-developer', loginWithMeteorDeveloperAccount);
Meteor.loginWithMeteorDeveloperAccount = function () {
return Accounts.applyLoginFunction('meteor-developer', arguments);
};
} else {
Accounts.addAutopublishFields({
// publish all fields including access token, which can legitimately be used

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: 'Login service for Meteor developer accounts',
version: '1.2.1'
version: '1.3.0'
});
Package.onUse(function (api) {

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Password support for accounts",
version: "1.3.5"
version: "1.3.6"
});
Package.onUse(function(api) {

View File

@@ -1126,5 +1126,3 @@ Meteor.users._ensureIndex('services.email.verificationTokens.token',
{unique: 1, sparse: 1});
Meteor.users._ensureIndex('services.password.reset.token',
{unique: 1, sparse: 1});
Meteor.users._ensureIndex('services.password.reset.when',
{sparse: 1});

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Login service for Twitter accounts",
version: "1.2.1"
version: "1.3.0"
});
Package.onUse(function(api) {

View File

@@ -1,7 +1,7 @@
Accounts.oauth.registerService('twitter');
if (Meteor.isClient) {
Meteor.loginWithTwitter = function(options, callback) {
const loginWithTwitter = function(options, callback) {
// support a callback without options
if (! callback && typeof options === "function") {
callback = options;
@@ -11,6 +11,10 @@ if (Meteor.isClient) {
var credentialRequestCompleteCallback = Accounts.oauth.credentialRequestCompleteHandler(callback);
Twitter.requestCredential(options, credentialRequestCompleteCallback);
};
Accounts.registerClientLoginFunction('twitter', loginWithTwitter);
Meteor.loginWithTwitter = function () {
return Accounts.applyLoginFunction('twitter', arguments);
};
} else {
var autopublishedFields = _.map(
// don't send access token. https://dev.twitter.com/discussions/5025

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Login service for Sina Weibo accounts",
version: "1.1.1"
version: "1.2.0"
});
Package.onUse(function(api) {

View File

@@ -1,7 +1,7 @@
Accounts.oauth.registerService('weibo');
if (Meteor.isClient) {
Meteor.loginWithWeibo = function(options, callback) {
const loginWithWeibo = function(options, callback) {
// support a callback without options
if (! callback && typeof options === "function") {
callback = options;
@@ -11,6 +11,10 @@ if (Meteor.isClient) {
var credentialRequestCompleteCallback = Accounts.oauth.credentialRequestCompleteHandler(callback);
Weibo.requestCredential(options, credentialRequestCompleteCallback);
};
Accounts.registerClientLoginFunction('weibo', loginWithWeibo);
Meteor.loginWithWeibo = function () {
return Accounts.applyLoginFunction('weibo', arguments);
};
} else {
Accounts.addAutopublishFields({
// publish all fields including access token, which can legitimately

View File

@@ -1,9 +1,9 @@
{
"dependencies": {
"acorn": {
"version": "4.0.11",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.11.tgz",
"from": "acorn@>=4.0.5 <4.1.0"
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.0.3.tgz",
"from": "acorn@>=5.0.0 <5.1.0"
},
"ansi-regex": {
"version": "2.1.1",
@@ -21,29 +21,29 @@
"from": "babel-code-frame@>=6.22.0 <7.0.0"
},
"babel-core": {
"version": "6.24.0",
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.24.0.tgz",
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.24.1.tgz",
"from": "babel-core@>=6.22.1 <7.0.0"
},
"babel-generator": {
"version": "6.24.0",
"resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.24.0.tgz",
"from": "babel-generator@>=6.24.0 <7.0.0"
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.24.1.tgz",
"from": "babel-generator@>=6.24.1 <7.0.0"
},
"babel-helper-builder-react-jsx": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.23.0.tgz",
"from": "babel-helper-builder-react-jsx@>=6.23.0 <7.0.0"
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.24.1.tgz",
"from": "babel-helper-builder-react-jsx@>=6.24.1 <7.0.0"
},
"babel-helper-call-delegate": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.22.0.tgz",
"from": "babel-helper-call-delegate@>=6.22.0 <7.0.0"
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
"from": "babel-helper-call-delegate@>=6.24.1 <7.0.0"
},
"babel-helper-define-map": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.23.0.tgz",
"from": "babel-helper-define-map@>=6.23.0 <7.0.0"
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz",
"from": "babel-helper-define-map@>=6.24.1 <7.0.0"
},
"babel-helper-evaluate-path": {
"version": "0.0.3",
@@ -56,19 +56,19 @@
"from": "babel-helper-flip-expressions@>=0.0.2 <0.0.3"
},
"babel-helper-function-name": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.23.0.tgz",
"from": "babel-helper-function-name@>=6.23.0 <7.0.0"
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz",
"from": "babel-helper-function-name@>=6.24.1 <7.0.0"
},
"babel-helper-get-function-arity": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.22.0.tgz",
"from": "babel-helper-get-function-arity@>=6.22.0 <7.0.0"
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz",
"from": "babel-helper-get-function-arity@>=6.24.1 <7.0.0"
},
"babel-helper-hoist-variables": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.22.0.tgz",
"from": "babel-helper-hoist-variables@>=6.22.0 <7.0.0"
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz",
"from": "babel-helper-hoist-variables@>=6.24.1 <7.0.0"
},
"babel-helper-is-nodes-equiv": {
"version": "0.0.1",
@@ -86,14 +86,14 @@
"from": "babel-helper-mark-eval-scopes@>=0.0.3 <0.0.4"
},
"babel-helper-optimise-call-expression": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.23.0.tgz",
"from": "babel-helper-optimise-call-expression@>=6.23.0 <7.0.0"
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz",
"from": "babel-helper-optimise-call-expression@>=6.24.1 <7.0.0"
},
"babel-helper-regex": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.22.0.tgz",
"from": "babel-helper-regex@>=6.22.0 <7.0.0"
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz",
"from": "babel-helper-regex@>=6.24.1 <7.0.0"
},
"babel-helper-remove-or-void": {
"version": "0.0.1",
@@ -101,9 +101,9 @@
"from": "babel-helper-remove-or-void@>=0.0.1 <0.0.2"
},
"babel-helper-replace-supers": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.23.0.tgz",
"from": "babel-helper-replace-supers@>=6.23.0 <7.0.0"
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz",
"from": "babel-helper-replace-supers@>=6.24.1 <7.0.0"
},
"babel-helper-to-multiple-sequence-expressions": {
"version": "0.0.3",
@@ -111,9 +111,9 @@
"from": "babel-helper-to-multiple-sequence-expressions@>=0.0.3 <0.0.4"
},
"babel-helpers": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.23.0.tgz",
"from": "babel-helpers@>=6.23.0 <7.0.0"
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz",
"from": "babel-helpers@>=6.24.1 <7.0.0"
},
"babel-messages": {
"version": "6.23.0",
@@ -131,8 +131,8 @@
"from": "babel-plugin-minify-constant-folding@>=0.0.4 <0.0.5",
"dependencies": {
"jsesc": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.4.0.tgz",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz",
"from": "jsesc@>=2.4.0 <3.0.0"
}
}
@@ -199,6 +199,11 @@
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz",
"from": "babel-plugin-syntax-async-generators@>=6.13.0 <7.0.0"
},
"babel-plugin-syntax-class-properties": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz",
"from": "babel-plugin-syntax-class-properties@>=6.8.0 <7.0.0"
},
"babel-plugin-syntax-dynamic-import": {
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz",
@@ -224,6 +229,11 @@
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz",
"from": "babel-plugin-syntax-trailing-function-commas@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-class-properties": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz",
"from": "babel-plugin-transform-class-properties@>=6.24.1 <7.0.0"
},
"babel-plugin-transform-es2015-arrow-functions": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
@@ -235,18 +245,18 @@
"from": "babel-plugin-transform-es2015-block-scoped-functions@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-es2015-block-scoping": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.23.0.tgz",
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz",
"from": "babel-plugin-transform-es2015-block-scoping@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-es2015-classes": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.23.0.tgz",
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz",
"from": "babel-plugin-transform-es2015-classes@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-es2015-computed-properties": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.22.0.tgz",
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz",
"from": "babel-plugin-transform-es2015-computed-properties@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-es2015-destructuring": {
@@ -265,28 +275,28 @@
"from": "babel-plugin-transform-es2015-literals@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-es2015-modules-commonjs": {
"version": "6.24.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.0.tgz",
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz",
"from": "babel-plugin-transform-es2015-modules-commonjs@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-es2015-modules-reify": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-reify/-/babel-plugin-transform-es2015-modules-reify-0.6.2.tgz",
"from": "babel-plugin-transform-es2015-modules-reify@>=0.6.0 <0.7.0"
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-reify/-/babel-plugin-transform-es2015-modules-reify-0.11.0.tgz",
"from": "babel-plugin-transform-es2015-modules-reify@>=0.11.0 <0.12.0"
},
"babel-plugin-transform-es2015-object-super": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.22.0.tgz",
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz",
"from": "babel-plugin-transform-es2015-object-super@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-es2015-parameters": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.23.0.tgz",
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz",
"from": "babel-plugin-transform-es2015-parameters@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-es2015-shorthand-properties": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.22.0.tgz",
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz",
"from": "babel-plugin-transform-es2015-shorthand-properties@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-es2015-spread": {
@@ -295,8 +305,8 @@
"from": "babel-plugin-transform-es2015-spread@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-es2015-sticky-regex": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.22.0.tgz",
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz",
"from": "babel-plugin-transform-es2015-sticky-regex@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-es2015-template-literals": {
@@ -310,8 +320,8 @@
"from": "babel-plugin-transform-es2015-typeof-symbol@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-es2015-unicode-regex": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.22.0.tgz",
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz",
"from": "babel-plugin-transform-es2015-unicode-regex@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-es3-property-literals": {
@@ -360,9 +370,9 @@
"from": "babel-plugin-transform-react-display-name@>=6.23.0 <7.0.0"
},
"babel-plugin-transform-react-jsx": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.23.0.tgz",
"from": "babel-plugin-transform-react-jsx@>=6.23.0 <7.0.0"
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz",
"from": "babel-plugin-transform-react-jsx@>=6.24.1 <7.0.0"
},
"babel-plugin-transform-react-jsx-self": {
"version": "6.22.0",
@@ -375,8 +385,8 @@
"from": "babel-plugin-transform-react-jsx-source@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-regenerator": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.22.0.tgz",
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz",
"from": "babel-plugin-transform-regenerator@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-regexp-constructors": {
@@ -410,9 +420,9 @@
"from": "babel-plugin-transform-simplify-comparison-operators@>=6.8.1 <7.0.0"
},
"babel-plugin-transform-strict-mode": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.22.0.tgz",
"from": "babel-plugin-transform-strict-mode@>=6.22.0 <7.0.0"
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
"from": "babel-plugin-transform-strict-mode@>=6.24.1 <7.0.0"
},
"babel-plugin-transform-undefined-to-void": {
"version": "6.8.0",
@@ -430,19 +440,19 @@
"from": "babel-preset-flow@>=6.23.0 <7.0.0"
},
"babel-preset-meteor": {
"version": "6.25.0",
"resolved": "https://registry.npmjs.org/babel-preset-meteor/-/babel-preset-meteor-6.25.0.tgz",
"from": "babel-preset-meteor@6.25.0"
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-preset-meteor/-/babel-preset-meteor-6.26.0.tgz",
"from": "babel-preset-meteor@6.26.0"
},
"babel-preset-react": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.23.0.tgz",
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz",
"from": "babel-preset-react@>=6.22.0 <7.0.0"
},
"babel-register": {
"version": "6.24.0",
"resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.0.tgz",
"from": "babel-register@>=6.24.0 <7.0.0"
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.1.tgz",
"from": "babel-register@>=6.24.1 <7.0.0"
},
"babel-runtime": {
"version": "6.23.0",
@@ -450,23 +460,23 @@
"from": "babel-runtime@>=6.22.0 <7.0.0"
},
"babel-template": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.23.0.tgz",
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.24.1.tgz",
"from": "babel-template@>=6.22.0 <7.0.0"
},
"babel-traverse": {
"version": "6.23.1",
"resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.23.1.tgz",
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.24.1.tgz",
"from": "babel-traverse@>=6.22.1 <7.0.0"
},
"babel-types": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.23.0.tgz",
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.24.1.tgz",
"from": "babel-types@>=6.22.0 <7.0.0"
},
"babylon": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.16.1.tgz",
"version": "6.17.0",
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.0.tgz",
"from": "babylon@>=6.15.0 <7.0.0"
},
"balanced-match": {
@@ -475,9 +485,9 @@
"from": "balanced-match@>=0.4.1 <0.5.0"
},
"brace-expansion": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz",
"from": "brace-expansion@>=1.0.0 <2.0.0"
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz",
"from": "brace-expansion@>=1.1.7 <2.0.0"
},
"chalk": {
"version": "1.1.3",
@@ -490,8 +500,8 @@
"from": "concat-map@0.0.1"
},
"convert-source-map": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.4.0.tgz",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz",
"from": "convert-source-map@>=1.3.0 <2.0.0"
},
"core-js": {
@@ -500,8 +510,8 @@
"from": "core-js@>=2.4.0 <3.0.0"
},
"debug": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz",
"version": "2.6.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.6.tgz",
"from": "debug@>=2.1.1 <3.0.0"
},
"detect-indent": {
@@ -579,15 +589,10 @@
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
"from": "loose-envify@>=1.0.0 <2.0.0"
},
"magic-string": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.19.0.tgz",
"from": "magic-string@>=0.19.0 <0.20.0"
},
"meteor-babel": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/meteor-babel/-/meteor-babel-0.19.1.tgz",
"from": "meteor-babel@0.19.1"
"version": "0.21.2",
"resolved": "https://registry.npmjs.org/meteor-babel/-/meteor-babel-0.21.2.tgz",
"from": "meteor-babel@0.21.2"
},
"meteor-babel-helpers": {
"version": "0.0.3",
@@ -595,8 +600,8 @@
"from": "meteor-babel-helpers@0.0.3"
},
"minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz",
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"from": "minimatch@>=3.0.2 <4.0.0"
},
"minimist": {
@@ -604,15 +609,25 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"from": "minimist@0.0.8"
},
"minipass": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.0.1.tgz",
"from": "minipass@>=2.0.0 <3.0.0"
},
"minizlib": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.0.3.tgz",
"from": "minizlib@>=1.0.3 <2.0.0"
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"from": "mkdirp@>=0.5.1 <0.6.0"
},
"ms": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
"from": "ms@0.7.2"
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz",
"from": "ms@0.7.3"
},
"number-is-nan": {
"version": "1.0.1",
@@ -645,14 +660,14 @@
"from": "regenerate@>=1.2.1 <2.0.0"
},
"regenerator-runtime": {
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.3.tgz",
"version": "0.10.5",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
"from": "regenerator-runtime@>=0.10.0 <0.11.0"
},
"regenerator-transform": {
"version": "0.9.8",
"resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.9.8.tgz",
"from": "regenerator-transform@0.9.8"
"version": "0.9.11",
"resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.9.11.tgz",
"from": "regenerator-transform@0.9.11"
},
"regexpu-core": {
"version": "2.0.0",
@@ -677,15 +692,20 @@
}
},
"reify": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/reify/-/reify-0.6.6.tgz",
"from": "reify@>=0.6.2 <0.7.0"
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/reify/-/reify-0.11.0.tgz",
"from": "reify@>=0.11.0 <0.12.0"
},
"repeating": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
"from": "repeating@>=2.0.0 <3.0.0"
},
"semver": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
"from": "semver@>=5.3.0 <6.0.0"
},
"slash": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
@@ -697,8 +717,8 @@
"from": "source-map@>=0.5.0 <0.6.0"
},
"source-map-support": {
"version": "0.4.14",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.14.tgz",
"version": "0.4.15",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz",
"from": "source-map-support@>=0.4.2 <0.5.0"
},
"strip-ansi": {
@@ -712,8 +732,8 @@
"from": "supports-color@>=2.0.0 <3.0.0"
},
"to-fast-properties": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.2.tgz",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
"from": "to-fast-properties@>=1.0.1 <2.0.0"
},
"trim-right": {
@@ -721,10 +741,10 @@
"resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
"from": "trim-right@>=1.0.1 <2.0.0"
},
"vlq": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.1.tgz",
"from": "vlq@>=0.2.1 <0.3.0"
"yallist": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
"from": "yallist@>=3.0.0 <4.0.0"
}
}
}

View File

@@ -86,8 +86,8 @@ BCp.processOneFileForTarget = function (inputFile, source) {
babelOptions.sourceMap = true;
babelOptions.filename =
babelOptions.sourceFileName = packageName
? "/packages/" + packageName + "/" + inputFilePath
: "/" + inputFilePath;
? "packages/" + packageName + "/" + inputFilePath
: inputFilePath;
babelOptions.sourceMapTarget = babelOptions.filename + ".map";

View File

@@ -21,6 +21,10 @@ Babel = {
// Deprecated, now a no-op.
validateExtraFeatures: Function.prototype,
parse: function (source) {
return Npm.require('meteor-babel').parse(source);
},
compile: function (source, options) {
var meteorBabel = Npm.require('meteor-babel');
options = options || getDefaultOptions();

View File

@@ -6,15 +6,15 @@ Package.describe({
// isn't possible because you can't publish a non-recommended
// release with package versions that don't have a pre-release
// identifier at the end (eg, -dev)
version: '6.19.0-beta.14'
version: '6.19.1-rc.1'
});
Npm.depends({
'meteor-babel': '0.19.1'
'meteor-babel': '0.21.2'
});
Package.onUse(function (api) {
api.use('ecmascript-runtime');
api.use('ecmascript-runtime', 'server');
api.addFiles([
'babel.js',

View File

@@ -73,7 +73,10 @@ Boilerplate.prototype._generateBoilerplateFromManifestAndSource =
if (item.type === 'css' && item.where === 'client') {
boilerplateBaseData.css.push(itemObj);
}
if (item.type === 'js' && item.where === 'client') {
if (item.type === 'js' && item.where === 'client' &&
// Dynamic JS modules should not be loaded eagerly in the
// initial HTML of the app.
! item.path.startsWith('dynamic/')) {
boilerplateBaseData.js.push(itemObj);
}
if (item.type === 'head') {

View File

@@ -1,15 +1,15 @@
Package.describe({
summary: "Generates the boilerplate html from program's manifest",
version: '1.0.11'
version: '1.1.0-rc.1'
});
Package.onUse(function (api) {
api.use([
'underscore@1.0.9',
'spacebars-compiler@1.0.12',
'spacebars@1.0.12',
'htmljs@1.0.10',
'ui@1.0.11',
'underscore',
'spacebars-compiler',
'spacebars',
'htmljs',
'ui',
], 'server');
api.addFiles(['boilerplate-generator.js'], 'server');
api.export(['Boilerplate'], 'server');

View File

@@ -98,15 +98,18 @@ exports.checkMany = function (versions) {
return Promise.all(ids.map(function (id) {
return new Promise(function (resolve, reject) {
var sourceRequest = sourcesByVersion.get(versions[id]);
sourceRequest.onerror = makeOnError(reject, "sourcesByVersion.get");
sourceRequest.onsuccess = function (event) {
var result = event.target.result;
if (result) {
sourcesById[id] = result.source;
}
resolve();
};
var version = versions[id];
if (version) {
var sourceRequest = sourcesByVersion.get(versions[id]);
sourceRequest.onerror = makeOnError(reject, "sourcesByVersion.get");
sourceRequest.onsuccess = function (event) {
var result = event.target.result;
if (result) {
sourcesById[id] = result.source;
}
resolve();
};
} else resolve();
});
})).then(finish, finish);
});

View File

@@ -1,161 +1,130 @@
var Module = module.constructor;
var delayPromise = Promise.resolve();
var requireMeta = meteorInstall._requireMeta;
var cache = require("./cache.js");
// Call module.dynamicImport(id) to fetch a module and any/all of its
// dependencies that have not already been fetched, and evaluate them as
// soon as they arrive. This runtime API makes it very easy to implement
// ECMAScript dynamic import(...) syntax.
Module.prototype.dynamicImport = function (id) {
// The real (not meta) parent module.
var module = this;
function get() {
return module.prefetch(id).then(function () {
return getNamespace(module, id);
}
return delayPromise.then(get).catch(function (error) {
var message = error.message;
if (! (message &&
message.startsWith("Cannot find module"))) {
throw error;
}
// Require the parent module from the complete meta graph.
var meta = requireMeta(module.id);
var versions = Object.create(null);
function walk(meta) {
if (meta.dynamic && ! meta.pending) {
meta.pending = true;
versions[meta.module.id] = meta.version;
meta.eachChild(walkChild);
}
}
function walkChild(childModule) {
return walk(childModule.exports);
}
meta.eachChild(walkChild, [id]);
var localTree;
var missingTree;
return cache.checkMany(versions).then(function (sources) {
Object.keys(sources).forEach(function (id) {
var source = sources[id];
if (source) {
addToTree(localTree = localTree || Object.create(null), id, source);
} else {
addToTree(missingTree = missingTree || Object.create(null), id, 1);
}
});
if (localTree) {
installResults(localTree, true);
}
return missingTree && fetchMissing(missingTree);
}).then(get);
});
};
// Results from fetchMissing must be delivered in the same order as calls
// to fetchMissing, because previous results may include modules needed by
// more recent calls. In practice, results are usually delivered in order,
// but might be delivered out of order because the __dynamicImport method
// calls this.unblock(). To achieve this ordering of results while still
// allowing parallel __dynamicImport method calls, we keep track of the
// most recent Promise returned by fetchMissing, and delay resolving the
// next Promise until the previous Promise has been resolved or rejected.
var lastFetchMissingPromise = delayPromise;
// Called by Module.prototype.prefetch if there are any missing dynamic
// modules that need to be fetched.
meteorInstall.fetch = function (ids) {
var tree = Object.create(null);
var versions = Object.create(null);
var dynamicVersions = require("./dynamic-versions.js");
var missing;
Object.keys(ids).forEach(function (id) {
var version = getFromTree(dynamicVersions, id);
if (version) {
versions[id] = version;
} else {
addToTree(missing = missing || Object.create(null), id, 1);
}
});
return cache.checkMany(versions).then(function (sources) {
Object.keys(sources).forEach(function (id) {
var source = sources[id];
if (source) {
var info = ids[id];
addToTree(tree, id, makeModuleFunction(source, info.options));
} else {
addToTree(missing = missing || Object.create(null), id, 1);
}
});
return missing && fetchMissing(missing).then(function (results) {
var versionsAndSourcesById = Object.create(null);
var flatResults = flattenModuleTree(results);
Object.keys(flatResults).forEach(function (id) {
var source = flatResults[id];
var info = ids[id];
addToTree(tree, id, makeModuleFunction(source, info.options));
var version = getFromTree(dynamicVersions, id);
if (version) {
versionsAndSourcesById[id] = {
version: version,
source: source
};
}
});
cache.setMany(versionsAndSourcesById);
});
}).then(function () {
return tree;
});
};
function flattenModuleTree(tree) {
var parts = [""];
var result = Object.create(null);
function walk(t) {
if (t && typeof t === "object") {
Object.keys(t).forEach(function (key) {
parts.push(key);
walk(t[key]);
parts.pop();
});
} else if (typeof t === "string") {
result[parts.join("/")] = t;
}
}
walk(tree);
return result;
}
function makeModuleFunction(source, options) {
// By calling (options && options.eval || eval) in a wrapper function,
// we delay the cost of parsing and evaluating the module code until the
// module is first imported.
return function () {
// If an options.eval function was provided in the second argument to
// meteorInstall when this bundle was first installed, use that
// function to parse and evaluate the dynamic module code in the scope
// of the package. Otherwise fall back to indirect (global) eval.
return (options && options.eval || eval)(
// Wrap the function(require,exports,module){...} expression in
// parentheses to force it to be parsed as an expression.
"(" + source + ")"
).apply(this, arguments);
};
}
function fetchMissing(missingTree) {
// Save the Promise that was most recent when fetchMissing was called.
var previousPromise = lastFetchMissingPromise;
// Update lastFetchMissingPromise immediately, without waiting for
// the results to be delivered.
return lastFetchMissingPromise = new Promise(function (resolve, reject) {
return new Promise(function (resolve, reject) {
Meteor.call(
"__dynamicImport",
missingTree,
function (error, resultsTree) {
if (error) {
reject(error);
} else {
resolve = resolve.bind(null, resultsTree)
// Continue even if previousPromise was rejected.
previousPromise.then(resolve, resolve);
}
error ? reject(error) : resolve(resultsTree);
}
);
}).then(installResults);
});
}
function installResults(resultsTree, doNotCache) {
var parts = [""];
var trees = [];
var options = [];
var versionsAndSourcesById = Object.create(null);
function walk(tree) {
if (typeof tree === "string") {
var meta = requireMeta(parts.join("/"));
var optionsIndex = options.indexOf(meta.options);
if (optionsIndex < 0) {
options[optionsIndex = options.length] = meta.options;
trees.push(Object.create(null));
}
// The results tree is partitioned into separate trees according
// to the meta.options object that governs the tree. Usually the
// number of trees will be approximately one, because options
// are shared by entire bundles.
addToTree(
trees[optionsIndex],
meta.module.id,
// By calling (meta.options.eval || eval) in a wrapper function,
// we delay the cost of parsing and evaluating the module code
// until the module is first imported.
function () {
// If an options.eval function was provided in the second
// argument to meteorInstall when this bundle was first
// installed, use that function to parse and evaluate the
// dynamic module code in the scope of the package. Otherwise
// fall back to indirect (global) eval.
return (meta.options.eval || eval)(
// Wrap the function(require,exports,module){...} expression
// in parentheses to force it to be parsed as an expression.
"(" + tree + ")"
).apply(this, arguments);
}
);
// Intentionally do not delay resolution waiting for the cache.
if (! doNotCache) {
versionsAndSourcesById[meta.module.id] = {
version: meta.version,
source: tree
};
}
} else {
Object.keys(tree).forEach(function (name) {
parts.push(name);
walk(tree[name]);
parts.pop(name);
});
}
}
walk(resultsTree);
trees.forEach(function (tree, i) {
meteorInstall(tree, options[i]);
function getFromTree(tree, id) {
id.split("/").every(function (part) {
return ! part || (tree = tree[part]);
});
if (! doNotCache) {
cache.setMany(versionsAndSourcesById);
}
return tree;
}
function addToTree(tree, id, value) {
@@ -172,7 +141,7 @@ function addToTree(tree, id, value) {
function getNamespace(module, id) {
var namespace = Object.create(null);
module.import(id, {
module.watch(module.require(id), {
"*": function (value, name) {
namespace[name] = value;
}

View File

@@ -0,0 +1,4 @@
// This magic double-underscored identifier gets replaced in
// tools/isobuild/bundler.js with a tree of hashes of all dynamic
// modules, for use in client.js and cache.js.
module.exports = __DYNAMIC_VERSIONS__;

View File

@@ -1,11 +1,15 @@
Package.describe({
name: "dynamic-import",
version: "0.1.0-beta.14",
summary: "Support for module.dynamicImport(id).then(namespace => ...)",
version: "0.1.0-rc.1",
summary: "Runtime support for Meteor 1.5 dynamic import(...) syntax",
documentation: "README.md"
});
Package.onUse(function (api) {
// Do not allow this package to be used in pre-Meteor 1.5 apps.
api.use("isobuild:dynamic-import@1.5.0");
// Modify browser policy only if browser-policy packages are used.
api.use("browser-policy-content", { weak: true });
api.use("modules");

View File

@@ -0,0 +1,6 @@
# ecmascript-runtime-client
[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/ecmascript-runtime-client) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/ecmascript-runtime-client)
***
[![Build Status](https://travis-ci.org/meteor/ecmascript-runtime.svg?branch=master)](https://travis-ci.org/meteor/ecmascript-runtime)
Polyfills for new ECMAScript 2015 APIs like Map and Set

View File

@@ -0,0 +1,20 @@
Package.describe({
name: "ecmascript-runtime-client",
version: "0.4.0",
summary: "Polyfills for new ECMAScript 2015 APIs like Map and Set",
git: "https://github.com/meteor/meteor/tree/devel/packages/ecmascript-runtime-client",
documentation: "README.md"
});
Package.onUse(function(api) {
// If the es5-shim package is installed, make sure it loads before
// ecmascript-runtime-server, since the runtime uses some ES5 APIs like
// Object.defineProperties that are buggy in older browsers.
api.use("es5-shim", { weak: true });
api.use("modules", "client");
api.use("promise", "client");
api.mainModule("runtime.js", "client");
api.export("Symbol", "client");
api.export("Map", "client");
api.export("Set", "client");
});

View File

@@ -0,0 +1,127 @@
try {
require("core-js/modules/es6.symbol");
require("core-js/modules/es6.map");
require("core-js/modules/es6.set");
} catch (e) {
throw new Error([
"The core-js npm package could not be found in your node_modules ",
"directory. Please run the following command to install it:",
"",
" meteor npm install --save core-js",
""
].join("\n"));
}
var core = require("core-js/modules/_core");
Symbol = exports.Symbol = core.Symbol;
Map = exports.Map = core.Map;
Set = exports.Set = core.Set;
// List of polyfills generated by babel-preset-env with the following
// .babelrc configuration:
//
// {
// "presets": [
// ["env", {
// "targets": {
// "browsers": [
// "last 3 versions"
// ]
// },
// "polyfill": true,
// "useBuiltIns": true
// }]
// ]
// }
//
// Supported browsers: http://browserl.ist/?q=last+3+versions
//
// Note that the es6.reflect.* modules have been commented out for bundle
// size reasons, and the es6.promise modules are not used because Meteor
// provides its own Promise polyfill.
require("core-js/modules/es6.typed.array-buffer");
require("core-js/modules/es6.typed.data-view");
require("core-js/modules/es6.typed.int8-array");
require("core-js/modules/es6.typed.uint8-array");
require("core-js/modules/es6.typed.uint8-clamped-array");
require("core-js/modules/es6.typed.int16-array");
require("core-js/modules/es6.typed.uint16-array");
require("core-js/modules/es6.typed.int32-array");
require("core-js/modules/es6.typed.uint32-array");
require("core-js/modules/es6.typed.float32-array");
require("core-js/modules/es6.typed.float64-array");
require("core-js/modules/es6.weak-map");
require("core-js/modules/es6.weak-set");
// require("core-js/modules/es6.reflect.apply");
// require("core-js/modules/es6.reflect.construct");
// require("core-js/modules/es6.reflect.define-property");
// require("core-js/modules/es6.reflect.delete-property");
// require("core-js/modules/es6.reflect.get");
// require("core-js/modules/es6.reflect.get-own-property-descriptor");
// require("core-js/modules/es6.reflect.get-prototype-of");
// require("core-js/modules/es6.reflect.has");
// require("core-js/modules/es6.reflect.is-extensible");
// require("core-js/modules/es6.reflect.own-keys");
// require("core-js/modules/es6.reflect.prevent-extensions");
// require("core-js/modules/es6.reflect.set");
// require("core-js/modules/es6.reflect.set-prototype-of");
require("core-js/modules/es6.object.assign");
require("core-js/modules/es6.object.is");
require("core-js/modules/es6.object.set-prototype-of");
// require("core-js/modules/es6.promise");
require("core-js/modules/es6.function.bind");
require("core-js/modules/es6.function.name");
require("core-js/modules/es6.function.has-instance");
require("core-js/modules/es6.string.raw");
require("core-js/modules/es6.string.from-code-point");
require("core-js/modules/es6.string.code-point-at");
require("core-js/modules/es6.string.repeat");
require("core-js/modules/es6.string.starts-with");
require("core-js/modules/es6.string.ends-with");
require("core-js/modules/es6.string.includes");
require("core-js/modules/es6.regexp.flags");
require("core-js/modules/es6.regexp.match");
require("core-js/modules/es6.regexp.replace");
require("core-js/modules/es6.regexp.split");
require("core-js/modules/es6.regexp.search");
require("core-js/modules/es6.array.from");
require("core-js/modules/es6.array.of");
require("core-js/modules/es6.array.copy-within");
require("core-js/modules/es6.array.find");
require("core-js/modules/es6.array.find-index");
require("core-js/modules/es6.array.fill");
require("core-js/modules/es6.array.iterator");
require("core-js/modules/es6.number.is-finite");
require("core-js/modules/es6.number.is-integer");
require("core-js/modules/es6.number.is-safe-integer");
require("core-js/modules/es6.number.is-nan");
require("core-js/modules/es6.number.epsilon");
require("core-js/modules/es6.number.min-safe-integer");
require("core-js/modules/es6.number.max-safe-integer");
require("core-js/modules/es6.math.acosh");
require("core-js/modules/es6.math.asinh");
require("core-js/modules/es6.math.atanh");
require("core-js/modules/es6.math.cbrt");
require("core-js/modules/es6.math.clz32");
require("core-js/modules/es6.math.cosh");
require("core-js/modules/es6.math.expm1");
require("core-js/modules/es6.math.fround");
require("core-js/modules/es6.math.hypot");
require("core-js/modules/es6.math.imul");
require("core-js/modules/es6.math.log1p");
require("core-js/modules/es6.math.log10");
require("core-js/modules/es6.math.log2");
require("core-js/modules/es6.math.sign");
require("core-js/modules/es6.math.sinh");
require("core-js/modules/es6.math.tanh");
require("core-js/modules/es6.math.trunc");
require("core-js/modules/es7.array.includes");
require("core-js/modules/es7.object.values");
require("core-js/modules/es7.object.entries");
require("core-js/modules/es7.object.get-own-property-descriptors");
require("core-js/modules/es7.string.pad-start");
require("core-js/modules/es7.string.pad-end");
require("core-js/modules/web.timers");
require("core-js/modules/web.immediate");
require("core-js/modules/web.dom.iterable");

View File

@@ -0,0 +1,9 @@
{
"dependencies": {
"core-js": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz",
"from": "core-js@2.4.1"
}
}
}

View File

@@ -0,0 +1,6 @@
# ecmascript-runtime-server
[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/ecmascript-runtime-server) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/ecmascript-runtime-server)
***
[![Build Status](https://travis-ci.org/meteor/ecmascript-runtime.svg?branch=master)](https://travis-ci.org/meteor/ecmascript-runtime)
Polyfills for new ECMAScript 2015 APIs like Map and Set

View File

@@ -0,0 +1,23 @@
Package.describe({
name: "ecmascript-runtime-server",
version: "0.4.0",
summary: "Polyfills for new ECMAScript 2015 APIs like Map and Set",
git: "https://github.com/meteor/meteor/tree/devel/packages/ecmascript-runtime-client",
documentation: "README.md"
});
Npm.depends({
"core-js": "2.4.1"
});
Package.onUse(function(api) {
// If the es5-shim package is installed, make sure it loads before
// ecmascript-runtime-server, since the runtime uses some ES5 APIs like
// Object.defineProperties that are buggy in older browsers.
api.use("es5-shim", { weak: true });
api.use(["modules", "promise"], "server");
api.mainModule("runtime.js", "server");
api.export("Symbol", "server");
api.export("Map", "server");
api.export("Set", "server");
});

View File

@@ -0,0 +1,71 @@
// The ecmascript-runtime-server package depends on its own copy of
// core-js using Npm.depends, so we don't have to check that core-js is
// available (as we do in ecmascript-runtime-client/runtime.js).
require("core-js/modules/es6.symbol");
require("core-js/modules/es6.map");
require("core-js/modules/es6.set");
var core = require("core-js/modules/_core");
Symbol = exports.Symbol = core.Symbol;
Map = exports.Map = core.Map;
Set = exports.Set = core.Set;
// List of polyfills generated by babel-preset-env with the following
// .babelrc configuration:
//
// {
// "presets": [
// ["env", {
// "targets": {
// "node": 4
// },
// "polyfill": true,
// "useBuiltIns": true
// }]
// ]
// }
//
// Note that the es6.reflect.* modules have been commented out for bundle
// size reasons.
require("core-js/modules/es6.typed.array-buffer");
require("core-js/modules/es6.typed.int8-array");
require("core-js/modules/es6.typed.uint8-array");
require("core-js/modules/es6.typed.uint8-clamped-array");
require("core-js/modules/es6.typed.int16-array");
require("core-js/modules/es6.typed.uint16-array");
require("core-js/modules/es6.typed.int32-array");
require("core-js/modules/es6.typed.uint32-array");
require("core-js/modules/es6.typed.float32-array");
require("core-js/modules/es6.typed.float64-array");
require("core-js/modules/es6.weak-map");
require("core-js/modules/es6.weak-set");
// require("core-js/modules/es6.reflect.apply");
// require("core-js/modules/es6.reflect.construct");
// require("core-js/modules/es6.reflect.define-property");
// require("core-js/modules/es6.reflect.delete-property");
// require("core-js/modules/es6.reflect.get");
// require("core-js/modules/es6.reflect.get-own-property-descriptor");
// require("core-js/modules/es6.reflect.get-prototype-of");
// require("core-js/modules/es6.reflect.has");
// require("core-js/modules/es6.reflect.is-extensible");
// require("core-js/modules/es6.reflect.own-keys");
// require("core-js/modules/es6.reflect.prevent-extensions");
// require("core-js/modules/es6.reflect.set");
// require("core-js/modules/es6.reflect.set-prototype-of");
require("core-js/modules/es6.function.bind");
require("core-js/modules/es6.function.name");
require("core-js/modules/es6.function.has-instance");
require("core-js/modules/es6.regexp.flags");
require("core-js/modules/es6.regexp.match");
require("core-js/modules/es6.regexp.replace");
require("core-js/modules/es6.regexp.split");
require("core-js/modules/es6.regexp.search");
require("core-js/modules/es6.array.from");
require("core-js/modules/es7.array.includes");
require("core-js/modules/es7.object.values");
require("core-js/modules/es7.object.entries");
require("core-js/modules/es7.object.get-own-property-descriptors");
require("core-js/modules/es7.string.pad-start");
require("core-js/modules/es7.string.pad-end");

View File

@@ -1,14 +0,0 @@
{
"dependencies": {
"core-js": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz",
"from": "core-js@2.4.1"
},
"meteor-ecmascript-runtime": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/meteor-ecmascript-runtime/-/meteor-ecmascript-runtime-0.2.9.tgz",
"from": "meteor-ecmascript-runtime@0.2.9"
}
}
}

View File

@@ -1,29 +1,14 @@
Package.describe({
name: "ecmascript-runtime",
version: "0.3.15",
version: "0.4.0",
summary: "Polyfills for new ECMAScript 2015 APIs like Map and Set",
git: "https://github.com/meteor/ecmascript-runtime",
documentation: "README.md"
});
Npm.depends({
"meteor-ecmascript-runtime": "0.2.9",
});
Package.onUse(function(api) {
// If the es5-shim package is installed, make sure it loads before
// ecmascript-runtime, since ecmascript-runtime uses some ES5 APIs like
// Object.defineProperties that are buggy in older browsers.
api.use("es5-shim@4.6.13", { weak: true });
api.use("modules@0.7.5");
api.use("promise@0.8.3");
api.mainModule("runtime.js");
api.export("Symbol");
api.export("Map");
api.export("Set");
api.imply("ecmascript-runtime-client", "client");
api.imply("ecmascript-runtime-server", "server");
});
Package.onTest(function(api) {

View File

@@ -1,6 +1,6 @@
Package.describe({
name: 'ecmascript',
version: '0.8.0-beta.14',
version: '0.8.0-rc.1',
summary: 'Compiler plugin that supports ES2015+ in all .js files',
documentation: 'README.md'
});

View File

@@ -1,79 +1,14 @@
{
"dependencies": {
"addressparser": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz",
"from": "addressparser@1.0.1"
},
"buildmail": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/buildmail/-/buildmail-4.0.1.tgz",
"from": "buildmail@4.0.1"
},
"httpntlm": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.6.1.tgz",
"from": "httpntlm@1.6.1"
},
"httpreq": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/httpreq/-/httpreq-0.4.23.tgz",
"from": "httpreq@>=0.4.22"
},
"iconv-lite": {
"version": "0.4.15",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz",
"from": "iconv-lite@0.4.15"
},
"libbase64": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/libbase64/-/libbase64-0.1.0.tgz",
"from": "libbase64@0.1.0"
},
"libmime": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/libmime/-/libmime-3.0.0.tgz",
"from": "libmime@3.0.0"
},
"libqp": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/libqp/-/libqp-1.1.0.tgz",
"from": "libqp@1.1.0"
},
"mailcomposer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/mailcomposer/-/mailcomposer-4.0.1.tgz",
"from": "mailcomposer@4.0.1"
},
"nodemailer-fetch": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz",
"from": "nodemailer-fetch@1.6.0"
},
"nodemailer-shared": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz",
"from": "nodemailer-shared@1.1.0"
},
"punycode": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
"from": "punycode@1.4.1"
},
"smtp-connection": {
"version": "2.12.2",
"resolved": "https://registry.npmjs.org/smtp-connection/-/smtp-connection-2.12.2.tgz",
"from": "smtp-connection@2.12.2"
"node4mailer": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/node4mailer/-/node4mailer-4.0.2.tgz",
"from": "node4mailer@4.0.2"
},
"stream-buffers": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-0.2.5.tgz",
"from": "stream-buffers@0.2.5"
},
"underscore": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz",
"from": "underscore@>=1.7.0 <1.8.0"
}
}
}

View File

@@ -1,6 +1,6 @@
var Future = Npm.require('fibers/future');
var urlModule = Npm.require('url');
var SMTPConnection = Npm.require('smtp-connection');
var nodemailer = Npm.require('node4mailer');
Email = {};
EmailTest = {};
@@ -8,61 +8,60 @@ EmailTest = {};
EmailInternals = {
NpmModules: {
mailcomposer: {
version: Npm.require('mailcomposer/package.json').version,
module: Npm.require('mailcomposer')
version: Npm.require('node4mailer/package.json').version,
module: Npm.require('node4mailer/lib/mail-composer')
},
nodemailer: {
version: Npm.require('node4mailer/package.json').version,
module: Npm.require('node4mailer')
}
}
};
var mailcomposer = EmailInternals.NpmModules.mailcomposer.module;
var MailComposer = EmailInternals.NpmModules.mailcomposer.module;
var makePool = function (mailUrlString) {
var mailUrl = urlModule.parse(mailUrlString);
if (mailUrl.protocol !== 'smtp:' && mailUrl.protocol !== 'smtps:')
var makeTransport = function (mailUrlString) {
var mailUrl = urlModule.parse(mailUrlString, true);
if (mailUrl.protocol !== 'smtp:' && mailUrl.protocol !== 'smtps:') {
throw new Error("Email protocol in $MAIL_URL (" +
mailUrlString + ") must be 'smtp' or 'smtps'");
var port = +(mailUrl.port);
var auth = false;
if (mailUrl.auth) {
var parts = mailUrl.auth.split(':', 2);
auth = {user: parts[0],
pass: parts[1]};
}
var pool = new SMTPConnection({
port: port, // Defaults to 25
host: mailUrl.hostname, // Defaults to "localhost"
secure: (port === 465) || (mailUrl.protocol === 'smtps:')
});
Meteor.wrapAsync(pool.connect, pool)();
if (auth) {
//_.bind(Future.wrap(pool.login), pool)(auth).wait();
Meteor.wrapAsync(pool.login, pool)(auth);
// Allow overriding pool setting, but default to true.
if (!mailUrl.query) {
mailUrl.query = {};
}
pool._syncSend = Meteor.wrapAsync(pool.send, pool);
return pool;
if (!mailUrl.query.pool) {
mailUrl.query.pool = 'true';
}
var transport = nodemailer.createTransport(
urlModule.format(mailUrl));
transport._syncSendMail = Meteor.wrapAsync(transport.sendMail, transport);
return transport;
};
var getPool = function() {
var getTransport = function() {
// We delay this check until the first call to Email.send, in case someone
// set process.env.MAIL_URL in startup code. Then we store in a cache until
// process.env.MAIL_URL changes.
var url = process.env.MAIL_URL;
if (this.cacheKey === undefined || this.cacheKey !== url) {
this.cacheKey = url;
this.cache = url ? makePool(url) : null;
this.cache = url ? makeTransport(url) : null;
}
return this.cache;
}
var next_devmode_mail_id = 0;
var nextDevModeMailId = 0;
var output_stream = process.stdout;
// Testing hooks
EmailTest.overrideOutputStream = function (stream) {
next_devmode_mail_id = 0;
nextDevModeMailId = 0;
output_stream = stream;
};
@@ -70,27 +69,27 @@ EmailTest.restoreOutputStream = function () {
output_stream = process.stdout;
};
var devModeSend = function (mc) {
var devmode_mail_id = next_devmode_mail_id++;
var devModeSend = function (mail) {
var devModeMailId = nextDevModeMailId++;
var stream = output_stream;
// This approach does not prevent other writers to stdout from interleaving.
stream.write("====== BEGIN MAIL #" + devmode_mail_id + " ======\n");
stream.write("====== BEGIN MAIL #" + devModeMailId + " ======\n");
stream.write("(Mail not sent; to enable sending, set the MAIL_URL " +
"environment variable.)\n");
var readStream = mc.createReadStream();
var readStream = new MailComposer(mail).compile().createReadStream();
readStream.pipe(stream, {end: false});
var future = new Future;
readStream.on('end', function () {
stream.write("====== END MAIL #" + devmode_mail_id + " ======\n");
stream.write("====== END MAIL #" + devModeMailId + " ======\n");
future.return();
});
future.wait();
};
var smtpSend = function (pool, mc) {
pool._syncSend(mc.getEnvelope(), mc.createReadStream());
var smtpSend = function (transport, mail) {
transport._syncSendMail(mail);
};
/**
@@ -105,28 +104,6 @@ EmailTest.hookSend = function (f) {
sendHooks.push(f);
};
// Old comment below
/**
* Send an email.
*
* Connects to the mail server configured via the MAIL_URL environment
* variable. If unset, prints formatted message to stdout. The "from" option
* is required, and at least one of "to", "cc", and "bcc" must be provided;
* all other options are optional.
*
* @param options
* @param options.from {String} RFC5322 "From:" address
* @param options.to {String|String[]} RFC5322 "To:" address[es]
* @param options.cc {String|String[]} RFC5322 "Cc:" address[es]
* @param options.bcc {String|String[]} RFC5322 "Bcc:" address[es]
* @param options.replyTo {String|String[]} RFC5322 "Reply-To:" address[es]
* @param options.subject {String} RFC5322 "Subject:" line
* @param options.text {String} RFC5322 mail body (plain text)
* @param options.html {String} RFC5322 mail body (HTML)
* @param options.headers {Object} custom RFC5322 headers (dictionary)
*/
// New API doc comment below
/**
* @summary Send an email. Throws an `Error` on failure to contact mail server
* or if mail server returns an error. All fields should match
@@ -135,10 +112,9 @@ EmailTest.hookSend = function (f) {
* If the `MAIL_URL` environment variable is set, actually sends the email.
* Otherwise, prints the contents of the email to standard out.
*
* Note that this package is based on mailcomposer version `4.0.1`, so make
* sure to refer to the documentation for that version if using the
* `attachments` or `mailComposer` options.
* [Click here to read the mailcomposer 4.0.1 docs](https://github.com/nodemailer/mailcomposer/blob/v4.0.1/README.md).
* Note that this package is based on **mailcomposer 4**, so make sure to refer to
* [the documentation](https://github.com/nodemailer/mailcomposer/blob/v4.0.1/README.md)
* for that version when using the `attachments` or `mailComposer` options.
*
* @locus Server
* @param {Object} options
@@ -150,44 +126,29 @@ EmailTest.hookSend = function (f) {
* @param {String} [options.messageId] Message-ID for this message; otherwise, will be set to a random value
* @param {String} [options.subject] "Subject:" line
* @param {String} [options.text|html] Mail body (in plain text and/or HTML)
* @param {String} [options.watchHtml] Mail body in HTML specific for Apple Watch
* @param {String} [options.icalEvent] iCalendar event attachment
* @param {String} [options.watchHtml] Mail body in HTML specific for Apple Watch
* @param {String} [options.icalEvent] iCalendar event attachment
* @param {Object} [options.headers] Dictionary of custom headers
* @param {Object[]} [options.attachments] Array of attachment objects, as
* described in the [mailcomposer documentation](https://github.com/nodemailer/mailcomposer/blob/v4.0.1/README.md#attachments).
* @param {MailComposer} [options.mailComposer] A [MailComposer](https://github.com/andris9/mailcomposer)
* object (or its `compile()` output) representing the message to be sent.
* Overrides all other options. You can access the `mailcomposer` npm module at
* `EmailInternals.NpmModules.mailcomposer.module`. This module is a function
* which assembles a MailComposer object and immediately `compile()`s it.
* Alternatively, you can create and pass a MailComposer object via
* `new EmailInternals.NpmModules.mailcomposer.module.MailComposer`.
* @param {MailComposer} [options.mailComposer] A [MailComposer](https://nodemailer.com/extras/mailcomposer/#e-mail-message-fields)
* object representing the message to be sent. Overrides all other options.
* You can create a `MailComposer` object via
* `new EmailInternals.NpmModules.mailcomposer.module`.
*/
Email.send = function (options) {
for (var i = 0; i < sendHooks.length; i++)
if (! sendHooks[i](options))
return;
var mc;
if (options.mailComposer) {
mc = options.mailComposer;
if (mc.compile) {
mc = mc.compile();
}
} else {
// mailcomposer now automatically adds date if omitted
//if (!options.hasOwnProperty('date') &&
// (!options.headers || !options.headers.hasOwnProperty('Date'))) {
// options['date'] = new Date().toUTCString().replace(/GMT/, '+0000');
//}
mc = mailcomposer(options);
options = options.mailComposer.mail;
}
var pool = getPool();
if (pool) {
smtpSend(pool, mc);
var transport = getTransport();
if (transport) {
smtpSend(transport, options);
} else {
devModeSend(mc);
devModeSend(options);
}
};

View File

@@ -22,7 +22,7 @@ function canonicalize(string) {
// Remove generated content for test.equal to succeed.
return string.replace(/Message-ID: <[^<>]*>\r\n/, "Message-ID: <...>\r\n")
.replace(/Date: (?!dummy).*\r\n/, "Date: ...\r\n")
.replace(/----[^\s"]+/g, "----...");
.replace(/(boundary="|^--)--[^\s"]+?(-Part|")/mg, "$1--...$2");
}
Tinytest.add("email - fully customizable", function (test) {
@@ -55,7 +55,7 @@ Tinytest.add("email - fully customizable", function (test) {
"\r\n" +
"This is the body\n" +
"of the message\n" +
"From us." +
"From us.\r\n" +
"====== END MAIL #0 ======\n");
});
});
@@ -108,33 +108,23 @@ Tinytest.add("email - multiple e-mails same stream", function (test) {
Tinytest.add("email - using mail composer", function (test) {
smokeEmailTest(function (stream) {
// Test direct MailComposer usage.
var mcs = [
// Test with MailComposer object (without compiling).
new EmailInternals.NpmModules.mailcomposer.module.MailComposer({
from: "a@b.com",
text: "body"
}),
// Test calling module as a function, which compiles MailComposer object.
EmailInternals.NpmModules.mailcomposer.module({
from: "a@b.com",
text: "body"
})
];
for (var i = 0; i < mcs.length; i++) {
Email.send({mailComposer: mcs[i]});
test.equal(canonicalize(stream.getContentsAsString("utf8")),
"====== BEGIN MAIL #"+i+" ======\n" +
devWarningBanner +
"Content-Type: text/plain\r\n" +
"From: a@b.com\r\n" +
"Message-ID: <...>\r\n" +
"Content-Transfer-Encoding: 7bit\r\n" +
"Date: ...\r\n" +
"MIME-Version: 1.0\r\n" +
"\r\n" +
"body" +
"====== END MAIL #"+i+" ======\n");
}
var mc = new EmailInternals.NpmModules.mailcomposer.module({
from: "a@b.com",
text: "body"
});
Email.send({mailComposer: mc});
test.equal(canonicalize(stream.getContentsAsString("utf8")),
"====== BEGIN MAIL #0 ======\n" +
devWarningBanner +
"Content-Type: text/plain\r\n" +
"From: a@b.com\r\n" +
"Message-ID: <...>\r\n" +
"Content-Transfer-Encoding: 7bit\r\n" +
"Date: ...\r\n" +
"MIME-Version: 1.0\r\n" +
"\r\n" +
"body\r\n" +
"====== END MAIL #0 ======\n");
});
});
@@ -181,7 +171,7 @@ Tinytest.add("email - long lines", function (test) {
"MIME-Version: 1.0\r\n" +
"\r\n" +
"This is a very very very very very very very very very very " +
"very very long =\r\ntext" +
"very very long =\r\ntext\r\n" +
"====== END MAIL #0 ======\n");
});
});
@@ -208,7 +198,7 @@ Tinytest.add("email - unicode", function (test) {
"Date: ...\r\n" +
"MIME-Version: 1.0\r\n" +
"\r\n" +
"I =E2=99=A5 Meteor" +
"I =E2=99=A5 Meteor\r\n" +
"====== END MAIL #0 ======\n");
});
});
@@ -227,24 +217,24 @@ Tinytest.add("email - text and html", function (test) {
"====== BEGIN MAIL #0 ======\n" +
devWarningBanner +
"Content-Type: multipart/alternative;\r\n" +
' boundary="----..."\r\n' +
' boundary="--...-Part_1"\r\n' +
"From: foo@example.com\r\n" +
"To: bar@example.com\r\n" +
"Message-ID: <...>\r\n" +
"Date: ...\r\n" +
"MIME-Version: 1.0\r\n" +
"\r\n" +
"----...\r\n" +
"----...-Part_1\r\n" +
"Content-Type: text/plain\r\n" +
"Content-Transfer-Encoding: 7bit\r\n" +
"\r\n" +
"*Cool*, man\r\n" +
"----...\r\n" +
"----...-Part_1\r\n" +
"Content-Type: text/html\r\n" +
"Content-Transfer-Encoding: 7bit\r\n" +
"\r\n" +
"<i>Cool</i>, man\r\n" +
"----...\r\n" +
"----...-Part_1--\r\n" +
"====== END MAIL #0 ======\n");
});
});

View File

@@ -1,17 +1,14 @@
Package.describe({
summary: "Send email messages",
version: "1.2.0"
version: "1.2.1"
});
Npm.depends({
mailcomposer: "4.0.1",
// Using smtp-connection@2 (instead of latest) because it shares
// nodemailer-shared with mailcomposer@4:
"smtp-connection": "2.12.2",
"stream-buffers": "0.2.5"});
node4mailer: "4.0.2",
"stream-buffers": "0.2.5"
});
Package.onUse(function (api) {
api.use('underscore', 'server');
api.export(['Email', 'EmailInternals'], 'server');
api.export('EmailTest', 'server', {testOnly: true});
api.addFiles('email.js', 'server');

View File

@@ -1,4 +1,5 @@
Facebook = {};
var crypto = Npm.require('crypto');
Facebook.handleAuthFromAccessToken = function handleAuthFromAccessToken(accessToken, expiresAt) {
// include all fields from facebook
@@ -79,10 +80,20 @@ var getTokenResponse = function (query) {
};
var getIdentity = function (accessToken, fields) {
var config = ServiceConfiguration.configurations.findOne({service: 'facebook'});
if (!config)
throw new ServiceConfiguration.ConfigError();
// Generate app secret proof that is a sha256 hash of the app access token, with the app secret as the key
// https://developers.facebook.com/docs/graph-api/securing-requests#appsecret_proof
var hmac = crypto.createHmac('sha256', OAuth.openSecret(config.secret));
hmac.update(accessToken);
try {
return HTTP.get("https://graph.facebook.com/v2.8/me", {
params: {
access_token: accessToken,
appsecret_proof: hmac.digest('hex'),
fields: fields.join(",")
}
}).data;

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Facebook OAuth flow",
version: "1.3.0"
version: "1.3.1-rc.1"
});
Package.onUse(function(api) {

View File

@@ -1,5 +1,13 @@
var Google = require("./namespace.js");
var ILLEGAL_PARAMETERS = {
'response_type': 1,
'client_id': 1,
'scope': 1,
'redirect_uri': 1,
'state': 1
};
// Request Google credentials for the user
// @param options {optional}
// @param credentialRequestCompleteCallback {Function} Callback function to call on
@@ -24,24 +32,26 @@ Google.requestCredential = function (options, credentialRequestCompleteCallback)
var credentialToken = Random.secret();
// we need the email scope to get user id from google.
var requiredScope = ['email'];
var scope = ['profile'];
if (options.requestPermissions)
scope = options.requestPermissions;
scope = _.union(scope, requiredScope);
var requiredScopes = { 'email': 1 };
var scopes = options.requestPermissions || ['profile'];
scopes.forEach(function (scope) {
requiredScopes[scope] = 1;
});
scopes = Object.keys(requiredScopes);
var loginUrlParameters = {};
if (config.loginUrlParameters){
_.extend(loginUrlParameters, config.loginUrlParameters)
Object.assign(loginUrlParameters, config.loginUrlParameters);
}
if (options.loginUrlParameters){
_.extend(loginUrlParameters, options.loginUrlParameters)
Object.assign(loginUrlParameters, options.loginUrlParameters);
}
var ILLEGAL_PARAMETERS = ['response_type', 'client_id', 'scope', 'redirect_uri', 'state'];
// validate options keys
_.each(_.keys(loginUrlParameters), function (key) {
if (_.contains(ILLEGAL_PARAMETERS, key))
// validate options keys
Object.keys(loginUrlParameters).forEach(function (key) {
if (ILLEGAL_PARAMETERS.hasOwnProperty(key)) {
throw new Error("Google.requestCredential: Invalid loginUrlParameter: " + key);
}
});
// backwards compatible options
@@ -60,16 +70,17 @@ Google.requestCredential = function (options, credentialRequestCompleteCallback)
var loginStyle = OAuth._loginStyle('google', config, options);
// https://developers.google.com/accounts/docs/OAuth2WebServer#formingtheurl
_.extend(loginUrlParameters, {
Object.assign(loginUrlParameters, {
"response_type": "code",
"client_id": config.clientId,
"scope": scope.join(' '), // space delimited
"scope": scopes.join(' '), // space delimited
"redirect_uri": OAuth._redirectUri('google', config),
"state": OAuth._stateParam(loginStyle, credentialToken, options.redirectUrl)
});
var loginUrl = 'https://accounts.google.com/o/oauth2/auth?' +
_.map(loginUrlParameters, function(value, param){
return encodeURIComponent(param) + '=' + encodeURIComponent(value);
Object.keys(loginUrlParameters).map(function (param) {
return encodeURIComponent(param) + '=' +
encodeURIComponent(loginUrlParameters[param]);
}).join("&");
OAuth.launchLogin({

View File

@@ -1,67 +1,88 @@
var Google = require("./namespace.js");
var Accounts = require("meteor/accounts-base").Accounts;
var hasOwn = Object.prototype.hasOwnProperty;
// https://developers.google.com/accounts/docs/OAuth2Login#userinfocall
Google.whitelistedFields = ['id', 'email', 'verified_email', 'name', 'given_name',
'family_name', 'picture', 'locale', 'timezone', 'gender'];
function getServiceDataFromTokens(tokens) {
var accessToken = tokens.accessToken;
var idToken = tokens.idToken;
var scopes = getScopes(accessToken);
var identity = getIdentity(accessToken);
var serviceData = {
accessToken: accessToken,
idToken: idToken,
scope: scopes
};
if (hasOwn.call(tokens, "expiresAt")) {
serviceData.expiresAt =
Date.now() + 1000 * parseInt(tokens.expiresIn, 10);
}
var fields = Object.create(null);
Google.whitelistedFields.forEach(function (name) {
if (hasOwn.call(identity, name)) {
fields[name] = identity[name];
}
});
Object.assign(serviceData, fields);
// only set the token in serviceData if it's there. this ensures
// that we don't lose old ones (since we only get this on the first
// log in attempt)
if (tokens.refreshToken) {
serviceData.refreshToken = tokens.refreshToken;
}
return {
serviceData: serviceData,
options: {
profile: {
name: identity.name
}
}
};
}
Accounts.registerLoginHandler(function (request) {
if (request.googleSignIn !== true) {
return;
}
var res = HTTP.get(
"https://www.googleapis.com/oauth2/v3/tokeninfo",
{ headers: { "User-Agent": "Meteor/1.0" },
params: { id_token: request.idToken }}
);
if (res.error) {
throw res.error;
}
if (res.statusCode === 200 &&
res.data.sub === request.userId) {
return Accounts.updateOrCreateUserFromExternalService("google", {
id: request.userId,
idToken: request.idToken,
accessToken: request.accessToken,
email: request.email,
picture: request.imageUrl
});
}
});
OAuth.registerService('google', 2, null, function(query) {
var response = getTokens(query);
var expiresAt = (+new Date) + (1000 * parseInt(response.expiresIn, 10));
var accessToken = response.accessToken;
var idToken = response.idToken;
var scopes = getScopes(accessToken);
var identity = getIdentity(accessToken);
var serviceData = {
accessToken: accessToken,
idToken: idToken,
expiresAt: expiresAt,
scope: scopes
const tokens = {
accessToken: request.accessToken,
refreshToken: request.refreshToken,
idToken: request.idToken,
};
var fields = _.pick(identity, Google.whitelistedFields);
_.extend(serviceData, fields);
if (request.serverAuthCode) {
Object.assign(tokens, getTokens({
code: request.serverAuthCode
}));
}
// only set the token in serviceData if it's there. this ensures
// that we don't lose old ones (since we only get this on the first
// log in attempt)
if (response.refreshToken)
serviceData.refreshToken = response.refreshToken;
const result = getServiceDataFromTokens(tokens);
return {
serviceData: serviceData,
options: {profile: {name: identity.name}}
};
return Accounts.updateOrCreateUserFromExternalService("google", {
id: request.userId,
idToken: request.idToken,
accessToken: request.accessToken,
email: request.email,
picture: request.imageUrl,
...result.serviceData,
}, result.options);
});
function getServiceData(query) {
return getServiceDataFromTokens(getTokens(query));
}
OAuth.registerService('google', 2, null, getServiceData);
// returns an object containing:
// - accessToken
// - expiresIn: lifetime of token in seconds
@@ -82,8 +103,10 @@ var getTokens = function (query) {
grant_type: 'authorization_code'
}});
} catch (err) {
throw _.extend(new Error("Failed to complete OAuth handshake with Google. " + err.message),
{response: err.response});
throw Object.assign(
new Error("Failed to complete OAuth handshake with Google. " + err.message),
{ response: err.response }
);
}
if (response.data.error) { // if the http response was a json object with an error attribute
@@ -104,8 +127,10 @@ var getIdentity = function (accessToken) {
"https://www.googleapis.com/oauth2/v1/userinfo",
{params: {access_token: accessToken}}).data;
} catch (err) {
throw _.extend(new Error("Failed to fetch identity from Google. " + err.message),
{response: err.response});
throw Object.assign(
new Error("Failed to fetch identity from Google. " + err.message),
{ response: err.response }
);
}
};
@@ -115,8 +140,10 @@ var getScopes = function (accessToken) {
"https://www.googleapis.com/oauth2/v1/tokeninfo",
{params: {access_token: accessToken}}).data.scope.split(' ');
} catch (err) {
throw _.extend(new Error("Failed to fetch tokeninfo from Google. " + err.message),
{response: err.response});
throw Object.assign(
new Error("Failed to fetch tokeninfo from Google. " + err.message),
{ response: err.response }
);
}
};

View File

@@ -59,12 +59,14 @@ exports.signIn = Google.signIn = function (options, callback) {
function getScopes(options) {
// we need the email scope to get user id from google.
var requiredScopes = ['email'];
var scopes = ['profile'];
if (options && options.requestPermissions) {
scopes = options.requestPermissions;
}
return _.union(scopes, requiredScopes);
var requiredScopes = { 'email': 1 };
var scopes = options.requestPermissions || ['profile'];
scopes.forEach(function (scope) {
requiredScopes[scope] = 1;
});
return Object.keys(requiredScopes);
}
exports.signOut = Google.signOut = function () {

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Google OAuth flow",
version: "1.2.1"
version: "1.2.4"
});
var cordovaPluginGooglePlusURL =
@@ -14,12 +14,11 @@ Cordova.depends({
});
Package.onUse(function(api) {
api.use("modules");
api.use("promise");
api.use("ecmascript");
api.use('oauth2', ['client', 'server']);
api.use('oauth', ['client', 'server']);
api.use('http', ['server']);
api.use(['underscore', 'service-configuration'], ['client', 'server']);
api.use('service-configuration');
api.use('random', 'client');
api.addFiles('google_server.js', 'server');

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Simulates local storage on IE 6,7 using userData",
version: "1.1.0-beta.14"
version: "1.1.0-rc.1"
});
Package.onUse(function (api) {

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: 'Meetup OAuth flow',
version: '1.0.0'
version: '1.0.1'
});
Package.onUse(function (api) {

View File

@@ -1,6 +1,6 @@
Package.describe({
name: 'meteor-base',
version: '1.0.4',
version: '1.1.0-rc.1',
// Brief, one-line summary of the package.
summary: 'Packages that every Meteor app needs',
// By default, Meteor will default to using README.md for documentation.
@@ -25,6 +25,9 @@ Package.onUse(function(api) {
'ddp',
'livedata', // XXX COMPAT WITH PACKAGES BUILT FOR 0.9.0.
// Runtime support for Meteor 1.5 dynamic import(...) syntax.
'dynamic-import',
// Push code changes to the client and automatically reload the page
'hot-code-push'
]);

View File

@@ -42,4 +42,4 @@ isOperatorObject = function (valueSelector, inconsistentOK) {
// string can be converted to integer
isNumericKey = function (s) {
return /^[0-9]+$/.test(s);
};
};

View File

@@ -1,3 +1,5 @@
import { assertHasValidFieldNames } from './validation.js';
// XXX type checking on selectors (graceful error if malformed)
// LocalCollection: a set of documents that supports queries and modifiers.
@@ -40,7 +42,11 @@ Minimongo = {};
// Use it to export private functions to test in Tinytest.
MinimongoTest = {};
MinimongoError = function (message) {
MinimongoError = function (message, options={}) {
if (typeof message === "string" && options.field) {
message += ` for field '${options.field}'`;
}
var e = new Error(message);
e.name = "MinimongoError";
return e;
@@ -536,31 +542,13 @@ LocalCollection.Cursor.prototype._depend = function (changers, _allow_unordered)
}
};
// XXX enforce rule that field names can't start with '$' or contain '.'
// (real mongodb does in fact enforce this)
// XXX possibly enforce that 'undefined' does not appear (we assume
// this in our handling of null and $exists)
LocalCollection.prototype.insert = function (doc, callback) {
var self = this;
doc = EJSON.clone(doc);
// Make sure field names do not contain Mongo restricted
// characters ('.', '$', '\0').
// https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names
if (doc) {
const invalidCharMsg = {
'.': "contain '.'",
'$': "start with '$'",
'\0': "contain null bytes",
};
JSON.stringify(doc, (key, value) => {
let match;
if (_.isString(key) && (match = key.match(/^\$|\.|\0/))) {
throw MinimongoError(`Key ${key} must not ${invalidCharMsg[match[0]]}`);
}
return value;
});
}
assertHasValidFieldNames(doc);
if (!_.has(doc, '_id')) {
// if you really want to use ObjectIDs, set this global.
@@ -711,7 +699,7 @@ LocalCollection.prototype.update = function (selector, mod, options, callback) {
}
if (!options) options = {};
var matcher = new Minimongo.Matcher(selector);
var matcher = new Minimongo.Matcher(selector, true);
// Save the original results of any query that we might need to
// _recomputeResults on, because _modifyAndNotify will mutate the objects in
@@ -794,8 +782,24 @@ LocalCollection.prototype.update = function (selector, mod, options, callback) {
// generate an id for it.
var insertedId;
if (updateCount === 0 && options.upsert) {
var newDoc = LocalCollection._removeDollarOperators(selector);
let selectorModifier = LocalCollection._selectorIsId(selector)
? { _id: selector }
: selector;
selectorModifier = LocalCollection._removeDollarOperators(selectorModifier);
const newDoc = {};
if (selectorModifier._id) {
newDoc._id = selectorModifier._id;
delete selectorModifier._id;
}
// This double _modify call is made to help work around an issue where collection
// upserts won't work properly, with nested properties (see issue #8631).
LocalCollection._modify(newDoc, {$set: selectorModifier});
LocalCollection._modify(newDoc, mod, {isInsert: true});
if (! newDoc._id && options.insertedId)
newDoc._id = options.insertedId;
insertedId = self.insert(newDoc);

View File

@@ -163,6 +163,22 @@ Tinytest.add("minimongo - basics", function (test) {
});
Tinytest.add("minimongo - error - no options", function (test) {
try {
throw MinimongoError("Not fun to have errors");
} catch (e) {
test.equal(e.message, "Not fun to have errors");
}
});
Tinytest.add("minimongo - error - with field", function (test) {
try {
throw MinimongoError("Cats are no fun", { field: "mice" });
} catch (e) {
test.equal(e.message, "Cats are no fun for field 'mice'");
}
});
Tinytest.add("minimongo - cursors", function (test) {
var c = new LocalCollection();
var res;
@@ -2191,6 +2207,13 @@ Tinytest.add("minimongo - modify", function (test) {
test.equal(actual, expected);
};
var upsertException = function (query, mod) {
var coll = new LocalCollection;
test.throws(function(){
coll.upsert(query, mod);
});
};
// document replacement
modify({}, {}, {});
modify({a: 12}, {}, {}); // tested against mongodb
@@ -2199,6 +2222,11 @@ Tinytest.add("minimongo - modify", function (test) {
exception({a: 12}, {a: 13, $set: {b: 13}});
exception({a: 12}, {$set: {b: 13}, a: 13});
exception({a: 12}, {$a: 13}); //invalid operator
exception({a: 12}, {b:{$a: 13}});
exception({a: 12}, {b:{'a.b': 13}});
exception({a: 12}, {b:{'\0a': 13}});
// keys
modify({}, {$set: {'a': 12}}, {a: 12});
modify({}, {$set: {'a.b': 12}}, {a: {b: 12}});
@@ -2273,13 +2301,6 @@ Tinytest.add("minimongo - modify", function (test) {
{'a.x': 1, 'a.y': 3},
{$set: {'a.$.z': 5}},
{a: [{x: 1}, {y: 3, z: 5}]});
// with $near, make sure it finds the closest one
modifyWithQuery({a: [{b: [1,1]},
{b: [ [3,3], [4,4] ]},
{b: [9,9]}]},
{'a.b': {$near: [5, 5]}},
{$set: {'a.$.b': 'k'}},
{a: [{b: [1,1]}, {b: 'k'}, {b: [9,9]}]});
modifyWithQuery({a: [{x: 1}, {y: 1}, {x: 1, y: 1}]},
{a: {$elemMatch: {x: 1, y: 1}}},
{$set: {'a.$.x': 2}},
@@ -2288,6 +2309,56 @@ Tinytest.add("minimongo - modify", function (test) {
{'a.b': {$elemMatch: {x: 1, y: 1}}},
{$set: {'a.$.b': 3}},
{a: [{b: 3}]});
// with $near, make sure it does not find the closest one (#3599)
modifyWithQuery({a: []},
{'a.b': {$near: [5, 5]}},
{$set: {'a.$.b': 'k'}},
{"a":[]});
modifyWithQuery({a: [{b: [ [3,3], [4,4] ]}]},
{'a.b': {$near: [5, 5]}},
{$set: {'a.$.b': 'k'}},
{"a":[{"b":"k"}]});
modifyWithQuery({a: [{b: [1,1]},
{b: [ [3,3], [4,4] ]},
{b: [9,9]}]},
{'a.b': {$near: [5, 5]}},
{$set: {'a.$.b': 'k'}},
{"a":[{"b":"k"},{"b":[[3,3],[4,4]]},{"b":[9,9]}]});
modifyWithQuery({a: [{b: [1,1]},
{b: [ [3,3], [4,4] ]},
{b: [9,9]}]},
{'a.b': {$near: [9, 9], $maxDistance: 1}},
{$set: {'a.$.b': 'k'}},
{"a":[{"b":"k"},{"b":[[3,3],[4,4]]},{"b":[9,9]}]});
modifyWithQuery({a: [{b: [1,1]},
{b: [ [3,3], [4,4] ]},
{b: [9,9]}]},
{'a.b': {$near: [9, 9]}},
{$set: {'a.$.b': 'k'}},
{"a":[{"b":"k"},{"b":[[3,3],[4,4]]},{"b":[9,9]}]});
modifyWithQuery({a: [{b: [9,9]},
{b: [ [3,3], [4,4] ]},
{b: [9,9]}]},
{'a.b': {$near: [9, 9]}},
{$set: {'a.$.b': 'k'}},
{"a":[{"b":"k"},{"b":[[3,3],[4,4]]},{"b":[9,9]}]});
modifyWithQuery({a: [{b:[4,3]},
{c: [1,1]}]},
{'a.c': {$near: [1, 1]}},
{$set: {'a.$.c': 'k'}},
{"a":[{"c": "k", "b":[4,3]},{"c":[1,1]}]});
modifyWithQuery({a: [{c: [9,9]},
{b: [ [3,3], [4,4] ]},
{b: [1,1]}]},
{'a.b': {$near: [1, 1]}},
{$set: {'a.$.b': 'k'}},
{"a":[{"c": [9,9], "b":"k"},{"b": [ [3,3], [4,4]]},{"b":[1,1]}]});
modifyWithQuery({a: [{c: [9,9], b:[4,3]},
{b: [ [3,3], [4,4] ]},
{b: [1,1]}]},
{'a.b': {$near: [1, 1]}},
{$set: {'a.$.b': 'k'}},
{"a":[{"c": [9,9], "b":"k"},{"b": [ [3,3], [4,4]]},{"b":[1,1]}]});
// $inc
modify({a: 1, b: 2}, {$inc: {a: 10}}, {a: 11, b: 2});
@@ -2367,6 +2438,12 @@ Tinytest.add("minimongo - modify", function (test) {
modify({}, {$set: {'x._id': 4}}, {x: {_id: 4}});
exception({}, {$set: {_id: 4}});
exception({_id: 4}, {$set: {_id: 4}}); // even not-changing _id is bad
//restricted field names
exception({a:{}}, {$set:{a:{$a:1}}});
exception({ a: {} }, { $set: { a: { c:
[{ b: { $a: 1 } }] } } });
exception({a:{}}, {$set:{a:{'\0a':1}}});
exception({a:{}}, {$set:{a:{'a.b':1}}});
// $unset
modify({}, {$unset: {a: 1}}, {});
@@ -2400,8 +2477,6 @@ Tinytest.add("minimongo - modify", function (test) {
{a: [1, 2, 3]});
modify({a: [true]}, {$push: {a: {$each: [1, 2, 3]}}},
{a: [true, 1, 2, 3]});
// No positive numbers for $slice
exception({}, {$push: {a: {$each: [], $slice: 5}}});
modify({a: [true]}, {$push: {a: {$each: [1, 2, 3], $slice: -2}}},
{a: [2, 3]});
modify({a: [false, true]}, {$push: {a: {$each: [1], $slice: -2}}},
@@ -2441,6 +2516,26 @@ Tinytest.add("minimongo - modify", function (test) {
{$push: {a: {$each: [{x: 3}], $position: 0, $sort: {x: 1}, $slice: 0}}},
{a: []}
);
//restricted field names
exception({}, {$push: {$a: 1}});
exception({}, {$push: {'\0a': 1}});
exception({}, {$push: {a: {$a:1}}});
exception({}, {$push: {a: {$each: [{$a:1}]}}});
exception({}, {$push: {a: {$each: [{"a.b":1}]}}});
exception({}, {$push: {a: {$each: [{'\0a':1}]}}});
modify({}, {$push: {a: {$each: [{'':1}]}}}, {a: [ { '': 1 } ]});
modify({}, {$push: {a: {$each: [{' ':1}]}}}, {a: [ { ' ': 1 } ]});
exception({}, {$push: {a: {$each: [{'.':1}]}}});
// #issue 5167
// $push $slice with positive numbers
modify({}, {$push: {a: {$each: [], $slice: 5}}}, {a:[]});
modify({a:[1,2,3]}, {$push: {a: {$each: [], $slice: 1}}}, {a:[1]});
modify({a:[1,2,3]}, {$push: {a: {$each: [4,5], $slice: 1}}}, {a:[1]});
modify({a:[1,2,3]}, {$push: {a: {$each: [4,5], $slice: 2}}}, {a:[1,2]});
modify({a:[1,2,3]}, {$push: {a: {$each: [4,5], $slice: 4}}}, {a:[1,2,3,4]});
modify({a:[1,2,3]}, {$push: {a: {$each: [4,5], $slice: 5}}}, {a:[1,2,3,4,5]});
modify({a:[1,2,3]}, {$push: {a: {$each: [4,5], $slice: 10}}}, {a:[1,2,3,4,5]});
// $pushAll
@@ -2459,6 +2554,9 @@ Tinytest.add("minimongo - modify", function (test) {
modify({a: []}, {$pushAll: {'a.1': []}}, {a: [null, []]});
modify({a: {}}, {$pushAll: {'a.x': [99]}}, {a: {x: [99]}});
modify({a: {}}, {$pushAll: {'a.x': []}}, {a: {x: []}});
exception({a: [1]}, {$pushAll: {a: [{$a:1}]}});
exception({a: [1]}, {$pushAll: {a: [{'\0a':1}]}});
exception({a: [1]}, {$pushAll: {a: [{"a.b":1}]}});
// $addToSet
modify({}, {$addToSet: {a: 1}}, {a: [1]});
@@ -2477,15 +2575,26 @@ Tinytest.add("minimongo - modify", function (test) {
modify({a: [{x: 1, y: 2}]}, {$addToSet: {a: {y: 2, x: 1}}},
{a: [{x: 1, y: 2}, {y: 2, x: 1}]});
modify({a: [1, 2]}, {$addToSet: {a: {$each: [3, 1, 4]}}}, {a: [1, 2, 3, 4]});
modify({a: [1, 2]}, {$addToSet: {a: {$each: [3, 1, 4], b: 12}}},
{a: [1, 2, 3, 4]}); // tested
modify({a: [1, 2]}, {$addToSet: {a: {b: 12, $each: [3, 1, 4]}}},
{a: [1, 2, {b: 12, $each: [3, 1, 4]}]}); // tested
modify({}, {$addToSet: {a: {$each: []}}}, {a: []});
modify({}, {$addToSet: {a: {$each: [1]}}}, {a: [1]});
modify({a: []}, {$addToSet: {'a.1': 99}}, {a: [null, [99]]});
modify({a: {}}, {$addToSet: {'a.x': 99}}, {a: {x: [99]}});
// invalid field names
exception({}, {$addToSet: {a: {$b:1}}});
exception({}, {$addToSet: {a: {"a.b":1}}});
exception({}, {$addToSet: {a: {"a.":1}}});
exception({}, {$addToSet: {a: {'\u0000a':1}}});
exception({a: [1, 2]}, {$addToSet: {a:{$each: [3, 1, {$a:1}]}}});
exception({a: [1, 2]}, {$addToSet: {a:{$each: [3, 1, {'\0a':1}]}}});
exception({a: [1, 2]}, {$addToSet: {a:{$each: [3, 1, [{$a:1}]]}}});
exception({a: [1, 2]}, {$addToSet: {a:{$each: [3, 1, [{b:{c:[{a:1},{"d.s":2}]}}]]}}});
exception({a: [1, 2]}, {$addToSet: {a:{b: [3, 1, [{b:{c:[{a:1},{"d.s":2}]}}]]}}});
//$each is first element and thus an operator
modify({a: [1, 2]}, {$addToSet: {a: {$each: [3, 1, 4], b: 12}}},{a: [ 1, 2, 3, 4 ]});
// this should fail because $each is now a field name (not first in object) and thus invalid field name with $
exception({a: [1, 2]}, {$addToSet: {a: {b: 12, $each: [3, 1, 4]}}});
// $pop
modify({}, {$pop: {a: 1}}, {}); // tested
modify({}, {$pop: {a: -1}}, {}); // tested
@@ -2558,11 +2667,23 @@ Tinytest.add("minimongo - modify", function (test) {
exception({}, {$rename: {'a': 'a'}});
exception({}, {$rename: {'a.b': 'a.b'}});
modify({a: 12, b: 13}, {$rename: {a: 'b'}}, {b: 12});
exception({a: [12]}, {$rename: {a: '$b'}});
exception({a: [12]}, {$rename: {a: '\0a'}});
// $setOnInsert
modify({a: 0}, {$setOnInsert: {a: 12}}, {a: 0});
upsert({a: 12}, {$setOnInsert: {b: 12}}, {a: 12, b: 12});
upsert({a: 12}, {$setOnInsert: {_id: 'test'}}, {_id: 'test', a: 12});
upsert({"a.b": 10}, {$setOnInsert: {a: {b: 10, c: 12}}}, {a: {b: 10, c: 12}});
upsert({"a.b": 10}, {$setOnInsert: {c: 12}}, {a: {b: 10}, c: 12});
upsert({"_id": 'test'}, {$setOnInsert: {c: 12}}, {_id: 'test', c: 12});
upsert('test', {$setOnInsert: {c: 12}}, {_id: 'test', c: 12});
upsertException({a: 0}, {$setOnInsert: {$a: 12}});
upsertException({a: 0}, {$setOnInsert: {'\0a': 12}});
upsert({a: 0}, {$setOnInsert: {b: {a:1}}}, {a:0, b:{a:1}});
upsertException({a: 0}, {$setOnInsert: {b: {$a:1}}});
upsertException({a: 0}, {$setOnInsert: {b: {'a.b':1}}});
upsertException({a: 0}, {$setOnInsert: {b: {'\0a':1}}});
exception({}, {$set: {_id: 'bad'}});
@@ -3142,13 +3263,14 @@ Tinytest.add("minimongo - $near operator tests", function (test) {
// 'y'.
testNear([2, 2], 1000, ['x', 'y']);
// Ensure that distance is used as a tie-breaker for sort.
// issue #3599
// Ensure that distance is not used as a tie-breaker for sort.
test.equal(
_.pluck(coll.find({'a.b': {$near: [1, 1]}}, {sort: {k: 1}}).fetch(), '_id'),
['x', 'y']);
test.equal(
_.pluck(coll.find({'a.b': {$near: [5, 5]}}, {sort: {k: 1}}).fetch(), '_id'),
['y', 'x']);
['x', 'y']);
var operations = [];
var cbs = log_callbacks(operations);
@@ -3167,6 +3289,35 @@ Tinytest.add("minimongo - $near operator tests", function (test) {
handle.stop();
});
// issue #2077
Tinytest.add("minimongo - $near and $geometry for legacy coordinates", function(test){
var coll = new LocalCollection();
coll.insert({
loc: {
x: 1,
y: 1
}
});
coll.insert({
loc: [-1,-1]
});
coll.insert({
loc: [40,-10]
});
coll.insert({
loc: {
x: -10,
y: 40
}
});
test.equal(coll.find({ 'loc': { $near: [0, 0], $maxDistance: 4 } }).count(), 2);
test.equal(coll.find({ 'loc': { $near: {$geometry: {type: "Point", coordinates: [0, 0]}}} }).count(), 4);
test.equal(coll.find({ 'loc': { $near: {$geometry: {type: "Point", coordinates: [0, 0]}, $maxDistance:200000}}}).count(), 2);
});
// Regression test for #4377. Previously, "replace" updates didn't clone the
// argument.
Tinytest.add("minimongo - update should clone", function (test) {

View File

@@ -1,3 +1,5 @@
import { assertHasValidFieldNames, assertIsValidFieldName } from './validation.js';
// XXX need a strategy for passing the binding of $ into this
// function, from the compiled selector
//
@@ -27,11 +29,7 @@ LocalCollection._modify = function (doc, mod, options) {
throw MinimongoError("Cannot change the _id of a document");
// replace the whole document
for (var k in mod) {
if (/\./.test(k))
throw MinimongoError(
"When replacing document, field name may not contain '.'");
}
assertHasValidFieldNames(mod);
newDoc = mod;
} else {
// apply modifiers to the doc.
@@ -155,8 +153,7 @@ var findModTarget = function (doc, keyparts, options) {
"' of list value " + JSON.stringify(doc[keypart]));
}
} else {
if (keypart.length && keypart.substr(0, 1) === '$')
throw MinimongoError("can't set field named " + keypart);
assertIsValidFieldName(keypart);
if (!(keypart in doc)) {
if (options.noCreate)
return undefined;
@@ -185,20 +182,24 @@ var MODIFIERS = {
$currentDate: function (target, field, arg) {
if (typeof arg === "object" && arg.hasOwnProperty("$type")) {
if (arg.$type !== "date") {
throw MinimongoError("Minimongo does currently only support the date type in $currentDate modifiers");
throw MinimongoError(
"Minimongo does currently only support the date type " +
"in $currentDate modifiers",
{ field });
}
} else if (arg !== true) {
throw MinimongoError("Invalid $currentDate modifier");
throw MinimongoError("Invalid $currentDate modifier", { field });
}
target[field] = new Date();
},
$min: function (target, field, arg) {
if (typeof arg !== "number") {
throw MinimongoError("Modifier $min allowed for numbers only");
throw MinimongoError("Modifier $min allowed for numbers only", { field });
}
if (field in target) {
if (typeof target[field] !== "number") {
throw MinimongoError("Cannot apply $min modifier to non-number");
throw MinimongoError(
"Cannot apply $min modifier to non-number", { field });
}
if (target[field] > arg) {
target[field] = arg;
@@ -209,11 +210,12 @@ var MODIFIERS = {
},
$max: function (target, field, arg) {
if (typeof arg !== "number") {
throw MinimongoError("Modifier $max allowed for numbers only");
throw MinimongoError("Modifier $max allowed for numbers only", { field });
}
if (field in target) {
if (typeof target[field] !== "number") {
throw MinimongoError("Cannot apply $max modifier to non-number");
throw MinimongoError(
"Cannot apply $max modifier to non-number", { field });
}
if (target[field] < arg) {
target[field] = arg;
@@ -224,10 +226,11 @@ var MODIFIERS = {
},
$inc: function (target, field, arg) {
if (typeof arg !== "number")
throw MinimongoError("Modifier $inc allowed for numbers only");
throw MinimongoError("Modifier $inc allowed for numbers only", { field });
if (field in target) {
if (typeof target[field] !== "number")
throw MinimongoError("Cannot apply $inc modifier to non-number");
throw MinimongoError(
"Cannot apply $inc modifier to non-number", { field });
target[field] += arg;
} else {
target[field] = arg;
@@ -235,20 +238,17 @@ var MODIFIERS = {
},
$set: function (target, field, arg) {
if (!_.isObject(target)) { // not an array or an object
var e = MinimongoError("Cannot set property on non-object field");
var e = MinimongoError(
"Cannot set property on non-object field", { field });
e.setPropertyError = true;
throw e;
}
if (target === null) {
var e = MinimongoError("Cannot set property on null");
var e = MinimongoError("Cannot set property on null", { field });
e.setPropertyError = true;
throw e;
}
if (_.isString(field) && field.indexOf('\0') > -1) {
// Null bytes are not allowed in Mongo field names
// https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names
throw MinimongoError(`Key ${field} must not contain null bytes`);
}
assertHasValidFieldNames(arg);
target[field] = arg;
},
$setOnInsert: function (target, field, arg) {
@@ -267,10 +267,12 @@ var MODIFIERS = {
if (target[field] === undefined)
target[field] = [];
if (!(target[field] instanceof Array))
throw MinimongoError("Cannot apply $push modifier to non-array");
throw MinimongoError(
"Cannot apply $push modifier to non-array", { field });
if (!(arg && arg.$each)) {
// Simple mode: not $each
assertHasValidFieldNames(arg);
target[field].push(arg);
return;
}
@@ -278,16 +280,18 @@ var MODIFIERS = {
// Fancy mode: $each (and maybe $slice and $sort and $position)
var toPush = arg.$each;
if (!(toPush instanceof Array))
throw MinimongoError("$each must be an array");
throw MinimongoError("$each must be an array", { field });
assertHasValidFieldNames(toPush);
// Parse $position
var position = undefined;
if ('$position' in arg) {
if (typeof arg.$position !== "number")
throw MinimongoError("$position must be a numeric value");
throw MinimongoError("$position must be a numeric value", { field });
// XXX should check to make sure integer
if (arg.$position < 0)
throw MinimongoError("$position in $push must be zero or positive");
throw MinimongoError(
"$position in $push must be zero or positive", { field });
position = arg.$position;
}
@@ -295,10 +299,8 @@ var MODIFIERS = {
var slice = undefined;
if ('$slice' in arg) {
if (typeof arg.$slice !== "number")
throw MinimongoError("$slice must be a numeric value");
throw MinimongoError("$slice must be a numeric value", { field });
// XXX should check to make sure integer
if (arg.$slice > 0)
throw MinimongoError("$slice in $push must be zero or negative");
slice = arg.$slice;
}
@@ -306,7 +308,7 @@ var MODIFIERS = {
var sortFunction = undefined;
if (arg.$sort) {
if (slice === undefined)
throw MinimongoError("$sort requires $slice to be present");
throw MinimongoError("$sort requires $slice to be present", { field });
// XXX this allows us to use a $sort whose value is an array, but that's
// actually an extension of the Node driver, so it won't work
// server-side. Could be confusing!
@@ -315,7 +317,7 @@ var MODIFIERS = {
for (var i = 0; i < toPush.length; i++) {
if (LocalCollection._f._type(toPush[i]) !== 3) {
throw MinimongoError("$push like modifiers using $sort " +
"require all elements to be objects");
"require all elements to be objects", { field });
}
}
}
@@ -339,18 +341,22 @@ var MODIFIERS = {
if (slice !== undefined) {
if (slice === 0)
target[field] = []; // differs from Array.slice!
else
else if (slice < 0)
target[field] = target[field].slice(slice);
else
target[field] = target[field].slice(0, slice);
}
},
$pushAll: function (target, field, arg) {
if (!(typeof arg === "object" && arg instanceof Array))
throw MinimongoError("Modifier $pushAll/pullAll allowed for arrays only");
assertHasValidFieldNames(arg);
var x = target[field];
if (x === undefined)
target[field] = arg;
else if (!(x instanceof Array))
throw MinimongoError("Cannot apply $pushAll modifier to non-array");
throw MinimongoError(
"Cannot apply $pushAll modifier to non-array", { field });
else {
for (var i = 0; i < arg.length; i++)
x.push(arg[i]);
@@ -360,18 +366,19 @@ var MODIFIERS = {
var isEach = false;
if (typeof arg === "object") {
//check if first key is '$each'
for (var k in arg) {
if (k === "$each")
isEach = true;
break;
const keys = Object.keys(arg);
if (keys[0] === "$each"){
isEach = true;
}
}
var values = isEach ? arg["$each"] : [arg];
assertHasValidFieldNames(values);
var x = target[field];
if (x === undefined)
target[field] = values;
else if (!(x instanceof Array))
throw MinimongoError("Cannot apply $addToSet modifier to non-array");
throw MinimongoError(
"Cannot apply $addToSet modifier to non-array", { field });
else {
_.each(values, function (value) {
for (var i = 0; i < x.length; i++)
@@ -388,7 +395,8 @@ var MODIFIERS = {
if (x === undefined)
return;
else if (!(x instanceof Array))
throw MinimongoError("Cannot apply $pop modifier to non-array");
throw MinimongoError(
"Cannot apply $pop modifier to non-array", { field });
else {
if (typeof arg === 'number' && arg < 0)
x.splice(0, 1);
@@ -403,7 +411,8 @@ var MODIFIERS = {
if (x === undefined)
return;
else if (!(x instanceof Array))
throw MinimongoError("Cannot apply $pull/pullAll modifier to non-array");
throw MinimongoError(
"Cannot apply $pull/pullAll modifier to non-array", { field });
else {
var out = [];
if (arg != null && typeof arg === "object" && !(arg instanceof Array)) {
@@ -430,14 +439,16 @@ var MODIFIERS = {
},
$pullAll: function (target, field, arg) {
if (!(typeof arg === "object" && arg instanceof Array))
throw MinimongoError("Modifier $pushAll/pullAll allowed for arrays only");
throw MinimongoError(
"Modifier $pushAll/pullAll allowed for arrays only", { field });
if (target === undefined)
return;
var x = target[field];
if (x === undefined)
return;
else if (!(x instanceof Array))
throw MinimongoError("Cannot apply $pull/pullAll modifier to non-array");
throw MinimongoError(
"Cannot apply $pull/pullAll modifier to non-array", { field });
else {
var out = [];
for (var i = 0; i < x.length; i++) {
@@ -457,15 +468,17 @@ var MODIFIERS = {
$rename: function (target, field, arg, keypath, doc) {
if (keypath === arg)
// no idea why mongo has this restriction..
throw MinimongoError("$rename source must differ from target");
throw MinimongoError("$rename source must differ from target", { field });
if (target === null)
throw MinimongoError("$rename source field invalid");
throw MinimongoError("$rename source field invalid", { field });
if (typeof arg !== "string")
throw MinimongoError("$rename target must be a string");
throw MinimongoError("$rename target must be a string", { field });
if (arg.indexOf('\0') > -1) {
// Null bytes are not allowed in Mongo field names
// https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names
throw MinimongoError("The 'to' field for $rename cannot contain an embedded null byte");
throw MinimongoError(
"The 'to' field for $rename cannot contain an embedded null byte",
{ field });
}
if (target === undefined)
return;
@@ -475,13 +488,13 @@ var MODIFIERS = {
var keyparts = arg.split('.');
var target2 = findModTarget(doc, keyparts, {forbidArray: true});
if (target2 === null)
throw MinimongoError("$rename target field invalid");
throw MinimongoError("$rename target field invalid", { field });
var field2 = keyparts.pop();
target2[field2] = v;
},
$bit: function (target, field, arg) {
// XXX mongo only supports $bit on integers, and we only support
// native javascript numbers (doubles) so far, so we can't support $bit
throw MinimongoError("$bit is not supported");
throw MinimongoError("$bit is not supported", { field });
}
};

View File

@@ -1,12 +1,13 @@
Package.describe({
summary: "Meteor's client-side datastore: a port of MongoDB to Javascript",
version: '1.0.21'
version: '1.2.0-rc.1'
});
Package.onUse(function (api) {
api.export('LocalCollection');
api.export('Minimongo');
api.export('MinimongoTest', { testOnly: true });
api.export('MinimongoError', { testOnly: true });
api.use([
'underscore',
'ejson',

View File

@@ -18,7 +18,7 @@
// Main entry point.
// var matcher = new Minimongo.Matcher({a: {$gt: 5}});
// if (matcher.documentMatches({a: 7})) ...
Minimongo.Matcher = function (selector) {
Minimongo.Matcher = function (selector, isUpdate = false) {
var self = this;
// A set (object mapping string -> *) of all of the document paths looked
// at by the selector. Also includes the empty string if it may look at any
@@ -41,6 +41,10 @@ Minimongo.Matcher = function (selector) {
// Sorter._useWithMatcher.
self._selector = null;
self._docMatcher = self._compileSelector(selector);
// Set to true if selection is done for an update operation
// Default is false
// Used for $near array update (issue #3599)
self._isUpdate = isUpdate;
};
_.extend(Minimongo.Matcher.prototype, {
@@ -435,9 +439,10 @@ var VALUE_OPERATORS = {
throw Error("$near can't be inside another $ operator");
matcher._hasGeoQuery = true;
// There are two kinds of geodata in MongoDB: coordinate pairs and
// There are two kinds of geodata in MongoDB: legacy coordinate pairs and
// GeoJSON. They use different distance metrics, too. GeoJSON queries are
// marked with a $geometry property.
// marked with a $geometry property, though legacy coordinates can be
// matched using $geometry.
var maxDistance, point, distance;
if (isPlainObject(operand) && _.has(operand, '$geometry')) {
@@ -448,8 +453,11 @@ var VALUE_OPERATORS = {
// XXX: for now, we don't calculate the actual distance between, say,
// polygon and circle. If people care about this use-case it will get
// a priority.
if (!value || !value.type)
if (!value)
return null;
if(!value.type)
return GeoJSON.pointDistance(point,
{ type: "Point", coordinates: pointToArray(value) });
if (value.type === "Point") {
return GeoJSON.pointDistance(point, value);
} else {
@@ -480,20 +488,29 @@ var VALUE_OPERATORS = {
// each within-$maxDistance branching point.
branchedValues = expandArraysInBranches(branchedValues);
var result = {result: false};
_.each(branchedValues, function (branch) {
var curDistance = distance(branch.value);
// Skip branches that aren't real points or are too far away.
if (curDistance === null || curDistance > maxDistance)
return;
// Skip anything that's a tie.
if (result.distance !== undefined && result.distance <= curDistance)
return;
_.every(branchedValues, function (branch) {
// if operation is an update, don't skip branches, just return the first one (#3599)
if (!matcher._isUpdate){
if (!(typeof branch.value === "object")){
return true;
}
var curDistance = distance(branch.value);
// Skip branches that aren't real points or are too far away.
if (curDistance === null || curDistance > maxDistance)
return true;
// Skip anything that's a tie.
if (result.distance !== undefined && result.distance <= curDistance)
return true;
}
result.result = true;
result.distance = curDistance;
if (!branch.arrayIndices)
delete result.arrayIndices;
else
result.arrayIndices = branch.arrayIndices;
if (matcher._isUpdate)
return false;
return true;
});
return result;
};

View File

@@ -81,23 +81,25 @@ _.extend(Minimongo.Sorter.prototype, {
getComparator: function (options) {
var self = this;
// If we have no distances, just use the comparator from the source
// specification (which defaults to "everything is equal".
if (!options || !options.distances) {
// If sort is specified or have no distances, just use the comparator from
// the source specification (which defaults to "everything is equal".
// issue #3599
// https://docs.mongodb.com/manual/reference/operator/query/near/#sort-operation
// sort effectively overrides $near
if (self._sortSpecParts.length || !options || !options.distances) {
return self._getBaseComparator();
}
var distances = options.distances;
// Return a comparator which first tries the sort specification, and if that
// says "it's equal", breaks ties using $near distances.
return composeComparators([self._getBaseComparator(), function (a, b) {
// Return a comparator which compares using $near distances.
return function (a, b) {
if (!distances.has(a._id))
throw Error("Missing distance for " + a._id);
if (!distances.has(b._id))
throw Error("Missing distance for " + b._id);
return distances.get(a._id) - distances.get(b._id);
}]);
};
},
_getPaths: function () {

View File

@@ -0,0 +1,24 @@
// Make sure field names do not contain Mongo restricted
// characters ('.', '$', '\0').
// https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names
const invalidCharMsg = {
'.': "contain '.'",
'$': "start with '$'",
'\0': "contain null bytes",
};
export function assertIsValidFieldName(key) {
let match;
if (_.isString(key) && (match = key.match(/^\$|\.|\0/))) {
throw MinimongoError(`Key ${key} must not ${invalidCharMsg[match[0]]}`);
}
};
// checks if all field names in an object are valid
export function assertHasValidFieldNames(doc){
if (doc && typeof doc === "object") {
JSON.stringify(doc, (key, value) => {
assertIsValidFieldName(key);
return value;
});
}
};

View File

@@ -1,9 +1,9 @@
{
"dependencies": {
"install": {
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/install/-/install-0.8.8.tgz",
"from": "install@0.8.8"
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/install/-/install-0.10.1.tgz",
"from": "install@0.10.1"
}
}
}

View File

@@ -2,4 +2,4 @@
// package.json files to the "main" field.
makeInstallerOptions.browser = true;
install = makeInstaller(makeInstallerOptions);
meteorInstall = makeInstaller(makeInstallerOptions);

View File

@@ -1,149 +0,0 @@
// The metaInstall function will be used to create a module graph that
// parallels the installed module graph. This parallel graph consists of
// metadata about all available modules, not only those already installed
// but also modules that can be fetched dynamically.
var metaInstall = makeInstaller({
browser: makeInstallerOptions.browser
});
meteorInstall = function (tree, options) {
if (isObject(tree)) {
var meta = Object.create(null);
var real = Object.create(null);
walk(options, tree, meta, real);
metaInstall(meta, options);
return install(real, options);
}
return install();
};
// Other packages (namely the dynamic-import package) call this function
// to retrieve module metadata from the metaInstall graph.
meteorInstall._requireMeta = metaInstall();
function isObject(value) {
return value && typeof value === "object";
}
function getOrSet(obj, name) {
return obj[name] = obj[name] || Object.create(null);
}
function walk(options, input, meta, real) {
Object.keys(input).forEach(function (name) {
var value = input[name];
if (tryChild(value, name, meta, real, options)) {
// If the value was a leaf node that we were able to handle, then we
// don't need to (and can't) keep walking it.
return;
}
if (isObject(value)) {
walk(
options,
value,
getOrSet(meta, name),
getOrSet(real, name)
);
}
});
return this;
}
function tryChild(value, name, meta, real, options) {
function tryFunc(value) {
if (typeof value === "function") {
meta[name] = makeMetaFunc({}, false, options);
real[name] = value;
return true;
}
}
if (Array.isArray(value)) {
return value.some(function (value) {
// Dynamic stub modules are represented by objects wrapped in array
// brackets. When we find one of these objects, we install it in the
// meta graph, but not in the installed modules graph. Later, this
// information may be used to fetch dynamic modules from the server,
// which will then be installed into the real graph.
if (isObject(value)) {
meta[name] = makeMetaFunc(value, true, options);
return true;
}
// Older versions of the install.js library supported wrapping a
// module function in an array that also contained the dependency
// identifier strings of that module. That style should no longer be
// used, but we might as well handle it gracefully, since it is not
// ambiguous with the [{...}] style.
return tryFunc(value);
});
}
// Installed (immediately importable) modules are represented by
// function expressions with the parameters (require, exports, module).
if (tryFunc(value)) {
return true;
}
// The install.js library supports a notion of aliases, represented by
// module identifier strings. This functionality works the same way in
// both the meta graph and the real graph.
if (typeof value === "string") {
meta[name] = value;
real[name] = value;
return true;
}
}
var requireReal = install();
function makeMetaFunc(value, dynamic, options) {
return function (require, exports, module) {
Object.assign(exports, value);
if (! dynamic &&
module.id.endsWith("/package.json")) {
// If the package.json file is not dynamic, require it from the real
// graph to read its "main" and "browser" properties.
var pkg = requireReal(module.id);
if (typeof pkg.main === "string") {
exports.main = pkg.main;
}
if (typeof pkg.browser === "string") {
exports.browser = pkg.browser;
}
}
exports.module = module;
exports.dynamic = !! dynamic;
exports.options = options;
// One of the purposes of the meta graph is to support traversing
// module dependencies without evaluating any actual module code.
// The eachChild function is essential to that traversal.
exports.eachChild = function (callback, idsToRequire) {
// By default, this function requires all value.deps dependencies
// before iterating over the resulting children, but the caller can
// provide a custom array of modules to require instead.
idsToRequire = idsToRequire || (value && value.deps);
if (Array.isArray(idsToRequire)) {
idsToRequire.forEach(require);
}
// After requiring any/all dependencies of this module, iterate over
// the children according to module.childrenById. Note that this
// includes all children ever imported by this module, including
// implicit modules such as package.json files.
Object.keys(module.childrenById).forEach(function (id) {
callback(module.childrenById[id]);
});
};
};
}

View File

@@ -1,13 +1,13 @@
Package.describe({
name: "modules-runtime",
version: "0.8.0-beta.14",
version: "0.8.0-rc.1",
summary: "CommonJS module system",
git: "https://github.com/benjamn/install",
documentation: "README.md"
});
Npm.depends({
install: "0.8.8"
install: "0.10.1"
});
Package.onUse(function(api) {
@@ -21,7 +21,6 @@ Package.onUse(function(api) {
api.addFiles("options.js");
api.addFiles("client.js", "client");
api.addFiles("server.js", "server");
api.addFiles("meteor-install.js");
api.export("meteorInstall");
});

View File

@@ -32,8 +32,8 @@ makeInstallerOptions.fallback.resolve = function (id, parentId, error) {
throw error;
};
install = makeInstaller(makeInstallerOptions);
var Module = install.Module;
meteorInstall = makeInstaller(makeInstallerOptions);
var Module = meteorInstall.Module;
Module.prototype.useNode = function () {
if (typeof npmRequire !== "function") {

View File

@@ -1,24 +1,34 @@
{
"dependencies": {
"acorn": {
"version": "4.0.11",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.11.tgz",
"from": "acorn@>=4.0.5 <4.1.0"
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.0.3.tgz",
"from": "acorn@>=5.0.0 <5.1.0"
},
"magic-string": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.19.0.tgz",
"from": "magic-string@>=0.19.0 <0.20.0"
"minipass": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.0.1.tgz",
"from": "minipass@>=2.0.0 <3.0.0"
},
"minizlib": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.0.3.tgz",
"from": "minizlib@>=1.0.3 <2.0.0"
},
"reify": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/reify/-/reify-0.6.6.tgz",
"from": "reify@0.6.6"
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/reify/-/reify-0.11.0.tgz",
"from": "reify@0.11.0"
},
"vlq": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.1.tgz",
"from": "vlq@>=0.2.1 <0.3.0"
"semver": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
"from": "semver@>=5.3.0 <6.0.0"
},
"yallist": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
"from": "yallist@>=3.0.0 <4.0.0"
}
}
}

View File

@@ -1,3 +0,0 @@
try {
Buffer = global.Buffer || require("buffer").Buffer;
} catch (noBuffer) {}

View File

@@ -1,6 +1,5 @@
require("./install-packages.js");
require("./stubs.js");
require("./buffer.js");
require("./process.js");
require("./reify.js");

View File

@@ -1,12 +1,12 @@
Package.describe({
name: "modules",
version: "0.9.0-beta.14",
version: "0.9.0-rc.1",
summary: "CommonJS module system",
documentation: "README.md"
});
Npm.depends({
reify: "0.6.6"
reify: "0.11.0"
});
Package.onUse(function(api) {
@@ -15,6 +15,5 @@ Package.onUse(function(api) {
api.mainModule("client.js", "client");
api.mainModule("server.js", "server");
api.export("meteorInstall");
api.export("Buffer");
api.export("process");
});

View File

@@ -1,5 +1,5 @@
var Module = module.constructor;
require("reify/lib/runtime").enable(Module);
var Mp = Module.prototype;
require("reify/lib/runtime").enable(Mp);
Mp.importSync = Mp.importSync || Mp.import;
Mp.import = Mp.import || Mp.importSync;

View File

@@ -1,4 +1,3 @@
require("./install-packages.js");
require("./buffer.js");
require("./process.js");
require("./reify.js");

View File

@@ -558,7 +558,7 @@ Mongo.Collection.prototype.update = function update(selector, modifier, ...optio
if (!(typeof options.insertedId === 'string' || options.insertedId instanceof Mongo.ObjectID))
throw new Error("insertedId must be string or ObjectID");
insertedId = options.insertedId;
} else if (! selector._id) {
} else if (!selector || !selector._id) {
insertedId = this._makeNewID();
options.insertedId = insertedId;
}

View File

@@ -1301,6 +1301,29 @@ testAsyncMulti('mongo-livedata - upsert without callback, ' + idGeneration, [
}
]);
// Regression test for https://github.com/meteor/meteor/issues/8666.
testAsyncMulti('mongo-livedata - upsert with an undefined selector, ' + idGeneration, [
function (test, expect) {
this.collectionName = Random.id();
if (Meteor.isClient) {
Meteor.call('createInsecureCollection', this.collectionName);
Meteor.subscribe('c-' + this.collectionName, expect());
}
}, function (test, expect) {
var coll = new Mongo.Collection(this.collectionName, collectionOptions);
var testWidget = {
name: 'Widget name'
};
coll.upsert(testWidget._id, testWidget, expect(function (error, insertDetails) {
test.isFalse(error);
test.equal(
coll.findOne(insertDetails.insertedId),
Object.assign({ _id: insertDetails.insertedId }, testWidget)
);
}));
}
]);
// See https://github.com/meteor/meteor/issues/594.
testAsyncMulti('mongo-livedata - document with length, ' + idGeneration, [
function (test, expect) {

View File

@@ -9,7 +9,7 @@
Package.describe({
summary: "Adaptor for using MongoDB and Minimongo over DDP",
version: '1.1.16'
version: '1.1.18-rc.1'
});
Npm.depends({

View File

@@ -0,0 +1 @@
node_modules

View File

@@ -0,0 +1,7 @@
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.

View File

@@ -0,0 +1,189 @@
{
"dependencies": {
"commander": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
"from": "commander@>=2.0.0 <3.0.0"
},
"d3": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/d3/-/d3-4.8.0.tgz",
"from": "d3@4.8.0"
},
"d3-array": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.0.tgz",
"from": "d3-array@1.2.0"
},
"d3-axis": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.6.tgz",
"from": "d3-axis@1.0.6"
},
"d3-brush": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.4.tgz",
"from": "d3-brush@1.0.4"
},
"d3-chord": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.4.tgz",
"from": "d3-chord@1.0.4"
},
"d3-collection": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.3.tgz",
"from": "d3-collection@1.0.3"
},
"d3-color": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz",
"from": "d3-color@1.0.3"
},
"d3-dispatch": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz",
"from": "d3-dispatch@1.0.3"
},
"d3-drag": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.0.4.tgz",
"from": "d3-drag@1.0.4"
},
"d3-dsv": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.5.tgz",
"from": "d3-dsv@1.0.5"
},
"d3-ease": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz",
"from": "d3-ease@1.0.3"
},
"d3-force": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.0.6.tgz",
"from": "d3-force@1.0.6"
},
"d3-format": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.2.0.tgz",
"from": "d3-format@1.2.0"
},
"d3-geo": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.6.3.tgz",
"from": "d3-geo@1.6.3"
},
"d3-hierarchy": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.4.tgz",
"from": "d3-hierarchy@1.1.4"
},
"d3-interpolate": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.1.4.tgz",
"from": "d3-interpolate@1.1.4"
},
"d3-path": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz",
"from": "d3-path@1.0.5"
},
"d3-polygon": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.3.tgz",
"from": "d3-polygon@1.0.3"
},
"d3-quadtree": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.3.tgz",
"from": "d3-quadtree@1.0.3"
},
"d3-queue": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/d3-queue/-/d3-queue-3.0.5.tgz",
"from": "d3-queue@3.0.5"
},
"d3-random": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.0.3.tgz",
"from": "d3-random@1.0.3"
},
"d3-request": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/d3-request/-/d3-request-1.0.5.tgz",
"from": "d3-request@1.0.5"
},
"d3-scale": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.5.tgz",
"from": "d3-scale@1.0.5"
},
"d3-selection": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.0.5.tgz",
"from": "d3-selection@1.0.5"
},
"d3-shape": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.0.6.tgz",
"from": "d3-shape@1.0.6"
},
"d3-time": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.6.tgz",
"from": "d3-time@1.0.6"
},
"d3-time-format": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.0.5.tgz",
"from": "d3-time-format@2.0.5"
},
"d3-timer": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.5.tgz",
"from": "d3-timer@1.0.5"
},
"d3-transition": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.0.4.tgz",
"from": "d3-transition@1.0.4"
},
"d3-voronoi": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz",
"from": "d3-voronoi@1.1.2"
},
"d3-zoom": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.1.4.tgz",
"from": "d3-zoom@1.1.4"
},
"graceful-readlink": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
"from": "graceful-readlink@>=1.0.0"
},
"iconv-lite": {
"version": "0.4.17",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.17.tgz",
"from": "iconv-lite@>=0.4.0 <0.5.0"
},
"pretty-bytes": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz",
"from": "pretty-bytes@4.0.2"
},
"rw": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
"from": "rw@>=1.0.0 <2.0.0"
},
"xmlhttprequest": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
"from": "xmlhttprequest@>=1.0.0 <2.0.0"
}
}
}

View File

@@ -0,0 +1 @@
# bundle-visualizer

View File

@@ -0,0 +1,3 @@
import { prefixedClass } from "./common.js";
export const rootContainer = prefixedClass("rootContainer");
export const mask = prefixedClass("mask");

View File

@@ -0,0 +1,46 @@
import { Meteor } from "meteor/meteor";
import {
classPrefix,
methodNameStats,
packageName,
} from "./common.js";
import * as classes from "./classNames.js";
import "./style.css";
Meteor.startup(() => {
import("./sunburst.js").then(s => main(s.Sunburst));
});
function main(builder) {
const { container, mask } = frameStage();
document.body.appendChild(mask);
document.body.appendChild(container);
Meteor.call(methodNameStats, (error, result) => {
if (error) {
console.error([
`${packageName}: Couldn't load stats for visualization.`,
"Are you using standard-minifier-js >= 2.1.0 as the minifier?",
].join(" "));
return;
}
// Load the JSON, which is `d3-hierarchy` digestible.
if (result) {
new builder({ container }).loadJson(result);
}
});
}
function frameStage() {
// Create the mask which will block out the main application.
const mask = document.createElement("div");
mask.setAttribute("class", `${classPrefix} ${classes.mask}`);
// Create the container which the SVG elements will be drawn into.
const container = document.createElement("div");
container.setAttribute("class", `${classPrefix} ${classes.rootContainer}`);
return { container, mask };
}

View File

@@ -0,0 +1,14 @@
export const packageName = "bundle-visualizer";
export const classPrefix = "meteorBundleVisualizer";
export const methodNameStats = `_meteor/${packageName}/stats`;
export const typeBundle = "bundle";
export const typePackage = "package";
export const typeNodeModules = "node_modules";
function capitalizeFirstLetter(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
export function prefixedClass(className) {
return `${classPrefix}${capitalizeFirstLetter(className)}`;
}

View File

@@ -0,0 +1,23 @@
Package.describe({
version: '1.0.0',
summary: 'Meteor bundle analysis and visualization.',
documentation: 'README.md',
});
Npm.depends({
"d3-selection": "1.0.5",
"d3-shape": "1.0.6",
"d3-hierarchy": "1.1.4",
"d3-transition": "1.0.4",
"pretty-bytes": "4.0.2",
});
Package.onUse(function(api) {
api.use('isobuild:dynamic-import@1.5.0');
api.use([
'ecmascript',
'dynamic-import',
]);
api.mainModule('server.js', 'server');
api.mainModule('client.js', 'client');
});

View File

@@ -0,0 +1,144 @@
import assert from "assert";
import { readFileSync as fsReadFileSync } from "fs";
import { Meteor } from "meteor/meteor";
import { WebAppInternals } from "meteor/webapp";
import {
methodNameStats,
packageName,
typeBundle,
typeNodeModules,
typePackage,
} from "./common.js";
if (Meteor.isProduction) {
console.warn([
`=> The "${packageName}" package is currently enabled. Visit your`,
"application in a web browser to view the client bundle analysis and",
"'meteor remove' the package before building/deploying the final bundle.",
].join(" "));
} else {
console.warn([
"=> In order to provide accurate measurements using minified bundles,",
`the "${packageName}" package requires running 'meteor --production'`,
"to simulate production bundling."
].join(" "));
}
function getStatBundles() {
const statFileFilter = f =>
f.type === "json" &&
f.absolutePath &&
f.absolutePath.endsWith(".stats.json");
// Read the stat file, but if it's in any way unusable just return null.
const readOrNull = file => {
try {
return JSON.parse(fsReadFileSync(file, "utf8"));
} catch (err) {
return null;
}
};
return Object.keys(WebAppInternals.staticFiles)
.map(staticFile => WebAppInternals.staticFiles[staticFile])
.filter(statFileFilter)
.map(statFile => ({
name: statFile.hash,
stats: readOrNull(statFile.absolutePath),
}));
}
function _childModules(node) {
return Object.keys(node)
.map(module => {
const result = {
name: module,
type: typeNodeModules,
};
if (typeof node[module] === "object") {
result.children = _childModules(node[module]);
} else {
result.size = node[module];
}
return result;
});
}
function d3TreeFromStats(stats) {
assert.strictEqual(typeof stats, "object",
"Must pass a stats object");
assert.strictEqual(typeof stats.minifiedBytesByPackage, "object",
"Stats object must contain a `minifiedBytesByPackage` object");
const sizeOrDetail = (name, node) => {
const result = {
name,
type: typePackage,
};
// A non-leaf is: [size (Number), limb (Object)]
// A leaf is size (Number)
if (Array.isArray(node)) {
const [, detail] = node;
result.children = _childModules(detail);
} else {
result.size = node;
}
return result;
};
// Main entry into the stats is the `minifiedBytesByPackage` attribute.
return Object.keys(stats.minifiedBytesByPackage)
.map(name =>
sizeOrDetail(name
// Change the "packages/bundle.js" name to "(bundle)"
.replace(/^[^\/]+\/(.*)\.js$/, "($1)"),
stats.minifiedBytesByPackage[name]));
}
Meteor.methods({
[methodNameStats]() {
const statBundles = getStatBundles();
// Silently return no data if not simulating production.
if (! Meteor.isProduction) {
return null;
}
if (! (statBundles && statBundles.length)) {
throw new Meteor.Error("no-stats-bundles", "Unable to retrieve stats");
}
const validStatBundles = statBundles.filter(statBundle => {
if (statBundle &&
statBundle.stats &&
statBundle.stats.minifier &&
statBundle.stats.minifier.name === "standard-minifier-js" &&
statBundle.stats.minifier.version.startsWith("2.1.")
) {
return true;
}
});
if (! validStatBundles.length) {
throw new Meteor.Error("no-valid-stats", "No valid stats bundles")
}
return {
name: "main",
children: validStatBundles.map((statBundle, index, array) => ({
// TODO: If multiple bundles, could
// show abbr. bundle names with:
// `...${bundle.name.substr(-3)}`,
name: "bundle" + (array.length > 1 ? ` (${index + 1})` : ""),
type: typeBundle,
children: d3TreeFromStats(statBundle.stats),
})),
};
}
});

View File

@@ -0,0 +1,101 @@
.meteorBundleVisualizer.meteorBundleVisualizerMask {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: #cdcdcd;
opacity: 0.5;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer {
position: absolute;
top: 0;
font-family: 'Open Sans', sans-serif;
font-size: 12px;
font-weight: 400;
width: 960px;
height: 700px;
margin-top: 10px;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerMain
{
float: left;
width: 950px;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerSequence
{
width: 800px;
height: 70px;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerSequence text,
{
font-weight: 600;
fill: #fff;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerChart
{
position: relative;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerChart path
{
stroke: #fff;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerExplanation
{
position: absolute;
top: 260px;
left: 405px;
width: 140px;
text-align: center;
color: #000;
z-index: -1;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerPercentage
{
font-size: 2.5em;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerBytes
{
font-size: 2em;
font-weight: bold;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerTrail
{
position: absolute;
font-family: 'Open Sans', sans-serif;
font-size: 12px;
left: 0;
min-width: 180px;
font-weight: bold;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerTrail .meteorBundleVisualizerTrailSegment
{
height: 40px;
border-radius: 3px;
border: 2px dotted black;
text-align: center;
vertical-align: middle;
line-height: 40px;
margin-bottom: 5px;
}

View File

@@ -0,0 +1,275 @@
/**
Inspired-by, borrowed-from and improved-upon another sundial provided under
the Apache License:
https://bl.ocks.org/kerryrodden/766f8f6d31f645c39f488a0befa1e3c8
Copyright 2013 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import assert from "assert";
import prettyBytes from "pretty-bytes";
// Make a custom "d3" object containing exactly what we need from the
// modularized d3 bundles.
const d3 = Object.assign({},
{ selectAll, select, mouse } = require("d3-selection"),
{ arc } = require("d3-shape"),
{ hierarchy, partition } = require("d3-hierarchy"),
{ keys, entries } = require("d3-collection"),
);
// This is imported only for its side effects, which affect the d3 namespace.
import "d3-transition";
import {
typeBundle,
typePackage,
typeNodeModules,
prefixedClass,
} from "./common.js";
import * as classes from "./classNames.js";
// Dimensions of sunburst.
const width = 950;
const height = 600;
const radius = Math.min(width, height) / 2;
// Mapping of step names to colors.
const DEFAULT_COLORS = {
"_default_": "#ababab",
[typeBundle]: "#de4f4f",
[typePackage]: "#de783b",
[typeNodeModules]: "#7b615c",
"meteor": "#6ab975",
"javascript": "#a173d1",
};
export class Sunburst {
constructor({
container,
colors = DEFAULT_COLORS,
} = {}) {
this.elements = {};
this.colors = colors;
this.totalSize = 0;
assert.strictEqual(typeof container, "object",
"Must pass a 'container' element");
this.elements.container = d3.select(container);
this.elements.main =
this.elements.container
.append("div")
.attr("class", prefixedClass("main"));
this.elements.sequence =
this.elements.main
.append("div")
.attr("class", prefixedClass("sequence"));
this.elements.chart =
this.elements.main
.append("div")
.attr("class", prefixedClass("chart"));
this.elements.explanation =
this.elements.chart
.append("div")
.attr("class", prefixedClass("explanation"));
this.elements.percentage =
this.elements.explanation
.append("span")
.attr("class", prefixedClass("percentage"));
// BR between percentage and bytes.
this.elements.explanation.append("br");
this.elements.bytes =
this.elements.explanation
.append("span")
.attr("class", prefixedClass("bytes"));
this.svg = this.elements.chart
.append("svg:svg")
.attr("width", width)
.attr("height", height);
this.vis = this.svg
.append("svg:g")
.attr("class", prefixedClass("top"))
.attr("transform", `translate(${width / 2},${height / 2})`);
this.partition = d3.partition()
.size([2 * Math.PI, radius * radius]);
this.arc = d3.arc()
.startAngle(d => d.x0)
.endAngle(d => d.x1)
.innerRadius(d => Math.sqrt(d.y0))
.outerRadius(d => Math.sqrt(d.y1));
}
getColor(data) {
if (data.type === typePackage) {
return this.colors[typePackage];
}
if (data.name.endsWith(".js")) {
return this.colors.javascript;
}
if (this.colors[data.name]) {
return this.colors[data.name];
}
return this.colors._default_;
}
initializeBreadcrumbTrail() {
// Add the svg area.
this.elements.trail =
this.elements.container
.append("div")
.attr("class", prefixedClass("trail"));
}
loadJson(json) {
// Basic setup of page elements.
this.initializeBreadcrumbTrail();
// Bounding circle underneath the sunburst, to make it easier to detect
// when the mouse leaves the parent g.
this.vis
.append("svg:circle")
.attr("r", radius)
.style("opacity", 0);
// Turn the data into a d3 hierarchy and calculate the sums.
this.root = d3.hierarchy(json)
.sum(d => d.size)
.sort((a, b) => b.value - a.value);
// For efficiency, filter nodes to keep only those large enough to see.
this.nodes = this
.partition(this.root)
.descendants()
.filter(d => d.x1 - d.x0 > 0.005); // 0.005 radians = 0.29 degrees
this.path = this.vis.data([json]).selectAll("path")
.data(this.nodes)
.enter()
.append("svg:path")
.attr("display", d => d.depth ? null : "none")
.attr("d", this.arc)
.attr("fill-rule", "evenodd")
.style("fill", d => this.getColor(d.data))
.style("opacity", 1)
.on("mouseover", this.mouseoverEvent());
// Add the mouseleave handler to the bounding circle.
this.vis.on("mouseleave", this.mouseleaveEvent());
// // Get total size of the tree = value of root node from partition.
this.totalSize = this.path.datum().value;
}
mouseoverEvent() {
const self = this;
return self.mouseover || (self.mouseover = function (d) {
const percentage = (100 * d.value / self.totalSize).toPrecision(3);
let percentageString = `${percentage}%`;
if (percentage < 0.1) {
percentageString = "< 0.1%";
}
self.elements.percentage
.text(percentageString);
self.elements.bytes
.text(prettyBytes(d.value || 0));
self.elements.explanation
.style("display", null);
const sequenceArray = d.ancestors().reverse();
sequenceArray.shift(); // remove root node from the array
self.updateBreadcrumbs(sequenceArray, percentageString);
// Fade all the segments.
d3.selectAll("path")
.style("opacity", 0.3);
// Then highlight only those that are an ancestor of the current segment.
self.vis.selectAll("path")
.filter((node) => sequenceArray.indexOf(node) >= 0)
.style("opacity", 1);
});
}
// Restore everything to full opacity when moving off the visualization.
mouseleaveEvent() {
const self = this;
return self.mouseleave || (self.mouseleave = function (d) {
// Hide the breadcrumb trail
self.elements.trail
.style("visibility", "hidden");
// Deactivate all segments during transition.
d3.selectAll("path").on("mouseover", null);
// Transition each segment to full opacity and then reactivate it.
d3.selectAll("path")
.transition()
.duration(1000)
.style("opacity", 1)
.on("end", function() {
d3.select(this).on("mouseover", self.mouseoverEvent());
});
self.elements.explanation
.style("display", "none");
});
}
// Update the breadcrumb trail to show the current sequence and percentage.
updateBreadcrumbs(nodeArray, percentageString) {
// Data join; key function combines name and depth (= position in sequence).
const trail = this.elements.trail
.selectAll("div")
.data(nodeArray, d => d.data.name + d.depth);
// Remove exiting nodes.
trail.exit().remove();
// Add breadcrumb and label for entering nodes.
const entering = trail.enter()
.append("div")
.attr("class", prefixedClass("trailSegment"))
.style("background-color", d => this.getColor(d.data))
.text(d => d.data.name);
// Merge enter and update selections; set position for all nodes.
entering
.merge(trail);
// Make the breadcrumb trail visible, if it's hidden.
this.elements.trail
.style("visibility", "");
}
}

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Reactive dictionary",
version: '1.1.8'
version: '1.1.9-rc.1'
});
Package.onUse(function (api) {

View File

@@ -6,6 +6,16 @@ Tinytest.add('ReactiveDict - set to undefined', function (test) {
test.equal(dict.get('foo'), undefined);
});
Tinytest.add('ReactiveDict - initialize with data', function (test) {
var now = new Date();
var dict = new ReactiveDict({
now: now
});
var nowFromDict = dict.get('now');
test.equal(nowFromDict, now);
});
Tinytest.add('ReactiveDict - setDefault', function (test) {
var dict = new ReactiveDict;
dict.set('A', 'blah');

View File

@@ -27,7 +27,10 @@ ReactiveDict = function (dictName) {
this.name = dictName;
} else if (typeof dictName === 'object') {
// back-compat case: dictName is actually migrationData
this.keys = dictName;
this.keys = {};
for (let [key, value] of Object.entries(dictName)) {
this.keys[key] = stringify(value);
}
} else {
throw new Error("Invalid ReactiveDict argument: " + dictName);
}

View File

@@ -1,6 +1,6 @@
Package.describe({
name: 'standard-minifier-js',
version: '2.0.0',
version: '2.1.0-rc.1',
summary: 'Standard javascript minifiers used with Meteor apps by default.',
documentation: 'README.md',
});
@@ -9,9 +9,14 @@ Package.registerBuildPlugin({
name: "minifyStdJS",
use: [
'minifier-js',
'babel-compiler',
'ecmascript'
],
sources: [
'plugin/minify-js.js',
'plugin/stats.js',
'plugin/visitor.js',
'plugin/utils.js',
],
});

View File

@@ -1,3 +1,5 @@
import { extractModuleSizesTree } from "./stats.js";
Plugin.registerMinifier({
extensions: ['js'],
archMatching: 'web'
@@ -108,37 +110,52 @@ MeteorBabelMinifier.prototype.processFilesForBundle = function(files, options) {
}
}
var allJs = '';
files.forEach(function (file) {
// Don't reminify *.min.js.
if (/\.min\.js$/.test(file.getPathInBundle())) {
allJs += file.getContentsAsString();
} else {
var minified;
const toBeAdded = {
data: "",
stats: Object.create(null)
};
try {
minified = meteorJsMinify(file.getContentsAsString());
files.forEach(file => {
// Don't reminify *.min.js.
if (/\.min\.js$/.test(file.getPathInBundle())) {
toBeAdded.data += file.getContentsAsString();
} else {
var minified;
if (!(minified && typeof minified.code === "string")) {
throw new Error();
}
} catch (err) {
var filePath = file.getPathInBundle();
try {
minified = meteorJsMinify(file.getContentsAsString());
maybeThrowMinifyErrorBySourceFile(err, file);
err.message += " while minifying " + filePath;
throw err;
if (!(minified && typeof minified.code === "string")) {
throw new Error();
}
allJs += minified.code;
}
allJs += '\n\n';
} catch (err) {
var filePath = file.getPathInBundle();
Plugin.nudge();
});
maybeThrowMinifyErrorBySourceFile(err, file);
err.message += " while minifying " + filePath;
throw err;
}
const tree = extractModuleSizesTree(minified.code);
if (tree) {
toBeAdded.stats[file.getPathInBundle()] =
[Buffer.byteLength(minified.code), tree];
} else {
toBeAdded.stats[file.getPathInBundle()] =
Buffer.byteLength(minified.code);
}
toBeAdded.data += minified.code;
}
toBeAdded.data += '\n\n';
Plugin.nudge();
});
if (files.length) {
files[0].addJavaScript({ data: allJs });
files[0].addJavaScript(toBeAdded);
}
};

View File

@@ -0,0 +1,83 @@
import Visitor from "./visitor.js";
// This RegExp will be used to scan the source for calls to meteorInstall,
// taking into consideration that the function name may have been mangled
// to something other than "meteorInstall" by the minifier.
const meteorInstallRegExp = new RegExp([
// If meteorInstall is called by its unminified name, then that's what
// we should be looking for in the AST.
/\b(meteorInstall)\(\{/,
// If the meteorInstall function name has been minified, we can figure
// out its mangled name by examining the import assingment.
/\b(\w+)=Package.modules.meteorInstall\b/,
/\b(\w+)=Package\["modules-runtime"\].meteorInstall\b/,
].map(exp => exp.source).join("|"));
export function extractModuleSizesTree(source) {
const match = meteorInstallRegExp.exec(source);
if (match) {
const ast = Babel.parse(source);
const name = match[1] || match[2] || match[3];
meteorInstallVisitor.visit(ast, name, source);
return meteorInstallVisitor.tree;
}
}
const meteorInstallVisitor = new (class extends Visitor {
reset(root, meteorInstallName, source) {
this.name = meteorInstallName;
this.source = source;
this.tree = null;
}
visitCallExpression(node) {
if (this.tree !== null) {
return;
}
if (isIdWithName(node.callee, this.name)) {
const source = this.source;
function walk(expr) {
if (expr.type !== "ObjectExpression") {
return Buffer.byteLength(source.slice(expr.start, expr.end));
}
const contents = Object.create(null);
expr.properties.forEach(prop => {
const keyName = getKeyName(prop.key);
if (typeof keyName === "string") {
contents[keyName] = walk(prop.value);
}
});
return contents;
}
this.tree = walk(node.arguments[0]);
} else {
this.visitChildren(node);
}
}
});
function isIdWithName(node, name) {
return node &&
node.type === "Identifier" &&
node.name === name;
}
function getKeyName(key) {
if (key.type === "Identifier") {
return key.name;
}
if (key.type === "StringLiteral" ||
key.type === "Literal") {
return key.value;
}
return null;
}

View File

@@ -0,0 +1,26 @@
"use strict";
const codeOfA = "A".charCodeAt(0);
const codeOfZ = "Z".charCodeAt(0);
export function isObject(value) {
return typeof value === "object" && value !== null;
}
// Without a complete list of Node .type names, we have to settle for this
// fuzzy matching of object shapes. However, the infeasibility of
// maintaining a complete list of type names is one of the reasons we're
// using the FastPath/Visitor abstraction in the first place.
export function isNodeLike(value) {
return isObject(value) &&
! Array.isArray(value) &&
isCapitalized(value.type);
}
function isCapitalized(string) {
if (typeof string !== "string") {
return false;
}
const code = string.charCodeAt(0);
return code >= codeOfA && code <= codeOfZ;
}

View File

@@ -0,0 +1,57 @@
"use strict";
import {
isObject,
isNodeLike,
} from "./utils.js";
const codeOfUnderscore = "_".charCodeAt(0);
export default class Visitor {
visit(root) {
this.reset.apply(this, arguments);
this.visitWithoutReset(root);
}
visitWithoutReset(node) {
if (Array.isArray(node)) {
node.forEach(this.visitWithoutReset, this);
} else if (isNodeLike(node)) {
const method = this["visit" + node.type];
if (typeof method === "function") {
// The method must call this.visitChildren(node) to continue
// traversing.
method.call(this, node);
} else {
this.visitChildren(node);
}
}
}
visitChildren(node) {
if (! isNodeLike(node)) {
return;
}
const keys = Object.keys(node);
const keyCount = keys.length;
for (let i = 0; i < keyCount; ++i) {
const key = keys[i];
if (key === "loc" || // Ignore .loc.{start,end} objects.
// Ignore "private" properties added by Babel.
key.charCodeAt(0) === codeOfUnderscore) {
continue;
}
const child = node[key];
if (! isObject(child)) {
// Ignore properties whose values aren't objects.
continue;
}
this.visitWithoutReset(child);
}
}
}

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Dependency tracker to allow reactive callbacks",
version: '1.1.2'
version: '1.1.3'
});
Package.onUse(function (api) {

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Serves a Meteor app over HTTP",
version: '1.3.15'
version: '1.3.16-rc.1'
});
Npm.depends({connect: "2.30.2",

Some files were not shown because too many files have changed in this diff Show More