diff --git a/packages/livedata/livedata_connection.js b/packages/livedata/livedata_connection.js index ed1e8accca..e32a39e3fa 100644 --- a/packages/livedata/livedata_connection.js +++ b/packages/livedata/livedata_connection.js @@ -519,7 +519,7 @@ _.extend(Connection.prototype, { self._subscriptions[id] = { id: id, name: name, - params: params, + params: EJSON.clone(params), inactive: false, ready: false, readyDeps: (typeof Deps !== "undefined") && new Deps.Dependency, @@ -644,6 +644,10 @@ _.extend(Connection.prototype, { ); } + // Keep our args safe from mutation (eg if we don't send the message for a + // while because of a wait method). + args = EJSON.clone(args); + // Lazily allocate method ID once we know that it'll be needed. var methodId = (function () { var id; @@ -691,6 +695,7 @@ _.extend(Connection.prototype, { // Because saveOriginals and retrieveOriginals aren't reentrant, // don't allow stubs to yield. return Meteor._noYieldsAllowed(function () { + // re-clone, so that the stub can't affect our caller's values return stub.apply(invocation, EJSON.clone(args)); }); } else { diff --git a/packages/livedata/livedata_server.js b/packages/livedata/livedata_server.js index d133e648c0..74d4206db1 100644 --- a/packages/livedata/livedata_server.js +++ b/packages/livedata/livedata_server.js @@ -1399,7 +1399,8 @@ _.extend(Server.prototype, { try { var result = DDP._CurrentInvocation.withValue(invocation, function () { return maybeAuditArgumentChecks( - handler, invocation, args, "internal call to '" + name + "'"); + handler, invocation, EJSON.clone(args), "internal call to '" + + name + "'"); }); } catch (e) { exception = e;