mongodb-livedata-server 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/livedata_server.d.ts +4 -4
- package/dist/livedata_server.js +11 -11
- package/dist/meteor/binary-heap/max_heap.d.ts +31 -31
- package/dist/meteor/binary-heap/max_heap.js +186 -186
- package/dist/meteor/binary-heap/min_heap.d.ts +6 -6
- package/dist/meteor/binary-heap/min_heap.js +17 -17
- package/dist/meteor/binary-heap/min_max_heap.d.ts +11 -11
- package/dist/meteor/binary-heap/min_max_heap.js +48 -48
- package/dist/meteor/callback-hook/hook.d.ts +11 -11
- package/dist/meteor/callback-hook/hook.js +78 -78
- package/dist/meteor/ddp/crossbar.d.ts +15 -15
- package/dist/meteor/ddp/crossbar.js +136 -136
- package/dist/meteor/ddp/heartbeat.d.ts +19 -19
- package/dist/meteor/ddp/heartbeat.js +77 -77
- package/dist/meteor/ddp/livedata_server.d.ts +141 -142
- package/dist/meteor/ddp/livedata_server.js +403 -403
- package/dist/meteor/ddp/method-invocation.d.ts +35 -35
- package/dist/meteor/ddp/method-invocation.js +72 -72
- package/dist/meteor/ddp/random-stream.d.ts +8 -8
- package/dist/meteor/ddp/random-stream.js +100 -100
- package/dist/meteor/ddp/session-collection-view.d.ts +20 -20
- package/dist/meteor/ddp/session-collection-view.js +106 -106
- package/dist/meteor/ddp/session-document-view.d.ts +8 -8
- package/dist/meteor/ddp/session-document-view.js +82 -82
- package/dist/meteor/ddp/session.d.ts +75 -75
- package/dist/meteor/ddp/session.js +590 -590
- package/dist/meteor/ddp/stream_server.d.ts +20 -21
- package/dist/meteor/ddp/stream_server.js +181 -181
- package/dist/meteor/ddp/subscription.d.ts +94 -94
- package/dist/meteor/ddp/subscription.js +370 -370
- package/dist/meteor/ddp/utils.d.ts +8 -8
- package/dist/meteor/ddp/utils.js +104 -104
- package/dist/meteor/ddp/writefence.d.ts +20 -20
- package/dist/meteor/ddp/writefence.js +111 -111
- package/dist/meteor/diff-sequence/diff.d.ts +17 -17
- package/dist/meteor/diff-sequence/diff.js +257 -257
- package/dist/meteor/ejson/ejson.d.ts +82 -82
- package/dist/meteor/ejson/ejson.js +568 -569
- package/dist/meteor/ejson/stringify.d.ts +2 -2
- package/dist/meteor/ejson/stringify.js +119 -119
- package/dist/meteor/ejson/utils.d.ts +12 -12
- package/dist/meteor/ejson/utils.js +42 -42
- package/dist/meteor/mongo/caching_change_observer.d.ts +16 -16
- package/dist/meteor/mongo/caching_change_observer.js +63 -63
- package/dist/meteor/mongo/doc_fetcher.d.ts +7 -7
- package/dist/meteor/mongo/doc_fetcher.js +53 -53
- package/dist/meteor/mongo/geojson_utils.d.ts +3 -3
- package/dist/meteor/mongo/geojson_utils.js +40 -41
- package/dist/meteor/mongo/live_connection.d.ts +28 -28
- package/dist/meteor/mongo/live_connection.js +264 -264
- package/dist/meteor/mongo/live_cursor.d.ts +25 -25
- package/dist/meteor/mongo/live_cursor.js +60 -60
- package/dist/meteor/mongo/minimongo_common.d.ts +84 -84
- package/dist/meteor/mongo/minimongo_common.js +1998 -1998
- package/dist/meteor/mongo/minimongo_matcher.d.ts +23 -23
- package/dist/meteor/mongo/minimongo_matcher.js +283 -283
- package/dist/meteor/mongo/minimongo_sorter.d.ts +16 -16
- package/dist/meteor/mongo/minimongo_sorter.js +268 -268
- package/dist/meteor/mongo/observe_driver_utils.d.ts +9 -9
- package/dist/meteor/mongo/observe_driver_utils.js +72 -73
- package/dist/meteor/mongo/observe_multiplexer.d.ts +46 -46
- package/dist/meteor/mongo/observe_multiplexer.js +203 -203
- package/dist/meteor/mongo/oplog-observe-driver.d.ts +68 -68
- package/dist/meteor/mongo/oplog-observe-driver.js +918 -918
- package/dist/meteor/mongo/oplog_tailing.d.ts +35 -35
- package/dist/meteor/mongo/oplog_tailing.js +352 -352
- package/dist/meteor/mongo/oplog_v2_converter.d.ts +1 -1
- package/dist/meteor/mongo/oplog_v2_converter.js +125 -126
- package/dist/meteor/mongo/polling_observe_driver.d.ts +30 -30
- package/dist/meteor/mongo/polling_observe_driver.js +216 -221
- package/dist/meteor/mongo/synchronous-cursor.d.ts +17 -17
- package/dist/meteor/mongo/synchronous-cursor.js +261 -261
- package/dist/meteor/mongo/synchronous-queue.d.ts +13 -13
- package/dist/meteor/mongo/synchronous-queue.js +110 -110
- package/dist/meteor/ordered-dict/ordered_dict.d.ts +31 -31
- package/dist/meteor/ordered-dict/ordered_dict.js +198 -198
- package/dist/meteor/random/AbstractRandomGenerator.d.ts +42 -42
- package/dist/meteor/random/AbstractRandomGenerator.js +92 -92
- package/dist/meteor/random/AleaRandomGenerator.d.ts +13 -13
- package/dist/meteor/random/AleaRandomGenerator.js +90 -90
- package/dist/meteor/random/NodeRandomGenerator.d.ts +16 -16
- package/dist/meteor/random/NodeRandomGenerator.js +42 -42
- package/dist/meteor/random/createAleaGenerator.d.ts +2 -2
- package/dist/meteor/random/createAleaGenerator.js +32 -32
- package/dist/meteor/random/createRandom.d.ts +1 -1
- package/dist/meteor/random/createRandom.js +22 -22
- package/dist/meteor/random/main.d.ts +1 -1
- package/dist/meteor/random/main.js +12 -12
- package/dist/meteor/types.d.ts +1 -1
- package/dist/meteor/types.js +2 -2
- package/package.json +5 -5
|
@@ -1,404 +1,404 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ClientSafeError = exports.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
this.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
self.
|
|
39
|
-
debugPrintExceptions: "
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
self.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
sendError('
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (msg
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
* @
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
* @
|
|
105
|
-
* @
|
|
106
|
-
* @
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ClientSafeError = exports.DDPServer = exports.DDP = void 0;
|
|
4
|
+
exports._calculateVersion = _calculateVersion;
|
|
5
|
+
exports.wrapInternalException = wrapInternalException;
|
|
6
|
+
exports.maybeAuditArgumentChecks = maybeAuditArgumentChecks;
|
|
7
|
+
exports.ddpError = ddpError;
|
|
8
|
+
const method_invocation_1 = require("./method-invocation");
|
|
9
|
+
const stream_server_1 = require("./stream_server");
|
|
10
|
+
const session_1 = require("./session");
|
|
11
|
+
const utils_1 = require("./utils");
|
|
12
|
+
const random_stream_1 = require("./random-stream");
|
|
13
|
+
const ejson_1 = require("../ejson/ejson");
|
|
14
|
+
const hook_1 = require("../callback-hook/hook");
|
|
15
|
+
exports.DDP = {};
|
|
16
|
+
class DDPServer {
|
|
17
|
+
constructor(options = {}, httpServer) {
|
|
18
|
+
this.publish_handlers = {};
|
|
19
|
+
this.universal_publish_handlers = [];
|
|
20
|
+
this.method_handlers = {};
|
|
21
|
+
this._publicationStrategies = {};
|
|
22
|
+
this.sessions = new Map(); // map from id to session
|
|
23
|
+
var self = this;
|
|
24
|
+
// The default heartbeat interval is 30 seconds on the server and 35
|
|
25
|
+
// seconds on the client. Since the client doesn't need to send a
|
|
26
|
+
// ping as long as it is receiving pings, this means that pings
|
|
27
|
+
// normally go from the server to the client.
|
|
28
|
+
//
|
|
29
|
+
// Note: Troposphere depends on the ability to mutate
|
|
30
|
+
// Meteor.server.options.heartbeatTimeout! This is a hack, but it's life.
|
|
31
|
+
self.options = Object.assign({ heartbeatInterval: 15000, heartbeatTimeout: 15000,
|
|
32
|
+
// For testing, allow responding to pings to be disabled.
|
|
33
|
+
respondToPings: true, defaultPublicationStrategy: DDPServer.publicationStrategies.SERVER_MERGE }, options);
|
|
34
|
+
// Map of callbacks to call when a new connection comes in to the
|
|
35
|
+
// server and completes DDP version negotiation. Use an object instead
|
|
36
|
+
// of an array so we can safely remove one from the list while
|
|
37
|
+
// iterating over it.
|
|
38
|
+
self.onConnectionHook = new hook_1.Hook({
|
|
39
|
+
debugPrintExceptions: "onConnection callback"
|
|
40
|
+
});
|
|
41
|
+
// Map of callbacks to call when a new message comes in.
|
|
42
|
+
self.onMessageHook = new hook_1.Hook({
|
|
43
|
+
debugPrintExceptions: "onMessage callback"
|
|
44
|
+
});
|
|
45
|
+
self.stream_server = new stream_server_1.StreamServer(httpServer);
|
|
46
|
+
self.stream_server.register(function (socket) {
|
|
47
|
+
// socket implements the SockJSConnection interface
|
|
48
|
+
socket._meteorSession = null;
|
|
49
|
+
var sendError = function (reason, offendingMessage) {
|
|
50
|
+
var msg = { msg: 'error', reason, offendingMessage };
|
|
51
|
+
socket.send((0, utils_1.stringifyDDP)(msg));
|
|
52
|
+
};
|
|
53
|
+
socket.on('data', async function (raw_msg) {
|
|
54
|
+
try {
|
|
55
|
+
try {
|
|
56
|
+
var msg = (0, utils_1.parseDDP)(raw_msg);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
sendError('Parse error');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (msg === null || !msg.msg) {
|
|
63
|
+
sendError('Bad request', msg);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (msg.msg === 'connect') {
|
|
67
|
+
if (socket._meteorSession) {
|
|
68
|
+
sendError("Already connected", msg);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
self._handleConnect(socket, msg);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (!socket._meteorSession) {
|
|
75
|
+
sendError('Must connect first', msg);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
await socket._meteorSession.processMessage(msg);
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
// XXX print stack nicely
|
|
82
|
+
console.log("Internal exception while processing message", msg, e);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
socket.on('close', function () {
|
|
86
|
+
if (socket._meteorSession) {
|
|
87
|
+
socket._meteorSession.close();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* @summary Register a callback to be called when a new DDP connection is made to the server.
|
|
94
|
+
* @locus Server
|
|
95
|
+
* @param {function} callback The function to call when a new DDP connection is established.
|
|
96
|
+
* @memberOf Meteor
|
|
97
|
+
* @importFromPackage meteor
|
|
98
|
+
*/
|
|
99
|
+
onConnection(fn) {
|
|
100
|
+
var self = this;
|
|
101
|
+
return self.onConnectionHook.register(fn);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* @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()`
|
|
105
|
+
* @locus Server
|
|
106
|
+
* @alias setPublicationStrategy
|
|
107
|
+
* @param publicationName {String}
|
|
108
|
+
* @param strategy {{useCollectionView: boolean, doAccountingForCollection: boolean}}
|
|
109
|
+
* @memberOf Meteor.server
|
|
110
|
+
* @importFromPackage meteor
|
|
111
|
+
*/
|
|
112
|
+
setPublicationStrategy(publicationName, strategy) {
|
|
113
|
+
if (!Object.values(DDPServer.publicationStrategies).includes(strategy)) {
|
|
110
114
|
throw new Error(`Invalid merge strategy: ${strategy}
|
|
111
|
-
for collection ${publicationName}`);
|
|
112
|
-
}
|
|
113
|
-
this._publicationStrategies[publicationName] = strategy;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* @summary Gets the publication strategy for the requested publication. You call this method from `Meteor.server`, like `Meteor.server.getPublicationStrategy()`
|
|
117
|
-
* @locus Server
|
|
118
|
-
* @alias getPublicationStrategy
|
|
119
|
-
* @param publicationName {String}
|
|
120
|
-
* @memberOf Meteor.server
|
|
121
|
-
* @importFromPackage meteor
|
|
122
|
-
* @return {{useCollectionView: boolean, doAccountingForCollection: boolean}}
|
|
123
|
-
*/
|
|
124
|
-
getPublicationStrategy(publicationName) {
|
|
125
|
-
return this._publicationStrategies[publicationName]
|
|
126
|
-
|| this.options.defaultPublicationStrategy;
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* @summary Register a callback to be called when a new DDP message is received.
|
|
130
|
-
* @locus Server
|
|
131
|
-
* @param {function} callback The function to call when a new DDP message is received.
|
|
132
|
-
* @memberOf Meteor
|
|
133
|
-
* @importFromPackage meteor
|
|
134
|
-
*/
|
|
135
|
-
onMessage(fn) {
|
|
136
|
-
var self = this;
|
|
137
|
-
return self.onMessageHook.register(fn);
|
|
138
|
-
}
|
|
139
|
-
_handleConnect(socket, msg) {
|
|
140
|
-
var self = this;
|
|
141
|
-
// The connect message must specify a version and an array of supported
|
|
142
|
-
// versions, and it must claim to support what it is proposing.
|
|
143
|
-
if (!(typeof (msg.version) === 'string' &&
|
|
144
|
-
Array.isArray(msg.support) &&
|
|
145
|
-
msg.support.every(s => typeof s === "string") &&
|
|
146
|
-
msg.support.includes(msg.version))) {
|
|
147
|
-
socket.send((0, utils_1.stringifyDDP)({
|
|
148
|
-
msg: 'failed',
|
|
149
|
-
version: utils_1.SUPPORTED_DDP_VERSIONS[0]
|
|
150
|
-
}));
|
|
151
|
-
socket.close();
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
// In the future, handle session resumption: something like:
|
|
155
|
-
// socket._meteorSession = self.sessions[msg.session]
|
|
156
|
-
var version = _calculateVersion(msg.support, utils_1.SUPPORTED_DDP_VERSIONS);
|
|
157
|
-
if (msg.version !== version) {
|
|
158
|
-
// The best version to use (according to the client's stated preferences)
|
|
159
|
-
// is not the one the client is trying to use. Inform them about the best
|
|
160
|
-
// version to use.
|
|
161
|
-
socket.send((0, utils_1.stringifyDDP)({ msg: 'failed', version: version }));
|
|
162
|
-
socket.close();
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
// Yay, version matches! Create a new session.
|
|
166
|
-
// Note: Troposphere depends on the ability to mutate
|
|
167
|
-
// Meteor.server.options.heartbeatTimeout! This is a hack, but it's life.
|
|
168
|
-
socket._meteorSession = new session_1.DDPSession(self, version, socket, self.options);
|
|
169
|
-
self.sessions.set(socket._meteorSession.id, socket._meteorSession);
|
|
170
|
-
self.onConnectionHook.each(function (callback) {
|
|
171
|
-
if (socket._meteorSession)
|
|
172
|
-
callback(socket._meteorSession.connectionHandle);
|
|
173
|
-
return true;
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Register a publish handler function.
|
|
178
|
-
*
|
|
179
|
-
* @param name {String} identifier for query
|
|
180
|
-
* @param handler {Function} publish handler
|
|
181
|
-
*
|
|
182
|
-
* Server will call handler function on each new subscription,
|
|
183
|
-
* either when receiving DDP sub message for a named subscription, or on
|
|
184
|
-
* DDP connect for a universal subscription.
|
|
185
|
-
*
|
|
186
|
-
* If name is null, this will be a subscription that is
|
|
187
|
-
* automatically established and permanently on for all connected
|
|
188
|
-
* client, instead of a subscription that can be turned on and off
|
|
189
|
-
* with subscribe().
|
|
190
|
-
*
|
|
191
|
-
*/
|
|
192
|
-
/**
|
|
193
|
-
* @summary Publish a record set.
|
|
194
|
-
* @memberOf Meteor
|
|
195
|
-
* @importFromPackage meteor
|
|
196
|
-
* @locus Server
|
|
197
|
-
* @param {String|Object} name If String, name of the record set. If Object, publications Dictionary of publish functions by name. If `null`, the set has no name, and the record set is automatically sent to all connected clients.
|
|
198
|
-
* @param {Function} func Function called on the server each time a client subscribes. Inside the function, `this` is the publish handler object, described below. If the client passed arguments to `subscribe`, the function is called with the same arguments.
|
|
199
|
-
*/
|
|
200
|
-
publish(name, handler) {
|
|
201
|
-
var self = this;
|
|
202
|
-
if (typeof name === "string") {
|
|
203
|
-
if (name in self.publish_handlers) {
|
|
204
|
-
console.log("Ignoring duplicate publish named '" + name + "'");
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
self.publish_handlers[name] = handler;
|
|
208
|
-
}
|
|
209
|
-
else if (name == null) {
|
|
210
|
-
self.universal_publish_handlers.push(handler);
|
|
211
|
-
// Spin up the new publisher on any existing session too. Run each
|
|
212
|
-
// session's subscription in a new Fiber, so that there's no change for
|
|
213
|
-
// self.sessions to change while we're running this loop.
|
|
214
|
-
self.sessions.forEach(function (session) {
|
|
215
|
-
if (!session._dontStartNewUniversalSubs) {
|
|
216
|
-
session._startSubscription(handler);
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
else {
|
|
221
|
-
for (const [key, value] of Object.entries(name)) {
|
|
222
|
-
self.publish(key, value);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
_removeSession(session) {
|
|
227
|
-
this.sessions.delete(session.id);
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* @summary Defines functions that can be invoked over the network by clients.
|
|
231
|
-
* @locus Anywhere
|
|
232
|
-
* @param {Object} methods Dictionary whose keys are method names and values are functions.
|
|
233
|
-
* @memberOf Meteor
|
|
234
|
-
* @importFromPackage meteor
|
|
235
|
-
*/
|
|
236
|
-
methods(methods) {
|
|
237
|
-
var self = this;
|
|
238
|
-
for (const [name, func] of Object.entries(methods)) {
|
|
239
|
-
if (typeof func !== 'function')
|
|
240
|
-
throw new Error("Method '" + name + "' must be a function");
|
|
241
|
-
if (self.method_handlers[name])
|
|
242
|
-
throw new Error("A method named '" + name + "' is already defined");
|
|
243
|
-
self.method_handlers[name] = func;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
// A version of the call method that always returns a Promise.
|
|
247
|
-
callAsync(name, ...args) {
|
|
248
|
-
return this.applyAsync(name, args);
|
|
249
|
-
}
|
|
250
|
-
// @param options {Optional Object}
|
|
251
|
-
applyAsync(name, args) {
|
|
252
|
-
// Run the handler
|
|
253
|
-
var handler = this.method_handlers[name];
|
|
254
|
-
if (!handler) {
|
|
255
|
-
return Promise.reject(ddpError(404, `Method '${name}' not found`));
|
|
256
|
-
}
|
|
257
|
-
// If this is a method call from within another method or publish function,
|
|
258
|
-
// get the user state from the outer method or publish function, otherwise
|
|
259
|
-
// don't allow setUserId to be called
|
|
260
|
-
var userId = null;
|
|
261
|
-
var setUserId = function () {
|
|
262
|
-
throw new Error("Can't call setUserId on a server initiated method call");
|
|
263
|
-
};
|
|
264
|
-
var connection = null;
|
|
265
|
-
var currentMethodInvocation = exports.DDP._CurrentMethodInvocation;
|
|
266
|
-
var currentPublicationInvocation = exports.DDP._CurrentPublicationInvocation;
|
|
267
|
-
var randomSeed = null;
|
|
268
|
-
if (currentMethodInvocation) {
|
|
269
|
-
userId = currentMethodInvocation.userId;
|
|
270
|
-
setUserId = function (userId) {
|
|
271
|
-
currentMethodInvocation.setUserId(userId);
|
|
272
|
-
};
|
|
273
|
-
connection = currentMethodInvocation.connection;
|
|
274
|
-
randomSeed = (0, random_stream_1.makeRpcSeed)(currentMethodInvocation, name);
|
|
275
|
-
}
|
|
276
|
-
else if (currentPublicationInvocation) {
|
|
277
|
-
userId = currentPublicationInvocation.userId;
|
|
278
|
-
setUserId = function (userId) {
|
|
279
|
-
currentPublicationInvocation._session._setUserId(userId);
|
|
280
|
-
};
|
|
281
|
-
connection = currentPublicationInvocation.connection;
|
|
282
|
-
}
|
|
283
|
-
var invocation = new method_invocation_1.MethodInvocation({
|
|
284
|
-
isSimulation: false,
|
|
285
|
-
userId,
|
|
286
|
-
setUserId,
|
|
287
|
-
connection,
|
|
288
|
-
randomSeed
|
|
289
|
-
});
|
|
290
|
-
return new Promise(resolve => {
|
|
291
|
-
const oldInvocation = exports.DDP._CurrentMethodInvocation;
|
|
292
|
-
try {
|
|
293
|
-
exports.DDP._CurrentMethodInvocation = invocation;
|
|
294
|
-
const result = maybeAuditArgumentChecks(handler, invocation, (0, ejson_1.clone)(args), "internal call to '" + name + "'");
|
|
295
|
-
resolve(result);
|
|
296
|
-
}
|
|
297
|
-
finally {
|
|
298
|
-
exports.DDP._CurrentMethodInvocation = oldInvocation;
|
|
299
|
-
}
|
|
300
|
-
}).then(ejson_1.clone);
|
|
301
|
-
}
|
|
302
|
-
_urlForSession(sessionId) {
|
|
303
|
-
var self = this;
|
|
304
|
-
var session = self.sessions.get(sessionId);
|
|
305
|
-
if (session)
|
|
306
|
-
return session._socketUrl;
|
|
307
|
-
else
|
|
308
|
-
return null;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
exports.DDPServer = DDPServer;
|
|
312
|
-
// Publication strategies define how we handle data from published cursors at the collection level
|
|
313
|
-
// This allows someone to:
|
|
314
|
-
// - Choose a trade-off between client-server bandwidth and server memory usage
|
|
315
|
-
// - Implement special (non-mongo) collections like volatile message queues
|
|
316
|
-
DDPServer.publicationStrategies = {
|
|
317
|
-
// SERVER_MERGE is the default strategy.
|
|
318
|
-
// When using this strategy, the server maintains a copy of all data a connection is subscribed to.
|
|
319
|
-
// This allows us to only send deltas over multiple publications.
|
|
320
|
-
SERVER_MERGE: {
|
|
321
|
-
useCollectionView: true,
|
|
322
|
-
doAccountingForCollection: true,
|
|
323
|
-
},
|
|
324
|
-
// The NO_MERGE_NO_HISTORY strategy results in the server sending all publication data
|
|
325
|
-
// directly to the client. It does not remember what it has previously sent
|
|
326
|
-
// to it will not trigger removed messages when a subscription is stopped.
|
|
327
|
-
// This should only be chosen for special use cases like send-and-forget queues.
|
|
328
|
-
NO_MERGE_NO_HISTORY: {
|
|
329
|
-
useCollectionView: false,
|
|
330
|
-
doAccountingForCollection: false,
|
|
331
|
-
},
|
|
332
|
-
// NO_MERGE is similar to NO_MERGE_NO_HISTORY but the server will remember the IDs it has
|
|
333
|
-
// sent to the client so it can remove them when a subscription is stopped.
|
|
334
|
-
// This strategy can be used when a collection is only used in a single publication.
|
|
335
|
-
NO_MERGE: {
|
|
336
|
-
useCollectionView: false,
|
|
337
|
-
doAccountingForCollection: true,
|
|
338
|
-
}
|
|
339
|
-
};
|
|
340
|
-
function _calculateVersion(clientSupportedVersions, serverSupportedVersions) {
|
|
341
|
-
var correctVersion = clientSupportedVersions.find(version => serverSupportedVersions.includes(version));
|
|
342
|
-
if (!correctVersion) {
|
|
343
|
-
correctVersion = serverSupportedVersions[0];
|
|
344
|
-
}
|
|
345
|
-
return correctVersion;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
//
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
//
|
|
355
|
-
//
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
exception =
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
//
|
|
366
|
-
//
|
|
367
|
-
//
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
this.
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
this.errorType = "Meteor.Error";
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
exports.ClientSafeError = ClientSafeError;
|
|
115
|
+
for collection ${publicationName}`);
|
|
116
|
+
}
|
|
117
|
+
this._publicationStrategies[publicationName] = strategy;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* @summary Gets the publication strategy for the requested publication. You call this method from `Meteor.server`, like `Meteor.server.getPublicationStrategy()`
|
|
121
|
+
* @locus Server
|
|
122
|
+
* @alias getPublicationStrategy
|
|
123
|
+
* @param publicationName {String}
|
|
124
|
+
* @memberOf Meteor.server
|
|
125
|
+
* @importFromPackage meteor
|
|
126
|
+
* @return {{useCollectionView: boolean, doAccountingForCollection: boolean}}
|
|
127
|
+
*/
|
|
128
|
+
getPublicationStrategy(publicationName) {
|
|
129
|
+
return this._publicationStrategies[publicationName]
|
|
130
|
+
|| this.options.defaultPublicationStrategy;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* @summary Register a callback to be called when a new DDP message is received.
|
|
134
|
+
* @locus Server
|
|
135
|
+
* @param {function} callback The function to call when a new DDP message is received.
|
|
136
|
+
* @memberOf Meteor
|
|
137
|
+
* @importFromPackage meteor
|
|
138
|
+
*/
|
|
139
|
+
onMessage(fn) {
|
|
140
|
+
var self = this;
|
|
141
|
+
return self.onMessageHook.register(fn);
|
|
142
|
+
}
|
|
143
|
+
_handleConnect(socket, msg) {
|
|
144
|
+
var self = this;
|
|
145
|
+
// The connect message must specify a version and an array of supported
|
|
146
|
+
// versions, and it must claim to support what it is proposing.
|
|
147
|
+
if (!(typeof (msg.version) === 'string' &&
|
|
148
|
+
Array.isArray(msg.support) &&
|
|
149
|
+
msg.support.every(s => typeof s === "string") &&
|
|
150
|
+
msg.support.includes(msg.version))) {
|
|
151
|
+
socket.send((0, utils_1.stringifyDDP)({
|
|
152
|
+
msg: 'failed',
|
|
153
|
+
version: utils_1.SUPPORTED_DDP_VERSIONS[0]
|
|
154
|
+
}));
|
|
155
|
+
socket.close();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// In the future, handle session resumption: something like:
|
|
159
|
+
// socket._meteorSession = self.sessions[msg.session]
|
|
160
|
+
var version = _calculateVersion(msg.support, utils_1.SUPPORTED_DDP_VERSIONS);
|
|
161
|
+
if (msg.version !== version) {
|
|
162
|
+
// The best version to use (according to the client's stated preferences)
|
|
163
|
+
// is not the one the client is trying to use. Inform them about the best
|
|
164
|
+
// version to use.
|
|
165
|
+
socket.send((0, utils_1.stringifyDDP)({ msg: 'failed', version: version }));
|
|
166
|
+
socket.close();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
// Yay, version matches! Create a new session.
|
|
170
|
+
// Note: Troposphere depends on the ability to mutate
|
|
171
|
+
// Meteor.server.options.heartbeatTimeout! This is a hack, but it's life.
|
|
172
|
+
socket._meteorSession = new session_1.DDPSession(self, version, socket, self.options);
|
|
173
|
+
self.sessions.set(socket._meteorSession.id, socket._meteorSession);
|
|
174
|
+
self.onConnectionHook.each(function (callback) {
|
|
175
|
+
if (socket._meteorSession)
|
|
176
|
+
callback(socket._meteorSession.connectionHandle);
|
|
177
|
+
return true;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Register a publish handler function.
|
|
182
|
+
*
|
|
183
|
+
* @param name {String} identifier for query
|
|
184
|
+
* @param handler {Function} publish handler
|
|
185
|
+
*
|
|
186
|
+
* Server will call handler function on each new subscription,
|
|
187
|
+
* either when receiving DDP sub message for a named subscription, or on
|
|
188
|
+
* DDP connect for a universal subscription.
|
|
189
|
+
*
|
|
190
|
+
* If name is null, this will be a subscription that is
|
|
191
|
+
* automatically established and permanently on for all connected
|
|
192
|
+
* client, instead of a subscription that can be turned on and off
|
|
193
|
+
* with subscribe().
|
|
194
|
+
*
|
|
195
|
+
*/
|
|
196
|
+
/**
|
|
197
|
+
* @summary Publish a record set.
|
|
198
|
+
* @memberOf Meteor
|
|
199
|
+
* @importFromPackage meteor
|
|
200
|
+
* @locus Server
|
|
201
|
+
* @param {String|Object} name If String, name of the record set. If Object, publications Dictionary of publish functions by name. If `null`, the set has no name, and the record set is automatically sent to all connected clients.
|
|
202
|
+
* @param {Function} func Function called on the server each time a client subscribes. Inside the function, `this` is the publish handler object, described below. If the client passed arguments to `subscribe`, the function is called with the same arguments.
|
|
203
|
+
*/
|
|
204
|
+
publish(name, handler) {
|
|
205
|
+
var self = this;
|
|
206
|
+
if (typeof name === "string") {
|
|
207
|
+
if (name in self.publish_handlers) {
|
|
208
|
+
console.log("Ignoring duplicate publish named '" + name + "'");
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
self.publish_handlers[name] = handler;
|
|
212
|
+
}
|
|
213
|
+
else if (name == null) {
|
|
214
|
+
self.universal_publish_handlers.push(handler);
|
|
215
|
+
// Spin up the new publisher on any existing session too. Run each
|
|
216
|
+
// session's subscription in a new Fiber, so that there's no change for
|
|
217
|
+
// self.sessions to change while we're running this loop.
|
|
218
|
+
self.sessions.forEach(function (session) {
|
|
219
|
+
if (!session._dontStartNewUniversalSubs) {
|
|
220
|
+
session._startSubscription(handler);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
for (const [key, value] of Object.entries(name)) {
|
|
226
|
+
self.publish(key, value);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
_removeSession(session) {
|
|
231
|
+
this.sessions.delete(session.id);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* @summary Defines functions that can be invoked over the network by clients.
|
|
235
|
+
* @locus Anywhere
|
|
236
|
+
* @param {Object} methods Dictionary whose keys are method names and values are functions.
|
|
237
|
+
* @memberOf Meteor
|
|
238
|
+
* @importFromPackage meteor
|
|
239
|
+
*/
|
|
240
|
+
methods(methods) {
|
|
241
|
+
var self = this;
|
|
242
|
+
for (const [name, func] of Object.entries(methods)) {
|
|
243
|
+
if (typeof func !== 'function')
|
|
244
|
+
throw new Error("Method '" + name + "' must be a function");
|
|
245
|
+
if (self.method_handlers[name])
|
|
246
|
+
throw new Error("A method named '" + name + "' is already defined");
|
|
247
|
+
self.method_handlers[name] = func;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// A version of the call method that always returns a Promise.
|
|
251
|
+
callAsync(name, ...args) {
|
|
252
|
+
return this.applyAsync(name, args);
|
|
253
|
+
}
|
|
254
|
+
// @param options {Optional Object}
|
|
255
|
+
applyAsync(name, args) {
|
|
256
|
+
// Run the handler
|
|
257
|
+
var handler = this.method_handlers[name];
|
|
258
|
+
if (!handler) {
|
|
259
|
+
return Promise.reject(ddpError(404, `Method '${name}' not found`));
|
|
260
|
+
}
|
|
261
|
+
// If this is a method call from within another method or publish function,
|
|
262
|
+
// get the user state from the outer method or publish function, otherwise
|
|
263
|
+
// don't allow setUserId to be called
|
|
264
|
+
var userId = null;
|
|
265
|
+
var setUserId = function () {
|
|
266
|
+
throw new Error("Can't call setUserId on a server initiated method call");
|
|
267
|
+
};
|
|
268
|
+
var connection = null;
|
|
269
|
+
var currentMethodInvocation = exports.DDP._CurrentMethodInvocation;
|
|
270
|
+
var currentPublicationInvocation = exports.DDP._CurrentPublicationInvocation;
|
|
271
|
+
var randomSeed = null;
|
|
272
|
+
if (currentMethodInvocation) {
|
|
273
|
+
userId = currentMethodInvocation.userId;
|
|
274
|
+
setUserId = function (userId) {
|
|
275
|
+
currentMethodInvocation.setUserId(userId);
|
|
276
|
+
};
|
|
277
|
+
connection = currentMethodInvocation.connection;
|
|
278
|
+
randomSeed = (0, random_stream_1.makeRpcSeed)(currentMethodInvocation, name);
|
|
279
|
+
}
|
|
280
|
+
else if (currentPublicationInvocation) {
|
|
281
|
+
userId = currentPublicationInvocation.userId;
|
|
282
|
+
setUserId = function (userId) {
|
|
283
|
+
currentPublicationInvocation._session._setUserId(userId);
|
|
284
|
+
};
|
|
285
|
+
connection = currentPublicationInvocation.connection;
|
|
286
|
+
}
|
|
287
|
+
var invocation = new method_invocation_1.MethodInvocation({
|
|
288
|
+
isSimulation: false,
|
|
289
|
+
userId,
|
|
290
|
+
setUserId,
|
|
291
|
+
connection,
|
|
292
|
+
randomSeed
|
|
293
|
+
});
|
|
294
|
+
return new Promise(resolve => {
|
|
295
|
+
const oldInvocation = exports.DDP._CurrentMethodInvocation;
|
|
296
|
+
try {
|
|
297
|
+
exports.DDP._CurrentMethodInvocation = invocation;
|
|
298
|
+
const result = maybeAuditArgumentChecks(handler, invocation, (0, ejson_1.clone)(args), "internal call to '" + name + "'");
|
|
299
|
+
resolve(result);
|
|
300
|
+
}
|
|
301
|
+
finally {
|
|
302
|
+
exports.DDP._CurrentMethodInvocation = oldInvocation;
|
|
303
|
+
}
|
|
304
|
+
}).then(ejson_1.clone);
|
|
305
|
+
}
|
|
306
|
+
_urlForSession(sessionId) {
|
|
307
|
+
var self = this;
|
|
308
|
+
var session = self.sessions.get(sessionId);
|
|
309
|
+
if (session)
|
|
310
|
+
return session._socketUrl;
|
|
311
|
+
else
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
exports.DDPServer = DDPServer;
|
|
316
|
+
// Publication strategies define how we handle data from published cursors at the collection level
|
|
317
|
+
// This allows someone to:
|
|
318
|
+
// - Choose a trade-off between client-server bandwidth and server memory usage
|
|
319
|
+
// - Implement special (non-mongo) collections like volatile message queues
|
|
320
|
+
DDPServer.publicationStrategies = {
|
|
321
|
+
// SERVER_MERGE is the default strategy.
|
|
322
|
+
// When using this strategy, the server maintains a copy of all data a connection is subscribed to.
|
|
323
|
+
// This allows us to only send deltas over multiple publications.
|
|
324
|
+
SERVER_MERGE: {
|
|
325
|
+
useCollectionView: true,
|
|
326
|
+
doAccountingForCollection: true,
|
|
327
|
+
},
|
|
328
|
+
// The NO_MERGE_NO_HISTORY strategy results in the server sending all publication data
|
|
329
|
+
// directly to the client. It does not remember what it has previously sent
|
|
330
|
+
// to it will not trigger removed messages when a subscription is stopped.
|
|
331
|
+
// This should only be chosen for special use cases like send-and-forget queues.
|
|
332
|
+
NO_MERGE_NO_HISTORY: {
|
|
333
|
+
useCollectionView: false,
|
|
334
|
+
doAccountingForCollection: false,
|
|
335
|
+
},
|
|
336
|
+
// NO_MERGE is similar to NO_MERGE_NO_HISTORY but the server will remember the IDs it has
|
|
337
|
+
// sent to the client so it can remove them when a subscription is stopped.
|
|
338
|
+
// This strategy can be used when a collection is only used in a single publication.
|
|
339
|
+
NO_MERGE: {
|
|
340
|
+
useCollectionView: false,
|
|
341
|
+
doAccountingForCollection: true,
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
function _calculateVersion(clientSupportedVersions, serverSupportedVersions) {
|
|
345
|
+
var correctVersion = clientSupportedVersions.find(version => serverSupportedVersions.includes(version));
|
|
346
|
+
if (!correctVersion) {
|
|
347
|
+
correctVersion = serverSupportedVersions[0];
|
|
348
|
+
}
|
|
349
|
+
return correctVersion;
|
|
350
|
+
}
|
|
351
|
+
;
|
|
352
|
+
// "blind" exceptions other than those that were deliberately thrown to signal
|
|
353
|
+
// errors to the client
|
|
354
|
+
function wrapInternalException(exception, context) {
|
|
355
|
+
if (!exception)
|
|
356
|
+
return exception;
|
|
357
|
+
// To allow packages to throw errors intended for the client but not have to
|
|
358
|
+
// depend on the Meteor.Error class, `isClientSafe` can be set to true on any
|
|
359
|
+
// error before it is thrown.
|
|
360
|
+
if (exception.isClientSafe) {
|
|
361
|
+
if (!(exception instanceof ClientSafeError)) {
|
|
362
|
+
const originalMessage = exception.message;
|
|
363
|
+
exception = ddpError(exception.error, exception.reason, exception.details);
|
|
364
|
+
exception.message = originalMessage;
|
|
365
|
+
}
|
|
366
|
+
return exception;
|
|
367
|
+
}
|
|
368
|
+
// Did the error contain more details that could have been useful if caught in
|
|
369
|
+
// server code (or if thrown from non-client-originated code), but also
|
|
370
|
+
// provided a "sanitized" version with more context than 500 Internal server
|
|
371
|
+
// error? Use that.
|
|
372
|
+
if (exception.sanitizedError) {
|
|
373
|
+
if (exception.sanitizedError.isClientSafe)
|
|
374
|
+
return exception.sanitizedError;
|
|
375
|
+
}
|
|
376
|
+
console.error("Error " + context + ":", exception);
|
|
377
|
+
return ddpError(500, "Internal server error");
|
|
378
|
+
}
|
|
379
|
+
;
|
|
380
|
+
// Audit argument checks, if the audit-argument-checks package exists (it is a
|
|
381
|
+
// weak dependency of this package).
|
|
382
|
+
function maybeAuditArgumentChecks(f, context, args, description) {
|
|
383
|
+
args = args || [];
|
|
384
|
+
/*if (Package['audit-argument-checks']) {
|
|
385
|
+
return Match._failIfArgumentsAreNotAllChecked(
|
|
386
|
+
f, context, args, description);
|
|
387
|
+
}*/
|
|
388
|
+
return f.apply(context, args);
|
|
389
|
+
}
|
|
390
|
+
;
|
|
391
|
+
function ddpError(error, reason, details) {
|
|
392
|
+
return { isClientSafe: true, error, reason, message: (reason ? reason + " " : "") + "[" + error + "]", errorType: "Meteor.Error" };
|
|
393
|
+
}
|
|
394
|
+
class ClientSafeError extends Error {
|
|
395
|
+
constructor(error, reason, details) {
|
|
396
|
+
super((reason ? reason + " " : "") + "[" + error + "]");
|
|
397
|
+
this.error = error;
|
|
398
|
+
this.reason = reason;
|
|
399
|
+
this.details = details;
|
|
400
|
+
this.isClientSafe = true;
|
|
401
|
+
this.errorType = "Meteor.Error";
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
exports.ClientSafeError = ClientSafeError;
|