Merge branch 'release-1.5.2' into release-1.6

This commit is contained in:
Ben Newman
2017-07-21 19:52:47 -04:00
30 changed files with 386 additions and 331 deletions

View File

@@ -45,6 +45,11 @@
* The `meteor-promise` package has been upgraded to version 0.8.5,
and the `promise` polyfill package has been upgraded to 8.0.1.
* `Accounts.config` no longer mistakenly allows tokens to expire when
the `loginExpirationInDays` option is set to `null`.
[Issue #5121](https://github.com/meteor/meteor/issues/5121)
[PR #8917](https://github.com/meteor/meteor/pull/8917)
## v1.5.1, 2017-07-12
* Node has been upgraded to version 4.8.4.

View File

@@ -213,8 +213,14 @@ export class AccountsCommon {
}
}
_getTokenLifetimeMs() {
return (this._options.loginExpirationInDays ||
// The options argument is only used by tests.
_getTokenLifetimeMs(options) {
options = options || this._options;
if (options.loginExpirationInDays === null) {
// We disable login expiration by returning Infinity
return Infinity;
}
return (options.loginExpirationInDays ||
DEFAULT_LOGIN_EXPIRATION_DAYS) * 24 * 60 * 60 * 1000;
}

View File

@@ -13,6 +13,24 @@ Tinytest.add('accounts - config validates keys', function (test) {
});
});
// test the loginExpirationInDays config
Tinytest.add( 'accounts - config - token limetime', function (test) {
var config = { loginExpirationInDays: 2 };
test.equal(Accounts._getTokenLifetimeMs(config), 2 * 24 * 60 * 60 * 1000);
});
Tinytest.add( 'accounts - config - unexpiring tokens', function (test) {
var config = { loginExpirationInDays: null };
test.equal(Accounts._getTokenLifetimeMs(config), Infinity);
});
Tinytest.add( 'accounts - config - default token limetime', function(test) {
var DEFAULT_LOGIN_EXPIRATION_DAYS = 90; // copied from accounts_common.js
var config1 = {};
var config2 = { loginExpirationInDays: DEFAULT_LOGIN_EXPIRATION_DAYS };
test.equal(Accounts._getTokenLifetimeMs(config1), Accounts._getTokenLifetimeMs(config2));
});
var idsInValidateNewUser = {};
Accounts.validateNewUser(function (user) {

View File

@@ -546,17 +546,17 @@ Meteor.methods({forgotPassword: function (options) {
Accounts.sendResetPasswordEmail(user._id, caseSensitiveEmail);
}});
// send the user an email with a link that when opened allows the user
// to set a new password, without the old password.
/**
* @summary Send an email with a link the user can use to reset their password.
* @summary Generates a reset token and saves it into the database.
* @locus Server
* @param {String} userId The id of the user to send email to.
* @param {String} [email] Optional. Which address of the user's to send the email to. This address must be in the user's `emails` list. Defaults to the first email in the list.
* @param {String} userId The id of the user to generate the reset token for.
* @param {String} email Which address of the user to generate the reset token for. This address must be in the user's `emails` list. If `null`, defaults to the first email in the list.
* @param {String} reason `resetPassword` or `enrollAccount`.
* @param {Object} [extraTokenData] Optional additional data to be added into the token record.
* @returns {Object} Object with {email, user, token} values.
* @importFromPackage accounts-base
*/
Accounts.sendResetPasswordEmail = function (userId, email) {
Accounts.generateResetToken = function (userId, email, reason, extraTokenData) {
// Make sure the user exists, and email is one of their addresses.
var user = Meteor.users.findOne(userId);
if (!user) {
@@ -574,44 +574,146 @@ Accounts.sendResetPasswordEmail = function (userId, email) {
}
var token = Random.secret();
var when = new Date();
var tokenRecord = {
token: token,
email: email,
when: when,
reason: 'reset'
when: new Date()
};
Meteor.users.update(userId, {$set: {
"services.password.reset": tokenRecord
if (reason === 'resetPassword') {
tokenRecord.reason = 'reset';
} else if (reason === 'enrollAccount') {
tokenRecord.reason = 'enroll';
} else if (reason) {
// fallback so that this function can be used for unknown reasons as well
tokenRecord.reason = reason;
}
if (extraTokenData) {
_.extend(tokenRecord, extraTokenData);
}
Meteor.users.update({_id: user._id}, {$set: {
'services.password.reset': tokenRecord
}});
// before passing to template, update user object with new token
Meteor._ensure(user, 'services', 'password').reset = tokenRecord;
var resetPasswordUrl = Accounts.urls.resetPassword(token);
return {email, user, token};
};
var options = {
to: email,
from: Accounts.emailTemplates.resetPassword.from
? Accounts.emailTemplates.resetPassword.from(user)
: Accounts.emailTemplates.from,
subject: Accounts.emailTemplates.resetPassword.subject(user)
};
if (typeof Accounts.emailTemplates.resetPassword.text === 'function') {
options.text =
Accounts.emailTemplates.resetPassword.text(user, resetPasswordUrl);
/**
* @summary Generates an e-mail verification token and saves it into the database.
* @locus Server
* @param {String} userId The id of the user to generate the e-mail verification token for.
* @param {String} email Which address of the user to generate the e-mail verification token for. This address must be in the user's `emails` list. If `null`, defaults to the first unverified email in the list.
* @param {Object} [extraTokenData] Optional additional data to be added into the token record.
* @returns {Object} Object with {email, user, token} values.
* @importFromPackage accounts-base
*/
Accounts.generateVerificationToken = function (userId, email, extraTokenData) {
// Make sure the user exists, and email is one of their addresses.
var user = Meteor.users.findOne(userId);
if (!user) {
handleError("Can't find user");
}
if (typeof Accounts.emailTemplates.resetPassword.html === 'function') {
options.html =
Accounts.emailTemplates.resetPassword.html(user, resetPasswordUrl);
// pick the first unverified email if we weren't passed an email.
if (!email) {
var emailRecord = _.find(user.emails || [], function (e) { return !e.verified; });
email = (emailRecord || {}).address;
if (!email) {
handleError("That user has no unverified email addresses.");
}
}
// make sure we have a valid email
if (!email || !_.contains(_.pluck(user.emails || [], 'address'), email)) {
handleError("No such email for user.");
}
var token = Random.secret();
var tokenRecord = {
token: token,
// TODO: This should probably be renamed to "email" to match reset token record.
address: email,
when: new Date()
};
if (extraTokenData) {
_.extend(tokenRecord, extraTokenData);
}
Meteor.users.update({_id: user._id}, {$push: {
'services.email.verificationTokens': tokenRecord
}});
// before passing to template, update user object with new token
Meteor._ensure(user, 'services', 'email');
if (!user.services.email.verificationTokens) {
user.services.email.verificationTokens = [];
}
user.services.email.verificationTokens.push(tokenRecord);
return {email, user, token};
};
/**
* @summary Creates options for email sending for reset password and enroll account emails.
* You can use this function when customizing a reset password or enroll account email sending.
* @locus Server
* @param {Object} email Which address of the user's to send the email to.
* @param {Object} user The user object to generate options for.
* @param {String} url URL to which user is directed to confirm the email.
* @param {String} reason `resetPassword` or `enrollAccount`.
* @returns {Object} Options which can be passed to `Email.send`.
* @importFromPackage accounts-base
*/
Accounts.generateOptionsForEmail = function (email, user, url, reason) {
var options = {
to: email,
from: Accounts.emailTemplates[reason].from
? Accounts.emailTemplates[reason].from(user)
: Accounts.emailTemplates.from,
subject: Accounts.emailTemplates[reason].subject(user)
};
if (typeof Accounts.emailTemplates[reason].text === 'function') {
options.text = Accounts.emailTemplates[reason].text(user, url);
}
if (typeof Accounts.emailTemplates[reason].html === 'function') {
options.html = Accounts.emailTemplates[reason].html(user, url);
}
if (typeof Accounts.emailTemplates.headers === 'object') {
options.headers = Accounts.emailTemplates.headers;
}
return options;
};
// send the user an email with a link that when opened allows the user
// to set a new password, without the old password.
/**
* @summary Send an email with a link the user can use to reset their password.
* @locus Server
* @param {String} userId The id of the user to send email to.
* @param {String} [email] Optional. Which address of the user's to send the email to. This address must be in the user's `emails` list. Defaults to the first email in the list.
* @param {Object} [extraTokenData] Optional additional data to be added into the token record.
* @returns {Object} Object with {email, user, token, url, options} values.
* @importFromPackage accounts-base
*/
Accounts.sendResetPasswordEmail = function (userId, email, extraTokenData) {
const {email: realEmail, user, token} =
Accounts.generateResetToken(userId, email, 'resetPassword', extraTokenData);
const url = Accounts.urls.resetPassword(token);
const options = Accounts.generateOptionsForEmail(realEmail, user, url, 'resetPassword');
Email.send(options);
return {email: realEmail, user, token, url, options};
};
// send the user an email informing them that their account was created, with
@@ -627,65 +729,17 @@ Accounts.sendResetPasswordEmail = function (userId, email) {
* @locus Server
* @param {String} userId The id of the user to send email to.
* @param {String} [email] Optional. Which address of the user's to send the email to. This address must be in the user's `emails` list. Defaults to the first email in the list.
* @param {Object} [extraTokenData] Optional additional data to be added into the token record.
* @returns {Object} Object with {email, user, token, url, options} values.
* @importFromPackage accounts-base
*/
Accounts.sendEnrollmentEmail = function (userId, email) {
// XXX refactor! This is basically identical to sendResetPasswordEmail.
// Make sure the user exists, and email is in their addresses.
var user = Meteor.users.findOne(userId);
if (!user) {
throw new Error("Can't find user");
}
// pick the first email if we weren't passed an email.
if (!email && user.emails && user.emails[0]) {
email = user.emails[0].address;
}
// make sure we have a valid email
if (!email || !_.contains(_.pluck(user.emails || [], 'address'), email)) {
throw new Error("No such email for user.");
}
var token = Random.secret();
var when = new Date();
var tokenRecord = {
token: token,
email: email,
when: when,
reason: 'enroll'
};
Meteor.users.update(userId, {$set: {
"services.password.reset": tokenRecord
}});
// before passing to template, update user object with new token
Meteor._ensure(user, 'services', 'password').reset = tokenRecord;
var enrollAccountUrl = Accounts.urls.enrollAccount(token);
var options = {
to: email,
from: Accounts.emailTemplates.enrollAccount.from
? Accounts.emailTemplates.enrollAccount.from(user)
: Accounts.emailTemplates.from,
subject: Accounts.emailTemplates.enrollAccount.subject(user)
};
if (typeof Accounts.emailTemplates.enrollAccount.text === 'function') {
options.text =
Accounts.emailTemplates.enrollAccount.text(user, enrollAccountUrl);
}
if (typeof Accounts.emailTemplates.enrollAccount.html === 'function') {
options.html =
Accounts.emailTemplates.enrollAccount.html(user, enrollAccountUrl);
}
if (typeof Accounts.emailTemplates.headers === 'object') {
options.headers = Accounts.emailTemplates.headers;
}
Accounts.sendEnrollmentEmail = function (userId, email, extraTokenData) {
const {email: realEmail, user, token} =
Accounts.generateResetToken(userId, email, 'enrollAccount', extraTokenData);
const url = Accounts.urls.enrollAccount(token);
const options = Accounts.generateOptionsForEmail(realEmail, user, url, 'enrollAccount');
Email.send(options);
return {email: realEmail, user, token, url, options};
};
@@ -782,71 +836,21 @@ Meteor.methods({resetPassword: function (token, newPassword) {
* @locus Server
* @param {String} userId The id of the user to send email to.
* @param {String} [email] Optional. Which address of the user's to send the email to. This address must be in the user's `emails` list. Defaults to the first unverified email in the list.
* @param {Object} [extraTokenData] Optional additional data to be added into the token record.
* @returns {Object} Object with {email, user, token, url, options} values.
* @importFromPackage accounts-base
*/
Accounts.sendVerificationEmail = function (userId, address) {
Accounts.sendVerificationEmail = function (userId, email, extraTokenData) {
// XXX Also generate a link using which someone can delete this
// account if they own said address but weren't those who created
// this account.
// Make sure the user exists, and address is one of their addresses.
var user = Meteor.users.findOne(userId);
if (!user)
throw new Error("Can't find user");
// pick the first unverified address if we weren't passed an address.
if (!address) {
var email = _.find(user.emails || [],
function (e) { return !e.verified; });
address = (email || {}).address;
if (!address) {
throw new Error("That user has no unverified email addresses.");
}
}
// make sure we have a valid address
if (!address || !_.contains(_.pluck(user.emails || [], 'address'), address))
throw new Error("No such email address for user.");
var tokenRecord = {
token: Random.secret(),
address: address,
when: new Date()};
Meteor.users.update(
{_id: userId},
{$push: {'services.email.verificationTokens': tokenRecord}});
// before passing to template, update user object with new token
Meteor._ensure(user, 'services', 'email');
if (!user.services.email.verificationTokens) {
user.services.email.verificationTokens = [];
}
user.services.email.verificationTokens.push(tokenRecord);
var verifyEmailUrl = Accounts.urls.verifyEmail(tokenRecord.token);
var options = {
to: address,
from: Accounts.emailTemplates.verifyEmail.from
? Accounts.emailTemplates.verifyEmail.from(user)
: Accounts.emailTemplates.from,
subject: Accounts.emailTemplates.verifyEmail.subject(user)
};
if (typeof Accounts.emailTemplates.verifyEmail.text === 'function') {
options.text =
Accounts.emailTemplates.verifyEmail.text(user, verifyEmailUrl);
}
if (typeof Accounts.emailTemplates.verifyEmail.html === 'function')
options.html =
Accounts.emailTemplates.verifyEmail.html(user, verifyEmailUrl);
if (typeof Accounts.emailTemplates.headers === 'object') {
options.headers = Accounts.emailTemplates.headers;
}
const {email: realEmail, user, token} =
Accounts.generateVerificationToken(userId, email, extraTokenData);
const url = Accounts.urls.verifyEmail(token);
const options = Accounts.generateOptionsForEmail(realEmail, user, url, 'verifyEmail');
Email.send(options);
return {email: realEmail, user, token, url, options};
};
// Take token from sendVerificationEmail, mark the email as verified,

View File

@@ -6,5 +6,5 @@ instead of the System WebView on Android",
});
Cordova.depends({
'cordova-plugin-crosswalk-webview': '2.2.0'
'cordova-plugin-crosswalk-webview': '2.3.0'
});

View File

@@ -94,6 +94,21 @@ var builtinConverters = [
return new Date(obj.$date);
}
},
{ // RegExp
matchJSONValue: function (obj) {
return _.has(obj, '$regexp') && _.has(obj, '$flags') && _.size(obj) === 2;
},
matchObject: function (obj) {
return obj instanceof RegExp;
},
toJSONValue: function (regexp) {
return { $regexp: regexp.source, $flags: regexp.flags };
},
fromJSONValue: function (obj) {
//replaces duplicate / invalid flags
return new RegExp(obj.$regexp, obj.$flags.replace(/[^gimuy]/g,'').replace(/(.)(?=.*\1)/g, ''));
}
},
{ // NaN, Inf, -Inf. (These are the only objects with typeof !== 'object'
// which we match.)
matchJSONValue: function (obj) {

View File

@@ -185,6 +185,16 @@ Tinytest.add("ejson - parse", function (test) {
);
});
Tinytest.add("ejson - regexp", function (test) {
test.equal(EJSON.stringify(/foo/gi), "{\"$regexp\":\"foo\",\"$flags\":\"gi\"}");
var d = new RegExp("foo", "gi");
var obj = { $regexp: "foo", $flags: "gi" };
var eObj = EJSON.toJSONValue(obj);
var roundTrip = EJSON.fromJSONValue(eObj);
test.equal(obj, roundTrip);
});
Tinytest.add("ejson - custom types", function (test) {
var testSameConstructors = function (obj, compareWith) {
test.equal(obj.constructor, compareWith.constructor);

View File

@@ -10,7 +10,7 @@ Package.describe({
});
Cordova.depends({
'cordova-plugin-splashscreen': '4.0.1'
'cordova-plugin-splashscreen': '4.0.3'
});
Package.onUse(function(api) {

View File

@@ -12,7 +12,7 @@ Npm.strip({
});
Cordova.depends({
'cordova-plugin-console': '1.0.5'
'cordova-plugin-console': '1.0.7'
});
Package.onUse(function (api) {

View File

@@ -775,8 +775,7 @@ ELEMENT_OPERATORS = {
var regexp;
if (valueSelector.$options !== undefined) {
// Options passed in $options (even the empty string) always overrides
// options in the RegExp object itself. (See also
// Mongo.Collection._rewriteSelector.)
// options in the RegExp object itself.
// Be clear that we only support the JS-supported options, not extended
// ones (eg, Mongo supports x and s). Ideally we would implement x and s

View File

@@ -4,5 +4,5 @@ Package.describe({
});
Cordova.depends({
'cordova-plugin-statusbar': '2.2.1'
'cordova-plugin-statusbar': '2.2.3'
});

View File

@@ -362,8 +362,7 @@ Mongo.Collection._publishCursor = function (cursor, sub, collection) {
// protect against dangerous selectors. falsey and {_id: falsey} are both
// likely programmer error, and not what you want, particularly for destructive
// operations. JS regexps don't serialize over DDP but can be trivially
// replaced by $regex. If a falsey _id is sent in, a new string _id will be
// operations. If a falsey _id is sent in, a new string _id will be
// generated and returned; if a fallbackId is provided, it will be returned
// instead.
Mongo.Collection._rewriteSelector = (selector, { fallbackId } = {}) => {
@@ -382,48 +381,8 @@ Mongo.Collection._rewriteSelector = (selector, { fallbackId } = {}) => {
return { _id: fallbackId || Random.id() };
}
var ret = {};
Object.keys(selector).forEach((key) => {
const value = selector[key];
// Mongo supports both {field: /foo/} and {field: {$regex: /foo/}}
if (value instanceof RegExp) {
ret[key] = convertRegexpToMongoSelector(value);
} else if (value && value.$regex instanceof RegExp) {
ret[key] = convertRegexpToMongoSelector(value.$regex);
// if value is {$regex: /foo/, $options: ...} then $options
// override the ones set on $regex.
if (value.$options !== undefined)
ret[key].$options = value.$options;
} else if (_.contains(['$or','$and','$nor'], key)) {
// Translate lower levels of $and/$or/$nor
ret[key] = _.map(value, function (v) {
return Mongo.Collection._rewriteSelector(v);
});
} else {
ret[key] = value;
}
});
return ret;
};
// convert a JS RegExp object to a Mongo {$regex: ..., $options: ...}
// selector
function convertRegexpToMongoSelector(regexp) {
check(regexp, RegExp); // safety belt
var selector = {$regex: regexp.source};
var regexOptions = '';
// JS RegExp objects support 'i', 'm', and 'g'. Mongo regex $options
// support 'i', 'm', 'x', and 's'. So we support 'i' and 'm' here.
if (regexp.ignoreCase)
regexOptions += 'i';
if (regexp.multiline)
regexOptions += 'm';
if (regexOptions)
selector.$options = regexOptions;
return selector;
}
};
// 'insert' immediately returns the inserted document's new _id.
// The others return values immediately if you are in a stub, an in-memory

View File

@@ -1183,7 +1183,7 @@ MongoConnection.prototype._observeChanges = function (
throw Error("You may not observe a cursor with {fields: {_id: 0}}");
}
var observeKey = JSON.stringify(
var observeKey = EJSON.stringify(
_.extend({ordered: ordered}, cursorDescription));
var multiplexer, observeDriver;

View File

@@ -2152,88 +2152,15 @@ _.each(Meteor.isServer ? [true, false] : [true], function (minimongo) {
}); // end idGeneration parametrization
Tinytest.add('mongo-livedata - rewrite selector', function (test) {
test.equal(Mongo.Collection._rewriteSelector({x: /^o+B/im}),
{x: {$regex: '^o+B', $options: 'im'}});
test.equal(Mongo.Collection._rewriteSelector({x: {$regex: /^o+B/im}}),
{x: {$regex: '^o+B', $options: 'im'}});
test.equal(Mongo.Collection._rewriteSelector({x: /^o+B/}),
{x: {$regex: '^o+B'}});
test.equal(Mongo.Collection._rewriteSelector({x: {$regex: /^o+B/}}),
{x: {$regex: '^o+B'}});
test.equal(Mongo.Collection._rewriteSelector('foo'),
{_id: 'foo'});
test.equal(
Mongo.Collection._rewriteSelector(
{'$or': [
{x: /^o/},
{y: /^p/},
{z: 'q'},
{w: {$regex: /^r/}}
]}
),
{'$or': [
{x: {$regex: '^o'}},
{y: {$regex: '^p'}},
{z: 'q'},
{w: {$regex: '^r'}}
]}
);
test.equal(
Mongo.Collection._rewriteSelector(
{'$or': [
{'$and': [
{x: /^a/i},
{y: /^b/},
{z: {$regex: /^c/i}},
{w: {$regex: '^[abc]', $options: 'i'}}, // make sure we don't break vanilla selectors
{v: {$regex: /O/, $options: 'i'}}, // $options should override the ones on the RegExp object
{u: {$regex: /O/m, $options: 'i'}} // $options should override the ones on the RegExp object
]},
{'$nor': [
{s: /^d/},
{t: /^e/i},
{u: {$regex: /^f/i}},
// even empty string overrides built-in flags
{v: {$regex: /^g/i, $options: ''}}
]}
]}
),
{'$or': [
{'$and': [
{x: {$regex: '^a', $options: 'i'}},
{y: {$regex: '^b'}},
{z: {$regex: '^c', $options: 'i'}},
{w: {$regex: '^[abc]', $options: 'i'}},
{v: {$regex: 'O', $options: 'i'}},
{u: {$regex: 'O', $options: 'i'}}
]},
{'$nor': [
{s: {$regex: '^d'}},
{t: {$regex: '^e', $options: 'i'}},
{u: {$regex: '^f', $options: 'i'}},
{v: {$regex: '^g', $options: ''}}
]}
]}
);
var oid = new Mongo.ObjectID();
test.equal(Mongo.Collection._rewriteSelector(oid),
{_id: oid});
// Make sure selectors with "length" properties are handled properly
// (verifies issue #8329 has been resolved).
const SomeSelector = function (length) {
this.length = length;
};
const length = 2;
const testSelector = new SomeSelector(length);
test.equal(
Mongo.Collection._rewriteSelector(testSelector),
{ length }
);
test.matches(
Mongo.Collection._rewriteSelector({ _id: null })._id,
/^\S+$/,

View File

@@ -21,7 +21,7 @@ Npm.strip({
});
Package.onUse(function (api) {
api.use('npm-mongo@2.2.24', 'server');
api.use('npm-mongo', 'server');
api.use('allow-deny');
api.use([

View File

@@ -32,14 +32,14 @@
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"mongodb": {
"version": "2.2.24",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.24.tgz",
"integrity": "sha1-gPQNbsW97A3ezw+c4BROeUxGRJo="
"version": "2.2.30",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.30.tgz",
"integrity": "sha1-jM2AH2dsgXIEDC8rR+lgKg1WNKs="
},
"mongodb-core": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.8.tgz",
"integrity": "sha1-sz4DcNClnZe2yx7GEFJ76elcosA="
"version": "2.1.14",
"resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.14.tgz",
"integrity": "sha1-E8uidkImtb49GJkq8Mljzl6g8P0="
},
"process-nextick-args": {
"version": "1.0.7",
@@ -47,29 +47,34 @@
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
},
"readable-stream": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.5.tgz",
"integrity": "sha1-ZvqLcg4UOLNkaB8q0aY8YYRIydA="
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz",
"integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE="
},
"require_optional": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.0.tgz",
"integrity": "sha1-UqhhN6hJco62ClVTNhf4+RT1mr8="
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
"integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g=="
},
"resolve-from": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
"integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
},
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"semver": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
"integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8="
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ=="
},
"util-deprecate": {
"version": "1.0.2",

View File

@@ -3,12 +3,12 @@
Package.describe({
summary: "Wrapper around the mongo npm package",
version: '2.2.24',
version: '2.2.30',
documentation: null
});
Npm.depends({
mongodb: "2.2.24"
mongodb: "2.2.30"
});
Package.onUse(function (api) {

View File

@@ -57,5 +57,5 @@ Package.onTest(function (api) {
});
Cordova.depends({
'cordova-plugin-inappbrowser': '1.6.1'
'cordova-plugin-inappbrowser': '1.7.1'
});

View File

@@ -14,9 +14,9 @@ Npm.strip({
});
Cordova.depends({
'cordova-plugin-whitelist': '1.3.1',
'cordova-plugin-wkwebview-engine': '1.1.1',
'cordova-plugin-meteor-webapp': '1.4.1'
'cordova-plugin-whitelist': '1.3.2',
'cordova-plugin-wkwebview-engine': '1.1.3',
'cordova-plugin-meteor-webapp': '1.4.2'
});
Package.onUse(function (api) {

View File

@@ -52,7 +52,7 @@ var packageJson = {
pathwatcher: "7.1.0",
optimism: "0.3.3",
'lru-cache': '4.0.1',
'cordova-lib': "6.4.0",
'cordova-lib': "7.0.1",
longjohn: '0.2.11'
}
};

View File

@@ -1579,8 +1579,31 @@ function doTestCommand(options) {
// cleaned up on process exit. Using a temporary app dir means that we can
// run multiple "test-packages" commands in parallel without them stomping
// on each other.
var testRunnerAppDir =
options['test-app-path'] || files.mkdtemp('meteor-test-run');
let testRunnerAppDir;
const testAppPath = options['test-app-path'];
if (testAppPath) {
try {
if (files.mkdir_p(testAppPath, 0o700)) {
testRunnerAppDir = testAppPath;
} else {
Console.error(
'The specified --test-app-path directory could not be used, as ' +
`"${testAppPath}" already exists and it is not a directory.`
);
return 1;
}
} catch (error) {
Console.error(
'Unable to create the specified --test-app-path directory of ' +
`"${testAppPath}".`
);
throw error;
}
}
if (!testRunnerAppDir) {
testRunnerAppDir = files.mkdtemp('meteor-test-run');
}
// Download packages for our architecture, and for the deploy server's
// architecture if we're deploying.
@@ -1611,7 +1634,19 @@ function doTestCommand(options) {
projectContextOptions.projectDir = testRunnerAppDir;
projectContextOptions.projectDirForLocalPackages = options.appDir;
require("./default-npm-deps.js").install(testRunnerAppDir);
try {
require("./default-npm-deps.js").install(testRunnerAppDir);
} catch (error) {
if (error.code === 'EACCES' && options['test-app-path']) {
Console.error(
'The specified --test-app-path directory of ' +
`"${testRunnerAppDir}" exists, but the current user does not have ` +
`read/write permission in it.`
);
}
throw error;
}
if (buildmessage.jobHasMessages()) {
return;
}

View File

@@ -11,8 +11,8 @@ export const CORDOVA_ARCH = "web.cordova";
export const CORDOVA_PLATFORMS = ['ios', 'android'];
export const CORDOVA_PLATFORM_VERSIONS = {
'android': '6.1.1',
'ios': '4.3.0'
'android': '6.2.3',
'ios': '4.4.0'
};
const PLATFORM_TO_DISPLAY_NAME_MAP = {

View File

@@ -51,29 +51,29 @@ const pinnedPlatformVersions = CORDOVA_PLATFORM_VERSIONS;
// Versions are taken from cordova-lib's package.json and should be updated
// when we update to a newer version of cordova-lib.
const pinnedPluginVersions = {
"cordova-plugin-battery-status": "1.2.2",
"cordova-plugin-camera": "2.3.1",
"cordova-plugin-console": "1.0.5",
"cordova-plugin-contacts": "2.2.1",
"cordova-plugin-device": "1.1.4",
"cordova-plugin-device-motion": "1.2.3",
"cordova-plugin-device-orientation": "1.0.5",
"cordova-plugin-dialogs": "1.3.1",
"cordova-plugin-file": "4.3.1",
"cordova-plugin-file-transfer": "1.6.1",
"cordova-plugin-geolocation": "2.4.1",
"cordova-plugin-globalization": "1.0.5",
"cordova-plugin-inappbrowser": "1.6.1",
"cordova-plugin-battery-status": "1.2.4",
"cordova-plugin-camera": "2.4.1",
"cordova-plugin-console": "1.0.7",
"cordova-plugin-contacts": "2.3.1",
"cordova-plugin-device": "1.1.6",
"cordova-plugin-device-motion": "1.2.5",
"cordova-plugin-device-orientation": "1.0.7",
"cordova-plugin-dialogs": "1.3.3",
"cordova-plugin-file": "4.3.3",
"cordova-plugin-file-transfer": "1.6.3",
"cordova-plugin-geolocation": "2.4.3",
"cordova-plugin-globalization": "1.0.7",
"cordova-plugin-inappbrowser": "1.7.1",
"cordova-plugin-legacy-whitelist": "1.1.2",
"cordova-plugin-media": "2.4.1",
"cordova-plugin-media-capture": "1.4.1",
"cordova-plugin-network-information": "1.3.1",
"cordova-plugin-splashscreen": "4.0.1",
"cordova-plugin-statusbar": "2.2.1",
"cordova-plugin-test-framework": "1.1.4",
"cordova-plugin-vibration": "2.1.3",
"cordova-plugin-whitelist": "1.3.1",
"cordova-plugin-wkwebview-engine": "1.1.1"
"cordova-plugin-media": "3.0.1",
"cordova-plugin-media-capture": "1.4.3",
"cordova-plugin-network-information": "1.3.3",
"cordova-plugin-splashscreen": "4.0.3",
"cordova-plugin-statusbar": "2.2.3",
"cordova-plugin-test-framework": "1.1.5",
"cordova-plugin-vibration": "2.1.5",
"cordova-plugin-whitelist": "1.3.2",
"cordova-plugin-wkwebview-engine": "1.1.3"
}
export class CordovaProject {

View File

@@ -6,21 +6,21 @@
meteor-base@1.1.0 # Packages every Meteor app needs to have
mobile-experience@1.0.4 # Packages for a great mobile UX
mongo@1.1.18 # The database Meteor supports right now
mongo@1.1.19 # The database Meteor supports right now
blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views
reactive-var@1.0.11 # Reactive variable for tracker
jquery@1.11.10 # Helpful client-side library
tracker@1.1.3 # Meteor's client-side reactive programming library
standard-minifier-css@1.3.4 # CSS minifier run for production mode
standard-minifier-js@2.1.0 # JS minifier run for production mode
standard-minifier-js@2.1.1 # JS minifier run for production mode
es5-shim@4.6.15 # ECMAScript 5 compatibility for older browsers.
ecmascript@0.8.0 # Enable ECMAScript2015+ syntax in app code
shell-server@0.2.3 # Server-side component of the `meteor shell` command
ecmascript@0.8.2 # Enable ECMAScript2015+ syntax in app code
shell-server@0.2.4 # Server-side component of the `meteor shell` command
autopublish@1.0.7 # Publish all data to the clients (for prototyping)
insecure@1.0.7 # Allow all DB writes from clients (for prototyping)
dynamic-import
dynamic-import@0.1.1
dispatch:mocha-phantomjs
dispatch:mocha-browser
lazy-test-package

View File

@@ -1 +1 @@
METEOR@1.5
METEOR@1.5.1

View File

@@ -6,22 +6,22 @@
meteor-base@1.1.0 # Packages every Meteor app needs to have
mobile-experience@1.0.4 # Packages for a great mobile UX
mongo@1.1.18 # The database Meteor supports right now
mongo@1.1.19 # The database Meteor supports right now
blaze-html-templates # Compile .html files into Meteor Blaze views
session@1.1.7 # Client-side reactive dictionary for your app
jquery@1.11.10 # Helpful client-side library
tracker@1.1.3 # Meteor's client-side reactive programming library
es5-shim@4.6.15 # ECMAScript 5 compatibility for older browsers.
ecmascript@0.8.0 # Enable ECMAScript2015+ syntax in app code
ecmascript@0.8.2 # Enable ECMAScript2015+ syntax in app code
coffeescript
modules-test-package
dispatch:mocha-phantomjs
dispatch:mocha-browser
standard-minifier-css@1.3.4
standard-minifier-js@2.1.0
standard-minifier-js@2.1.1
client-only-ecmascript
modules-test-plugin
shell-server@0.2.3
dynamic-import
shell-server@0.2.4
dynamic-import@0.1.1

View File

@@ -1 +1 @@
METEOR@1.5
METEOR@1.5.1

View File

@@ -74,8 +74,7 @@ if (process.platform !== "win32") {
});
var run = s.run("add", packageName);
run.waitSecs(60);
run.matchErr("colons");
run.matchErrBeforeExit("colons");
});
});
}

View File

@@ -596,3 +596,75 @@ selftest.define("old cli tests (converted)", function () {
run.expectExit(0);
files.unlink(files.pathJoin(s.cwd, 'settings.js'));
});
// Added to address https://github.com/meteor/meteor/issues/8897.
selftest.define(
'meteor test-packages --test-app-path directory',
function () {
var s = new Sandbox();
var run;
// If test-app-path doesn't exist, it should be created.
var testAppPath = '/tmp/meteor_test_app_path';
files.rm_recursive(testAppPath);
selftest.expectFalse(files.exists(testAppPath));
s.createApp('test-app-path-app', 'package-tests', {
dontPrepareApp: true
});
s.cd('test-app-path-app/packages/say-something', function () {
run = s.run(
'test-packages',
'--once',
{ 'test-app-path': testAppPath },
'./'
);
run.match('Started');
selftest.expectTrue(files.exists(testAppPath));
run.stop();
files.rm_recursive(testAppPath);
});
// If test-app-path already exists, make sure that directory is used.
var testAppPath = '/tmp/meteor_test_app_path';
files.rm_recursive(testAppPath);
files.mkdir_p(testAppPath);
selftest.expectTrue(files.exists(testAppPath));
selftest.expectFalse(files.exists(testAppPath + '/.meteor'));
s.createApp('test-app-path-app', 'package-tests', {
dontPrepareApp: true
});
s.cd('test-app-path-app/packages/say-something', function () {
run = s.run(
'test-packages',
'--once',
{ 'test-app-path': testAppPath },
'./'
);
run.match('Started');
selftest.expectTrue(files.exists(testAppPath + '/.meteor'));
run.stop();
files.rm_recursive(testAppPath);
});
// If test-app-path already exists but is a file instead of a directory,
// show a console error message explaining this, and exit.
var testAppPath = '/tmp/meteor_test_app_path';
files.rm_recursive(testAppPath);
files.writeFile(testAppPath, '<3 meteor');
selftest.expectTrue(files.exists(testAppPath));
s.createApp('test-app-path-app', 'package-tests', {
dontPrepareApp: true
});
s.cd('test-app-path-app/packages/say-something', function () {
run = s.run(
'test-packages',
'--once',
{ 'test-app-path': testAppPath },
'./'
);
run.matchErr('is not a directory');
run.expectExit(1);
files.rm_recursive(testAppPath);
});
}
);

View File

@@ -109,6 +109,7 @@ selftest.define("change packages during hot code push", [], function () {
run.waitSecs(5);
run.match("myapp");
run.match("proxy");
run.waitSecs(5);
run.match("MongoDB");
run.waitSecs(5);
run.match("your app");