Merge branch 'devel' into release-1.5

This commit is contained in:
Ben Newman
2017-04-20 16:46:03 -04:00
37 changed files with 470 additions and 451 deletions

View File

@@ -9,6 +9,10 @@
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.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).
## 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

@@ -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.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.0.tgz",
"from": "jsesc@>=2.4.0 <3.0.0"
}
}
@@ -235,18 +235,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 +265,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.7.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-reify/-/babel-plugin-transform-es2015-modules-reify-0.7.0.tgz",
"from": "babel-plugin-transform-es2015-modules-reify@>=0.7.0 <0.8.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 +295,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 +310,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 +360,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 +375,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 +410,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 +430,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,18 +450,18 @@
"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": {
@@ -475,8 +475,8 @@
"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",
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz",
"from": "brace-expansion@>=1.0.0 <2.0.0"
},
"chalk": {
@@ -490,8 +490,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": {
@@ -579,15 +579,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.20.1",
"resolved": "https://registry.npmjs.org/meteor-babel/-/meteor-babel-0.20.1.tgz",
"from": "meteor-babel@0.20.1"
},
"meteor-babel-helpers": {
"version": "0.0.3",
@@ -650,9 +645,9 @@
"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,9 +672,9 @@
}
},
"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.7.4",
"resolved": "https://registry.npmjs.org/reify/-/reify-0.7.4.tgz",
"from": "reify@>=0.7.2 <0.8.0"
},
"repeating": {
"version": "2.0.1",
@@ -720,11 +715,6 @@
"version": "1.0.1",
"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"
}
}
}

View File

@@ -10,7 +10,7 @@ Package.describe({
});
Npm.depends({
'meteor-babel': '0.19.1'
'meteor-babel': '0.20.1'
});
Package.onUse(function (api) {

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,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

@@ -40,7 +40,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;

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;

View File

@@ -185,20 +185,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 +213,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 +229,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,12 +241,13 @@ 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;
}
@@ -267,7 +274,8 @@ 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
@@ -278,16 +286,17 @@ 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 });
// 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 +304,11 @@ 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");
throw MinimongoError(
"$slice in $push must be zero or negative", { field });
slice = arg.$slice;
}
@@ -306,7 +316,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 +325,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 });
}
}
}
@@ -350,7 +360,8 @@ var MODIFIERS = {
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]);
@@ -371,7 +382,8 @@ var MODIFIERS = {
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 +400,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 +416,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 +444,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 +473,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 +493,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.0.22'
});
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

@@ -1,24 +1,14 @@
{
"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"
},
"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"
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.0.3.tgz",
"from": "acorn@>=5.0.0 <5.1.0"
},
"reify": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/reify/-/reify-0.6.6.tgz",
"from": "reify@0.6.6"
},
"vlq": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.1.tgz",
"from": "vlq@>=0.2.1 <0.3.0"
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/reify/-/reify-0.7.4.tgz",
"from": "reify@0.7.4"
}
}
}

View File

@@ -6,7 +6,7 @@ Package.describe({
});
Npm.depends({
reify: "0.6.6"
reify: "0.7.4"
});
Package.onUse(function(api) {

View File

@@ -14,7 +14,7 @@ var packageJson = {
npm: "4.4.4",
"node-gyp": "3.6.0",
"node-pre-gyp": "0.6.34",
"meteor-babel": "0.19.1",
"meteor-babel": "0.20.1",
"meteor-promise": "0.8.0",
fibers: "1.0.15",
promise: "7.1.1",

View File

@@ -353,7 +353,7 @@ export class NodeModulesDirectory {
// Options consumed by readDirsFromJSON are listed above. Any other
// options will be passed on to NodeModulesDirectory constructor via
// this callerInfo object:
...callerInfo,
...callerInfo
}) {
assert.strictEqual(typeof callerInfo.sourceRoot, "string");