mongodb-livedata-server 0.0.1

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.
Files changed (94) hide show
  1. package/README.md +63 -0
  2. package/dist/livedata_server.js +9 -0
  3. package/dist/meteor/binary-heap/max_heap.js +186 -0
  4. package/dist/meteor/binary-heap/min_heap.js +17 -0
  5. package/dist/meteor/binary-heap/min_max_heap.js +48 -0
  6. package/dist/meteor/callback-hook/hook.js +78 -0
  7. package/dist/meteor/ddp/crossbar.js +136 -0
  8. package/dist/meteor/ddp/heartbeat.js +77 -0
  9. package/dist/meteor/ddp/livedata_server.js +403 -0
  10. package/dist/meteor/ddp/method-invocation.js +72 -0
  11. package/dist/meteor/ddp/random-stream.js +100 -0
  12. package/dist/meteor/ddp/session-collection-view.js +106 -0
  13. package/dist/meteor/ddp/session-document-view.js +82 -0
  14. package/dist/meteor/ddp/session.js +570 -0
  15. package/dist/meteor/ddp/stream_server.js +181 -0
  16. package/dist/meteor/ddp/subscription.js +347 -0
  17. package/dist/meteor/ddp/utils.js +104 -0
  18. package/dist/meteor/ddp/writefence.js +111 -0
  19. package/dist/meteor/diff-sequence/diff.js +257 -0
  20. package/dist/meteor/ejson/ejson.js +569 -0
  21. package/dist/meteor/ejson/stringify.js +119 -0
  22. package/dist/meteor/ejson/utils.js +42 -0
  23. package/dist/meteor/id-map/id_map.js +92 -0
  24. package/dist/meteor/mongo/caching_change_observer.js +94 -0
  25. package/dist/meteor/mongo/doc_fetcher.js +53 -0
  26. package/dist/meteor/mongo/geojson_utils.js +41 -0
  27. package/dist/meteor/mongo/live_connection.js +264 -0
  28. package/dist/meteor/mongo/live_cursor.js +57 -0
  29. package/dist/meteor/mongo/minimongo_common.js +2002 -0
  30. package/dist/meteor/mongo/minimongo_matcher.js +217 -0
  31. package/dist/meteor/mongo/minimongo_sorter.js +268 -0
  32. package/dist/meteor/mongo/observe_driver_utils.js +73 -0
  33. package/dist/meteor/mongo/observe_multiplexer.js +228 -0
  34. package/dist/meteor/mongo/oplog-observe-driver.js +919 -0
  35. package/dist/meteor/mongo/oplog_tailing.js +352 -0
  36. package/dist/meteor/mongo/oplog_v2_converter.js +126 -0
  37. package/dist/meteor/mongo/polling_observe_driver.js +195 -0
  38. package/dist/meteor/mongo/synchronous-cursor.js +261 -0
  39. package/dist/meteor/mongo/synchronous-queue.js +110 -0
  40. package/dist/meteor/ordered-dict/ordered_dict.js +198 -0
  41. package/dist/meteor/random/AbstractRandomGenerator.js +92 -0
  42. package/dist/meteor/random/AleaRandomGenerator.js +90 -0
  43. package/dist/meteor/random/NodeRandomGenerator.js +42 -0
  44. package/dist/meteor/random/createAleaGenerator.js +32 -0
  45. package/dist/meteor/random/createRandom.js +22 -0
  46. package/dist/meteor/random/main.js +12 -0
  47. package/livedata_server.ts +3 -0
  48. package/meteor/LICENSE +28 -0
  49. package/meteor/binary-heap/max_heap.ts +225 -0
  50. package/meteor/binary-heap/min_heap.ts +15 -0
  51. package/meteor/binary-heap/min_max_heap.ts +53 -0
  52. package/meteor/callback-hook/hook.ts +85 -0
  53. package/meteor/ddp/crossbar.ts +148 -0
  54. package/meteor/ddp/heartbeat.ts +97 -0
  55. package/meteor/ddp/livedata_server.ts +473 -0
  56. package/meteor/ddp/method-invocation.ts +86 -0
  57. package/meteor/ddp/random-stream.ts +102 -0
  58. package/meteor/ddp/session-collection-view.ts +119 -0
  59. package/meteor/ddp/session-document-view.ts +92 -0
  60. package/meteor/ddp/session.ts +708 -0
  61. package/meteor/ddp/stream_server.ts +204 -0
  62. package/meteor/ddp/subscription.ts +392 -0
  63. package/meteor/ddp/utils.ts +119 -0
  64. package/meteor/ddp/writefence.ts +130 -0
  65. package/meteor/diff-sequence/diff.ts +295 -0
  66. package/meteor/ejson/ejson.ts +601 -0
  67. package/meteor/ejson/stringify.ts +122 -0
  68. package/meteor/ejson/utils.ts +38 -0
  69. package/meteor/id-map/id_map.ts +84 -0
  70. package/meteor/mongo/caching_change_observer.ts +120 -0
  71. package/meteor/mongo/doc_fetcher.ts +52 -0
  72. package/meteor/mongo/geojson_utils.ts +42 -0
  73. package/meteor/mongo/live_connection.ts +302 -0
  74. package/meteor/mongo/live_cursor.ts +79 -0
  75. package/meteor/mongo/minimongo_common.ts +2440 -0
  76. package/meteor/mongo/minimongo_matcher.ts +275 -0
  77. package/meteor/mongo/minimongo_sorter.ts +331 -0
  78. package/meteor/mongo/observe_driver_utils.ts +79 -0
  79. package/meteor/mongo/observe_multiplexer.ts +256 -0
  80. package/meteor/mongo/oplog-observe-driver.ts +1049 -0
  81. package/meteor/mongo/oplog_tailing.ts +414 -0
  82. package/meteor/mongo/oplog_v2_converter.ts +124 -0
  83. package/meteor/mongo/polling_observe_driver.ts +247 -0
  84. package/meteor/mongo/synchronous-cursor.ts +293 -0
  85. package/meteor/mongo/synchronous-queue.ts +119 -0
  86. package/meteor/ordered-dict/ordered_dict.ts +229 -0
  87. package/meteor/random/AbstractRandomGenerator.ts +99 -0
  88. package/meteor/random/AleaRandomGenerator.ts +96 -0
  89. package/meteor/random/NodeRandomGenerator.ts +37 -0
  90. package/meteor/random/createAleaGenerator.ts +31 -0
  91. package/meteor/random/createRandom.ts +19 -0
  92. package/meteor/random/main.ts +8 -0
  93. package/package.json +30 -0
  94. package/tsconfig.json +10 -0
