diff --git a/packages/ddp-server/crossbar.js b/packages/ddp-server/crossbar.js index dc97d820ab..047825a798 100644 --- a/packages/ddp-server/crossbar.js +++ b/packages/ddp-server/crossbar.js @@ -16,11 +16,11 @@ DDPServer._Crossbar = function (options) { self.factName = options.factName || null; }; -_.extend(DDPServer._Crossbar.prototype, { +Object.assign(DDPServer._Crossbar.prototype, { // msg is a trigger or a notification _collectionForMessage: function (msg) { var self = this; - if (! _.has(msg, 'collection')) { + if (!('collection' in msg)) { return ''; } else if (typeof(msg.collection) === 'string') { if (msg.collection === '') @@ -47,7 +47,7 @@ _.extend(DDPServer._Crossbar.prototype, { var collection = self._collectionForMessage(trigger); var record = {trigger: EJSON.clone(trigger), callback: callback}; - if (! _.has(self.listenersByCollection, collection)) { + if (! (collection in self.listenersByCollection)) { self.listenersByCollection[collection] = {}; self.listenersByCollectionCount[collection] = 0; } @@ -88,13 +88,13 @@ _.extend(DDPServer._Crossbar.prototype, { var collection = self._collectionForMessage(notification); - if (! _.has(self.listenersByCollection, collection)) { + if (!(collection in self.listenersByCollection)) { return; } var listenersForCollection = self.listenersByCollection[collection]; var callbackIds = []; - _.each(listenersForCollection, function (l, id) { + Object.entries(listenersForCollection).forEach(function ([id, l]) { if (self._matches(notification, l.trigger)) { callbackIds.push(id); } @@ -110,7 +110,7 @@ _.extend(DDPServer._Crossbar.prototype, { // first gets reduced down to the empty object (and then never gets // increased again). for (const id of callbackIds) { - if (_.has(listenersForCollection, id)) { + if (id in listenersForCollection) { await listenersForCollection[id].callback(notification); } } @@ -150,10 +150,9 @@ _.extend(DDPServer._Crossbar.prototype, { return false; } - return _.all(trigger, function (triggerValue, key) { - return !_.has(notification, key) || - EJSON.equals(triggerValue, notification[key]); - }); + return Object.keys(trigger).every(function (key) { + return !(key in notification) || EJSON.equals(trigger[key], notification[key]); + }); } }); @@ -164,4 +163,4 @@ _.extend(DDPServer._Crossbar.prototype, { // message from being sent). DDPServer._InvalidationCrossbar = new DDPServer._Crossbar({ factName: "invalidation-crossbar-listeners" -}); +}); \ No newline at end of file diff --git a/packages/ddp-server/livedata_server.js b/packages/ddp-server/livedata_server.js index bdb13eaa52..a79d0febf2 100644 --- a/packages/ddp-server/livedata_server.js +++ b/packages/ddp-server/livedata_server.js @@ -1,5 +1,10 @@ +import isEmpty from 'lodash.isempty'; +import isString from 'lodash.isstring'; +import isObject from 'lodash.isobject'; + DDPServer = {}; + // Publication strategies define how we handle data from published cursors at the collection level // This allows someone to: // - Choose a trade-off between client-server bandwidth and server memory usage @@ -89,7 +94,7 @@ DDPServer._getCurrentFence = function () { return currentInvocation ? currentInvocation.fence : undefined; }; -_.extend(SessionDocumentView.prototype, { +Object.assign(SessionDocumentView.prototype, { getFields: function () { var self = this; @@ -197,7 +202,7 @@ Object.assign(SessionCollectionView.prototype, { diff: function (previous) { var self = this; DiffSequence.diffMaps(previous.documents, self.documents, { - both: _.bind(self.diffDocument, self), + both: self.diffDocument.bind(self), rightOnly: function (id, nowDV) { self.callbacks.added(self.collectionName, id, nowDV.getFields()); @@ -243,7 +248,7 @@ Object.assign(SessionCollectionView.prototype, { } docView.existsIn.add(subscriptionHandle); var changeCollector = {}; - _.each(fields, function (value, key) { + Object.entries(fields).forEach(function ([key, value]) { docView.changeField( subscriptionHandle, key, value, changeCollector, true); }); @@ -259,7 +264,7 @@ Object.assign(SessionCollectionView.prototype, { var docView = self.documents.get(id); if (!docView) throw new Error("Could not find element with id " + id + " to change"); - _.each(changed, function (value, key) { + Object.entries(changed).forEach(function ([key, value]) { if (value === undefined) docView.clearField(subscriptionHandle, key, changedResult); else @@ -401,7 +406,7 @@ Object.assign(Session.prototype, { if (self._isSending) { self.send({msg: "ready", subs: subscriptionIds}); } else { - _.each(subscriptionIds, function (subscriptionId) { + subscriptionIds.forEach(function (subscriptionId) { self._pendingReady.push(subscriptionId); }); } @@ -419,7 +424,7 @@ Object.assign(Session.prototype, { }, sendChanged(collectionName, id, fields) { - if (_.isEmpty(fields)) + if (isEmpty(fields)) return; if (this._canSend(collectionName)) { @@ -441,9 +446,9 @@ Object.assign(Session.prototype, { getSendCallbacks: function () { var self = this; return { - added: _.bind(self.sendAdded, self), - changed: _.bind(self.sendChanged, self), - removed: _.bind(self.sendRemoved, self) + added: self.sendAdded.bind(self), + changed: self.sendChanged.bind(self), + removed: self.sendRemoved.bind(self) }; }, @@ -493,8 +498,8 @@ Object.assign(Session.prototype, { // Make a shallow copy of the set of universal handlers and start them. If // additional universal publishers start while we're running them (due to // yielding), they will run separately as part of Server.publish. - var handlers = _.clone(self.server.universal_publish_handlers); - _.each(handlers, function (handler) { + var handlers = [...self.server.universal_publish_handlers]; + handlers.forEach(function (handler) { self._startSubscription(handler); }); }, @@ -536,7 +541,7 @@ Object.assign(Session.prototype, { // Defer calling the close callbacks, so that the caller closing // the session isn't waiting for all the callbacks to complete. - _.each(self._closeCallbacks, function (callback) { + self._closeCallbacks.forEach(function (callback) { callback(); }); }); @@ -639,7 +644,7 @@ Object.assign(Session.prototype, { return true; }); - if (_.has(self.protocol_handlers, msg.msg)) { + if (msg.msg in self.protocol_handlers) { const result = self.protocol_handlers[msg.msg].call( self, msg, @@ -674,7 +679,7 @@ Object.assign(Session.prototype, { // reject malformed messages if (typeof (msg.id) !== "string" || typeof (msg.name) !== "string" || - (('params' in msg) && !(msg.params instanceof Array))) { + ('params' in msg && !(msg.params instanceof Array))) { self.sendError("Malformed subscription", msg); return; } @@ -743,7 +748,7 @@ Object.assign(Session.prototype, { // for forwards compatibility. if (typeof (msg.id) !== "string" || typeof (msg.method) !== "string" || - (('params' in msg) && !(msg.params instanceof Array)) || + ('params' in msg && !(msg.params instanceof Array)) || (('randomSeed' in msg) && (typeof msg.randomSeed !== "string"))) { self.sendError("Malformed method invocation", msg); return; @@ -814,34 +819,16 @@ Object.assign(Session.prototype, { } } - - - const getCurrentMethodInvocationResult = () => - DDP._CurrentMethodInvocation.withValue( + resolve(DDPServer._CurrentWriteFence.withValue( + fence, + () => DDP._CurrentMethodInvocation.withValue( invocation, - () => - maybeAuditArgumentChecks( - handler, - invocation, - msg.params, - "call to '" + msg.method + "'" - ), - { - name: 'getCurrentMethodInvocationResult', - keyName: 'getCurrentMethodInvocationResult', - } - ); - - resolve( - DDPServer._CurrentWriteFence.withValue( - fence, - getCurrentMethodInvocationResult, - { - name: 'DDPServer._CurrentWriteFence', - keyName: '_CurrentWriteFence', - } + () => maybeAuditArgumentChecks( + handler, invocation, msg.params, + "call to '" + msg.method + "'" + ) ) - ); + )); }); async function finish() { @@ -961,7 +948,7 @@ Object.assign(Session.prototype, { Meteor._noYieldsAllowed(function () { self._isSending = true; self._diffCollectionViews(beforeCVs); - if (!_.isEmpty(self._pendingReady)) { + if (!isEmpty(self._pendingReady)) { self.sendReady(self._pendingReady); self._pendingReady = []; } @@ -1050,7 +1037,7 @@ Object.assign(Session.prototype, { return self.socket.remoteAddress; var forwardedFor = self.socket.headers["x-forwarded-for"]; - if (! _.isString(forwardedFor)) + if (!isString(forwardedFor)) return null; forwardedFor = forwardedFor.trim().split(/\s*,\s*/); @@ -1244,9 +1231,9 @@ Object.assign(Subscription.prototype, { // _publishCursor only returns after the initial added callbacks have run. // mark subscription as ready. self.ready(); - } else if (_.isArray(res)) { + } else if (Array.isArray(res)) { // Check all the elements are cursors - if (! _.all(res, isCursor)) { + if (! res.every(isCursor)) { self.error(new Error("Publish function returned an array of non-Cursors")); return; } @@ -1257,7 +1244,7 @@ Object.assign(Subscription.prototype, { for (var i = 0; i < res.length; ++i) { var collectionName = res[i]._getCollectionName(); - if (_.has(collectionNames, collectionName)) { + if (collectionNames[collectionName]) { self.error(new Error( "Publish function returned multiple cursors for collection " + collectionName)); @@ -1302,7 +1289,7 @@ Object.assign(Subscription.prototype, { // Tell listeners, so they can clean up var callbacks = self._stopCallbacks; self._stopCallbacks = []; - _.each(callbacks, function (callback) { + callbacks.forEach(function (callback) { callback(); }); }, @@ -1589,33 +1576,33 @@ Object.assign(Server.prototype, { }, /** - * @summary Set publication strategy for the given collection. Publications strategies are available from `DDPServer.publicationStrategies`. You call this method from `Meteor.server`, like `Meteor.server.setPublicationStrategy()` + * @summary Set publication strategy for the given publication. Publications strategies are available from `DDPServer.publicationStrategies`. You call this method from `Meteor.server`, like `Meteor.server.setPublicationStrategy()` * @locus Server * @alias setPublicationStrategy - * @param collectionName {String} + * @param publicationName {String} * @param strategy {{useCollectionView: boolean, doAccountingForCollection: boolean}} * @memberOf Meteor.server * @importFromPackage meteor */ - setPublicationStrategy(collectionName, strategy) { + setPublicationStrategy(publicationName, strategy) { if (!Object.values(publicationStrategies).includes(strategy)) { throw new Error(`Invalid merge strategy: ${strategy} - for collection ${collectionName}`); + for collection ${publicationName}`); } - this._publicationStrategies[collectionName] = strategy; + this._publicationStrategies[publicationName] = strategy; }, /** - * @summary Gets the publication strategy for the requested collection. You call this method from `Meteor.server`, like `Meteor.server.getPublicationStrategy()` + * @summary Gets the publication strategy for the requested publication. You call this method from `Meteor.server`, like `Meteor.server.getPublicationStrategy()` * @locus Server * @alias getPublicationStrategy - * @param collectionName {String} + * @param publicationName {String} * @memberOf Meteor.server * @importFromPackage meteor * @return {{useCollectionView: boolean, doAccountingForCollection: boolean}} */ - getPublicationStrategy(collectionName) { - return this._publicationStrategies[collectionName] + getPublicationStrategy(publicationName) { + return this._publicationStrategies[publicationName] || this.options.defaultPublicationStrategy; }, @@ -1637,9 +1624,9 @@ Object.assign(Server.prototype, { // The connect message must specify a version and an array of supported // versions, and it must claim to support what it is proposing. if (!(typeof (msg.version) === 'string' && - _.isArray(msg.support) && - _.all(msg.support, _.isString) && - _.contains(msg.support, msg.version))) { + Array.isArray(msg.support) && + msg.support.every(isString) && + msg.support.includes(msg.version))) { socket.send(DDPCommon.stringifyDDP({msg: 'failed', version: DDPCommon.SUPPORTED_DDP_VERSIONS[0]})); socket.close(); @@ -1704,7 +1691,7 @@ Object.assign(Server.prototype, { publish: function (name, handler, options) { var self = this; - if (! _.isObject(name)) { + if (!isObject(name)) { options = options || {}; if (name && name in self.publish_handlers) { @@ -1752,7 +1739,7 @@ Object.assign(Server.prototype, { } } else{ - _.each(name, function(value, key) { + Object.entries(name).forEach(function([key, value]) { self.publish(key, value, {}); }); } @@ -1783,7 +1770,7 @@ Object.assign(Server.prototype, { */ methods: function (methods) { var self = this; - _.each(methods, function (func, name) { + Object.entries(methods).forEach(function ([name, func]) { if (typeof func !== 'function') throw new Error("Method '" + name + "' must be a function"); if (self.method_handlers[name]) @@ -1923,8 +1910,8 @@ Object.assign(Server.prototype, { var calculateVersion = function (clientSupportedVersions, serverSupportedVersions) { - var correctVersion = _.find(clientSupportedVersions, function (version) { - return _.contains(serverSupportedVersions, version); + var correctVersion = clientSupportedVersions.find(function (version) { + return serverSupportedVersions.includes(version); }); if (!correctVersion) { correctVersion = serverSupportedVersions[0]; @@ -1964,8 +1951,7 @@ var wrapInternalException = function (exception, context) { // Did the error contain more details that could have been useful if caught in // server code (or if thrown from non-client-originated code), but also - // provided a "sanitized" version with more context than 500 Internal server - // error? Use that. + // provided a "sanitized" version with more context than 500 Internal server error? Use that. if (exception.sanitizedError) { if (exception.sanitizedError.isClientSafe) return exception.sanitizedError; @@ -1986,4 +1972,4 @@ var maybeAuditArgumentChecks = function (f, context, args, description) { f, context, args, description); } return f.apply(context, args); -}; +}; \ No newline at end of file diff --git a/packages/ddp-server/package.js b/packages/ddp-server/package.js index 2bbd7a97d2..8f6526e19e 100644 --- a/packages/ddp-server/package.js +++ b/packages/ddp-server/package.js @@ -7,6 +7,10 @@ Package.describe({ Npm.depends({ "permessage-deflate": "0.1.7", sockjs: "0.3.24", + "lodash.once": "4.1.1", + "lodash.isempty": "4.4.0", + "lodash.isstring": "4.0.1", + "lodash.isobject": "3.0.2" }); Package.onUse(function (api) { @@ -15,7 +19,6 @@ Package.onUse(function (api) { "check", "random", "ejson", - "underscore", "retry", "mongo-id", "diff-sequence", @@ -62,7 +65,6 @@ Package.onTest(function (api) { api.use("mongo", ["client", "server"]); api.use("test-helpers", ["client", "server"]); api.use([ - "underscore", "tinytest", "random", "tracker", diff --git a/packages/ddp-server/server_convenience.js b/packages/ddp-server/server_convenience.js index e3adfb5b2e..4ee2a16a8e 100755 --- a/packages/ddp-server/server_convenience.js +++ b/packages/ddp-server/server_convenience.js @@ -11,7 +11,7 @@ Meteor.refresh = async function (notification) { // Proxy the public methods of Meteor.server so they can // be called directly on Meteor. -_.each( + [ 'publish', 'isAsyncCall', @@ -22,8 +22,8 @@ _.each( 'applyAsync', 'onConnection', 'onMessage', - ], + ].forEach( function(name) { - Meteor[name] = _.bind(Meteor.server[name], Meteor.server); + Meteor[name] = Meteor.server[name].bind(Meteor.server); } ); diff --git a/packages/ddp-server/session_view_tests.js b/packages/ddp-server/session_view_tests.js index 0459a45107..ce85208174 100644 --- a/packages/ddp-server/session_view_tests.js +++ b/packages/ddp-server/session_view_tests.js @@ -1,3 +1,5 @@ +import isEmpty from 'lodash.isempty'; + var newView = function(test) { var results = []; var view = new DDPServer._SessionCollectionView('test', { @@ -5,7 +7,7 @@ var newView = function(test) { results.push({fun: 'added', id: id, fields: fields}); }, changed: function (collection, id, changed) { - if (_.isEmpty(changed)) + if (isEmpty(changed)) return; results.push({fun: 'changed', id: id, changed: changed}); }, @@ -17,8 +19,8 @@ var newView = function(test) { view: view, results: results }; - _.each(["added", "changed", "removed"], function (it) { - v[it] = _.bind(view[it], view); + ["added", "changed", "removed"].forEach(function (it) { + v[it] = view[it].bind(view); }); v.expectResult = function (result) { test.equal(results.shift(), result); @@ -182,7 +184,7 @@ Tinytest.add('livedata - sessionview - change to canonical value produces no cha v.added("B", "A1", {foo: "baz"}); var canon = "bar"; var maybeResults = v.drain(); - if (!_.isEmpty(maybeResults)) { + if (!isEmpty(maybeResults)) { // if something happened, it was a change message to baz. // if nothing did, canon is still bar. test.length(maybeResults, 1); @@ -390,4 +392,4 @@ Tinytest.add('livedata - sessionview - clear undefined value', function (test) { v.changed("A", "A1", {field: undefined}); v.expectNoResult(); -}); +}); \ No newline at end of file diff --git a/packages/ddp-server/stream_server.js b/packages/ddp-server/stream_server.js index bda0414130..d57b81456c 100644 --- a/packages/ddp-server/stream_server.js +++ b/packages/ddp-server/stream_server.js @@ -1,3 +1,5 @@ +import once from 'lodash.once'; + // By default, we use the permessage-deflate extension with default // configuration. If $SERVER_WEBSOCKET_COMPRESSION is set, then it must be valid // JSON. If it represents a falsey value, then we do not use permessage-deflate @@ -9,7 +11,7 @@ // crash the tool during isopacket load if your JSON doesn't parse. This is only // a problem because the tool has to load the DDP server code just in order to // be a DDP client; see https://github.com/meteor/meteor/issues/3452 .) -var websocketExtensions = _.once(function () { +var websocketExtensions = once(function () { var extensions = []; var websocketCompressionConfig = process.env.SERVER_WEBSOCKET_COMPRESSION @@ -116,7 +118,9 @@ StreamServer = function () { socket.write(data); }; socket.on('close', function () { - self.open_sockets = _.without(self.open_sockets, socket); + self.open_sockets = self.open_sockets.filter(function(value) { + return value !== socket; + }); }); self.open_sockets.push(socket); @@ -128,7 +132,7 @@ StreamServer = function () { // call all our callbacks when we get a new socket. they will do the // work of setting up handlers and such for specific messages. - _.each(self.registration_callbacks, function (callback) { + self.registration_callbacks.forEach(function (callback) { callback(socket); }); }); @@ -141,7 +145,7 @@ Object.assign(StreamServer.prototype, { register: function (callback) { var self = this; self.registration_callbacks.push(callback); - _.each(self.all_sockets(), function (socket) { + self.all_sockets().forEach(function (socket) { callback(socket); }); }, @@ -149,7 +153,7 @@ Object.assign(StreamServer.prototype, { // get a list of all sockets all_sockets: function () { var self = this; - return _.values(self.open_sockets); + return Object.values(self.open_sockets); }, // Redirect /websocket to /sockjs/websocket in order to not expose @@ -183,11 +187,11 @@ Object.assign(StreamServer.prototype, { parsedUrl.pathname = self.prefix + '/websocket'; request.url = url.format(parsedUrl); } - _.each(oldHttpServerListeners, function(oldListener) { + oldHttpServerListeners.forEach(function(oldListener) { oldListener.apply(httpServer, args); }); }; httpServer.addListener(event, newListener); }); } -}); +}); \ No newline at end of file