diff --git a/package.json b/package.json index 29e692c729..fd16128033 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,11 @@ "named": "never", "asyncArrow": "always" } - ] + ], + "complexity": "off", + "func-names": "off", + "no-undef": "warn", + "curly": "off" } } } diff --git a/packages/babel-compiler/.npm/package/npm-shrinkwrap.json b/packages/babel-compiler/.npm/package/npm-shrinkwrap.json index 4d5d9cb5aa..da32024bb1 100644 --- a/packages/babel-compiler/.npm/package/npm-shrinkwrap.json +++ b/packages/babel-compiler/.npm/package/npm-shrinkwrap.json @@ -715,9 +715,9 @@ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==" }, "electron-to-chromium": { - "version": "1.4.340", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.340.tgz", - "integrity": "sha512-zx8hqumOqltKsv/MF50yvdAlPF9S/4PXbyfzJS6ZGhbddGkRegdwImmfSVqCkEziYzrIGZ/TlrzBND4FysfkDg==" + "version": "1.4.337", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.337.tgz", + "integrity": "sha512-W8gdzXG86mVPoc56eM8YA+QiLxaAxJ8cmDjxZgfhLLWVvZQxyA918w5tX2JEWApZta45T1/sYcmFHTsTOUE3nw==" }, "escalade": { "version": "3.1.1", diff --git a/packages/minimongo/cursor.js b/packages/minimongo/cursor.js index f09f3db2fb..12ede3aa69 100644 --- a/packages/minimongo/cursor.js +++ b/packages/minimongo/cursor.js @@ -227,7 +227,7 @@ export default class Cursor { * @param {Object} callbacks Functions to call to deliver the result set as it * changes */ - async observeChanges(options) { + observeChanges(options) { const ordered = LocalCollection._observeChangesCallbacksAreOrdered(options); // there are several places that assume you aren't combining skip/limit with @@ -237,19 +237,16 @@ export default class Cursor { if (!options._allow_unordered && !ordered && (this.skip || this.limit)) { throw new Error( "Must use an ordered observe with skip or limit (i.e. 'addedBefore' " + - "for observeChanges or 'addedAt' for observe, instead of 'added')." + "for observeChanges or 'addedAt' for observe, instead of 'added')." ); } if (this.fields && (this.fields._id === 0 || this.fields._id === false)) { - throw Error('You may not observe a cursor with {fields: {_id: 0}}'); + throw Error("You may not observe a cursor with {fields: {_id: 0}}"); } - const distances = ( - this.matcher.hasGeoQuery() && - ordered && - new LocalCollection._IdMap - ); + const distances = + this.matcher.hasGeoQuery() && ordered && new LocalCollection._IdMap(); const query = { cursor: this, @@ -259,7 +256,7 @@ export default class Cursor { ordered, projectionFn: this._projectionFn, resultsSnapshot: null, - sorter: ordered && this.sorter + sorter: ordered && this.sorter, }; let qid; @@ -271,10 +268,13 @@ export default class Cursor { this.collection.queries[qid] = query; } - query.results = this._getRawObjects({ordered, distances: query.distances}); + query.results = this._getRawObjects({ + ordered, + distances: query.distances, + }); if (this.collection.paused) { - query.resultsSnapshot = ordered ? [] : new LocalCollection._IdMap; + query.resultsSnapshot = ordered ? [] : new LocalCollection._IdMap(); } // wrap callbacks we were passed. callbacks only fire when not paused and @@ -284,43 +284,23 @@ export default class Cursor { // furthermore, callbacks enqueue until the operation we're working on is // done. - const wrapCallback = fn => { + const wrapCallback = (fn) => { if (!fn) { return () => {}; } const self = this; - if (Meteor.isClient) { - return function(/* args*/) { - if (self.collection.paused) { - return; - } - - const args = arguments; - - self.collection._observeQueue.queueTask(() => { - fn.apply(this, args); - }); - }; - } - - return function(/* args*/) { + return function (/* args*/) { if (self.collection.paused) { return; } - let resolve; - const promise = new Promise(r => resolve = r); - const args = arguments; self.collection._observeQueue.queueTask(() => { fn.apply(this, args); - resolve(); }); - - return promise; }; }; @@ -334,36 +314,38 @@ export default class Cursor { } if (!options._suppress_initial && !this.collection.paused) { - const handler = async doc => { + const handler = (doc) => { const fields = EJSON.clone(doc); delete fields._id; if (ordered) { - await query.addedBefore(doc._id, this._projectionFn(fields), null); + query.addedBefore(doc._id, this._projectionFn(fields), null); } - await query.added(doc._id, this._projectionFn(fields)); + query.added(doc._id, this._projectionFn(fields)); }; // it means it's just an array if (query.results.length) { for (const doc of query.results) { - await handler(doc); + handler(doc); } } // it means it's an id map if (query.results?.size?.()) { - await query.results.forEachAsync(handler); + query.results.forEachAsync(handler); } } - const handle = Object.assign(new LocalCollection.ObserveHandle, { + const handle = Object.assign(new LocalCollection.ObserveHandle(), { collection: this.collection, stop: () => { if (this.reactive) { delete this.collection.queries[qid]; } - } + }, + isReady: false, + isReadyPromise: null, }); if (this.reactive && Tracker.active) { @@ -379,7 +361,14 @@ export default class Cursor { // run the observe callbacks resulting from the initial contents // before we leave the observe. - await this.collection._observeQueue.drain(); + const isReadyPromise = this.collection._observeQueue.drain(); + + if (Meteor.isClient) handle.isReady = true; + else isReadyPromise.then(() => (handle.isReady = true)); + + handle.isReadyPromise = Meteor.isClient + ? Promise.resolve() + : isReadyPromise; return handle; } diff --git a/packages/minimongo/local_collection.js b/packages/minimongo/local_collection.js index a06bf58aa1..47e6a9647d 100644 --- a/packages/minimongo/local_collection.js +++ b/packages/minimongo/local_collection.js @@ -1510,7 +1510,7 @@ LocalCollection._modify = (doc, modifier, options = {}) => { }); }; -LocalCollection._observeFromObserveChanges = async (cursor, observeCallbacks) => { +LocalCollection._observeFromObserveChanges = (cursor, observeCallbacks) => { const transform = cursor.getTransform() || (doc => doc); let suppressed = !!observeCallbacks._suppress_initial; @@ -1647,10 +1647,15 @@ LocalCollection._observeFromObserveChanges = async (cursor, observeCallbacks) => // So we can mark it as safe to reduce the ejson clones. // This is tested by the `mongo-livedata - (extended) scribbling` tests changeObserver.applyChange._fromObserve = true; - const handle = await cursor.observeChanges(changeObserver.applyChange, + const handle = cursor.observeChanges(changeObserver.applyChange, { nonMutatingCallbacks: true }); - suppressed = false; + // If needed, re-enable callbacks as soon as the initial batch is ready. + if (suppressed) { + handle.isReadyPromise.then(() => { + suppressed = false; + }); + } return handle; }; diff --git a/packages/minimongo/minimongo_tests_client.js b/packages/minimongo/minimongo_tests_client.js index 38c74440a2..ca690875c1 100644 --- a/packages/minimongo/minimongo_tests_client.js +++ b/packages/minimongo/minimongo_tests_client.js @@ -1867,7 +1867,7 @@ Tinytest.addAsync('minimongo - observe ordered with projection', async test => { let handle; const c = new LocalCollection(); - handle = await c.find({}, {sort: {a: 1}, fields: { a: 1 }}).observe(cbs); + handle = c.find({}, {sort: {a: 1}, fields: { a: 1 }}).observe(cbs); test.isTrue(handle.collection === c); await c.insertAsync({_id: 'foo', a: 1, b: 2}); @@ -1896,20 +1896,20 @@ Tinytest.addAsync('minimongo - observe ordered with projection', async test => { const cursor = c.find({}, {fields: {a: 1, _id: 0}}); await test.throwsAsync(async () => { - await cursor.observeChanges({added() {}}); + await cursor.observeChanges({ added() {} }); }); - await test.throwsAsync(async () => { - await cursor.observe({added() {}}); + test.throws(() => { + cursor.observe({ added() {} }); }); // test initial inserts (and backwards sort) - handle = await c.find({}, {sort: {a: -1}, fields: { a: 1 } }).observe(cbs); + handle = c.find({}, {sort: {a: -1}, fields: { a: 1 } }).observe(cbs); test.equal(operations.shift(), ['added', {a: 2}, 0, null]); test.equal(operations.shift(), ['added', {a: 1}, 1, null]); handle.stop(); // test _suppress_initial - handle = await c.find({}, {sort: {a: -1}, fields: { a: 1 }}).observe(Object.assign(cbs, {_suppress_initial: true})); + handle = c.find({}, {sort: {a: -1}, fields: { a: 1 }}).observe(Object.assign(cbs, {_suppress_initial: true})); test.equal(operations.shift(), undefined); await c.insertAsync({a: 100, b: { foo: 'bar' }}); test.equal(operations.shift(), ['added', {a: 100}, 0, idA2]); @@ -1917,7 +1917,7 @@ Tinytest.addAsync('minimongo - observe ordered with projection', async test => { // test skip and limit. await c.removeAsync({}); - handle = await c.find({}, {sort: {a: 1}, skip: 1, limit: 2, fields: { blacklisted: 0 }}).observe(cbs); + handle = c.find({}, {sort: {a: 1}, skip: 1, limit: 2, fields: { blacklisted: 0 }}).observe(cbs); test.equal(operations.shift(), undefined); await c.insertAsync({a: 1, blacklisted: 1324}); test.equal(operations.shift(), undefined); @@ -1939,7 +1939,7 @@ Tinytest.addAsync('minimongo - observe ordered with projection', async test => { // test _no_indices await c.removeAsync({}); - handle = await c.find({}, {sort: {a: 1}, fields: { a: 1 }}).observe(Object.assign(cbs, {_no_indices: true})); + handle = c.find({}, {sort: {a: 1}, fields: { a: 1 }}).observe(Object.assign(cbs, {_no_indices: true})); await c.insertAsync({_id: 'foo', a: 1, zoo: 'crazy'}); test.equal(operations.shift(), ['added', {a: 1}, -1, null]); await c.updateAsync({a: 1}, {$set: {a: 2, foobar: 'player'}}); @@ -3108,7 +3108,7 @@ Tinytest.addAsync('minimongo - observe ordered', async test => { let handle; const c = new LocalCollection(); - handle = await c.find({}, {sort: {a: 1}}).observe(cbs); + handle = c.find({}, {sort: {a: 1}}).observe(cbs); test.isTrue(handle.collection === c); await c.insertAsync({_id: 'foo', a: 1}); @@ -3135,14 +3135,20 @@ Tinytest.addAsync('minimongo - observe ordered', async test => { test.equal(operations.shift(), undefined); // test initial inserts (and backwards sort) - handle = await c.find({}, {sort: {a: -1}}).observe(cbs); + handle = c.find({}, {sort: {a: -1}}).observe(cbs); test.equal(operations.shift(), ['added', {a: 2}, 0, null]); test.equal(operations.shift(), ['added', {a: 1}, 1, null]); handle.stop(); // test _suppress_initial - handle = await c.find({}, {sort: {a: -1}}).observe(Object.assign({ - _suppress_initial: true}, cbs)); + handle = c.find({}, { sort: { a: -1 } }).observe( + Object.assign( + { + _suppress_initial: true, + }, + cbs + ) + ); test.equal(operations.shift(), undefined); await c.insertAsync({a: 100}); test.equal(operations.shift(), ['added', {a: 100}, 0, idA2]); @@ -3150,7 +3156,7 @@ Tinytest.addAsync('minimongo - observe ordered', async test => { // test skip and limit. await c.removeAsync({}); - handle = await c.find({}, {sort: {a: 1}, skip: 1, limit: 2}).observe(cbs); + handle = c.find({}, { sort: { a: 1 }, skip: 1, limit: 2 }).observe(cbs); test.equal(operations.shift(), undefined); await c.insertAsync({a: 1}); test.equal(operations.shift(), undefined); @@ -3174,7 +3180,7 @@ Tinytest.addAsync('minimongo - observe ordered', async test => { await c.insertAsync({a: 1}); await c.insertAsync({_id: 'two', a: 2}); await c.insertAsync({a: 3}); - handle = await c.find({}, {sort: {a: 1}, limit: 2}).observe(cbs); + handle = c.find({}, { sort: { a: 1 }, limit: 2 }).observe(cbs); test.equal(operations.shift(), ['added', {a: 1}, 0, null]); test.equal(operations.shift(), ['added', {a: 2}, 1, null]); test.equal(operations.shift(), undefined); @@ -3187,7 +3193,7 @@ Tinytest.addAsync('minimongo - observe ordered', async test => { // test _no_indices await c.removeAsync({}); - handle = await c.find({}, {sort: {a: 1}}).observe(Object.assign(cbs, {_no_indices: true})); + handle = c.find({}, {sort: {a: 1}}).observe(Object.assign(cbs, {_no_indices: true})); await c.insertAsync({_id: 'foo', a: 1}); test.equal(operations.shift(), ['added', {a: 1}, -1, null]); await c.updateAsync({a: 1}, {$set: {a: 2}}); @@ -3207,63 +3213,86 @@ Tinytest.addAsync('minimongo - observe ordered', async test => { handle.stop(); }); -[true, false].forEach(ordered => { - Tinytest.addAsync(`minimongo - observe ordered: ${ordered}`, async test => { +[true, false].forEach((ordered) => { + Tinytest.addAsync(`minimongo - observe ordered: ${ordered}`, async (test) => { const c = new LocalCollection(); - let ev = ''; - const makecb = tag => { + let ev = ""; + const makecb = (tag) => { const ret = {}; - ['added', 'changed', 'removed'].forEach(fn => { + ["added", "changed", "removed"].forEach((fn) => { const fnName = ordered ? `${fn}At` : fn; - ret[fnName] = doc => { + ret[fnName] = (doc) => { ev = `${ev + fn.substr(0, 1) + tag + doc._id}_`; }; }); return ret; }; - const expect = x => { + const expect = (x) => { test.equal(ev, x); - ev = ''; + ev = ""; }; - await c.insertAsync({_id: 1, name: 'strawberry', tags: ['fruit', 'red', 'squishy']}); - await c.insertAsync({_id: 2, name: 'apple', tags: ['fruit', 'red', 'hard']}); - await c.insertAsync({_id: 3, name: 'rose', tags: ['flower', 'red', 'squishy']}); + await c.insertAsync({ + _id: 1, + name: "strawberry", + tags: ["fruit", "red", "squishy"], + }); + await c.insertAsync({ + _id: 2, + name: "apple", + tags: ["fruit", "red", "hard"], + }); + await c.insertAsync({ + _id: 3, + name: "rose", + tags: ["flower", "red", "squishy"], + }); // This should work equally well for ordered and unordered observations // (because the callbacks don't look at indices and there's no 'moved' // callback). - let handle = await c.find({tags: 'flower'}).observe(makecb('a')); - expect('aa3_'); - await c.updateAsync({name: 'rose'}, {$set: {tags: ['bloom', 'red', 'squishy']}}); - expect('ra3_'); - await c.updateAsync({name: 'rose'}, {$set: {tags: ['flower', 'red', 'squishy']}}); - expect('aa3_'); - await c.updateAsync({name: 'rose'}, {$set: {food: false}}); - expect('ca3_'); + let handle = c.find({ tags: "flower" }).observe(makecb("a")); + expect("aa3_"); + await c.updateAsync( + { name: "rose" }, + { $set: { tags: ["bloom", "red", "squishy"] } } + ); + expect("ra3_"); + await c.updateAsync( + { name: "rose" }, + { $set: { tags: ["flower", "red", "squishy"] } } + ); + expect("aa3_"); + await c.updateAsync({ name: "rose" }, { $set: { food: false } }); + expect("ca3_"); c.remove({}); - expect('ra3_'); - await c.insertAsync({_id: 4, name: 'daisy', tags: ['flower']}); - expect('aa4_'); + expect("ra3_"); + await c.insertAsync({ _id: 4, name: "daisy", tags: ["flower"] }); + expect("aa4_"); handle.stop(); // After calling stop, no more callbacks are called. - await c.insertAsync({_id: 5, name: 'iris', tags: ['flower']}); - expect(''); + await c.insertAsync({ _id: 5, name: "iris", tags: ["flower"] }); + expect(""); // Test that observing a lookup by ID works. - handle = await c.find(4).observe(makecb('b')); - expect('ab4_'); - await c.updateAsync(4, {$set: {eek: 5}}); - expect('cb4_'); + handle = c.find(4).observe(makecb("b")); + expect("ab4_"); + await c.updateAsync(4, { $set: { eek: 5 } }); + expect("cb4_"); handle.stop(); // Test observe with reactive: false. - handle = await c.find({tags: 'flower'}, {reactive: false}).observe(makecb('c')); - expect('ac4_ac5_'); + handle = c + .find({ tags: "flower" }, { reactive: false }) + .observe(makecb("c")); + // TODO: think about this one below. + const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + await sleep(10); + expect("ac4_ac5_"); // This insert shouldn't trigger a callback because it's not reactive. - await c.insertAsync({_id: 6, name: 'river', tags: ['flower']}); - expect(''); + await c.insertAsync({ _id: 6, name: "river", tags: ["flower"] }); + expect(""); handle.stop(); }); }); @@ -3365,7 +3394,7 @@ Tinytest.addAsync('minimongo - pause', async test => { const cbs = log_callbacks(operations); const c = new LocalCollection(); - const h = await c.find({}).observe(cbs); + const h = c.find({}).observe(cbs); // remove and add cancel out. await c.insertAsync({_id: 1, a: 1}); @@ -3378,7 +3407,7 @@ Tinytest.addAsync('minimongo - pause', async test => { await c.insertAsync({_id: 1, a: 1}); test.length(operations, 0); - await c.resumeObserversClient(); + c.resumeObserversClient(); test.length(operations, 0); @@ -3388,7 +3417,7 @@ Tinytest.addAsync('minimongo - pause', async test => { await c.updateAsync({_id: 1}, {a: 2}); await c.updateAsync({_id: 1}, {a: 3}); - await c.resumeObserversClient(); + c.resumeObserversClient(); test.equal(operations.shift(), ['changed', {a: 3}, 0, {a: 1}]); test.length(operations, 0); @@ -3396,7 +3425,7 @@ Tinytest.addAsync('minimongo - pause', async test => { c.pauseObservers(); test.equal(await c.removeAsync({}), 1); test.length(operations, 0); - await c.resumeObserversClient(); + c.resumeObserversClient(); test.equal(operations.shift(), ['removed', 1, 0, {a: 3}]); test.length(operations, 0); @@ -3566,8 +3595,8 @@ Tinytest.addAsync('minimongo - $near operator tests', async test => { await coll.insertAsync({ rest: { loc: [-3, 3] } }); await coll.insertAsync({ rest: { loc: [5, 5] } }); - test.equal(coll.find({ 'rest.loc': { $near: [0, 0], $maxDistance: 30 } }).count(), 3); - test.equal(coll.find({ 'rest.loc': { $near: [0, 0], $maxDistance: 4 } }).count(), 1); + test.equal(await coll.find({ 'rest.loc': { $near: [0, 0], $maxDistance: 30 } }).count(), 3); + test.equal(await coll.find({ 'rest.loc': { $near: [0, 0], $maxDistance: 4 } }).count(), 1); const points = await coll.find({ 'rest.loc': { $near: [0, 0], $maxDistance: 6 } }).fetchAsync(); points.forEach((point, i, points) => { test.isTrue(!i || distance([0, 0], point.rest.loc) >= distance([0, 0], points[i - 1].rest.loc)); @@ -3703,7 +3732,7 @@ Tinytest.addAsync('minimongo - $near operator tests', async test => { const operations = []; const cbs = log_callbacks(operations); - const handle = await coll.find({'a.b': {$near: [7, 7]}}).observe(cbs); + const handle = coll.find({'a.b': {$near: [7, 7]}}).observe(cbs); test.length(operations, 2); test.equal(operations.shift(), ['added', {k: 9, a: {b: [5, 5]}}, 0, null]);