@@ -0,0 +1,217 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MinimongoMatcher = void 0;
4
+ const minimongo_common_1 = require("./minimongo_common");
5
+ const ejson_1 = require("../ejson/ejson");
6
+ // The minimongo selector compiler!
7
+ // Terminology:
8
+ // - a 'selector' is the EJSON object representing a selector
9
+ // - a 'matcher' is its compiled form (whether a full Minimongo.Matcher
10
+ // object or one of the component lambdas that matches parts of it)
11
+ // - a 'result object' is an object with a 'result' field and maybe
12
+ // distance and arrayIndices.
13
+ // - a 'branched value' is an object with a 'value' field and maybe
14
+ // 'dontIterate' and 'arrayIndices'.
15
+ // - a 'document' is a top-level object that can be stored in a collection.
16
+ // - a 'lookup function' is a function that takes in a document and returns
17
+ // an array of 'branched values'.
18
+ // - a 'branched matcher' maps from an array of branched values to a result
19
+ // object.
20
+ // - an 'element matcher' maps from a single value to a bool.
21
+ // Main entry point.
22
+ // var matcher = new Minimongo.Matcher({a: {$gt: 5}});
23
+ // if (matcher.documentMatches({a: 7})) ...
24
+ class MinimongoMatcher {
25
+ constructor(selector, isUpdate) {
26
+ this.canBecomeTrueByModifier = function (modifier) {
27
+ if (!this.affectedByModifier(modifier)) {
28
+ return false;
29
+ }
30
+ if (!this.isSimple()) {
31
+ return true;
32
+ }
33
+ modifier = Object.assign({ $set: {}, $unset: {} }, modifier);
34
+ const modifierPaths = [].concat(Object.keys(modifier.$set), Object.keys(modifier.$unset));
35
+ if (this._getPaths().some(pathHasNumericKeys) ||
36
+ modifierPaths.some(pathHasNumericKeys)) {
37
+ return true;
38
+ }
39
+ // check if there is a $set or $unset that indicates something is an
40
+ // object rather than a scalar in the actual object where we saw $-operator
41
+ // NOTE: it is correct since we allow only scalars in $-operators
42
+ // Example: for selector {'a.b': {$gt: 5}} the modifier {'a.b.c':7} would
43
+ // definitely set the result to false as 'a.b' appears to be an object.
44
+ const expectedScalarIsObject = Object.keys(this._selector).some(path => {
45
+ if (!(0, minimongo_common_1.isOperatorObject)(this._selector[path])) {
46
+ return false;
47
+ }
48
+ return modifierPaths.some(modifierPath => modifierPath.startsWith(`${path}.`));
49
+ });
50
+ if (expectedScalarIsObject) {
51
+ return false;
52
+ }
53
+ // See if we can apply the modifier on the ideally matching object. If it
54
+ // still matches the selector, then the modifier could have turned the real
55
+ // object in the database into something matching.
56
+ const matchingDocument = (0, ejson_1.clone)(this.matchingDocument());
57
+ // The selector is too complex, anything can happen.
58
+ if (matchingDocument === null) {
59
+ return true;
60
+ }
61
+ try {
62
+ (0, minimongo_common_1._modify)(matchingDocument, modifier);
63
+ }
64
+ catch (error) {
65
+ // Couldn't set a property on a field which is a scalar or null in the
66
+ // selector.
67
+ // Example:
68
+ // real document: { 'a.b': 3 }
69
+ // selector: { 'a': 12 }
70
+ // converted selector (ideal document): { 'a': 12 }
71
+ // modifier: { $set: { 'a.b': 4 } }
72
+ // We don't know what real document was like but from the error raised by
73
+ // $set on a scalar field we can reason that the structure of real document
74
+ // is completely different.
75
+ if (error.name === 'MinimongoError' && error.setPropertyError) {
76
+ return false;
77
+ }
78
+ throw error;
79
+ }
80
+ return this.documentMatches(matchingDocument).result;
81
+ };
82
+ // A set (object mapping string -> *) of all of the document paths looked
83
+ // at by the selector. Also includes the empty string if it may look at any
84
+ // path (eg, $where).
85
+ this._paths = {};
86
+ // Set to true if compilation finds a $near.
87
+ this._hasGeoQuery = false;
88
+ // Set to true if compilation finds a $where.
89
+ this._hasWhere = false;
90
+ // Set to false if compilation finds anything other than a simple equality
91
+ // or one or more of '$gt', '$gte', '$lt', '$lte', '$ne', '$in', '$nin' used
92
+ // with scalars as operands.
93
+ this._isSimple = true;
94
+ // Set to a dummy document which always matches this Matcher. Or set to null
95
+ // if such document is too hard to find.
96
+ this._matchingDocument = undefined;
97
+ // A clone of the original selector. It may just be a function if the user
98
+ // passed in a function; otherwise is definitely an object (eg, IDs are
99
+ // translated into {_id: ID} first. Used by canBecomeTrueByModifier and
100
+ // Sorter._useWithMatcher.
101
+ this._selector = null;
102
+ this._docMatcher = this._compileSelector(selector);
103
+ // Set to true if selection is done for an update operation
104
+ // Default is false
105
+ // Used for $near array update (issue #3599)
106
+ this._isUpdate = isUpdate;
107
+ }
108
+ documentMatches(doc) {
109
+ if (doc !== Object(doc)) {
110
+ throw Error('documentMatches needs a document');
111
+ }
112
+ return this._docMatcher(doc);
113
+ }
114
+ hasGeoQuery() {
115
+ return this._hasGeoQuery;
116
+ }
117
+ hasWhere() {
118
+ return this._hasWhere;
119
+ }
120
+ isSimple() {
121
+ return this._isSimple;
122
+ }
123
+ // Given a selector, return a function that takes one argument, a
124
+ // document. It returns a result object.
125
+ _compileSelector(selector) {
126
+ // you can pass a literal function instead of a selector
127
+ if (selector instanceof Function) {
128
+ this._isSimple = false;
129
+ this._selector = selector;
130
+ this._recordPathUsed('');
131
+ return doc => ({ result: !!selector.call(doc) });
132
+ }
133
+ // protect against dangerous selectors. falsey and {_id: falsey} are both
134
+ // likely programmer error, and not what you want, particularly for
135
+ // destructive operations.
136
+ if (!selector || minimongo_common_1.hasOwn.call(selector, '_id') && !selector._id) {
137
+ this._isSimple = false;
138
+ return minimongo_common_1.nothingMatcher;
139
+ }
140
+ // Top level can't be an array or true or binary.
141
+ if (Array.isArray(selector) ||
142
+ (0, ejson_1.isBinary)(selector) ||
143
+ typeof selector === 'boolean') {
144
+ throw new Error(`Invalid selector: ${selector}`);
145
+ }
146
+ this._selector = (0, ejson_1.clone)(selector);
147
+ return (0, minimongo_common_1.compileDocumentSelector)(selector, this, { isRoot: true });
148
+ }
149
+ affectedByModifier(modifier) {
150
+ // safe check for $set/$unset being objects
151
+ modifier = Object.assign({ $set: {}, $unset: {} }, modifier);
152
+ const meaningfulPaths = this._getPaths();
153
+ const modifiedPaths = [].concat(Object.keys(modifier.$set), Object.keys(modifier.$unset));
154
+ return modifiedPaths.some(path => {
155
+ const mod = path.split('.');
156
+ return meaningfulPaths.some(meaningfulPath => {
157
+ const sel = meaningfulPath.split('.');
158
+ let i = 0, j = 0;
159
+ while (i < sel.length && j < mod.length) {
160
+ if ((0, minimongo_common_1.isNumericKey)(sel[i]) && (0, minimongo_common_1.isNumericKey)(mod[j])) {
161
+ // foo.4.bar selector affected by foo.4 modifier
162
+ // foo.3.bar selector unaffected by foo.4 modifier
163
+ if (sel[i] === mod[j]) {
164
+ i++;
165
+ j++;
166
+ }
167
+ else {
168
+ return false;
169
+ }
170
+ }
171
+ else if ((0, minimongo_common_1.isNumericKey)(sel[i])) {
172
+ // foo.4.bar selector unaffected by foo.bar modifier
173
+ return false;
174
+ }
175
+ else if ((0, minimongo_common_1.isNumericKey)(mod[j])) {
176
+ j++;
177
+ }
178
+ else if (sel[i] === mod[j]) {
179
+ i++;
180
+ j++;
181
+ }
182
+ else {
183
+ return false;
184
+ }
185
+ }
186
+ // One is a prefix of another, taking numeric fields into account
187
+ return true;
188
+ });
189
+ });
190
+ }
191
+ // Knows how to combine a mongo selector and a fields projection to a new fields
192
+ // projection taking into account active fields from the passed selector.
193
+ // @returns Object - projection object (same as fields option of mongo cursor)
194
+ combineIntoProjection(projection) {
195
+ const selectorPaths = (0, minimongo_common_1._pathsElidingNumericKeys)(this._getPaths());
196
+ // Special case for $where operator in the selector - projection should depend
197
+ // on all fields of the document. getSelectorPaths returns a list of paths
198
+ // selector depends on. If one of the paths is '' (empty string) representing
199
+ // the root or the whole document, complete projection should be returned.
200
+ if (selectorPaths.includes('')) {
201
+ return {};
202
+ }
203
+ return (0, minimongo_common_1.combineImportantPathsIntoProjection)(selectorPaths, projection);
204
+ }
205
+ // Returns a list of key paths the given selector is looking for. It includes
206
+ // the empty string if there is a $where.
207
+ _getPaths() {
208
+ return Object.keys(this._paths);
209
+ }
210
+ _recordPathUsed(path) {
211
+ this._paths[path] = true;
212
+ }
213
+ }
214
+ exports.MinimongoMatcher = MinimongoMatcher;
215
+ function pathHasNumericKeys(path) {
216
+ return path.split('.').some(minimongo_common_1.isNumericKey);
217
+ }
@@ -0,0 +1,268 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const minimongo_common_1 = require("./minimongo_common");
4
+ const minimongo_matcher_1 = require("./minimongo_matcher");
5
+ // Give a sort spec, which can be in any of these forms:
6
+ // {"key1": 1, "key2": -1}
7
+ // [["key1", "asc"], ["key2", "desc"]]
8
+ // ["key1", ["key2", "desc"]]
9
+ //
10
+ // (.. with the first form being dependent on the key enumeration
11
+ // behavior of your javascript VM, which usually does what you mean in
12
+ // this case if the key names don't look like integers ..)
13
+ //
14
+ // return a function that takes two objects, and returns -1 if the
15
+ // first object comes first in order, 1 if the second object comes
16
+ // first, or 0 if neither object comes before the other.
17
+ class MinimongoSorter {
18
+ constructor(spec) {
19
+ this.combineIntoProjection = function (projection) {
20
+ return (0, minimongo_common_1.combineImportantPathsIntoProjection)((0, minimongo_common_1._pathsElidingNumericKeys)(this._getPaths()), projection);
21
+ };
22
+ this._sortSpecParts = [];
23
+ this._sortFunction = null;
24
+ const addSpecPart = (path, ascending) => {
25
+ if (!path) {
26
+ throw Error('sort keys must be non-empty');
27
+ }
28
+ if (path.charAt(0) === '$') {
29
+ throw Error(`unsupported sort key: ${path}`);
30
+ }
31
+ this._sortSpecParts.push({
32
+ ascending,
33
+ lookup: (0, minimongo_common_1.makeLookupFunction)(path, { forSort: true }),
34
+ path
35
+ });
36
+ };
37
+ if (spec instanceof Array) {
38
+ spec.forEach(element => {
39
+ if (typeof element === 'string') {
40
+ addSpecPart(element, true);
41
+ }
42
+ else {
43
+ addSpecPart(element[0], element[1] !== 'desc');
44
+ }
45
+ });
46
+ }
47
+ else if (typeof spec === 'object') {
48
+ Object.keys(spec).forEach(key => {
49
+ addSpecPart(key, spec[key] >= 0);
50
+ });
51
+ }
52
+ else if (typeof spec === 'function') {
53
+ this._sortFunction = spec;
54
+ }
55
+ else {
56
+ throw Error(`Bad sort specification: ${JSON.stringify(spec)}`);
57
+ }
58
+ // If a function is specified for sorting, we skip the rest.
59
+ if (this._sortFunction) {
60
+ return;
61
+ }
62
+ // To implement affectedByModifier, we piggy-back on top of Matcher's
63
+ // affectedByModifier code; we create a selector that is affected by the
64
+ // same modifiers as this sort order. This is only implemented on the
65
+ // server.
66
+ if (this.affectedByModifier) {
67
+ const selector = {};
68
+ this._sortSpecParts.forEach(spec => {
69
+ selector[spec.path] = 1;
70
+ });
71
+ this._selectorForAffectedByModifier = new minimongo_matcher_1.MinimongoMatcher(selector);
72
+ }
73
+ this._keyComparator = composeComparators(this._sortSpecParts.map((spec, i) => this._keyFieldComparator(i)));
74
+ }
75
+ affectedByModifier(modifier) {
76
+ return this._selectorForAffectedByModifier.affectedByModifier(modifier);
77
+ }
78
+ getComparator(options) {
79
+ // If sort is specified or have no distances, just use the comparator from
80
+ // the source specification (which defaults to "everything is equal".
81
+ // issue #3599
82
+ // https://docs.mongodb.com/manual/reference/operator/query/near/#sort-operation
83
+ // sort effectively overrides $near
84
+ if (this._sortSpecParts.length || !options || !options.distances) {
85
+ return this._getBaseComparator();
86
+ }
87
+ const distances = options.distances;
88
+ // Return a comparator which compares using $near distances.
89
+ return (a, b) => {
90
+ if (!distances.has(a._id)) {
91
+ throw Error(`Missing distance for ${a._id}`);
92
+ }
93
+ if (!distances.has(b._id)) {
94
+ throw Error(`Missing distance for ${b._id}`);
95
+ }
96
+ return distances.get(a._id) - distances.get(b._id);
97
+ };
98
+ }
99
+ // Takes in two keys: arrays whose lengths match the number of spec
100
+ // parts. Returns negative, 0, or positive based on using the sort spec to
101
+ // compare fields.
102
+ _compareKeys(key1, key2) {
103
+ if (key1.length !== this._sortSpecParts.length ||
104
+ key2.length !== this._sortSpecParts.length) {
105
+ throw Error('Key has wrong length');
106
+ }
107
+ return this._keyComparator(key1, key2);
108
+ }
109
+ // Iterates over each possible "key" from doc (ie, over each branch), calling
110
+ // 'cb' with the key.
111
+ _generateKeysFromDoc(doc, cb) {
112
+ if (this._sortSpecParts.length === 0) {
113
+ throw new Error('can\'t generate keys without a spec');
114
+ }
115
+ const pathFromIndices = indices => `${indices.join(',')},`;
116
+ let knownPaths = null;
117
+ // maps index -> ({'' -> value} or {path -> value})
118
+ const valuesByIndexAndPath = this._sortSpecParts.map(spec => {
119
+ // Expand any leaf arrays that we find, and ignore those arrays
120
+ // themselves. (We never sort based on an array itself.)
121
+ let branches = (0, minimongo_common_1.expandArraysInBranches)(spec.lookup(doc), true);
122
+ // If there are no values for a key (eg, key goes to an empty array),
123
+ // pretend we found one undefined value.
124
+ if (!branches.length) {
125
+ branches = [{ value: void 0 }];
126
+ }
127
+ const element = Object.create(null);
128
+ let usedPaths = false;
129
+ branches.forEach(branch => {
130
+ if (!branch.arrayIndices) {
131
+ // If there are no array indices for a branch, then it must be the
132
+ // only branch, because the only thing that produces multiple branches
133
+ // is the use of arrays.
134
+ if (branches.length > 1) {
135
+ throw Error('multiple branches but no array used?');
136
+ }
137
+ element[''] = branch.value;
138
+ return;
139
+ }
140
+ usedPaths = true;
141
+ const path = pathFromIndices(branch.arrayIndices);
142
+ if (minimongo_common_1.hasOwn.call(element, path)) {
143
+ throw Error(`duplicate path: ${path}`);
144
+ }
145
+ element[path] = branch.value;
146
+ // If two sort fields both go into arrays, they have to go into the
147
+ // exact same arrays and we have to find the same paths. This is
148
+ // roughly the same condition that makes MongoDB throw this strange
149
+ // error message. eg, the main thing is that if sort spec is {a: 1,
150
+ // b:1} then a and b cannot both be arrays.
151
+ //
152
+ // (In MongoDB it seems to be OK to have {a: 1, 'a.x.y': 1} where 'a'
153
+ // and 'a.x.y' are both arrays, but we don't allow this for now.
154
+ // #NestedArraySort
155
+ // XXX achieve full compatibility here
156
+ if (knownPaths && !minimongo_common_1.hasOwn.call(knownPaths, path)) {
157
+ throw Error('cannot index parallel arrays');
158
+ }
159
+ });
160
+ if (knownPaths) {
161
+ // Similarly to above, paths must match everywhere, unless this is a
162
+ // non-array field.
163
+ if (!minimongo_common_1.hasOwn.call(element, '') &&
164
+ Object.keys(knownPaths).length !== Object.keys(element).length) {
165
+ throw Error('cannot index parallel arrays!');
166
+ }
167
+ }
168
+ else if (usedPaths) {
169
+ knownPaths = {};
170
+ Object.keys(element).forEach(path => {
171
+ knownPaths[path] = true;
172
+ });
173
+ }
174
+ return element;
175
+ });
176
+ if (!knownPaths) {
177
+ // Easy case: no use of arrays.
178
+ const soleKey = valuesByIndexAndPath.map(values => {
179
+ if (!minimongo_common_1.hasOwn.call(values, '')) {
180
+ throw Error('no value in sole key case?');
181
+ }
182
+ return values[''];
183
+ });
184
+ cb(soleKey);
185
+ return;
186
+ }
187
+ Object.keys(knownPaths).forEach(path => {
188
+ const key = valuesByIndexAndPath.map(values => {
189
+ if (minimongo_common_1.hasOwn.call(values, '')) {
190
+ return values[''];
191
+ }
192
+ if (!minimongo_common_1.hasOwn.call(values, path)) {
193
+ throw Error('missing path?');
194
+ }
195
+ return values[path];
196
+ });
197
+ cb(key);
198
+ });
199
+ }
200
+ // Returns a comparator that represents the sort specification (but not
201
+ // including a possible geoquery distance tie-breaker).
202
+ _getBaseComparator() {
203
+ if (this._sortFunction) {
204
+ return this._sortFunction;
205
+ }
206
+ // If we're only sorting on geoquery distance and no specs, just say
207
+ // everything is equal.
208
+ if (!this._sortSpecParts.length) {
209
+ return (doc1, doc2) => 0;
210
+ }
211
+ return (doc1, doc2) => {
212
+ const key1 = this._getMinKeyFromDoc(doc1);
213
+ const key2 = this._getMinKeyFromDoc(doc2);
214
+ return this._compareKeys(key1, key2);
215
+ };
216
+ }
217
+ // Finds the minimum key from the doc, according to the sort specs. (We say
218
+ // "minimum" here but this is with respect to the sort spec, so "descending"
219
+ // sort fields mean we're finding the max for that field.)
220
+ //
221
+ // Note that this is NOT "find the minimum value of the first field, the
222
+ // minimum value of the second field, etc"... it's "choose the
223
+ // lexicographically minimum value of the key vector, allowing only keys which
224
+ // you can find along the same paths". ie, for a doc {a: [{x: 0, y: 5}, {x:
225
+ // 1, y: 3}]} with sort spec {'a.x': 1, 'a.y': 1}, the only keys are [0,5] and
226
+ // [1,3], and the minimum key is [0,5]; notably, [0,3] is NOT a key.
227
+ _getMinKeyFromDoc(doc) {
228
+ let minKey = null;
229
+ this._generateKeysFromDoc(doc, key => {
230
+ if (minKey === null) {
231
+ minKey = key;
232
+ return;
233
+ }
234
+ if (this._compareKeys(key, minKey) < 0) {
235
+ minKey = key;
236
+ }
237
+ });
238
+ return minKey;
239
+ }
240
+ _getPaths() {
241
+ return this._sortSpecParts.map(part => part.path);
242
+ }
243
+ // Given an index 'i', returns a comparator that compares two key arrays based
244
+ // on field 'i'.
245
+ _keyFieldComparator(i) {
246
+ const invert = !this._sortSpecParts[i].ascending;
247
+ return (key1, key2) => {
248
+ const compare = minimongo_common_1._f._cmp(key1[i], key2[i]);
249
+ return invert ? -compare : compare;
250
+ };
251
+ }
252
+ }
253
+ exports.default = MinimongoSorter;
254
+ // Given an array of comparators
255
+ // (functions (a,b)->(negative or positive or zero)), returns a single
256
+ // comparator which uses each comparator in order and returns the first
257
+ // non-zero value.
258
+ function composeComparators(comparatorArray) {
259
+ return (a, b) => {
260
+ for (let i = 0; i < comparatorArray.length; ++i) {
261
+ const compare = comparatorArray[i](a, b);
262
+ if (compare !== 0) {
263
+ return compare;
264
+ }
265
+ }
266
+ return 0;
267
+ };
268
+ }
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports._idsMatchedBySelector = exports.forEachTrigger = exports.listenAll = void 0;
4
+ const crossbar_1 = require("../ddp/crossbar");
5
+ // Listen for the invalidation messages that will trigger us to poll the
6
+ // database for changes. If this selector specifies specific IDs, specify them
7
+ // here, so that updates to different specific IDs don't cause us to poll.
8
+ // listenCallback is the same kind of (notification, complete) callback passed
9
+ // to InvalidationCrossbar.listen.
10
+ function listenAll(cursorDescription, listenCallback) {
11
+ var listeners = [];
12
+ forEachTrigger(cursorDescription, function (trigger) {
13
+ listeners.push(crossbar_1._InvalidationCrossbar.listen(trigger, listenCallback));
14
+ });
15
+ return {
16
+ stop: function () {
17
+ for (const listener of listeners) {
18
+ listener.stop();
19
+ }
20
+ }
21
+ };
22
+ }
23
+ exports.listenAll = listenAll;
24
+ function forEachTrigger(cursorDescription, triggerCallback) {
25
+ var key = { collection: cursorDescription.collectionName };
26
+ var specificIds = _idsMatchedBySelector(cursorDescription.selector);
27
+ if (specificIds) {
28
+ for (const id of specificIds) {
29
+ triggerCallback(Object.assign({ id: id }, key));
30
+ }
31
+ triggerCallback(Object.assign({ dropCollection: true, id: null }, key));
32
+ }
33
+ else {
34
+ triggerCallback(key);
35
+ }
36
+ // Everyone cares about the database being dropped.
37
+ triggerCallback({ dropDatabase: true });
38
+ }
39
+ exports.forEachTrigger = forEachTrigger;
40
+ function _idsMatchedBySelector(selector) {
41
+ if (!selector) {
42
+ return null;
43
+ }
44
+ // Do we have an _id clause?
45
+ if (selector.hasOwnProperty('_id')) {
46
+ // Is the _id clause just an ID?
47
+ if (typeof selector._id === "string") {
48
+ return [selector._id];
49
+ }
50
+ // Is the _id clause {_id: {$in: ["x", "y", "z"]}}?
51
+ if (selector._id
52
+ && "$in" in selector._id
53
+ && Array.isArray(selector._id.$in)
54
+ && selector._id.$in.length
55
+ && selector._id.$in.every(id => typeof id === "string")) {
56
+ return selector._id.$in;
57
+ }
58
+ return null;
59
+ }
60
+ // If this is a top-level $and, and any of the clauses constrain their
61
+ // documents, then the whole selector is constrained by any one clause's
62
+ // constraint. (Well, by their intersection, but that seems unlikely.)
63
+ if ("$and" in selector && Array.isArray(selector.$and)) {
64
+ for (let i = 0; i < selector.$and.length; ++i) {
65
+ const subIds = _idsMatchedBySelector(selector.$and[i]);
66
+ if (subIds) {
67
+ return subIds;
68
+ }
69
+ }
70
+ }
71
+ return null;
72
+ }
73
+ exports._idsMatchedBySelector = _idsMatchedBySelector;