mongodb-livedata-server 0.0.14 → 0.1.0
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 +70 -69
- package/dist/meteor/binary-heap/max_heap.d.ts +2 -2
- package/dist/meteor/binary-heap/max_heap.js +7 -7
- package/dist/meteor/binary-heap/min_max_heap.d.ts +1 -1
- package/dist/meteor/binary-heap/min_max_heap.js +3 -3
- package/dist/meteor/ddp/livedata_server.d.ts +3 -2
- package/dist/meteor/ddp/livedata_server.js +1 -0
- package/dist/meteor/ddp/session-collection-view.js +3 -4
- package/dist/meteor/ddp/session.d.ts +6 -2
- package/dist/meteor/ddp/session.js +20 -5
- package/dist/meteor/ddp/stream_server.js +1 -1
- package/dist/meteor/ddp/subscription.d.ts +2 -0
- package/dist/meteor/ddp/subscription.js +13 -0
- package/dist/meteor/diff-sequence/diff.d.ts +2 -3
- package/dist/meteor/mongo/caching_change_observer.d.ts +2 -3
- package/dist/meteor/mongo/caching_change_observer.js +6 -38
- package/dist/meteor/mongo/live_cursor.js +3 -0
- package/dist/meteor/mongo/observe_multiplexer.d.ts +3 -1
- package/dist/meteor/mongo/observe_multiplexer.js +14 -38
- package/dist/meteor/mongo/oplog-observe-driver.d.ts +4 -3
- package/dist/meteor/mongo/oplog-observe-driver.js +25 -26
- package/dist/meteor/mongo/polling_observe_driver.js +30 -4
- package/dist/meteor/ordered-dict/ordered_dict.d.ts +2 -2
- package/dist/meteor/ordered-dict/ordered_dict.js +2 -2
- package/git-filter-repo.py +4005 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,69 +1,70 @@
|
|
|
1
|
-
MongoDB Live Data Server
|
|
2
|
-
========================
|
|
3
|
-
|
|
4
|
-
This project is essentially a MongoDB live data driver (based either on polling or on Oplog tailing) combined with a DDP server, extracted
|
|
5
|
-
out of [Meteor](https://github.com/meteor/meteor), with **Fibers** and **underscore** dependencies removed and code converted to Typescript.
|
|
6
|
-
|
|
7
|
-
Live data is one of the root concepts of Meteor. Data is served via WebSockets via the DDP protocol and updated automatically whenever something changes in the database. Also, calling server methods via WebSocket is supported.
|
|
8
|
-
|
|
9
|
-
Using Meteor locks you into the Meteor ecosystem, which has some problems (mostly for historical reasons). Using live data as a separate npm package might be preferable in many scenarios. Also, people who are trying to migrate from Meteor, might find this package useful as an intermediate step.
|
|
10
|
-
|
|
11
|
-
### Installation
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
npm i mongodb-livedata-server
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
### Usage
|
|
18
|
-
|
|
19
|
-
As a most common example, this is how you can use livedata with Express.js:
|
|
20
|
-
|
|
21
|
-
```js
|
|
22
|
-
const { DDPServer, LiveCursor, LiveMongoConnection } = require('mongodb-livedata-server')
|
|
23
|
-
const express = require('express')
|
|
24
|
-
const app = express()
|
|
25
|
-
const port = 3000
|
|
26
|
-
|
|
27
|
-
app.get('/', (req, res) => {
|
|
28
|
-
res.send('Hello World!')
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
const httpServer = app.listen(port, () => {
|
|
32
|
-
console.log(`Example app listening on port ${port}`)
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
const liveMongoConnection = new LiveMongoConnection(process.env.MONGO_URL, {
|
|
36
|
-
oplogUrl: process.env.MONGO_OPLOG_URL
|
|
37
|
-
});
|
|
38
|
-
const liveDataServer = new DDPServer({}, httpServer);
|
|
39
|
-
|
|
40
|
-
liveDataServer.methods({
|
|
41
|
-
"test-method": async (msg) => {
|
|
42
|
-
console.log("Test msg: ", msg);
|
|
43
|
-
return "hello! Current timestamp is: " + Date.now()
|
|
44
|
-
}
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
liveDataServer.publish({
|
|
48
|
-
"test-subscription": async () => {
|
|
49
|
-
return new LiveCursor(liveMongoConnection, "test-collection", { category: "apples" });
|
|
50
|
-
}
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
`liveDataServer.methods` and `liveDataServer.publish` have exactly same interface as [Meteor.methods](https://docs.meteor.com/api/methods.html#Meteor-methods) and [Meteor.publish](https://docs.meteor.com/api/pubsub.html#Meteor-publish) respectively, notice however that when publishing subscriptions, you must use `LiveCursor` rather than a normal MongoDB cursor.
|
|
56
|
-
|
|
57
|
-
### Important notes
|
|
58
|
-
|
|
59
|
-
- The project is in alpha. Use at your own risk.
|
|
60
|
-
- Neither method context nor subscription context have the `unblock` method anymore (because this package doesn't use Fibers)
|
|
61
|
-
- Meteor syntax for MongoDB queries is not supported. Please always use MongoDB Node.js driver syntax. For example, instead of
|
|
62
|
-
```ts
|
|
63
|
-
const doc = myCollection.findOne(id);
|
|
64
|
-
```
|
|
65
|
-
use
|
|
66
|
-
```ts
|
|
67
|
-
const doc = await myCollection.findOne({ _id: id });
|
|
68
|
-
```
|
|
69
|
-
- Neither MongoDB.ObjectId nor it's Meteor.js alternative is supported at the moment. String ids only.
|
|
1
|
+
MongoDB Live Data Server
|
|
2
|
+
========================
|
|
3
|
+
|
|
4
|
+
This project is essentially a MongoDB live data driver (based either on polling or on Oplog tailing) combined with a DDP server, extracted
|
|
5
|
+
out of [Meteor](https://github.com/meteor/meteor), with **Fibers** and **underscore** dependencies removed and code converted to Typescript.
|
|
6
|
+
|
|
7
|
+
Live data is one of the root concepts of Meteor. Data is served via WebSockets via the DDP protocol and updated automatically whenever something changes in the database. Also, calling server methods via WebSocket is supported.
|
|
8
|
+
|
|
9
|
+
Using Meteor locks you into the Meteor ecosystem, which has some problems (mostly for historical reasons). Using live data as a separate npm package might be preferable in many scenarios. Also, people who are trying to migrate from Meteor, might find this package useful as an intermediate step.
|
|
10
|
+
|
|
11
|
+
### Installation
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
npm i mongodb-livedata-server
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Usage
|
|
18
|
+
|
|
19
|
+
As a most common example, this is how you can use livedata with Express.js:
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
const { DDPServer, LiveCursor, LiveMongoConnection } = require('mongodb-livedata-server')
|
|
23
|
+
const express = require('express')
|
|
24
|
+
const app = express()
|
|
25
|
+
const port = 3000
|
|
26
|
+
|
|
27
|
+
app.get('/', (req, res) => {
|
|
28
|
+
res.send('Hello World!')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const httpServer = app.listen(port, () => {
|
|
32
|
+
console.log(`Example app listening on port ${port}`)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const liveMongoConnection = new LiveMongoConnection(process.env.MONGO_URL, {
|
|
36
|
+
oplogUrl: process.env.MONGO_OPLOG_URL
|
|
37
|
+
});
|
|
38
|
+
const liveDataServer = new DDPServer({}, httpServer);
|
|
39
|
+
|
|
40
|
+
liveDataServer.methods({
|
|
41
|
+
"test-method": async (msg) => {
|
|
42
|
+
console.log("Test msg: ", msg);
|
|
43
|
+
return "hello! Current timestamp is: " + Date.now()
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
liveDataServer.publish({
|
|
48
|
+
"test-subscription": async () => {
|
|
49
|
+
return new LiveCursor(liveMongoConnection, "test-collection", { category: "apples" });
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`liveDataServer.methods` and `liveDataServer.publish` have exactly same interface as [Meteor.methods](https://docs.meteor.com/api/methods.html#Meteor-methods) and [Meteor.publish](https://docs.meteor.com/api/pubsub.html#Meteor-publish) respectively, notice however that when publishing subscriptions, you must use `LiveCursor` rather than a normal MongoDB cursor.
|
|
56
|
+
|
|
57
|
+
### Important notes
|
|
58
|
+
|
|
59
|
+
- The project is in alpha. Use at your own risk.
|
|
60
|
+
- Neither method context nor subscription context have the `unblock` method anymore (because this package doesn't use Fibers)
|
|
61
|
+
- Meteor syntax for MongoDB queries is not supported. Please always use MongoDB Node.js driver syntax. For example, instead of
|
|
62
|
+
```ts
|
|
63
|
+
const doc = myCollection.findOne(id);
|
|
64
|
+
```
|
|
65
|
+
use
|
|
66
|
+
```ts
|
|
67
|
+
const doc = await myCollection.findOne({ _id: id });
|
|
68
|
+
```
|
|
69
|
+
- Neither MongoDB.ObjectId nor it's Meteor.js alternative is supported at the moment. String ids only.
|
|
70
|
+
- Starting from 0.1.0, this library extends DDP with `init` message, which is used to avoid initial spam of `added` messages.
|
|
@@ -18,12 +18,12 @@ export declare class MaxHeap {
|
|
|
18
18
|
_swap(idxA: any, idxB: any): void;
|
|
19
19
|
get(id: any): any;
|
|
20
20
|
set(id: any, value: any): void;
|
|
21
|
-
|
|
21
|
+
delete(id: any): void;
|
|
22
22
|
has(id: any): any;
|
|
23
23
|
empty(): boolean;
|
|
24
24
|
clear(): void;
|
|
25
25
|
forEach(iterator: any): void;
|
|
26
|
-
size(): number;
|
|
26
|
+
get size(): number;
|
|
27
27
|
setDefault(id: any, def: any): any;
|
|
28
28
|
clone(): MaxHeap;
|
|
29
29
|
maxElementId(): any;
|
|
@@ -51,14 +51,14 @@ class MaxHeap {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
_downHeap(idx) {
|
|
54
|
-
while (leftChildIdx(idx) < this.size
|
|
54
|
+
while (leftChildIdx(idx) < this.size) {
|
|
55
55
|
const left = leftChildIdx(idx);
|
|
56
56
|
const right = rightChildIdx(idx);
|
|
57
57
|
let largest = idx;
|
|
58
|
-
if (left < this.size
|
|
58
|
+
if (left < this.size) {
|
|
59
59
|
largest = this._maxIndex(largest, left);
|
|
60
60
|
}
|
|
61
|
-
if (right < this.size
|
|
61
|
+
if (right < this.size) {
|
|
62
62
|
largest = this._maxIndex(largest, right);
|
|
63
63
|
}
|
|
64
64
|
if (largest === idx) {
|
|
@@ -121,7 +121,7 @@ class MaxHeap {
|
|
|
121
121
|
this._upHeap(this._heap.length - 1);
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
|
-
|
|
124
|
+
delete(id) {
|
|
125
125
|
if (this.has(id)) {
|
|
126
126
|
const last = this._heap.length - 1;
|
|
127
127
|
const idx = this._heapIdx.get(id);
|
|
@@ -143,7 +143,7 @@ class MaxHeap {
|
|
|
143
143
|
return this._heapIdx.has(id);
|
|
144
144
|
}
|
|
145
145
|
empty() {
|
|
146
|
-
return
|
|
146
|
+
return this.size === 0;
|
|
147
147
|
}
|
|
148
148
|
clear() {
|
|
149
149
|
this._heap = [];
|
|
@@ -153,7 +153,7 @@ class MaxHeap {
|
|
|
153
153
|
forEach(iterator) {
|
|
154
154
|
this._heap.forEach(obj => iterator(obj.value, obj.id));
|
|
155
155
|
}
|
|
156
|
-
size() {
|
|
156
|
+
get size() {
|
|
157
157
|
return this._heap.length;
|
|
158
158
|
}
|
|
159
159
|
setDefault(id, def) {
|
|
@@ -168,7 +168,7 @@ class MaxHeap {
|
|
|
168
168
|
return clone;
|
|
169
169
|
}
|
|
170
170
|
maxElementId() {
|
|
171
|
-
return this.size
|
|
171
|
+
return this.size > 0 ? this._heap[0].id : null;
|
|
172
172
|
}
|
|
173
173
|
_selfCheck() {
|
|
174
174
|
for (let i = 1; i < this._heap.length; i++) {
|
|
@@ -3,7 +3,7 @@ export declare class MinMaxHeap extends MaxHeap {
|
|
|
3
3
|
private _minHeap;
|
|
4
4
|
constructor(comparator: any, options?: MaxHeapOptions);
|
|
5
5
|
set(id: string, value: any): void;
|
|
6
|
-
|
|
6
|
+
delete(id: string): void;
|
|
7
7
|
clear(): void;
|
|
8
8
|
setDefault(id: string, def: any): any;
|
|
9
9
|
clone(): MinMaxHeap;
|
|
@@ -24,9 +24,9 @@ class MinMaxHeap extends max_heap_1.MaxHeap {
|
|
|
24
24
|
super.set(id, value);
|
|
25
25
|
this._minHeap.set(id, value);
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
super.
|
|
29
|
-
this._minHeap.
|
|
27
|
+
delete(id) {
|
|
28
|
+
super.delete(id);
|
|
29
|
+
this._minHeap.delete(id);
|
|
30
30
|
}
|
|
31
31
|
clear() {
|
|
32
32
|
super.clear();
|
|
@@ -3,6 +3,7 @@ import { MethodInvocation } from "./method-invocation";
|
|
|
3
3
|
import { StreamServerSocket } from "./stream_server";
|
|
4
4
|
import { DDPSession, SessionConnectionHandle } from "./session";
|
|
5
5
|
import { Server } from "http";
|
|
6
|
+
import { Hook } from "../callback-hook/hook";
|
|
6
7
|
import { Subscription } from "./subscription";
|
|
7
8
|
export declare const DDP: {
|
|
8
9
|
_CurrentPublicationInvocation: Subscription;
|
|
@@ -17,9 +18,9 @@ interface PublicationStrategy {
|
|
|
17
18
|
export declare class DDPServer {
|
|
18
19
|
private options;
|
|
19
20
|
private onConnectionHook;
|
|
20
|
-
|
|
21
|
+
onMessageHook: Hook;
|
|
21
22
|
private publish_handlers;
|
|
22
|
-
|
|
23
|
+
universal_publish_handlers: any[];
|
|
23
24
|
private method_handlers;
|
|
24
25
|
private _publicationStrategies;
|
|
25
26
|
private sessions;
|
|
@@ -370,6 +370,7 @@ function wrapInternalException(exception, context) {
|
|
|
370
370
|
if (exception.sanitizedError.isClientSafe)
|
|
371
371
|
return exception.sanitizedError;
|
|
372
372
|
}
|
|
373
|
+
console.error("Error " + context + ":", exception);
|
|
373
374
|
return ddpError(500, "Internal server error");
|
|
374
375
|
}
|
|
375
376
|
exports.wrapInternalException = wrapInternalException;
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.SessionCollectionView = void 0;
|
|
4
4
|
const diff_1 = require("../diff-sequence/diff");
|
|
5
5
|
const ejson_1 = require("../ejson/ejson");
|
|
6
|
-
const id_map_1 = require("../id-map/id_map");
|
|
7
6
|
const session_document_view_1 = require("./session-document-view");
|
|
8
7
|
/**
|
|
9
8
|
* Represents a client's view of a single collection
|
|
@@ -15,10 +14,10 @@ class SessionCollectionView {
|
|
|
15
14
|
constructor(collectionName, callbacks) {
|
|
16
15
|
this.collectionName = collectionName;
|
|
17
16
|
this.callbacks = callbacks;
|
|
18
|
-
this.documents = new
|
|
17
|
+
this.documents = new Map();
|
|
19
18
|
}
|
|
20
19
|
isEmpty() {
|
|
21
|
-
return this.documents.
|
|
20
|
+
return this.documents.size === 0;
|
|
22
21
|
}
|
|
23
22
|
diff(previous) {
|
|
24
23
|
diff_1.DiffSequence.diffMaps(previous.documents, this.documents, {
|
|
@@ -91,7 +90,7 @@ class SessionCollectionView {
|
|
|
91
90
|
if (docView.existsIn.size === 0) {
|
|
92
91
|
// it is gone from everyone
|
|
93
92
|
self.callbacks.removed(self.collectionName, id);
|
|
94
|
-
self.documents.
|
|
93
|
+
self.documents.delete(id);
|
|
95
94
|
}
|
|
96
95
|
else {
|
|
97
96
|
var changed = {};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import DoubleEndedQueue from "double-ended-queue";
|
|
2
2
|
import { StreamServerSocket } from "./stream_server";
|
|
3
3
|
import { DDPServer } from "./livedata_server";
|
|
4
|
+
import { SessionCollectionView } from "./session-collection-view";
|
|
4
5
|
import { SubscriptionHandle } from "./subscription";
|
|
6
|
+
import { OrderedDict } from "../ordered-dict/ordered_dict";
|
|
5
7
|
export interface SessionConnectionHandle {
|
|
6
8
|
id: string;
|
|
7
9
|
close: Function;
|
|
@@ -20,7 +22,7 @@ interface DDPMessage {
|
|
|
20
22
|
}
|
|
21
23
|
export declare class DDPSession {
|
|
22
24
|
id: string;
|
|
23
|
-
server:
|
|
25
|
+
server: DDPServer;
|
|
24
26
|
inQueue: DoubleEndedQueue<any>;
|
|
25
27
|
userId: string | null;
|
|
26
28
|
connectionHandle: SessionConnectionHandle;
|
|
@@ -41,6 +43,7 @@ export declare class DDPSession {
|
|
|
41
43
|
constructor(server: DDPServer, version: string, socket: StreamServerSocket, options: any);
|
|
42
44
|
sendReady(subscriptionIds: string[]): void;
|
|
43
45
|
_canSend(collectionName: any): boolean;
|
|
46
|
+
sendInitialAdds(collectionName: string, docs: Map<string, any> | OrderedDict): void;
|
|
44
47
|
sendAdded(collectionName: string, id: string, fields: Record<string, any>): void;
|
|
45
48
|
sendChanged(collectionName: string, id: string, fields: Record<string, any>): void;
|
|
46
49
|
sendRemoved(collectionName: string, id: string): void;
|
|
@@ -49,7 +52,8 @@ export declare class DDPSession {
|
|
|
49
52
|
changed: any;
|
|
50
53
|
removed: any;
|
|
51
54
|
};
|
|
52
|
-
getCollectionView(collectionName: string):
|
|
55
|
+
getCollectionView(collectionName: string): SessionCollectionView;
|
|
56
|
+
initialAdds(subscriptionHandle: SubscriptionHandle, collectionName: string, docs: Map<string, any> | OrderedDict): void;
|
|
53
57
|
added(subscriptionHandle: SubscriptionHandle, collectionName: string, id: string, fields: Record<string, any>): void;
|
|
54
58
|
removed(subscriptionHandle: SubscriptionHandle, collectionName: string, id: string): void;
|
|
55
59
|
changed(subscriptionHandle: SubscriptionHandle, collectionName: string, id: string, fields: Record<string, any>): void;
|
|
@@ -14,7 +14,6 @@ const utils_1 = require("./utils");
|
|
|
14
14
|
const diff_1 = require("../diff-sequence/diff");
|
|
15
15
|
const session_collection_view_1 = require("./session-collection-view");
|
|
16
16
|
const subscription_1 = require("./subscription");
|
|
17
|
-
const id_map_1 = require("../id-map/id_map");
|
|
18
17
|
class DDPSession {
|
|
19
18
|
constructor(server, version, socket, options) {
|
|
20
19
|
this.protocol_handlers = {
|
|
@@ -161,7 +160,7 @@ class DDPSession {
|
|
|
161
160
|
self._namedSubs = new Map();
|
|
162
161
|
self._universalSubs = [];
|
|
163
162
|
self.userId = null;
|
|
164
|
-
self.collectionViews = new
|
|
163
|
+
self.collectionViews = new Map();
|
|
165
164
|
// Set this to false to not send messages when collectionViews are
|
|
166
165
|
// modified. This is done when rerunning subs in _setUserId and those messages
|
|
167
166
|
// are calculated via a diff instead.
|
|
@@ -230,6 +229,13 @@ class DDPSession {
|
|
|
230
229
|
_canSend(collectionName) {
|
|
231
230
|
return this._isSending || !this.server.getPublicationStrategy(collectionName).useCollectionView;
|
|
232
231
|
}
|
|
232
|
+
sendInitialAdds(collectionName, docs) {
|
|
233
|
+
if (this._canSend(collectionName)) {
|
|
234
|
+
const items = [];
|
|
235
|
+
docs.forEach(doc => items.push(doc));
|
|
236
|
+
this.send({ msg: "init", collection: collectionName, items });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
233
239
|
sendAdded(collectionName, id, fields) {
|
|
234
240
|
if (this._canSend(collectionName))
|
|
235
241
|
this.send({ msg: "added", collection: collectionName, id, fields });
|
|
@@ -266,6 +272,15 @@ class DDPSession {
|
|
|
266
272
|
}
|
|
267
273
|
return ret;
|
|
268
274
|
}
|
|
275
|
+
initialAdds(subscriptionHandle, collectionName, docs) {
|
|
276
|
+
if (this.server.getPublicationStrategy(collectionName).useCollectionView) {
|
|
277
|
+
const view = this.getCollectionView(collectionName);
|
|
278
|
+
docs.forEach((doc, id) => view.added(subscriptionHandle, id, doc));
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
this.sendInitialAdds(collectionName, docs);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
269
284
|
added(subscriptionHandle, collectionName, id, fields) {
|
|
270
285
|
if (this.server.getPublicationStrategy(collectionName).useCollectionView) {
|
|
271
286
|
const view = this.getCollectionView(collectionName);
|
|
@@ -280,7 +295,7 @@ class DDPSession {
|
|
|
280
295
|
const view = this.getCollectionView(collectionName);
|
|
281
296
|
view.removed(subscriptionHandle, id);
|
|
282
297
|
if (view.isEmpty()) {
|
|
283
|
-
this.collectionViews.
|
|
298
|
+
this.collectionViews.delete(collectionName);
|
|
284
299
|
}
|
|
285
300
|
}
|
|
286
301
|
else {
|
|
@@ -316,7 +331,7 @@ class DDPSession {
|
|
|
316
331
|
return;
|
|
317
332
|
// Drop the merge box data immediately.
|
|
318
333
|
self.inQueue = null;
|
|
319
|
-
self.collectionViews = new
|
|
334
|
+
self.collectionViews = new Map();
|
|
320
335
|
if (self.heartbeat) {
|
|
321
336
|
self.heartbeat.stop();
|
|
322
337
|
self.heartbeat = null;
|
|
@@ -457,7 +472,7 @@ class DDPSession {
|
|
|
457
472
|
// update the userId.
|
|
458
473
|
self._isSending = false;
|
|
459
474
|
var beforeCVs = self.collectionViews;
|
|
460
|
-
self.collectionViews = new
|
|
475
|
+
self.collectionViews = new Map();
|
|
461
476
|
self.userId = userId;
|
|
462
477
|
// _setUserId is normally called from a Meteor method with
|
|
463
478
|
// DDP._CurrentMethodInvocation set. But DDP._CurrentMethodInvocation is not
|
|
@@ -48,7 +48,7 @@ class StreamServer {
|
|
|
48
48
|
}
|
|
49
49
|
else {
|
|
50
50
|
serverOptions.faye_server_options = {
|
|
51
|
-
extensions: [permessage_deflate_1.default.configure({})]
|
|
51
|
+
extensions: [permessage_deflate_1.default.configure({ maxWindowBits: 11, memLevel: 4 })]
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
this.server = sockjs_1.default.createServer(serverOptions);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { OrderedDict } from "../ordered-dict/ordered_dict";
|
|
1
2
|
import { AsyncFunction } from "../types";
|
|
2
3
|
import { DDPSession, SessionConnectionHandle } from "./session";
|
|
3
4
|
export type SubscriptionHandle = `N${string}` | `U${string}`;
|
|
@@ -53,6 +54,7 @@ export declare class Subscription {
|
|
|
53
54
|
*/
|
|
54
55
|
onStop(func: () => void): void;
|
|
55
56
|
_isDeactivated(): boolean;
|
|
57
|
+
initialAdds(collectionName: string, documents: Map<string, any> | OrderedDict): void;
|
|
56
58
|
/**
|
|
57
59
|
* @summary Call inside the publish function. Informs the subscriber that a document has been added to the record set.
|
|
58
60
|
* @locus Server
|
|
@@ -276,6 +276,19 @@ class Subscription {
|
|
|
276
276
|
var self = this;
|
|
277
277
|
return self._deactivated || self._session.inQueue === null;
|
|
278
278
|
}
|
|
279
|
+
initialAdds(collectionName, documents) {
|
|
280
|
+
if (this._isDeactivated())
|
|
281
|
+
return;
|
|
282
|
+
if (this._session.server.getPublicationStrategy(collectionName).doAccountingForCollection) {
|
|
283
|
+
let ids = this._documents.get(collectionName);
|
|
284
|
+
if (ids == null) {
|
|
285
|
+
ids = new Set();
|
|
286
|
+
this._documents.set(collectionName, ids);
|
|
287
|
+
}
|
|
288
|
+
documents.forEach((_doc, id) => ids.add(id));
|
|
289
|
+
}
|
|
290
|
+
this._session.initialAdds(this._subscriptionHandle, collectionName, documents);
|
|
291
|
+
}
|
|
279
292
|
/**
|
|
280
293
|
* @summary Call inside the publish function. Informs the subscriber that a document has been added to the record set.
|
|
281
294
|
* @locus Server
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { IdMap } from "../id-map/id_map";
|
|
2
1
|
interface DiffCallbacks {
|
|
3
2
|
both: (key: string, left: any, right: any) => void;
|
|
4
3
|
leftOnly: (key: string, value: any) => void;
|
|
@@ -6,11 +5,11 @@ interface DiffCallbacks {
|
|
|
6
5
|
}
|
|
7
6
|
interface DiffSequence {
|
|
8
7
|
diffQueryChanges(ordered: boolean, oldResults: any[], newResults: any[], observer: any, options?: any): any;
|
|
9
|
-
diffQueryChanges(ordered: boolean, oldResults:
|
|
8
|
+
diffQueryChanges(ordered: boolean, oldResults: Map<string, any>, newResults: Map<string, any>, observer: any, options?: any): any;
|
|
10
9
|
diffQueryUnorderedChanges(oldResults: any, newResults: any, observer: any, options?: any): any;
|
|
11
10
|
diffQueryOrderedChanges(old_results: any, new_results: any, observer: any, options?: any): any;
|
|
12
11
|
diffObjects(left: Record<string, any>, right: Record<string, any>, callbacks: DiffCallbacks): any;
|
|
13
|
-
diffMaps(left:
|
|
12
|
+
diffMaps(left: Map<string, any>, right: Map<string, any>, callbacks: DiffCallbacks): any;
|
|
14
13
|
makeChangedFields(newDoc: Record<string, any>, oldDoc: Record<string, any>): any;
|
|
15
14
|
applyChanges(doc: Record<string, any>, changeFields: Record<string, any>): void;
|
|
16
15
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { IdMap } from "../id-map/id_map";
|
|
2
1
|
import { OrderedDict } from "../ordered-dict/ordered_dict";
|
|
3
2
|
export declare class _CachingChangeObserver {
|
|
4
|
-
docs: OrderedDict |
|
|
3
|
+
docs: OrderedDict | Map<string, any>;
|
|
5
4
|
applyChange: {
|
|
5
|
+
initialAdds?: (docs: OrderedDict | Map<string, any>) => void;
|
|
6
6
|
added?: (id: string, fields: any) => void;
|
|
7
7
|
changed?: (id: string, fields: any) => void;
|
|
8
8
|
removed?: (id: string) => void;
|
|
@@ -11,7 +11,6 @@ export declare class _CachingChangeObserver {
|
|
|
11
11
|
};
|
|
12
12
|
private ordered;
|
|
13
13
|
constructor(options?: {
|
|
14
|
-
callbacks?: any;
|
|
15
14
|
ordered?: boolean;
|
|
16
15
|
});
|
|
17
16
|
}
|
|
@@ -8,27 +8,11 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports._CachingChangeObserver = void 0;
|
|
10
10
|
const diff_1 = require("../diff-sequence/diff");
|
|
11
|
-
const ejson_1 = require("../ejson/ejson");
|
|
12
|
-
const id_map_1 = require("../id-map/id_map");
|
|
13
11
|
const ordered_dict_1 = require("../ordered-dict/ordered_dict");
|
|
14
12
|
// available as `this` to those callbacks.
|
|
15
13
|
class _CachingChangeObserver {
|
|
16
14
|
constructor(options = {}) {
|
|
17
|
-
|
|
18
|
-
!!(options.callbacks.addedBefore || options.callbacks.movedBefore));
|
|
19
|
-
if (options.hasOwnProperty('ordered')) {
|
|
20
|
-
this.ordered = options.ordered;
|
|
21
|
-
if (options.callbacks && options.ordered !== orderedFromCallbacks) {
|
|
22
|
-
throw Error('ordered option doesn\'t match callbacks');
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
else if (options.callbacks) {
|
|
26
|
-
this.ordered = orderedFromCallbacks;
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
throw Error('must provide ordered or callbacks');
|
|
30
|
-
}
|
|
31
|
-
const callbacks = options.callbacks || {};
|
|
15
|
+
this.ordered = options.ordered || false;
|
|
32
16
|
if (this.ordered) {
|
|
33
17
|
this.docs = new ordered_dict_1.OrderedDict();
|
|
34
18
|
this.applyChange = {
|
|
@@ -36,13 +20,6 @@ class _CachingChangeObserver {
|
|
|
36
20
|
// Take a shallow copy since the top-level properties can be changed
|
|
37
21
|
const doc = Object.assign({}, fields);
|
|
38
22
|
doc._id = id;
|
|
39
|
-
if (callbacks.addedBefore) {
|
|
40
|
-
callbacks.addedBefore.call(this, id, (0, ejson_1.clone)(fields), before);
|
|
41
|
-
}
|
|
42
|
-
// This line triggers if we provide added with movedBefore.
|
|
43
|
-
if (callbacks.added) {
|
|
44
|
-
callbacks.added.call(this, id, (0, ejson_1.clone)(fields));
|
|
45
|
-
}
|
|
46
23
|
// XXX could `before` be a falsy ID? Technically
|
|
47
24
|
// idStringify seems to allow for them -- though
|
|
48
25
|
// OrderedDict won't call stringify on a falsy arg.
|
|
@@ -50,27 +27,24 @@ class _CachingChangeObserver {
|
|
|
50
27
|
},
|
|
51
28
|
movedBefore: (id, before) => {
|
|
52
29
|
const doc = this.docs.get(id);
|
|
53
|
-
if (callbacks.movedBefore) {
|
|
54
|
-
callbacks.movedBefore.call(this, id, before);
|
|
55
|
-
}
|
|
56
30
|
this.docs.moveBefore(id, before || null);
|
|
57
31
|
},
|
|
58
32
|
};
|
|
59
33
|
}
|
|
60
34
|
else {
|
|
61
|
-
this.docs = new
|
|
35
|
+
this.docs = new Map();
|
|
62
36
|
this.applyChange = {
|
|
63
37
|
added: (id, fields) => {
|
|
64
38
|
// Take a shallow copy since the top-level properties can be changed
|
|
65
39
|
const doc = Object.assign({}, fields);
|
|
66
|
-
if (callbacks.added) {
|
|
67
|
-
callbacks.added.call(this, id, (0, ejson_1.clone)(fields));
|
|
68
|
-
}
|
|
69
40
|
doc._id = id;
|
|
70
41
|
this.docs.set(id, doc);
|
|
71
42
|
},
|
|
72
43
|
};
|
|
73
44
|
}
|
|
45
|
+
this.applyChange.initialAdds = (docs) => {
|
|
46
|
+
this.docs = docs;
|
|
47
|
+
};
|
|
74
48
|
// The methods in _IdMap and OrderedDict used by these callbacks are
|
|
75
49
|
// identical.
|
|
76
50
|
this.applyChange.changed = (id, fields) => {
|
|
@@ -78,16 +52,10 @@ class _CachingChangeObserver {
|
|
|
78
52
|
if (!doc) {
|
|
79
53
|
throw new Error(`Unknown id for changed: ${id}`);
|
|
80
54
|
}
|
|
81
|
-
if (callbacks.changed) {
|
|
82
|
-
callbacks.changed.call(this, id, (0, ejson_1.clone)(fields));
|
|
83
|
-
}
|
|
84
55
|
diff_1.DiffSequence.applyChanges(doc, fields);
|
|
85
56
|
};
|
|
86
57
|
this.applyChange.removed = id => {
|
|
87
|
-
|
|
88
|
-
callbacks.removed.call(this, id);
|
|
89
|
-
}
|
|
90
|
-
this.docs.remove(id);
|
|
58
|
+
this.docs.delete(id);
|
|
91
59
|
};
|
|
92
60
|
}
|
|
93
61
|
}
|
|
@@ -19,6 +19,9 @@ class LiveCursor {
|
|
|
19
19
|
}
|
|
20
20
|
async _publishCursor(sub) {
|
|
21
21
|
const observeHandle = await this.mongo._observeChanges(this.cursorDescription, false, {
|
|
22
|
+
initialAdds: (docs) => {
|
|
23
|
+
sub.initialAdds(this.cursorDescription.collectionName, docs);
|
|
24
|
+
},
|
|
22
25
|
added: (id, fields) => {
|
|
23
26
|
sub.added(this.cursorDescription.collectionName, id, fields);
|
|
24
27
|
},
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { OrderedDict } from "../ordered-dict/ordered_dict";
|
|
1
2
|
export interface ObserveCallbacks {
|
|
2
3
|
added: (id: string, fields: Record<string, any>) => void;
|
|
3
4
|
changed: (id: string, fields: Record<string, any>) => void;
|
|
4
5
|
removed: (id: string) => void;
|
|
5
6
|
addedBefore?: (id: string, fields: Record<string, any>, before?: any) => void;
|
|
6
7
|
movedBefore?: (id: string, fields: Record<string, any>, before?: any) => void;
|
|
8
|
+
initialAdds: (docs: Map<string, any> | OrderedDict) => void;
|
|
7
9
|
_testOnlyPollCallback?: any;
|
|
8
10
|
}
|
|
9
11
|
export declare class ObserveMultiplexer {
|
|
@@ -27,12 +29,12 @@ export declare class ObserveMultiplexer {
|
|
|
27
29
|
callbackNames(): string[];
|
|
28
30
|
_ready(): boolean;
|
|
29
31
|
_applyCallback(callbackName: string, args: any): Promise<void>;
|
|
30
|
-
_sendAdds(handle: ObserveHandle): void;
|
|
31
32
|
}
|
|
32
33
|
export declare class ObserveHandle {
|
|
33
34
|
private _multiplexer;
|
|
34
35
|
nonMutatingCallbacks: boolean;
|
|
35
36
|
_id: number;
|
|
37
|
+
_initialAdds: ObserveCallbacks["initialAdds"];
|
|
36
38
|
_addedBefore: ObserveCallbacks["addedBefore"];
|
|
37
39
|
_movedBefore: ObserveCallbacks["movedBefore"];
|
|
38
40
|
_added: ObserveCallbacks["added"];
|