From 568eace1b0726b3e2fb823292e30b0e2f0efb4ca Mon Sep 17 00:00:00 2001 From: italo jose Date: Thu, 9 Apr 2026 10:42:47 -0300 Subject: [PATCH 01/15] refactor: applying linter fixes --- .fmtignore | 19 ++ .oxlintignore | 19 ++ packages/binary-heap/max-heap.js | 4 +- packages/callback-hook/hook.js | 2 +- packages/check/check.d.ts | 6 +- packages/check/isPlainObject.js | 9 +- packages/check/match.js | 2 +- packages/check/match_test.js | 26 +-- packages/diff-sequence/diff.js | 73 ++++---- packages/diff-sequence/tests.js | 58 +++--- packages/ejson/ejson.d.ts | 12 +- packages/ejson/ejson_tests.js | 7 +- packages/ejson/stringify.js | 20 +-- packages/id-map/id-map.js | 4 +- packages/logging/logging.js | 16 +- packages/logging/logging_browser.js | 2 +- packages/logging/logging_test.js | 8 +- packages/ordered-dict/ordered_dict.js | 26 +-- packages/random/AbstractRandomGenerator.js | 1 - packages/random/AleaRandomGenerator.js | 4 +- packages/random/NodeRandomGenerator.js | 2 +- .../reload-safety-belt-tests.js | 4 +- packages/retry/retry.js | 4 +- packages/session/session_tests.js | 6 +- packages/test-helpers/async_multi.js | 39 ++-- packages/test-helpers/callback_logger.js | 34 ++-- packages/test-helpers/canonicalize_html.js | 32 ++-- packages/test-helpers/connection_server.js | 22 ++- packages/test-helpers/domutils.js | 12 +- packages/test-helpers/event_simulation.js | 10 +- packages/test-helpers/render_div.js | 2 +- packages/test-helpers/seeded_random.js | 7 +- packages/test-helpers/seeded_random_test.js | 12 +- packages/test-helpers/try_all_permutations.js | 14 +- .../test-helpers/try_all_permutations_test.js | 20 +-- packages/test-in-browser/driver.js | 170 +++++++++--------- packages/test-in-console/driver.js | 73 ++++---- packages/test-in-console/puppeteer_runner.js | 2 +- packages/test-in-console/reporter.js | 4 +- packages/tinytest/tinytest.js | 57 +++--- packages/tinytest/tinytest_client.js | 24 ++- packages/tinytest/tinytest_server.js | 8 +- packages/webapp-hashing/webapp-hashing.js | 10 +- 43 files changed, 444 insertions(+), 442 deletions(-) diff --git a/.fmtignore b/.fmtignore index fe089bcddd..a6d95b9ccb 100644 --- a/.fmtignore +++ b/.fmtignore @@ -35,3 +35,22 @@ packages/* !packages/non-core/bundle-visualizer/ !packages/non-core/mongo-decimal/ !packages/non-core/xmlbuilder/ +!packages/base64/ +!packages/binary-heap/ +!packages/diff-sequence/ +!packages/callback-hook/ +!packages/ejson/ +!packages/check/ +!packages/random/ +!packages/id-map/ +!packages/ordered-dict/ +!packages/rate-limit/ +!packages/retry/ +!packages/logging/ +!packages/reload-safetybelt/ +!packages/session/ +!packages/test-helpers/ +!packages/test-in-browser/ +!packages/test-in-console/ +!packages/tinytest/ +!packages/webapp-hashing/ diff --git a/.oxlintignore b/.oxlintignore index 31fd4c2bcb..97fd0289b7 100644 --- a/.oxlintignore +++ b/.oxlintignore @@ -32,3 +32,22 @@ packages/* !packages/non-core/bundle-visualizer/ !packages/non-core/mongo-decimal/ !packages/non-core/xmlbuilder/ +!packages/base64/ +!packages/binary-heap/ +!packages/diff-sequence/ +!packages/callback-hook/ +!packages/ejson/ +!packages/check/ +!packages/random/ +!packages/id-map/ +!packages/ordered-dict/ +!packages/rate-limit/ +!packages/retry/ +!packages/logging/ +!packages/reload-safetybelt/ +!packages/session/ +!packages/test-helpers/ +!packages/test-in-browser/ +!packages/test-in-console/ +!packages/tinytest/ +!packages/webapp-hashing/ diff --git a/packages/binary-heap/max-heap.js b/packages/binary-heap/max-heap.js index 235fed1d0d..79eec0400e 100644 --- a/packages/binary-heap/max-heap.js +++ b/packages/binary-heap/max-heap.js @@ -204,9 +204,7 @@ export class MaxHeap { _selfCheck() { for (let i = 1; i < this._heap.length; i++) { if (this._maxIndex(parentIdx(i), i) !== parentIdx(i)) { - throw new Error(`An item with id ${this._heap[i].id}` + - " has a parent younger than it: " + - this._heap[parentIdx(i)].id); + throw new Error(`An item with id ${this._heap[i].id} has a parent younger than it: ${this._heap[parentIdx(i)].id}`); } } } diff --git a/packages/callback-hook/hook.js b/packages/callback-hook/hook.js index d1156f79fd..256ffd83d3 100644 --- a/packages/callback-hook/hook.js +++ b/packages/callback-hook/hook.js @@ -161,7 +161,7 @@ function dontBindEnvironment(func, onException, _this) { const description = onException || "callback of async function"; onException = function (error) { Meteor._debug( - "Exception in " + description, + `Exception in ${description}`, error ); }; diff --git a/packages/check/check.d.ts b/packages/check/check.d.ts index 84e2239ed0..975d8be9f9 100644 --- a/packages/check/check.d.ts +++ b/packages/check/check.d.ts @@ -32,9 +32,9 @@ export namespace Match { unknown; /** Matches any value. */ - var Any: Matcher; + let Any: Matcher; /** Matches a signed 32-bit integer. Doesn’t match `Infinity`, `-Infinity`, or `NaN`. */ - var Integer: Matcher; + let Integer: Matcher; /** * Matches either `undefined`, `null`, or pattern. If used in an object, matches only if the key is not set as opposed to the value being set to `undefined` or `null`. This set of conditions @@ -66,7 +66,7 @@ export namespace Match { function Where(condition: (val: any) => val is T): Matcher; function Where(condition: (val: any) => boolean): Matcher; - var NonEmptyString: Matcher; + let NonEmptyString: Matcher; /** * Returns true if the value matches the pattern. * @param value The value to check diff --git a/packages/check/isPlainObject.js b/packages/check/isPlainObject.js index bf392c70af..cdbbee772e 100644 --- a/packages/check/isPlainObject.js +++ b/packages/check/isPlainObject.js @@ -13,16 +13,13 @@ const ObjectFunctionString = fnToString.call(Object); const getProto = Object.getPrototypeOf; export const isPlainObject = obj => { - let proto; - let Ctor; - // Detect obvious negatives // Use toString instead of jQuery.type to catch host objects if (!obj || toString.call(obj) !== '[object Object]') { return false; } - proto = getProto(obj); + const proto = getProto(obj); // Objects with no prototype (e.g., `Object.create( null )`) are plain if (!proto) { @@ -30,7 +27,7 @@ export const isPlainObject = obj => { } // Objects with prototype are plain iff they were constructed by a global Object function - Ctor = hasOwn.call(proto, 'constructor') && proto.constructor; - return typeof Ctor === 'function' && + const Ctor = hasOwn.call(proto, 'constructor') && proto.constructor; + return typeof Ctor === 'function' && fnToString.call(Ctor) === ObjectFunctionString; }; diff --git a/packages/check/match.js b/packages/check/match.js index 20f534c2e8..4fc072e539 100644 --- a/packages/check/match.js +++ b/packages/check/match.js @@ -452,7 +452,7 @@ const testSubtree = (value, pattern, collectErrors = false, errors = [], path = } }); - for (let key in Object(value)) { + for (const key in Object(value)) { const subValue = value[key]; const objPath = path ? `${path}.${key}` : key; if (hasOwn.call(requiredPatterns, key)) { diff --git a/packages/check/match_test.js b/packages/check/match_test.js index 2aaa21cae7..37a4d54cfe 100644 --- a/packages/check/match_test.js +++ b/packages/check/match_test.js @@ -56,7 +56,7 @@ Tinytest.add('check - check', test => { try { check(pair[0], type); return true; - } catch (e) { + } catch { return false; } })); @@ -72,7 +72,7 @@ Tinytest.add('check - check', test => { try { check(pair[0], type); return true; - } catch (e) { + } catch { return false; } })); @@ -338,7 +338,7 @@ Tinytest.add('check - check throw all errors', test => { try { check(pair[0], type); return true; - } catch (e) { + } catch { return false; } })); @@ -354,7 +354,7 @@ Tinytest.add('check - check throw all errors', test => { try { check(pair[0], type); return true; - } catch (e) { + } catch { return false; } })); @@ -655,14 +655,14 @@ Tinytest.add('check - argument checker', test => { doesntCheckAllArguments(() => {}, undefined); doesntCheckAllArguments(() => {}, null); doesntCheckAllArguments(() => {}, 1); - doesntCheckAllArguments((x, ...args) => check(args, [String]), 1, 'asdf', 'foo'); - doesntCheckAllArguments((x, y) => check(x, Boolean), true, false); + doesntCheckAllArguments((_x, ...args) => check(args, [String]), 1, 'asdf', 'foo'); + doesntCheckAllArguments((x, _y) => check(x, Boolean), true, false); // One "true" check doesn't count for all. - doesntCheckAllArguments((x, y) => check(x, Boolean), true, true); + doesntCheckAllArguments((x, _y) => check(x, Boolean), true, true); // For non-primitives, we really do require that each arg gets checked. - doesntCheckAllArguments((x, y) => { + doesntCheckAllArguments((x, _y) => { check(x, [Boolean]); check(x, [Boolean]); }, [true], [true]); @@ -671,7 +671,7 @@ Tinytest.add('check - argument checker', test => { // differentiate between "two calls to check x, both of which are true" and // "check x and check y, both of which are true" (for any interned primitive // type). - checksAllArguments((x, y) => { + checksAllArguments((x, _y) => { check(x, Boolean); check(x, Boolean); }, true, true); @@ -705,7 +705,7 @@ Tinytest.add('check - Match error path', test => { // Numbers only, can be accessed w/o quotes match({ '1231': 123 }, { '1231': String }, '[1231]'); - match({ '1234abcd': 123 }, { '1234abcd': String }, '[\"1234abcd\"]'); + match({ '1234abcd': 123 }, { '1234abcd': String }, '["1234abcd"]'); match({ $set: { people: 'nice' } }, { $set: { people: [String] } }, '$set.people'); match({ _underscore: 'should work' }, { _underscore: Number }, '_underscore'); @@ -719,7 +719,7 @@ Tinytest.add('check - Match error path', test => { [[[{ foo: String }]]], '[1][0][0].foo'); // JS keyword - match({ 'return': 0 }, { 'return': String }, '[\"return\"]'); + match({ 'return': 0 }, { 'return': String }, '["return"]'); }); Tinytest.add('check - Match error message', test => { @@ -748,7 +748,7 @@ Tinytest.add('check - Match error message', test => { match({}, Match.ObjectIncluding({ bar: String }), "Missing key 'bar'"); match(null, Object, 'Expected object, got null'); match(null, Function, 'Expected function, got null'); - match('bar', 'foo', 'Expected foo, got \"bar\"'); + match('bar', 'foo', 'Expected foo, got "bar"'); match(3.14, Match.Integer, 'Expected Integer, got 3.14'); match(false, [Boolean], 'Expected array, got false'); match([null, null], [String], 'Expected string, got null in field [0]'); @@ -763,7 +763,7 @@ Tinytest.add('check - Match error message', test => { }; const testInstanceChild = new TestInstanceChild() - const testInstanceParent = new TestInstanceParent(testInstanceChild); + new TestInstanceParent(testInstanceChild); match(testInstanceChild, TestInstanceParent, `Expected ${(TestInstanceParent.name || 'particular constructor')}`); const circleFoo = {}; diff --git a/packages/diff-sequence/diff.js b/packages/diff-sequence/diff.js index d24fa616ed..0893a923c0 100644 --- a/packages/diff-sequence/diff.js +++ b/packages/diff-sequence/diff.js @@ -3,7 +3,7 @@ export const DiffSequence = {}; const hasOwn = Object.prototype.hasOwnProperty; function isObjEmpty(obj) { - for (let key in Object(obj)) { + for (const key in Object(obj)) { if (hasOwn.call(obj, key)) { return false; } @@ -28,26 +28,26 @@ DiffSequence.diffQueryChanges = function (ordered, oldResults, newResults, DiffSequence.diffQueryUnorderedChanges = function (oldResults, newResults, observer, options) { options = options || {}; - var projectionFn = options.projectionFn || EJSON.clone; + const projectionFn = options.projectionFn || EJSON.clone; if (observer.movedBefore) { throw new Error("_diffQueryUnordered called with a movedBefore observer!"); } newResults.forEach(function (newDoc, id) { - var oldDoc = oldResults.get(id); + const oldDoc = oldResults.get(id); if (oldDoc) { if (observer.changed && !EJSON.equals(oldDoc, newDoc)) { - var projectedNew = projectionFn(newDoc); - var projectedOld = projectionFn(oldDoc); - var changedFields = + const projectedNew = projectionFn(newDoc); + const projectedOld = projectionFn(oldDoc); + const changedFields = DiffSequence.makeChangedFields(projectedNew, projectedOld); if (! isObjEmpty(changedFields)) { observer.changed(id, changedFields); } } } else if (observer.added) { - var fields = projectionFn(newDoc); + const fields = projectionFn(newDoc); delete fields._id; observer.added(newDoc._id, fields); } @@ -64,16 +64,16 @@ DiffSequence.diffQueryUnorderedChanges = function (oldResults, newResults, DiffSequence.diffQueryOrderedChanges = function (old_results, new_results, observer, options) { options = options || {}; - var projectionFn = options.projectionFn || EJSON.clone; + const projectionFn = options.projectionFn || EJSON.clone; - var new_presence_of_id = {}; + const new_presence_of_id = {}; new_results.forEach(function (doc) { if (new_presence_of_id[doc._id]) Meteor._debug("Duplicate _id in new_results"); new_presence_of_id[doc._id] = true; }); - var old_index_of_id = {}; + const old_index_of_id = {}; old_results.forEach(function (doc, i) { if (doc._id in old_index_of_id) Meteor._debug("Duplicate _id in old_results"); @@ -114,26 +114,26 @@ DiffSequence.diffQueryOrderedChanges = function (old_results, new_results, // // unmoved: the output of the algorithm; members of the LCS, // in the form of indices into new_results - var unmoved = []; + const unmoved = []; // max_seq_len: length of LCS found so far - var max_seq_len = 0; + let max_seq_len = 0; // seq_ends[i]: the index into new_results of the last doc in a // common subsequence of length of i+1 <= max_seq_len - var N = new_results.length; - var seq_ends = new Array(N); + const N = new_results.length; + const seq_ends = Array.from({ length: N }); // ptrs: the common subsequence ending with new_results[n] extends // a common subsequence ending with new_results[ptr[n]], unless // ptr[n] is -1. - var ptrs = new Array(N); + const ptrs = Array.from({ length: N }); // virtual sequence of old indices of new results - var old_idx_seq = function(i_new) { + const old_idx_seq = function(i_new) { return old_index_of_id[new_results[i_new]._id]; }; // for each item in new_results, use it to extend a common subsequence // of length j <= max_seq_len - for(var i=0; i= 0) { unmoved.push(idx); idx = ptrs[idx]; @@ -166,23 +166,24 @@ DiffSequence.diffQueryOrderedChanges = function (old_results, new_results, unmoved.push(new_results.length); old_results.forEach(function (doc) { - if (!new_presence_of_id[doc._id]) - observer.removed && observer.removed(doc._id); + if (!new_presence_of_id[doc._id]) { + if (observer.removed) observer.removed(doc._id); + } }); // for each group of things in the new_results that is anchored by an unmoved // element, iterate through the things before it. - var startOfGroup = 0; + let startOfGroup = 0; unmoved.forEach(function (endOfGroup) { - var groupId = new_results[endOfGroup] ? new_results[endOfGroup]._id : null; - var oldDoc, newDoc, fields, projectedNew, projectedOld; - for (var i = startOfGroup; i < endOfGroup; i++) { + const groupId = new_results[endOfGroup] ? new_results[endOfGroup]._id : null; + let oldDoc, newDoc, fields, projectedNew, projectedOld; + for (let i = startOfGroup; i < endOfGroup; i++) { newDoc = new_results[i]; if (!hasOwn.call(old_index_of_id, newDoc._id)) { fields = projectionFn(newDoc); delete fields._id; - observer.addedBefore && observer.addedBefore(newDoc._id, fields, groupId); - observer.added && observer.added(newDoc._id, fields); + if (observer.addedBefore) observer.addedBefore(newDoc._id, fields, groupId); + if (observer.added) observer.added(newDoc._id, fields); } else { // moved oldDoc = old_results[old_index_of_id[newDoc._id]]; @@ -190,9 +191,9 @@ DiffSequence.diffQueryOrderedChanges = function (old_results, new_results, projectedOld = projectionFn(oldDoc); fields = DiffSequence.makeChangedFields(projectedNew, projectedOld); if (!isObjEmpty(fields)) { - observer.changed && observer.changed(newDoc._id, fields); + if (observer.changed) observer.changed(newDoc._id, fields); } - observer.movedBefore && observer.movedBefore(newDoc._id, groupId); + if (observer.movedBefore) observer.movedBefore(newDoc._id, groupId); } } if (groupId) { @@ -202,7 +203,7 @@ DiffSequence.diffQueryOrderedChanges = function (old_results, new_results, projectedOld = projectionFn(oldDoc); fields = DiffSequence.makeChangedFields(projectedNew, projectedOld); if (!isObjEmpty(fields)) { - observer.changed && observer.changed(newDoc._id, fields); + if (observer.changed) observer.changed(newDoc._id, fields); } } startOfGroup = endOfGroup+1; @@ -222,9 +223,9 @@ DiffSequence.diffObjects = function (left, right, callbacks) { Object.keys(left).forEach(key => { const leftValue = left[key]; if (hasOwn.call(right, key)) { - callbacks.both && callbacks.both(key, leftValue, right[key]); + if (callbacks.both) callbacks.both(key, leftValue, right[key]); } else { - callbacks.leftOnly && callbacks.leftOnly(key, leftValue); + if (callbacks.leftOnly) callbacks.leftOnly(key, leftValue); } }); @@ -241,9 +242,9 @@ DiffSequence.diffObjects = function (left, right, callbacks) { DiffSequence.diffMaps = function (left, right, callbacks) { left.forEach(function (leftValue, key) { if (right.has(key)){ - callbacks.both && callbacks.both(key, leftValue, right.get(key)); + if (callbacks.both) callbacks.both(key, leftValue, right.get(key)); } else { - callbacks.leftOnly && callbacks.leftOnly(key, leftValue); + if (callbacks.leftOnly) callbacks.leftOnly(key, leftValue); } }); @@ -258,9 +259,9 @@ DiffSequence.diffMaps = function (left, right, callbacks) { DiffSequence.makeChangedFields = function (newDoc, oldDoc) { - var fields = {}; + const fields = {}; DiffSequence.diffObjects(oldDoc, newDoc, { - leftOnly: function (key, value) { + leftOnly: function (key, _value) { fields[key] = undefined; }, rightOnly: function (key, value) { diff --git a/packages/diff-sequence/tests.js b/packages/diff-sequence/tests.js index 8c593baee0..dc8e71a724 100644 --- a/packages/diff-sequence/tests.js +++ b/packages/diff-sequence/tests.js @@ -1,11 +1,11 @@ Tinytest.add("diff-sequence - diff changes ordering", function (test) { - var makeDocs = function (ids) { + const makeDocs = function (ids) { return ids.map(function (id) { return {_id: id};}); }; - var testMutation = function (a, b) { - var aa = makeDocs(a); - var bb = makeDocs(b); - var aaCopy = EJSON.clone(aa); + const testMutation = function (a, b) { + const aa = makeDocs(a); + const bb = makeDocs(b); + const aaCopy = EJSON.clone(aa); DiffSequence.diffQueryOrderedChanges(aa, bb, { addedBefore: function (id, doc, before) { @@ -13,7 +13,7 @@ Tinytest.add("diff-sequence - diff changes ordering", function (test) { aaCopy.push( Object.assign({_id: id}, doc)); return; } - for (var i = 0; i < aaCopy.length; i++) { + for (let i = 0; i < aaCopy.length; i++) { if (aaCopy[i]._id === before) { aaCopy.splice(i, 0, Object.assign({_id: id}, doc)); return; @@ -21,8 +21,8 @@ Tinytest.add("diff-sequence - diff changes ordering", function (test) { } }, movedBefore: function (id, before) { - var found; - for (var i = 0; i < aaCopy.length; i++) { + let found; + for (let i = 0; i < aaCopy.length; i++) { if (aaCopy[i]._id === id) { found = aaCopy[i]; aaCopy.splice(i, 1); @@ -32,7 +32,7 @@ Tinytest.add("diff-sequence - diff changes ordering", function (test) { aaCopy.push( Object.assign({_id: id}, found)); return; } - for (i = 0; i < aaCopy.length; i++) { + for (let i = 0; i < aaCopy.length; i++) { if (aaCopy[i]._id === before) { aaCopy.splice(i, 0, Object.assign({_id: id}, found)); return; @@ -40,10 +40,8 @@ Tinytest.add("diff-sequence - diff changes ordering", function (test) { } }, removed: function (id) { - var found; - for (var i = 0; i < aaCopy.length; i++) { + for (let i = 0; i < aaCopy.length; i++) { if (aaCopy[i]._id === id) { - found = aaCopy[i]; aaCopy.splice(i, 1); } } @@ -52,7 +50,7 @@ Tinytest.add("diff-sequence - diff changes ordering", function (test) { test.equal(aaCopy, bb); }; - var testBothWays = function (a, b) { + const testBothWays = function (a, b) { testMutation(a, b); testMutation(b, a); }; @@ -70,54 +68,54 @@ Tinytest.add("diff-sequence - diff", function (test) { // test correctness - var diffTest = function(origLen, newOldIdx) { - var oldResults = new Array(origLen); - for (var i = 1; i <= origLen; i++) + const diffTest = function(origLen, newOldIdx) { + const oldResults = Array.from({ length: origLen }); + for (let i = 1; i <= origLen; i++) oldResults[i-1] = {_id: i}; - var newResults = newOldIdx.map(function(n) { - var doc = {_id: Math.abs(n)}; + const newResults = newOldIdx.map(function(n) { + const doc = {_id: Math.abs(n)}; if (n < 0) doc.changed = true; return doc; }); - var find = function (arr, id) { - for (var i = 0; i < arr.length; i++) { + const find = function (arr, id) { + for (let i = 0; i < arr.length; i++) { if (EJSON.equals(arr[i]._id, id)) return i; } return -1; }; - var results = [...oldResults]; - var observer = { + const results = [...oldResults]; + const observer = { addedBefore: function(id, fields, before) { - var before_idx; + let before_idx; if (before === null) before_idx = results.length; else before_idx = find (results, before); - var doc = Object.assign({_id: id}, fields); + const doc = Object.assign({_id: id}, fields); test.isFalse(before_idx < 0 || before_idx > results.length); results.splice(before_idx, 0, doc); }, removed: function(id) { - var at_idx = find (results, id); + const at_idx = find (results, id); test.isFalse(at_idx < 0 || at_idx >= results.length); results.splice(at_idx, 1); }, changed: function(id, fields) { - var at_idx = find (results, id); - var oldDoc = results[at_idx]; - var doc = EJSON.clone(oldDoc); + const at_idx = find (results, id); + const oldDoc = results[at_idx]; + const doc = EJSON.clone(oldDoc); DiffSequence.applyChanges(doc, fields); test.isFalse(at_idx < 0 || at_idx >= results.length); test.equal(doc._id, oldDoc._id); results[at_idx] = doc; }, movedBefore: function(id, before) { - var old_idx = find(results, id); - var new_idx; + const old_idx = find(results, id); + let new_idx; if (before === null) new_idx = results.length; else diff --git a/packages/ejson/ejson.d.ts b/packages/ejson/ejson.d.ts index 61c3a7674c..53533f16d0 100644 --- a/packages/ejson/ejson.d.ts +++ b/packages/ejson/ejson.d.ts @@ -1,6 +1,6 @@ export interface EJSONableCustomType { clone?(): EJSONableCustomType; - equals?(other: Object): boolean; + equals?(other: object): boolean; toJSONValue(): JSONable; typeName(): string; } @@ -9,10 +9,10 @@ export type EJSONableProperty = | number | string | boolean - | Object + | object | number[] | string[] - | Object[] + | object[] | Date | Uint8Array | EJSONableCustomType @@ -28,10 +28,10 @@ export interface JSONable { | number | string | boolean - | Object + | object | number[] | string[] - | Object[] + | object[] | undefined | null; } @@ -54,7 +54,7 @@ export namespace EJSON { function fromJSONValue(val: JSONable): any; - function isBinary(x: Object): x is Uint8Array; + function isBinary(x: object): x is Uint8Array; function newBinary(size: number): Uint8Array; function parse(str: string): EJSON; diff --git a/packages/ejson/ejson_tests.js b/packages/ejson/ejson_tests.js index aaad7aa103..cdb08168f6 100644 --- a/packages/ejson/ejson_tests.js +++ b/packages/ejson/ejson_tests.js @@ -205,11 +205,10 @@ Tinytest.add('ejson - parse', test => { Tinytest.add("ejson - regexp", test => { test.equal(EJSON.stringify(/foo/gi), "{\"$regexp\":\"foo\",\"$flags\":\"gi\"}"); - var d = new RegExp("foo", "gi"); - var obj = { $regexp: "foo", $flags: "gi" }; + const obj = { $regexp: "foo", $flags: "gi" }; - var eObj = EJSON.toJSONValue(obj); - var roundTrip = EJSON.fromJSONValue(eObj); + const eObj = EJSON.toJSONValue(obj); + const roundTrip = EJSON.fromJSONValue(eObj); test.equal(obj, roundTrip); }); diff --git a/packages/ejson/stringify.js b/packages/ejson/stringify.js index 564b374bc8..3d6f8a4ea9 100644 --- a/packages/ejson/stringify.js +++ b/packages/ejson/stringify.js @@ -52,15 +52,9 @@ const str = (key, holder, singleIndent, outerIndent, canonical) => { if (partial.length === 0) { v = '[]'; } else if (innerIndent) { - v = '[\n' + - innerIndent + - partial.join(',\n' + - innerIndent) + - '\n' + - outerIndent + - ']'; + v = `[\n${innerIndent}${partial.join(`,\n${innerIndent}`)}\n${outerIndent}]`; } else { - v = '[' + partial.join(',') + ']'; + v = `[${partial.join(',')}]`; } return v; } @@ -82,15 +76,9 @@ const str = (key, holder, singleIndent, outerIndent, canonical) => { if (partial.length === 0) { v = '{}'; } else if (innerIndent) { - v = '{\n' + - innerIndent + - partial.join(',\n' + - innerIndent) + - '\n' + - outerIndent + - '}'; + v = `{\n${innerIndent}${partial.join(`,\n${innerIndent}`)}\n${outerIndent}}`; } else { - v = '{' + partial.join(',') + '}'; + v = `{${partial.join(',')}}`; } return v; } diff --git a/packages/id-map/id-map.js b/packages/id-map/id-map.js index c0c58ff057..ab26c3ff42 100644 --- a/packages/id-map/id-map.js +++ b/packages/id-map/id-map.js @@ -42,7 +42,7 @@ export class IdMap { // Iterates over the items in the map. Return `false` to break the loop. forEach(iterator) { // don't use _.each, because we can't break out of it. - for (let [key, value] of this._map){ + for (const [key, value] of this._map){ const breakIfFalse = iterator.call( null, value, @@ -55,7 +55,7 @@ export class IdMap { } async forEachAsync(iterator) { - for (let [key, value] of this._map){ + for (const [key, value] of this._map){ const breakIfFalse = await iterator.call( null, value, diff --git a/packages/logging/logging.js b/packages/logging/logging.js index dba14f775e..609c6db5f3 100644 --- a/packages/logging/logging.js +++ b/packages/logging/logging.js @@ -213,7 +213,7 @@ Log._getCallerDetails = () => { Log.parse = (line) => { let obj = null; if (line && line.startsWith('{')) { // might be json generated from calling 'Log' - try { obj = EJSON.parse(line); } catch (e) {} + try { obj = EJSON.parse(line); } catch { } } // XXX should probably check fields other than 'time' @@ -227,7 +227,7 @@ Log.parse = (line) => { // formats a log object into colored human and machine-readable text Log.format = (obj, options = {}) => { obj = { ...obj }; // don't mutate the argument - let { + const { time, timeInexact, level = 'info', @@ -235,11 +235,13 @@ Log.format = (obj, options = {}) => { line: lineNumber, app: appName = '', originApp, - message = '', program = '', satellite = '', stderr = '', } = obj; + let { + message = '', + } = obj; if (!(time instanceof Date)) { throw new Error("'time' must be a Date object"); @@ -260,13 +262,7 @@ Log.format = (obj, options = {}) => { const dateStamp = time.getFullYear().toString() + pad2(time.getMonth() + 1 /*0-based*/) + pad2(time.getDate()); - const timeStamp = pad2(time.getHours()) + - ':' + - pad2(time.getMinutes()) + - ':' + - pad2(time.getSeconds()) + - '.' + - pad3(time.getMilliseconds()); + const timeStamp = `${pad2(time.getHours())}:${pad2(time.getMinutes())}:${pad2(time.getSeconds())}.${pad3(time.getMilliseconds())}`; // eg in San Francisco in June this will be '(-7)' const utcOffsetStr = `(${(-(new Date().getTimezoneOffset() / 60))})`; diff --git a/packages/logging/logging_browser.js b/packages/logging/logging_browser.js index 028ee184b1..6d727d66bb 100644 --- a/packages/logging/logging_browser.js +++ b/packages/logging/logging_browser.js @@ -1,4 +1,4 @@ Formatter = {}; -Formatter.prettify = function(line, color){ +Formatter.prettify = function(line, _color){ return line; }; diff --git a/packages/logging/logging_test.js b/packages/logging/logging_test.js index 03e2c75c3c..612770e733 100644 --- a/packages/logging/logging_test.js +++ b/packages/logging/logging_test.js @@ -20,7 +20,7 @@ Tinytest.add("logging - _getCallerDetails", function (test) { // Note that we want this to work in --production too, so we need to allow // for the minified filename test.matches( - eval(code), + eval(code), // oxlint-disable-line no-eval -- intentional eval for testing caller details /^(?:eval|local-test_logging\.js|[a-f0-9]{40}\.js)/ ); } @@ -172,7 +172,7 @@ Tinytest.add("logging - parse", function (test) { Tinytest.add("logging - format", function (test) { const time = new Date(2012, 9 - 1 /*0-based*/, 8, 7, 6, 5, 4); - const utcOffsetStr = "(" + -(new Date().getTimezoneOffset() / 60) + ")"; + const utcOffsetStr = `(${-(new Date().getTimezoneOffset() / 60)})`; ["debug", "info", "warn", "error"].forEach(function (level) { test.equal( @@ -223,7 +223,7 @@ Tinytest.add("logging - format", function (test) { }), `${level .charAt(0) - .toUpperCase()}20120908-07:06:05.004${utcOffsetStr} [myApp via proxy] (server:app.js:42) message {\"foo\":\"bar\"}` + .toUpperCase()}20120908-07:06:05.004${utcOffsetStr} [myApp via proxy] (server:app.js:42) message {"foo":"bar"}` ); // stderr @@ -354,7 +354,7 @@ Tinytest.add("logging - formats - without time", function (test) { }), `${level .charAt(0) - .toUpperCase()} [myApp via proxy] (server:app.js:42) message {\"foo\":\"bar\"}` + .toUpperCase()} [myApp via proxy] (server:app.js:42) message {"foo":"bar"}` ); // stderr diff --git a/packages/ordered-dict/ordered_dict.js b/packages/ordered-dict/ordered_dict.js index 471d334f89..c55a6bfc74 100644 --- a/packages/ordered-dict/ordered_dict.js +++ b/packages/ordered-dict/ordered_dict.js @@ -35,7 +35,7 @@ export class OrderedDict { // the "prefix keys with a space" thing comes from here // https://github.com/documentcloud/underscore/issues/376#issuecomment-2815649 _k(key) { - return " " + this._stringify(key); + return ` ${this._stringify(key)}`; } empty() { @@ -75,8 +75,8 @@ export class OrderedDict { putBefore(key, item, before) { if (this._dict[this._k(key)]) - throw new Error("Item " + key + " already present in OrderedDict"); - var elt = before ? + throw new Error(`Item ${key} already present in OrderedDict`); + const elt = before ? element(key, item, this._dict[this._k(before)]) : element(key, item, null); if (typeof elt.next === "undefined") @@ -91,9 +91,9 @@ export class OrderedDict { } remove(key) { - var elt = this._dict[this._k(key)]; + const elt = this._dict[this._k(key)]; if (typeof elt === "undefined") - throw new Error("Item " + key + " not present in OrderedDict"); + throw new Error(`Item ${key} not present in OrderedDict`); this._linkEltOut(elt); this._size--; delete this._dict[this._k(key)]; @@ -118,10 +118,10 @@ export class OrderedDict { // Stops whenever iter returns OrderedDict.BREAK, or after the last element. forEach(iter, context = null) { - var i = 0; - var elt = this._first; + let i = 0; + let elt = this._first; while (elt !== null) { - var b = iter.call(context, elt.value, elt.key, i); + const b = iter.call(context, elt.value, elt.key, i); if (b === OrderedDict.BREAK) return; elt = elt.next; i++; @@ -169,7 +169,7 @@ export class OrderedDict { prev(key) { if (this.has(key)) { - var elt = this._dict[this._k(key)]; + const elt = this._dict[this._k(key)]; if (elt.prev) return elt.prev.key; } @@ -178,7 +178,7 @@ export class OrderedDict { next(key) { if (this.has(key)) { - var elt = this._dict[this._k(key)]; + const elt = this._dict[this._k(key)]; if (elt.next) return elt.next.key; } @@ -186,8 +186,8 @@ export class OrderedDict { } moveBefore(key, before) { - var elt = this._dict[this._k(key)]; - var eltBefore = before ? this._dict[this._k(before)] : null; + const elt = this._dict[this._k(key)]; + const eltBefore = before ? this._dict[this._k(before)] : null; if (typeof elt === "undefined") { throw new Error("Item to move is not present"); } @@ -205,7 +205,7 @@ export class OrderedDict { // Linear, sadly. indexOf(key) { - var ret = null; + let ret = null; this.forEach((v, k, i) => { if (this._k(k) === this._k(key)) { ret = i; diff --git a/packages/random/AbstractRandomGenerator.js b/packages/random/AbstractRandomGenerator.js index 75173a3b57..2190bb6bc0 100644 --- a/packages/random/AbstractRandomGenerator.js +++ b/packages/random/AbstractRandomGenerator.js @@ -7,7 +7,6 @@ // window.crypto.getRandomValues() or alea, the primitive is fraction and we use // that to construct hex string. -import { Meteor } from 'meteor/meteor'; const UNMISTAKABLE_CHARS = '23456789ABCDEFGHJKLMNPQRSTWXYZabcdefghijkmnopqrstuvwxyz'; const BASE64_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + diff --git a/packages/random/AleaRandomGenerator.js b/packages/random/AleaRandomGenerator.js index 2683c9b6cd..809a038c17 100644 --- a/packages/random/AleaRandomGenerator.js +++ b/packages/random/AleaRandomGenerator.js @@ -62,8 +62,8 @@ function Alea(seeds) { }; random.uint32 = () => random() * 0x100000000; // 2^32 - random.fract53 = () => random() + - ((random() * 0x200000 | 0) * 1.1102230246251565e-16); // 2^-53 + // oxlint-disable-next-line -- intentional bitwise OR for float-to-int (Alea PRNG algorithm) + random.fract53 = () => random() + ((random() * 0x200000 | 0) * 1.1102230246251565e-16); // 2^-53 random.version = 'Alea 0.9'; random.args = seeds; diff --git a/packages/random/NodeRandomGenerator.js b/packages/random/NodeRandomGenerator.js index a87c37366e..f2cb8ff7a4 100644 --- a/packages/random/NodeRandomGenerator.js +++ b/packages/random/NodeRandomGenerator.js @@ -25,7 +25,7 @@ export default class NodeRandomGenerator extends RandomGenerator { // non-cryptographically strong if not available. try { bytes = crypto.randomBytes(numBytes); - } catch (e) { + } catch { // XXX should re-throw any error except insufficient entropy bytes = crypto.pseudoRandomBytes(numBytes); } diff --git a/packages/reload-safetybelt/reload-safety-belt-tests.js b/packages/reload-safetybelt/reload-safety-belt-tests.js index dcfed5e569..b09d9e1206 100644 --- a/packages/reload-safetybelt/reload-safety-belt-tests.js +++ b/packages/reload-safetybelt/reload-safety-belt-tests.js @@ -1,9 +1,9 @@ await (async () => { - var script = await Assets.getTextAsync("safetybelt.js"); + const script = await Assets.getTextAsync("safetybelt.js"); Tinytest.add("reload-safetybelt - safety belt is added", function (test) { test.isTrue( - Object.values(WebAppInternals.additionalStaticJs).some( function (js, pathname) { + Object.values(WebAppInternals.additionalStaticJs).some( function (js, _pathname) { return js === script; }) ); diff --git a/packages/retry/retry.js b/packages/retry/retry.js index 48e62adc4a..b510509ede 100644 --- a/packages/retry/retry.js +++ b/packages/retry/retry.js @@ -45,7 +45,7 @@ export class Retry { // fuzz the timeout randomly, to avoid reconnect storms when a // server goes down. - var timeout = Math.min( + const timeout = Math.min( this.maxTimeout, this.baseTimeout * Math.pow(this.exponent, count) ) * ( @@ -57,7 +57,7 @@ export class Retry { // Call `fn` after a delay, based on the `count` of which retry this is. retryLater(count, fn) { - var timeout = this._timeout(count); + const timeout = this._timeout(count); if (this.retryTimer) clearTimeout(this.retryTimer); this.retryTimer = Meteor.setTimeout(fn, timeout); diff --git a/packages/session/session_tests.js b/packages/session/session_tests.js index 3b004c344d..82eb6da69a 100644 --- a/packages/session/session_tests.js +++ b/packages/session/session_tests.js @@ -104,7 +104,7 @@ Tinytest.add('session - objects are cloned', function (test) { }); Tinytest.add('session - context invalidation for get', function (test) { - var xGetExecutions = 0; + let xGetExecutions = 0; Tracker.autorun(function () { ++xGetExecutions; Session.get('x'); @@ -125,7 +125,7 @@ Tinytest.add('session - context invalidation for get', function (test) { }); Tinytest.add('session - context invalidation for equals', function (test) { - var xEqualsExecutions = 0; + let xEqualsExecutions = 0; Tracker.autorun(function () { ++xEqualsExecutions; Session.equals('x', 5); @@ -158,7 +158,7 @@ Tinytest.add( 'session - context invalidation for equals with undefined', function (test) { // Make sure the special casing for equals undefined works. - var yEqualsExecutions = 0; + let yEqualsExecutions = 0; Tracker.autorun(function () { ++yEqualsExecutions; Session.equals('y', undefined); diff --git a/packages/test-helpers/async_multi.js b/packages/test-helpers/async_multi.js index c9c09ac66e..86c46e20d7 100644 --- a/packages/test-helpers/async_multi.js +++ b/packages/test-helpers/async_multi.js @@ -42,8 +42,8 @@ // } // ]); -var ExpectationManager = function (test, onComplete) { - var self = this; +const ExpectationManager = function (test, onComplete) { + const self = this; self.test = test; self.onComplete = onComplete; @@ -54,12 +54,13 @@ var ExpectationManager = function (test, onComplete) { Object.assign(ExpectationManager.prototype, { expect: function (/* arguments */) { - var self = this; + const self = this; + let expected; if (typeof arguments[0] === "function") - var expected = arguments[0]; + expected = arguments[0]; else - var expected = Array.from(arguments); + expected = Array.from(arguments); if (self.closed) throw new Error("Too late to add more expectations to the test"); @@ -86,13 +87,13 @@ Object.assign(ExpectationManager.prototype, { }, done: function () { - var self = this; + const self = this; self.closed = true; self._check_complete(); }, cancel: function () { - var self = this; + const self = this; if (! self.dead) { self.dead = true; return true; @@ -101,7 +102,7 @@ Object.assign(ExpectationManager.prototype, { }, _check_complete: function () { - var self = this; + const self = this; if (!self.outstanding && self.closed && !self.dead) { self.dead = true; self.onComplete(); @@ -111,27 +112,27 @@ Object.assign(ExpectationManager.prototype, { testAsyncMulti = function (name, funcs, { isOnly = false } = {}) { // XXX Tests on remote browsers are _slow_. We need a better solution. - var timeout = 180000; + const timeout = 180000; const addFunction = isOnly ? Tinytest.onlyAsync : Tinytest.addAsync; addFunction(name, function (test, onComplete) { - var remaining = [...funcs] - var context = {}; - var i = 0; + const remaining = [...funcs] + const context = {}; + let i = 0; - var runNext = function () { - var func = remaining.shift(); + const runNext = function () { + const func = remaining.shift(); if (!func) { delete test.extraDetails.asyncBlock; onComplete(); } else { - var em = new ExpectationManager(test, function () { + const em = new ExpectationManager(test, function () { clearTimeout(timer); runNext(); }); - var timer = setTimeout(function () { + const timer = setTimeout(function () { if (em.cancel()) { test.fail({type: "timeout", message: "Async batch timed out"}); onComplete(); @@ -169,9 +170,9 @@ testAsyncMulti = function (name, funcs, { isOnly = false } = {}) { simplePoll = function (fn, success, failed, timeout, step) { timeout = timeout || 10000; step = step || 100; - var start = (new Date()).valueOf(); + const start = (new Date()).valueOf(); let timeOutId; - var helper = function () { + const helper = function () { if (fn()) { success(); Meteor.clearTimeout(timeOutId); @@ -190,7 +191,7 @@ simplePoll = function (fn, success, failed, timeout, step) { pollUntil = function (expect, f, timeout, step, noFail) { noFail = noFail || false; step = step || 100; - var expectation = expect(true); + const expectation = expect(true); simplePoll( f, function () { expectation(true) }, diff --git a/packages/test-helpers/callback_logger.js b/packages/test-helpers/callback_logger.js index 0cac2c825c..0bfd86ea8a 100644 --- a/packages/test-helpers/callback_logger.js +++ b/packages/test-helpers/callback_logger.js @@ -8,19 +8,19 @@ import isEqual from 'lodash.isequal'; // timeout for the callback. Because we're using Node Fibers to yield & start // ourselves, the asynchronous version is only available on the server. -var TIMEOUT = 1000; +const TIMEOUT = 1000; // Run the given function, passing it a correctly-set-up callback logger as an // argument. If we're meant to be running asynchronously, the function gets its // own Fiber. withCallbackLogger = function (test, callbackNames, async, fun) { - var logger = new CallbackLogger(test, callbackNames); + const logger = new CallbackLogger(test, callbackNames); return fun(logger); }; -var CallbackLogger = function (test, callbackNames) { - var self = this; +const CallbackLogger = function (test, callbackNames) { + const self = this; self._log = []; self._test = test; callbackNames.forEach(function (callbackName) { @@ -31,19 +31,19 @@ var CallbackLogger = function (test, callbackNames) { }; CallbackLogger.prototype.expectResult = async function (callbackName, args) { - var self = this; + const self = this; await self._waitForLengthOrTimeout(1); if (self._log.length === 0) { - self._test.fail(["Expected callback " + callbackName + " got none"]); + self._test.fail([`Expected callback ${callbackName} got none`]); return; } - var result = self._log.shift(); + const result = self._log.shift(); self._test.equal(result.callback, callbackName); self._test.equal(result.args, args); }; CallbackLogger.prototype.expectResultOnly = async function (callbackName, args) { - var self = this; + const self = this; await self.expectResult(callbackName, args); self._expectNoResultImpl(); }; @@ -55,7 +55,7 @@ CallbackLogger.prototype.expectResultOnly = async function (callbackName, args) // }; CallbackLogger.prototype._waitForLengthOrTimeout = function (len) { - var self = this; + const self = this; const timeoutControl = { executionTime: 0 }; return new Promise(resolve => { const waitFunc = () => { @@ -71,18 +71,18 @@ CallbackLogger.prototype._waitForLengthOrTimeout = function (len) { }; CallbackLogger.prototype.expectResultUnordered = async function (list) { - var self = this; + const self = this; await self._waitForLengthOrTimeout(list.length); list = [...list]; - var i = list.length; + let i = list.length; while (i > 0) { - var found = false; - var dequeued = self._log.shift(); - for (var j = 0; j < list.length; j++) { + let found = false; + const dequeued = self._log.shift(); + for (let j = 0; j < list.length; j++) { if (isEqual(list[j], dequeued)) { list.splice(j, 1); found = true; @@ -90,18 +90,18 @@ CallbackLogger.prototype.expectResultUnordered = async function (list) { } } if (!found) - self._test.fail(["Found unexpected result: " + JSON.stringify(dequeued)]); + self._test.fail([`Found unexpected result: ${JSON.stringify(dequeued)}`]); i--; } }; CallbackLogger.prototype._expectNoResultImpl = function () { - var self = this; + const self = this; self._test.length(self._log, 0); }; CallbackLogger.prototype.expectNoResult = async function (fn) { - var self = this; + const self = this; if (typeof fn === "function") { // If a function is provided, empty self._log and then call the diff --git a/packages/test-helpers/canonicalize_html.js b/packages/test-helpers/canonicalize_html.js index 1d159b85ae..142ab5372c 100644 --- a/packages/test-helpers/canonicalize_html.js +++ b/packages/test-helpers/canonicalize_html.js @@ -1,5 +1,5 @@ canonicalizeHtml = function(html) { - var h = html; + let h = html; // kill IE-specific comments inserted by DomRange h = h.replace(//g, ''); h = h.replace(//g, ''); @@ -40,31 +40,31 @@ canonicalizeHtml = function(html) { // c="d"', assume they are separated by a single space and values // are double- or single-quoted, but allow for spaces inside the // quotes. Split on space following quote. - var attrList = attrs.replace(/(\w)='([^']*)' /g, "$1='$2'\u0000"); + let attrList = attrs.replace(/(\w)='([^']*)' /g, "$1='$2'\u0000"); attrList = attrList.replace(/(\w)="([^"]*)" /g, '$1="$2"\u0000'); attrList = attrList.split("\u0000"); // put attributes in alphabetical order attrList.sort(); - var tagContents = [tagName]; + const tagContents = [tagName]; - for(var i=0; i'; + return `<${tagContents.join(' ')}>`; }); return h; }; diff --git a/packages/test-helpers/connection_server.js b/packages/test-helpers/connection_server.js index 255bfed03d..d760936a38 100644 --- a/packages/test-helpers/connection_server.js +++ b/packages/test-helpers/connection_server.js @@ -4,15 +4,12 @@ // side connection and the server side connection handle. Call `failed` on // failure. makeTestConnection = function (test, succeeded, failed) { - // The connection from the client side. - var clientConn; - // Track incoming connections server side until we know which one is // ours. - var serverConns = {}; + const serverConns = {}; // Add incoming connections to `serverConns`. - var onConnectionHandle = Meteor.onConnection(function (serverConn) { + const onConnectionHandle = Meteor.onConnection(function (serverConn) { test.isTrue(typeof serverConn.id === 'string', "connection handle id exists and is a string"); if (serverConns[serverConn.id]) { test.fail("onConnection callback called multiple times for same session id"); @@ -22,10 +19,16 @@ makeTestConnection = function (test, succeeded, failed) { } }); + // Connect and wait until the connection receives its session id. + // Disable retries so that when the connection is closed we don't + // automatically keep reconnecting on the client side. + // The connection from the client side. + const clientConn = DDP.connect(Meteor.absoluteUrl(), {retry: false}); + // We've succeeded when we get the session id on the client side. - var onClientSessionId = function (sessionId) { + const onClientSessionId = function (sessionId) { test.isTrue(clientConn.status().connected); - var serverConn = serverConns[sessionId]; + const serverConn = serverConns[sessionId]; if (! serverConn) { test.fail("No onConnection received server side for connected client"); failed(); @@ -34,11 +37,6 @@ makeTestConnection = function (test, succeeded, failed) { succeeded(clientConn, serverConn); } }; - - // Connect and wait until the connection receives its session id. - // Disable retries so that when the connection is closed we don't - // automatically keep reconnecting on the client side. - clientConn = DDP.connect(Meteor.absoluteUrl(), {retry: false}); simplePoll( function () { return clientConn._lastSessionId; diff --git a/packages/test-helpers/domutils.js b/packages/test-helpers/domutils.js index c2c097e209..e1bbe3e0e9 100644 --- a/packages/test-helpers/domutils.js +++ b/packages/test-helpers/domutils.js @@ -1,13 +1,13 @@ -var testDiv = document.createElement("div"); +const testDiv = document.createElement("div"); testDiv.innerHTML = "
"; // Need to wrap in a div rather than directly creating SELECT to avoid // *another* IE bug. -var testSelectDiv = document.createElement("div"); +const testSelectDiv = document.createElement("div"); testSelectDiv.innerHTML = ""; testSelectDiv.firstChild.setAttribute("name", "myname"); // Tests that, if true, indicate browser quirks present. -var quirks = { +const quirks = { // IE loses initial whitespace when setting innerHTML. leadingWhitespaceKilled: (testDiv.firstChild.nodeType !== 3), @@ -41,8 +41,8 @@ DomUtils.setElementValue = function (node, value) { // match valid OPTION values... and moreover, the OPTION value must be // explicitly given as an attribute, not just as the text. So we hunt for // the OPTION and select it. - var options = node.querySelectorAll('option'); - for (var i = 0; i < options.length; ++i) { + const options = node.querySelectorAll('option'); + for (let i = 0; i < options.length; ++i) { if (DomUtils.getElementValue(options[i]) === value) { options[i].selected = true; return; @@ -58,7 +58,7 @@ DomUtils.getElementValue = function (node) { if (node.nodeName === 'OPTION') { // Inspired by jQuery.valHooks.option.get. - var val = node.attributes.value; + const val = node.attributes.value; return !val || val.specified ? node.value : node.text; } else if (node.nodeName === 'SELECT') { if (node.selectedIndex < 0) diff --git a/packages/test-helpers/event_simulation.js b/packages/test-helpers/event_simulation.js index c4f8cc1086..16bf7a75ca 100644 --- a/packages/test-helpers/event_simulation.js +++ b/packages/test-helpers/event_simulation.js @@ -4,17 +4,17 @@ simulateEvent = function (node, event, args, options) { node = (node.jquery ? node[0] : node); - var bubbles = (options && "bubbles" in options) ? options.bubbles : true; + const bubbles = (options && "bubbles" in options) ? options.bubbles : true; if (document.createEvent) { - var e = document.createEvent("Event"); + const e = document.createEvent("Event"); e.initEvent(event, bubbles, true); Object.assign(e, args); node.dispatchEvent(e); } else { - var e = document.createEventObject(); + const e = document.createEventObject(); Object.assign(e, args); - node.fireEvent("on" + event, e); + node.fireEvent(`on${event}`, e); } }; @@ -43,7 +43,7 @@ clickElement = function(elem) { simulateEvent(elem, 'click'); }; -var inDocument = function (elem) { +const inDocument = function (elem) { while ((elem = elem.parentNode)) { if (elem == document) { return true; diff --git a/packages/test-helpers/render_div.js b/packages/test-helpers/render_div.js index e7daa1ec79..40e5789880 100644 --- a/packages/test-helpers/render_div.js +++ b/packages/test-helpers/render_div.js @@ -1,5 +1,5 @@ renderToDiv = function (template, optData) { - var div = document.createElement("DIV"); + const div = document.createElement("DIV"); if (optData == null) { Blaze.render(template, div); } else { diff --git a/packages/test-helpers/seeded_random.js b/packages/test-helpers/seeded_random.js index 6f52568781..ae014222ad 100644 --- a/packages/test-helpers/seeded_random.js +++ b/packages/test-helpers/seeded_random.js @@ -16,11 +16,10 @@ SeededRandom.prototype.nextIntBetween = function(min, max) { return Math.floor(this.next() * (max-min+1)) + min; }; SeededRandom.prototype.nextIdentifier = function(optLen) { - var letters = []; - var len = (typeof optLen === "number" ? optLen : 12); - for(var i=0; i= 1; i--) + let expected_count = 1; + for (let i = n; i >= 1; i--) expected_count *= i; test.equal(Object.keys(seen).length, expected_count); }; - for (var i = 1; i <= 5; i++) + for (let i = 1; i <= 5; i++) examine(i); try_all_permutations(); diff --git a/packages/test-in-browser/driver.js b/packages/test-in-browser/driver.js index e469fa7fa5..35ff7bbe46 100644 --- a/packages/test-in-browser/driver.js +++ b/packages/test-in-browser/driver.js @@ -10,17 +10,17 @@ const arraysEqual = (a, b) => { // dependency for the count of tests running/passed/failed, etc. drives // the navbar and the like. -var countDep = new Tracker.Dependency; +const countDep = new Tracker.Dependency; // things that change on countDep -var running = true; -var totalCount = 0; -var passedCount = 0; -var failedCount = 0; -var failedTests = []; +let running = true; +let totalCount = 0; +let passedCount = 0; +let failedCount = 0; +let failedTests = []; // Dependency for when a new top level group is added. Each group and // each test have their own dependency objects. -var topLevelGroupsDep = new Tracker.Dependency; +const topLevelGroupsDep = new Tracker.Dependency; // An array of top-level groups. // @@ -38,36 +38,36 @@ var topLevelGroupsDep = new Tracker.Dependency; // - server: boolean // - fullName: string // - dep: Tracker.Dependency object for this test. fires when the test completes. -var resultTree = []; +let resultTree = []; Session.set("uncaughtErrors", []); -window.onerror = (message, source, line) => { +window.onerror = (message, _source, _line) => { const uncaughtErrors = new Set(Session.get("uncaughtErrors")); uncaughtErrors.add(message); Session.set("uncaughtErrors", Array.from(uncaughtErrors)); }; -var getGroupPathFromURL = function() { - var pathname = window.location.pathname; - var match = pathname.match(/^\/group\/(.+)$/); +const getGroupPathFromURL = function() { + const pathname = window.location.pathname; + const match = pathname.match(/^\/group\/(.+)$/); if (match) { try { return JSON.parse(decodeURIComponent(match[1])); - } catch (e) { + } catch { console.warn('Invalid group path in URL:', match[1]); } } return ["tinytest"]; }; -var setGroupPathInURL = function(groupPath, pushState = true) { - var newURL = '/'; +const setGroupPathInURL = function(groupPath, pushState = true) { + let newURL = '/'; if (!arraysEqual(groupPath, ['tinytest'])) { - newURL = '/group/' + encodeURIComponent(JSON.stringify(groupPath)); + newURL = `/group/${encodeURIComponent(JSON.stringify(groupPath))}`; } - var historyState = { groupPath: groupPath }; + const historyState = { groupPath: groupPath }; if (pushState) { window.history.pushState(historyState, '', newURL); @@ -77,18 +77,18 @@ var setGroupPathInURL = function(groupPath, pushState = true) { }; // Initialize group path from URL, fallback to default -var initialGroupPath = getGroupPathFromURL(); +const initialGroupPath = getGroupPathFromURL(); Session.setDefault("groupPath", initialGroupPath); Session.set("rerunScheduled", false); // Safeguards for rapid navigation -var isNavigating = false; -var lastNavigationTime = 0; -var navigationDebounceMs = 200; // Minimum time between navigations +let isNavigating = false; +let lastNavigationTime = 0; +const navigationDebounceMs = 200; // Minimum time between navigations // Handle browser back/forward navigation -window.addEventListener('popstate', function(event) { - var now = Date.now(); +window.addEventListener('popstate', function(_event) { + const now = Date.now(); // Safeguard 1: Prevent overlapping navigations if (isNavigating) { @@ -102,8 +102,8 @@ window.addEventListener('popstate', function(event) { return; } - var newGroupPath = getGroupPathFromURL(); - var currentGroupPath = Session.get("groupPath"); + const newGroupPath = getGroupPathFromURL(); + const currentGroupPath = Session.get("groupPath"); if (!arraysEqual(newGroupPath, currentGroupPath)) { // Set navigation flag @@ -143,13 +143,13 @@ runTests = function () { topLevelGroupsDep.changed(); // Get current group path from URL (in case of refresh) - var currentGroupPath = getGroupPathFromURL(); + const currentGroupPath = getGroupPathFromURL(); Session.set("groupPath", currentGroupPath); // Only update URL if it's actually different from what we expect - var expectedURL; + let expectedURL; if (currentGroupPath && currentGroupPath.length > 0 && JSON.stringify(currentGroupPath) !== JSON.stringify(["tinytest"])) { - expectedURL = '/group/' + encodeURIComponent(JSON.stringify(currentGroupPath)); + expectedURL = `/group/${encodeURIComponent(JSON.stringify(currentGroupPath))}`; } else { expectedURL = '/'; } @@ -167,7 +167,7 @@ runTests = function () { Tracker.flush(); Tinytest._runTestsEverywhere(reportResults, function () { running = false; - Meteor.onTestsComplete && Meteor.onTestsComplete(); + if (Meteor.onTestsComplete) Meteor.onTestsComplete(); countDep.changed(); Tracker.flush(); @@ -184,11 +184,11 @@ runTests = function () { // report a series of events in a single test, or just the existence of // that test if no events. this is the entry point for test results to // this module. -var reportResults = function(results) { - var test = _findTestForResults(results); +const reportResults = function(results) { + const test = _findTestForResults(results); // Tolerate repeated reports: first undo the effect of any previous report - var status = _testStatus(test); + let status = _testStatus(test); if (status === "failed") { failedCount--; countDep.changed(); @@ -206,7 +206,7 @@ var reportResults = function(results) { test.events.sort(function (a, b) { return a.sequence - b.sequence; }); - var out = []; + const out = []; test.events.forEach(function (e) { if (out.length === 0 || out[out.length - 1].sequence !== e.sequence) out.push(e); @@ -237,9 +237,9 @@ var reportResults = function(results) { }; // forget all of the events for a particular test -var forgetEvents = function (results) { - var test = _findTestForResults(results); - var status = _testStatus(test); +const forgetEvents = function (results) { + const test = _findTestForResults(results); + const status = _testStatus(test); if (status === "failed") { failedCount--; countDep.changed(); @@ -255,18 +255,18 @@ var forgetEvents = function (results) { // corresponding leaf object in resultTree, creating one if it doesn't // exist. it will be an object with attributes 'name', 'parent', and // possibly 'events'. -var _findTestForResults = function (results) { - var groupPath = results.groupPath; // array +const _findTestForResults = function (results) { + const groupPath = results.groupPath; // array if ((! Array.isArray(groupPath)) || (groupPath.length < 1)) { throw new Error("Test must be part of a group"); } - var group; - var i = 0; + let group; + let i = 0; groupPath.forEach(function(gname) { - var array = (group ? (group.groups || (group.groups = [])) + const array = (group ? (group.groups || (group.groups = [])) : resultTree); - var newGroup = array.find(function(g) { return g.name === gname; }); + let newGroup = array.find(function(g) { return g.name === gname; }); if (! newGroup) { newGroup = { name: gname, @@ -285,16 +285,16 @@ var _findTestForResults = function (results) { i++; }); - var testName = results.test; - var server = !!results.server; - var test = (group.tests || (group.tests = [])).find( + const testName = results.test; + const server = !!results.server; + let test = (group.tests || (group.tests = [])).find( function(t) { return t.name === testName && t.server === server; }); if (! test) { // create test - var nameParts = [...groupPath]; + const nameParts = [...groupPath]; nameParts.push(testName); - var fullName = nameParts.join(' - '); + const fullName = nameParts.join(' - '); test = { name: testName, parent: group, @@ -317,9 +317,9 @@ var _findTestForResults = function (results) { //// Helpers on test objects //// -var _testTime = function(t) { +const _testTime = function(t) { if (t.events && t.events.length > 0) { - var lastEvent = t.events[t.events.length - 1]; + const lastEvent = t.events[t.events.length - 1]; if (lastEvent.type === "finish") { if ((typeof lastEvent.timeMs) === "number") { return lastEvent.timeMs; @@ -329,8 +329,8 @@ var _testTime = function(t) { return null; }; -var _testStatus = function(t) { - var events = t.events || []; +const _testStatus = function(t) { + const events = t.events || []; if (events.find(function(x) { return x.type === "exception"; })) { // "exception" should be last event, except race conditions on the // server can make this not the case. Technically we can't tell @@ -368,8 +368,8 @@ Template.navBar.helpers({ countDep.depend(); // walk whole tree to get all tests - var walk = function (groups) { - var total = 0; + const walk = function (groups) { + let total = 0; (groups || []).forEach(function (group) { (group.tests || []).forEach(function (t) { @@ -431,7 +431,7 @@ Template.progressBar.helpers({ //// Template - groupNav -var changeToPath = function (path) { +const changeToPath = function (path) { // Update URL with new group path (pushState creates new history entry) setGroupPathInURL(path, true); @@ -445,9 +445,9 @@ var changeToPath = function (path) { Template.groupNav.helpers({ groupPaths: function () { - var groupPath = Session.get("groupPath"); - var ret = []; - for (var i = 1; i <= groupPath.length; i++) { + const groupPath = Session.get("groupPath"); + const ret = []; + for (let i = 1; i <= groupPath.length; i++) { ret.push({path: groupPath.slice(0,i), name: groupPath[i-1]}); } return ret; @@ -456,7 +456,7 @@ Template.groupNav.helpers({ return Session.get("rerunScheduled"); }, isFiltered: function () { - var groupPath = Session.get("groupPath"); + const groupPath = Session.get("groupPath"); return groupPath.length > 1 || groupPath[0] !== "tinytest"; } }); @@ -476,10 +476,10 @@ Template.groupNav.events({ Template.groupNav.onRendered(function () { Tinytest._onCurrentClientTest = function (name) { - name = (name ? 'C: '+name : ''); + name = (name ? `C: ${name}` : ''); // Set the DOM directly so that it's immediate and we // don't wait for Tracker to flush. - var span = document.getElementById('current-client-test'); + const span = document.getElementById('current-client-test'); if (span) { span.innerHTML = ''; span.appendChild(document.createTextNode(name)); @@ -542,7 +542,7 @@ Template.test_group.events({ Template.test.helpers({ test_status_display: function() { - var status = _testStatus(this); + const status = _testStatus(this); if (status == "failed") { return "FAIL"; } else if (status == "succeeded") { @@ -553,13 +553,12 @@ Template.test.helpers({ }, test_time_display: function() { - var time = _testTime(this); - return (typeof time === "number") ? time + " ms" : ""; + const time = _testTime(this); + return (typeof time === "number") ? `${time} ms` : ""; }, test_class: function() { - var events = this.events || []; - var classes = [_testStatus(this)]; + const classes = [_testStatus(this)]; if (this.expanded) { classes.push("expanded"); @@ -571,15 +570,15 @@ Template.test.helpers({ }, eventsArray: function() { - var events = this.events.filter(function(e) { + const events = this.events.filter(function(e) { return e.type != "finish"; }); - var partitionBy = function(seq, func) { - var result = []; - var lastValue = {}; + const partitionBy = function(seq, func) { + const result = []; + let lastValue = {}; seq.forEach(function(x) { - var newValue = func(x); + const newValue = func(x); if (newValue === lastValue) { result[result.length-1].push(x); } else { @@ -590,7 +589,7 @@ Template.test.helpers({ return result; }; - var dupLists = partitionBy( + const dupLists = partitionBy( events.map(function(e) { // XXX XXX We need something better than stringify! // stringify([undefined]) === "[null]" @@ -600,7 +599,7 @@ Template.test.helpers({ }), function(x) { return x.str; }); return dupLists.map(function(L) { - var obj = L[0].obj; + const obj = L[0].obj; return (L.length > 1) ? Object.assign({times: L.length}, obj) : obj; }); } @@ -627,7 +626,7 @@ Template.event.events({ }); // e.g. doDiff('abc', 'bcd') => [[-1, 'a'], [0, 'bc'], [1, 'd']] -var doDiff = function (str1, str2) { +const doDiff = function (str1, str2) { const diff = diffChars(str1, str2); return diff.map(part => { @@ -640,24 +639,23 @@ var doDiff = function (str1, str2) { Template.event.helpers({ get_details: function() { - var details = this.details; + let details = this.details; if (! details) { return null; } else { - var type = details.type; - var stack = details.stack; + const type = details.type; + const stack = details.stack; details = Array.isArray(details) && [...details] || Object.assign({}, details); delete details.type; delete details.stack; - var prepare = function(details) { - if (type === 'string_equal') { - var diff = doDiff(details.actual, - details.expected); - } + const prepare = function(details) { + const diff = type === 'string_equal' + ? doDiff(details.actual, details.expected) + : null; return Object.entries(details).map(function([key, val]) { @@ -665,16 +663,16 @@ Template.event.helpers({ // in particular for multiline strings if (type === 'string_equal' && (key === 'actual' || key === 'expected')) { - var html = '
';
+            let html = `
`;
             diff.forEach(function (piece) {
-              var which = piece[0];
-              var text = piece[1];
+              const which = piece[0];
+              const text = piece[1];
               if (which === 0 ||
                   which === (key === 'actual' ? -1 : 1)) {
-                var htmlBit = Blaze._escape(text).replace(
+                let htmlBit = Blaze._escape(text).replace(
                     /\n/g, '
'); if (which !== 0) - htmlBit = '' + htmlBit + ''; + htmlBit = `${htmlBit}`; html += htmlBit; } }); diff --git a/packages/test-in-console/driver.js b/packages/test-in-console/driver.js index 4f66667d0d..ea334f64d8 100644 --- a/packages/test-in-console/driver.js +++ b/packages/test-in-console/driver.js @@ -15,7 +15,7 @@ TEST_STATUS = { }; // xUnit format uses XML output -var XML_CHAR_MAP = { +const XML_CHAR_MAP = { '<': '<', '>': '>', '&': '&', @@ -24,63 +24,62 @@ var XML_CHAR_MAP = { }; // Escapes a string for insertion into XML -var escapeXml = function (s) { +const escapeXml = function (s) { return s.replace(/[<>&"']/g, function (c) { return XML_CHAR_MAP[c]; }); } // Returns a human name for a test -var getName = function (result) { - return (result.server ? "S: " : "C: ") + - result.groupPath.join(" - ") + " - " + result.test; +const getName = function (result) { + return `${result.server ? "S: " : "C: "}${result.groupPath.join(" - ")} - ${result.test}`; }; // Calls console.log, but returns silently if console.log is not available -var log = function (/*arguments*/) { +const log = function (/*arguments*/) { if (typeof console !== 'undefined') { console.log.apply(console, arguments); } }; -var MAGIC_PREFIX = '##_meteor_magic##'; +const MAGIC_PREFIX = '##_meteor_magic##'; // Write output so that other tools can read it // Output is sent to console.log, prefixed with the magic prefix and then the facility // By grepping for the prefix, other tools can get the 'special' output -var logMagic = function (facility, s) { - log(MAGIC_PREFIX + facility + ': ' + s); +const logMagic = function (facility, s) { + log(`${MAGIC_PREFIX}${facility}: ${s}`); }; // Logs xUnit output, if xunit output is enabled // This uses logMagic with a facility of xunit -var xunit = function (s) { +const xunit = function (s) { if (xunitEnabled) { logMagic('xunit', s); } }; -var passed = 0; -var failed = 0; -var whereFailed = []; -var expected = 0; -var resultSet = {}; -var toReport = []; +let passed = 0; +let failed = 0; +const whereFailed = []; +let expected = 0; +const resultSet = {}; +let toReport = []; -var hrefPath = window.location.href.split("/"); -var platform = decodeURIComponent(hrefPath.length && hrefPath[hrefPath.length - 1]); +const hrefPath = window.location.href.split("/"); +let platform = decodeURIComponent(hrefPath.length && hrefPath[hrefPath.length - 1]); if (!platform) platform = "local"; // We enable xUnit output when platform is xunit -var xunitEnabled = (platform == 'xunit'); +const xunitEnabled = (platform == 'xunit'); -var doReport = Meteor && +const doReport = Meteor && Meteor.settings && Meteor.settings.public && Meteor.settings.public.runId; -var report = function (name, last) { +const report = function (name, last) { if (doReport) { - var data = { + const data = { run_id: Meteor.settings.public.runId, testPath: resultSet[name].testPath, status: resultSet[name].status, @@ -100,8 +99,8 @@ var report = function (name, last) { toReport.push(EJSON.toJSONValue(data)); } }; -var sendReports = function (callback) { - var reports = toReport; +const sendReports = function (callback) { + const reports = toReport; if (!callback) callback = function () {}; toReport = []; @@ -117,9 +116,9 @@ runTests = function () { Tinytest._runTestsEverywhere( function (results) { - var name = getName(results); + const name = getName(results); if (!(name in resultSet)) { - var testPath = EJSON.clone(results.groupPath); + const testPath = EJSON.clone(results.groupPath); testPath.push(results.test); resultSet[name] = { name: name, @@ -210,22 +209,22 @@ runTests = function () { // Also log xUnit output xunit(''); - resultSet.forEach(function (result, name) { - var classname = result.testPath.join('.').replace(/ /g, '-') + (result.server ? "-server" : "-client"); - var name = result.test.replace(/ /g, '-') + (result.server ? "-server" : "-client"); - var time = ""; - var error = ""; + resultSet.forEach(function (result, _name) { + const classname = result.testPath.join('.').replace(/ /g, '-') + (result.server ? "-server" : "-client"); + const testName = result.test.replace(/ /g, '-') + (result.server ? "-server" : "-client"); + let time = ""; + let error = ""; result.events.forEach(function (event) { switch (event.type) { case "finish": - var timeMs = event.timeMs; + const timeMs = event.timeMs; if (timeMs !== undefined) { - time = (timeMs / 1000) + ""; + time = `${timeMs / 1000}`; } break; case "exception": - var details = event.details || {}; - error = (details.message || '?') + " filename=" + (details.filename || '?') + " line=" + (details.line || '?'); + const details = event.details || {}; + error = `${details.message || '?'} filename=${details.filename || '?'} line=${details.line || '?'}`; break; } }); @@ -238,9 +237,9 @@ runTests = function () { break; } - xunit(''); + xunit(``); if (error) { - xunit(' ' + escapeXml(error) + ''); + xunit(` ${escapeXml(error)}`); } xunit(''); }); diff --git a/packages/test-in-console/puppeteer_runner.js b/packages/test-in-console/puppeteer_runner.js index 7065341278..fb476d1b70 100644 --- a/packages/test-in-console/puppeteer_runner.js +++ b/packages/test-in-console/puppeteer_runner.js @@ -47,7 +47,7 @@ async function runNextUrl(browser) { async function poll() { if (await isDone(page)) { - let failCount = await getFailCount(page); + const failCount = await getFailCount(page); console.log(`Tests complete with ${ failCount } failures`); console.log(`Tests complete with ${ await getPassCount(page) } passes`); if (failCount > 0) { diff --git a/packages/test-in-console/reporter.js b/packages/test-in-console/reporter.js index 7fb49b28ce..d147b1d34b 100644 --- a/packages/test-in-console/reporter.js +++ b/packages/test-in-console/reporter.js @@ -2,9 +2,7 @@ let url = null; if (Meteor.settings?.public?.runId && Meteor.settings?.public?.reportTo) { - url = Meteor.settings.public.reportTo + - "/report/" + - Meteor.settings.public.runId; + url = `${Meteor.settings.public.reportTo}/report/${Meteor.settings.public.runId}`; } Meteor.methods({ diff --git a/packages/tinytest/tinytest.js b/packages/tinytest/tinytest.js index b66097aafb..9a418145be 100644 --- a/packages/tinytest/tinytest.js +++ b/packages/tinytest/tinytest.js @@ -21,7 +21,7 @@ export class TestCaseResults { } ok(doc) { - var ok = {type: "ok"}; + const ok = {type: "ok"}; if (doc) ok.details = doc; if (this.expecting_failure) { @@ -51,8 +51,8 @@ export class TestCaseResults { if (this.stop_at_offset === 0) { if (Meteor.isClient) { // Only supported on the browser for now.. - var now = (+new Date); - debugger; + const now = (+new Date); + if ((+new Date) - now < 100) alert("To use this feature, first enable your browser's debugger."); } @@ -64,14 +64,14 @@ export class TestCaseResults { // Get filename and line number of failure if we're using v8 (Chrome or // Node). if (Error.captureStackTrace) { - var savedPrepareStackTrace = Error.prepareStackTrace; + const savedPrepareStackTrace = Error.prepareStackTrace; Error.prepareStackTrace = function(_, stack){ return stack; }; - var err = new Error; + const err = new Error; Error.captureStackTrace(err); - var stack = err.stack; + const stack = err.stack; Error.prepareStackTrace = savedPrepareStackTrace; - for (var i = stack.length - 1; i >= 0; --i) { - var frame = stack[i]; + for (let i = stack.length - 1; i >= 0; --i) { + const frame = stack[i]; // Heuristic: use the OUTERMOST line which is in a :tests.js // file (this is less likely to be a test helper function). const fileName = frame?.getFileName ? frame.getFileName() : null; @@ -125,7 +125,7 @@ export class TestCaseResults { * actual. Otherwise do a deep comparison, as implemented by _.isEqual. */ - var matched; + let matched; // XXX remove cruft specific to liverange if (typeof expected === "object" && expected && expected.nodeType) { matched = expected === actual; @@ -142,7 +142,7 @@ export class TestCaseResults { if (expected.length !== actual.length) this.fail({type: "assert_equal", message: "lengths of typed arrays do not match", expected: expected.length, actual: actual.length}); - for (var i = 0; i < expected.length; i++) { + for (let i = 0; i < expected.length; i++) { this.equal(actual[i], expected[i]); } } else { @@ -333,7 +333,7 @@ export class TestCaseResults { } include(s, v, message, not) { - var pass = false; + let pass = false; if (s instanceof Array) { pass = s.some(it => isEqual(v, it)); } else if (s && typeof s === "object") { @@ -403,7 +403,7 @@ export class TestCase { this.name = name; this.func = func; - var nameParts = name.split(" - ").map(s => { + const nameParts = name.split(" - ").map(s => { return s.replace(/^\s*|\s*$/g, ""); // trim }); this.shortName = nameParts.pop(); @@ -468,8 +468,7 @@ export const TestManager = new (class TestManager { addCase(test, options = {}) { if (test.name in this.tests) throw new Error( - "Every test needs a unique name, but there are two tests named '" + - test.name + "'"); + `Every test needs a unique name, but there are two tests named '${test.name}'`); if (__meteor_runtime_config__.tinytestFilter && test.name.indexOf(__meteor_runtime_config__.tinytestFilter) === -1) { return; @@ -525,7 +524,7 @@ export class TestRun { } _prefixMatch(testPath) { - for (var i = 0; i < this._pathPrefix.length; i++) { + for (let i = 0; i < this._pathPrefix.length; i++) { if (!testPath[i] || this._pathPrefix[i] !== testPath[i]) { return false; } @@ -534,7 +533,7 @@ export class TestRun { } _runTest(test, onComplete, stop_at_offset) { - var startTime = (+new Date); + const startTime = (+new Date); Tinytest._currentRunningTestName = test.name; return test.run(event => { @@ -548,7 +547,7 @@ export class TestRun { /* onComplete */ if (test.timedOut) return; - var totalTime = (+new Date) - startTime; + const totalTime = (+new Date) - startTime; this._report(test, {type: "finish", timeMs: totalTime}); onComplete(); }, exception => { @@ -577,7 +576,7 @@ export class TestRun { // _runOne(test, onComplete, stop_at_offset) { if (! this._prefixMatch(test.groupPath)) { - onComplete && onComplete(); + if (onComplete) onComplete(); return; } @@ -611,32 +610,32 @@ export class TestRun { }); Promise.race([runnerPromise, timeoutPromise]).finally(() => { - onComplete && onComplete(); + if (onComplete) onComplete(); }); }); } else { // client return this._runTest(test, () => { - onComplete && onComplete(); + if (onComplete) onComplete(); }, stop_at_offset); } } run(onComplete) { - var tests = this.manager.ordered_tests.slice(0); - var reportCurrent = function (name) { + const tests = this.manager.ordered_tests.slice(0); + const reportCurrent = function (name) { if (Meteor.isClient) Tinytest._onCurrentClientTest(name); }; const runNext = () => { if (tests.length) { - var t = tests.shift(); + const t = tests.shift(); reportCurrent(t.name); this._runOne(t, runNext); } else { reportCurrent(null); - onComplete && onComplete(); + if (onComplete) onComplete(); } }; @@ -647,9 +646,9 @@ export class TestRun { // failure record, try to rerun that particular test up to that // failure, and then open the debugger. debug(cookie, onComplete) { - var test = this.manager.tests[cookie.name]; + const test = this.manager.tests[cookie.name]; if (!test) - throw new Error("No such test '" + cookie.name + "'"); + throw new Error(`No such test '${cookie.name}'`); this._runOne(test, onComplete, cookie.offset); } @@ -703,7 +702,7 @@ Tinytest.only = function (name, func) { // onReport. Call onComplete when it's done. // Tinytest._runTests = function (onReport, onComplete, pathPrefix) { - var testRun = TestManager.createRun(onReport, pathPrefix); + const testRun = TestManager.createRun(onReport, pathPrefix); testRun.run(onComplete); }; @@ -728,7 +727,7 @@ Tinytest._getCurrentRunningTestOnClient = function () { // failure event output by _runTests. // Tinytest._debugTest = function (cookie, onReport, onComplete) { - var testRun = TestManager.createRun(onReport); + const testRun = TestManager.createRun(onReport); testRun.debug(cookie, onComplete); }; @@ -736,7 +735,7 @@ Tinytest._debugTest = function (cookie, onReport, onComplete) { // and then called with `null` when the client tests are // done. This is used to provide a live display of the current // running client test on the test results page. -Tinytest._onCurrentClientTest = function (name) {}; +Tinytest._onCurrentClientTest = function (_name) {}; Tinytest._TestCaseResults = TestCaseResults; Tinytest._TestCase = TestCase; diff --git a/packages/tinytest/tinytest_client.js b/packages/tinytest/tinytest_client.js index 1611614d96..4f9a62bf76 100644 --- a/packages/tinytest/tinytest_client.js +++ b/packages/tinytest/tinytest_client.js @@ -18,30 +18,30 @@ export { Tinytest }; // Default is currently true (serial operation), but we will likely // change this to false in future. Tinytest._runTestsEverywhere = function (onReport, onComplete, pathPrefix, options) { - var runId = Random.id(); - var localComplete = false; - var localStarted = false; - var remoteComplete = false; - var done = false; + const runId = Random.id(); + let localComplete = false; + let localStarted = false; + let remoteComplete = false; + let done = false; options = { serial: true, ...options, }; - var serial = !!options.serial; + const serial = !!options.serial; - var maybeDone = function () { + const maybeDone = function () { if (!done && localComplete && remoteComplete) { done = true; - onComplete && onComplete(); + if (onComplete) onComplete(); } if (serial && remoteComplete && !localStarted) { startLocalTests(); } }; - var startLocalTests = function() { + const startLocalTests = function() { localStarted = true; Tinytest._runTests(onReport, function () { localComplete = true; @@ -49,7 +49,7 @@ Tinytest._runTestsEverywhere = function (onReport, onComplete, pathPrefix, optio }, pathPrefix); }; - var handle; + const handle = Meteor.subscribe(ServerTestResultsSubscription, runId); Meteor.connection.registerStoreClient(ServerTestResultsCollection, { update(msg) { @@ -89,9 +89,7 @@ Tinytest._runTestsEverywhere = function (onReport, onComplete, pathPrefix, optio } }); - handle = Meteor.subscribe(ServerTestResultsSubscription, runId); - - Meteor.call('tinytest/run', runId, pathPrefix, function (error, result) { + Meteor.call('tinytest/run', runId, pathPrefix, function (error, _result) { if (error) { // XXX better report error throw new Error("Test server returned an error"); diff --git a/packages/tinytest/tinytest_server.js b/packages/tinytest/tinytest_server.js index 4c514c1059..393b9e475c 100644 --- a/packages/tinytest/tinytest_server.js +++ b/packages/tinytest/tinytest_server.js @@ -51,7 +51,7 @@ Meteor.methods({ reportsForRun.set(runId, Object.create(null)); function addReport(key, report) { - var fields = {}; + const fields = {}; fields[key] = report; const handles = handlesForRun.get(runId); if (handles) { @@ -64,15 +64,15 @@ Meteor.methods({ } function onReport(report) { - var dummyKey = Random.id(); + const dummyKey = Random.id(); addReport(dummyKey, report); } function onComplete() { // We send an object for current and future compatibility, // though we could get away with just sending { complete: true } - var report = { done: true }; - var key = 'complete'; + const report = { done: true }; + const key = 'complete'; addReport(key, report); } diff --git a/packages/webapp-hashing/webapp-hashing.js b/packages/webapp-hashing/webapp-hashing.js index 0968b45323..d7d70f63e5 100644 --- a/packages/webapp-hashing/webapp-hashing.js +++ b/packages/webapp-hashing/webapp-hashing.js @@ -13,15 +13,13 @@ WebAppHashing = {}; WebAppHashing.calculateClientHash = function (manifest, includeFilter, runtimeConfigOverride) { - var hash = createHash('sha1'); + const hash = createHash('sha1'); // Omit the old hashed client values in the new hash. These may be // modified in the new boilerplate. - var { autoupdateVersion, autoupdateVersionRefreshable, autoupdateVersionCordova, ...runtimeCfg } = __meteor_runtime_config__; + const { autoupdateVersion: _av, autoupdateVersionRefreshable: _avr, autoupdateVersionCordova: _avc, ...runtimeCfgBase } = __meteor_runtime_config__; - if (runtimeConfigOverride) { - runtimeCfg = runtimeConfigOverride; - } + const runtimeCfg = runtimeConfigOverride || runtimeCfgBase; hash.update(JSON.stringify(runtimeCfg, 'utf8')); @@ -43,7 +41,7 @@ WebAppHashing.calculateCordovaCompatibilityHash = // Sort plugins first so iteration order doesn't affect the hash const plugins = Object.keys(pluginVersions).sort(); - for (let plugin of plugins) { + for (const plugin of plugins) { const version = pluginVersions[plugin]; hash.update(plugin); hash.update(version); From 163f18d512d4320d57c4ddf3e14cc83607000cfa Mon Sep 17 00:00:00 2001 From: italo jose Date: Thu, 9 Apr 2026 10:42:55 -0300 Subject: [PATCH 02/15] fix: update oxlint ignore rule for Alea PRNG bitwise operation --- packages/random/AleaRandomGenerator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/random/AleaRandomGenerator.js b/packages/random/AleaRandomGenerator.js index 809a038c17..8338b6a6a0 100644 --- a/packages/random/AleaRandomGenerator.js +++ b/packages/random/AleaRandomGenerator.js @@ -62,7 +62,7 @@ function Alea(seeds) { }; random.uint32 = () => random() * 0x100000000; // 2^32 - // oxlint-disable-next-line -- intentional bitwise OR for float-to-int (Alea PRNG algorithm) + // oxlint-disable-next-line oxc/erasing-op -- intentional bitwise OR for float-to-int (Alea PRNG algorithm) random.fract53 = () => random() + ((random() * 0x200000 | 0) * 1.1102230246251565e-16); // 2^-53 random.version = 'Alea 0.9'; From 7d7e8bfaa1a28f9b83072a6ddf74bfdd1c9c0d86 Mon Sep 17 00:00:00 2001 From: italo jose Date: Thu, 9 Apr 2026 10:47:55 -0300 Subject: [PATCH 03/15] chore: add oxlint phase-2 commit hashes to .git-blame-ignore-revs --- .git-blame-ignore-revs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index f3b89b5261..a7a5f382f2 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -3,3 +3,7 @@ # auto-format and lint fixes 2026-03-27 1bfad0bcbb9c495172d1475f1c6d54df8d34ceae +# oxlint phase-2 lint fixes 2026-04-09 +568eace1b0726b3e2fb823292e30b0e2f0efb4ca +163f18d512d4320d57c4ddf3e14cc83607000cfa + From 2031f19b232998e80c20283dbe663891d316191e Mon Sep 17 00:00:00 2001 From: italo jose Date: Thu, 9 Apr 2026 10:49:27 -0300 Subject: [PATCH 04/15] chore: add oxlint phase-2 lint fix commit to ignore-revs list --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index a7a5f382f2..f9bc27fa1d 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -4,6 +4,7 @@ 1bfad0bcbb9c495172d1475f1c6d54df8d34ceae # oxlint phase-2 lint fixes 2026-04-09 +5f50d759d3bb08cebada41481d9b38b015c3fca7 568eace1b0726b3e2fb823292e30b0e2f0efb4ca 163f18d512d4320d57c4ddf3e14cc83607000cfa From de68f14b9c8e0916a2ac0b239a79d16c5e2adef9 Mon Sep 17 00:00:00 2001 From: italo jose Date: Thu, 9 Apr 2026 10:51:03 -0300 Subject: [PATCH 05/15] applying fmt to many packages --- packages/base64/base64.js | 45 +- packages/base64/base64_test.js | 49 +- packages/base64/package.js | 16 +- packages/binary-heap/binary-heap-tests.js | 34 +- packages/binary-heap/binary-heap.js | 6 +- packages/binary-heap/max-heap.js | 30 +- packages/binary-heap/min-heap.js | 4 +- packages/binary-heap/min-max-heap.js | 7 +- packages/binary-heap/package.js | 16 +- packages/callback-hook/hook.js | 32 +- packages/callback-hook/hook_tests.js | 59 +- packages/callback-hook/package.js | 14 +- packages/check/check.d.ts | 23 +- packages/check/isPlainObject.js | 9 +- packages/check/match.js | 247 +++--- packages/check/match_test.js | 734 +++++++++++------- packages/check/package.js | 24 +- packages/diff-sequence/diff.js | 70 +- packages/diff-sequence/package.js | 23 +- packages/diff-sequence/tests.js | 75 +- packages/ejson/custom_models_for_tests.js | 21 +- packages/ejson/ejson.d.ts | 9 +- packages/ejson/ejson.js | 125 +-- packages/ejson/ejson_tests.js | 275 ++++--- packages/ejson/package.js | 18 +- packages/ejson/stringify.js | 136 ++-- packages/ejson/utils.js | 43 +- packages/id-map/id-map.js | 27 +- packages/id-map/package.js | 10 +- packages/logging/logging.d.ts | 18 +- packages/logging/logging.js | 214 ++--- packages/logging/logging_browser.js | 4 +- packages/logging/logging_cordova.js | 6 +- packages/logging/logging_server.js | 6 +- packages/logging/logging_test.js | 71 +- packages/logging/package.js | 30 +- packages/ordered-dict/ordered_dict.js | 59 +- packages/ordered-dict/package.js | 10 +- packages/random/AbstractRandomGenerator.js | 27 +- packages/random/AleaRandomGenerator.js | 26 +- packages/random/BrowserRandomGenerator.js | 4 +- packages/random/NodeRandomGenerator.js | 10 +- packages/random/createAleaGenerator.js | 36 +- packages/random/createRandom.js | 6 +- packages/random/main_client.js | 16 +- packages/random/main_server.js | 4 +- packages/random/package.js | 22 +- packages/random/random_tests.js | 27 +- packages/rate-limit/package.js | 38 +- packages/rate-limit/rate-limit-tests.js | 375 +++++---- packages/rate-limit/rate-limit.js | 130 ++-- packages/reload-safetybelt/package.js | 2 +- .../reload-safety-belt-tests.js | 6 +- .../reload-safetybelt/reload-safety-belt.js | 2 +- packages/reload-safetybelt/safetybelt.js | 10 +- packages/retry/package.js | 10 +- packages/retry/retry.js | 12 +- packages/session/package.js | 24 +- packages/session/session.d.ts | 2 +- packages/session/session.js | 4 +- packages/session/session_tests.js | 259 +++--- packages/test-helpers/async_multi.js | 65 +- packages/test-helpers/callback_logger.js | 9 +- packages/test-helpers/canonicalize_html.js | 55 +- packages/test-helpers/connection_client.js | 14 +- packages/test-helpers/connection_server.js | 22 +- packages/test-helpers/current_style.js | 10 +- packages/test-helpers/domutils.js | 25 +- packages/test-helpers/event_simulation.js | 23 +- packages/test-helpers/mock.js | 10 +- packages/test-helpers/package.js | 13 +- packages/test-helpers/seeded_random.js | 27 +- packages/test-helpers/try_all_permutations.js | 19 +- .../test-helpers/try_all_permutations_test.js | 104 ++- packages/test-helpers/wait.js | 13 +- packages/test-in-browser/driver.css | 296 ++++--- packages/test-in-browser/driver.html | 166 ++-- packages/test-in-browser/driver.js | 366 +++++---- packages/test-in-browser/package.js | 37 +- packages/test-in-console/driver.js | 168 ++-- packages/test-in-console/package.js | 20 +- packages/test-in-console/puppeteer_runner.js | 60 +- packages/test-in-console/reporter.js | 11 +- packages/test-in-console/test.css | 3 +- packages/tinytest/model.js | 6 +- packages/tinytest/package.js | 19 +- packages/tinytest/tinytest.js | 380 ++++----- packages/tinytest/tinytest_client.js | 35 +- packages/tinytest/tinytest_server.js | 30 +- packages/webapp-hashing/package.js | 16 +- packages/webapp-hashing/webapp-hashing.js | 29 +- 91 files changed, 2872 insertions(+), 2830 deletions(-) diff --git a/packages/base64/base64.js b/packages/base64/base64.js index de33c8566a..ee0d187b5f 100644 --- a/packages/base64/base64.js +++ b/packages/base64/base64.js @@ -4,22 +4,21 @@ const BASE_64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234 const BASE_64_VALS = Object.create(null); -const getChar = val => BASE_64_CHARS.charAt(val); -const getVal = ch => ch === '=' ? -1 : BASE_64_VALS[ch]; +const getChar = (val) => BASE_64_CHARS.charAt(val); +const getVal = (ch) => (ch === "=" ? -1 : BASE_64_VALS[ch]); for (let i = 0; i < BASE_64_CHARS.length; i++) { BASE_64_VALS[getChar(i)] = i; -}; +} -const encode = array => { +const encode = (array) => { if (typeof array === "string") { const str = array; array = newBinary(str.length); for (let i = 0; i < str.length; i++) { const ch = str.charCodeAt(i); - if (ch > 0xFF) { - throw new Error( - "Not ascii. Base64.encode can only take ascii strings."); + if (ch > 0xff) { + throw new Error("Not ascii. Base64.encode can only take ascii strings."); } array[i] = ch; @@ -35,16 +34,16 @@ const encode = array => { for (let i = 0; i < array.length; i++) { switch (i % 3) { case 0: - a = (array[i] >> 2) & 0x3F; + a = (array[i] >> 2) & 0x3f; b = (array[i] & 0x03) << 4; break; case 1: - b = b | (array[i] >> 4) & 0xF; - c = (array[i] & 0xF) << 2; + b = b | ((array[i] >> 4) & 0xf); + c = (array[i] & 0xf) << 2; break; case 2: - c = c | (array[i] >> 6) & 0x03; - d = array[i] & 0x3F; + c = c | ((array[i] >> 6) & 0x03); + d = array[i] & 0x3f; answer.push(getChar(a)); answer.push(getChar(b)); answer.push(getChar(c)); @@ -61,28 +60,26 @@ const encode = array => { answer.push(getChar(a)); answer.push(getChar(b)); if (c == null) { - answer.push('='); + answer.push("="); } else { answer.push(getChar(c)); } if (d == null) { - answer.push('='); + answer.push("="); } } return answer.join(""); }; - - // XXX This is a weird place for this to live, but it's used both by // this package and 'ejson', and we can't put it in 'ejson' without // introducing a circular dependency. It should probably be in its own // package or as a helper in a package that both 'base64' and 'ejson' // use. -const newBinary = len => { - if (typeof Uint8Array === 'undefined' || typeof ArrayBuffer === 'undefined') { +const newBinary = (len) => { + if (typeof Uint8Array === "undefined" || typeof ArrayBuffer === "undefined") { const ret = []; for (let i = 0; i < len; i++) { ret.push(0); @@ -94,11 +91,11 @@ const newBinary = len => { return new Uint8Array(new ArrayBuffer(len)); }; -const decode = str => { +const decode = (str) => { let len = Math.floor((str.length * 3) / 4); - if (str.charAt(str.length - 1) == '=') { + if (str.charAt(str.length - 1) == "=") { len--; - if (str.charAt(str.length - 2) == '=') { + if (str.charAt(str.length - 2) == "=") { len--; } } @@ -117,19 +114,19 @@ const decode = str => { switch (i % 4) { case 0: if (v < 0) { - throw new Error('invalid base64 string'); + throw new Error("invalid base64 string"); } one = v << 2; break; case 1: if (v < 0) { - throw new Error('invalid base64 string'); + throw new Error("invalid base64 string"); } one = one | (v >> 4); arr[j++] = one; - two = (v & 0x0F) << 4; + two = (v & 0x0f) << 4; break; case 2: if (v >= 0) { diff --git a/packages/base64/base64_test.js b/packages/base64/base64_test.js index d1438c90d9..f8002db2a4 100644 --- a/packages/base64/base64_test.js +++ b/packages/base64/base64_test.js @@ -1,58 +1,57 @@ -import { Base64 } from './base64.js'; +import { Base64 } from "./base64.js"; -const asciiToArray = str => { +const asciiToArray = (str) => { const arr = Base64.newBinary(str.length); for (let i = 0; i < str.length; i++) { const c = str.charCodeAt(i); - if (c > 0xFF) { + if (c > 0xff) { throw new Error("Not ascii"); } arr[i] = c; } - + return arr; }; -const arrayToAscii = arr => arr - .reduce( - (prev, charCode) => prev.push(String.fromCharCode(charCode)) && prev, [] - ).join(''); +const arrayToAscii = (arr) => + arr.reduce((prev, charCode) => prev.push(String.fromCharCode(charCode)) && prev, []).join(""); -Tinytest.add("base64 - testing the test", test => { - test.equal(arrayToAscii(asciiToArray("The quick brown fox jumps over the lazy dog")), - "The quick brown fox jumps over the lazy dog"); +Tinytest.add("base64 - testing the test", (test) => { + test.equal( + arrayToAscii(asciiToArray("The quick brown fox jumps over the lazy dog")), + "The quick brown fox jumps over the lazy dog", + ); }); -Tinytest.add("base64 - empty", test => { +Tinytest.add("base64 - empty", (test) => { test.equal(Base64.encode(EJSON.newBinary(0)), ""); test.equal(Base64.decode(""), EJSON.newBinary(0)); }); - -Tinytest.add("base64 - wikipedia examples", test => { +Tinytest.add("base64 - wikipedia examples", (test) => { const tests = [ - {txt: "pleasure.", res: "cGxlYXN1cmUu"}, - {txt: "leasure.", res: "bGVhc3VyZS4="}, - {txt: "easure.", res: "ZWFzdXJlLg=="}, - {txt: "asure.", res: "YXN1cmUu"}, - {txt: "sure.", res: "c3VyZS4="} + { txt: "pleasure.", res: "cGxlYXN1cmUu" }, + { txt: "leasure.", res: "bGVhc3VyZS4=" }, + { txt: "easure.", res: "ZWFzdXJlLg==" }, + { txt: "asure.", res: "YXN1cmUu" }, + { txt: "sure.", res: "c3VyZS4=" }, ]; - tests.forEach(t => { + tests.forEach((t) => { test.equal(Base64.encode(asciiToArray(t.txt)), t.res); test.equal(arrayToAscii(Base64.decode(t.res)), t.txt); }); }); -Tinytest.add("base64 - non-text examples", test => { +Tinytest.add("base64 - non-text examples", (test) => { const tests = [ - {array: [0, 0, 0], b64: "AAAA"}, - {array: [0, 0, 1], b64: "AAAB"} + { array: [0, 0, 0], b64: "AAAA" }, + { array: [0, 0, 1], b64: "AAAB" }, ]; - tests.forEach(t => { + tests.forEach((t) => { test.equal(Base64.encode(t.array), t.b64); const expectedAsBinary = EJSON.newBinary(t.array.length); - t.array.forEach((val, i) => expectedAsBinary[i] = val); + t.array.forEach((val, i) => (expectedAsBinary[i] = val)); test.equal(Base64.decode(t.b64), expectedAsBinary); }); }); diff --git a/packages/base64/package.js b/packages/base64/package.js index 1a1009051f..de0032deb3 100644 --- a/packages/base64/package.js +++ b/packages/base64/package.js @@ -1,15 +1,15 @@ Package.describe({ summary: "Base64 encoding and decoding", - version: '1.0.13', + version: "1.0.13", }); -Package.onUse(api => { - api.export('Base64'); - api.use('ecmascript'); - api.mainModule('base64.js'); +Package.onUse((api) => { + api.export("Base64"); + api.use("ecmascript"); + api.mainModule("base64.js"); }); -Package.onTest(api => { - api.use(['ecmascript', 'tinytest', 'ejson']); - api.addFiles('base64_test.js', ['client', 'server']); +Package.onTest((api) => { + api.use(["ecmascript", "tinytest", "ejson"]); + api.addFiles("base64_test.js", ["client", "server"]); }); diff --git a/packages/binary-heap/binary-heap-tests.js b/packages/binary-heap/binary-heap-tests.js index 1f3d740ac7..d4b7d4ee39 100644 --- a/packages/binary-heap/binary-heap-tests.js +++ b/packages/binary-heap/binary-heap-tests.js @@ -1,8 +1,8 @@ -import { MaxHeap } from './max-heap.js'; -import { MinMaxHeap } from './min-max-heap.js'; +import { MaxHeap } from "./max-heap.js"; +import { MinMaxHeap } from "./min-max-heap.js"; // Based on underscore implementation (Fisher-Yates shuffle) -const shuffle = arr => { +const shuffle = (arr) => { let j = 0; let temp = null; @@ -33,7 +33,7 @@ const range = (start, stop, step = 1) => { return range; }; -Tinytest.add("binary-heap - simple max-heap tests", test => { +Tinytest.add("binary-heap - simple max-heap tests", (test) => { const h = new MaxHeap((a, b) => a - b); h.set("a", 1); h.set("b", 233); @@ -63,7 +63,7 @@ Tinytest.add("binary-heap - simple max-heap tests", test => { test.equal(h.maxElementId(), "a"); }); -Tinytest.add("binary-heap - big test for max-heap", test => { +Tinytest.add("binary-heap - big test for max-heap", (test) => { const positiveNumbers = shuffle(range(1, 41)); const negativeNumbers = shuffle(range(-1, -41, -1)); const allNumbers = [...negativeNumbers, ...positiveNumbers]; @@ -71,7 +71,7 @@ Tinytest.add("binary-heap - big test for max-heap", test => { const heap = new MaxHeap((a, b) => a - b); const output = []; - allNumbers.forEach(n => heap.set(n, n)); + allNumbers.forEach((n) => heap.set(n, n)); allNumbers.forEach(() => { const maxId = heap.maxElementId(); @@ -84,7 +84,7 @@ Tinytest.add("binary-heap - big test for max-heap", test => { test.equal(output, allNumbers); }); -Tinytest.add("binary-heap - min-max heap tests", test => { +Tinytest.add("binary-heap - min-max heap tests", (test) => { const h = new MinMaxHeap((a, b) => a - b); h.set("a", 1); h.set("b", 233); @@ -116,7 +116,7 @@ Tinytest.add("binary-heap - min-max heap tests", test => { test.equal(h.minElementId(), "a"); }); -Tinytest.add("binary-heap - big test for min-max-heap", test => { +Tinytest.add("binary-heap - big test for min-max-heap", (test) => { const N = 500; const positiveNumbers = shuffle(range(1, N + 1)); const negativeNumbers = shuffle(range(-1, -N - 1, -1)); @@ -126,7 +126,7 @@ Tinytest.add("binary-heap - big test for min-max-heap", test => { let output = []; const initialSets = [...allNumbers]; - allNumbers.forEach(n => { + allNumbers.forEach((n) => { heap.set(n, n); heap._selfCheck(); heap._minHeap._selfCheck(); @@ -135,7 +135,7 @@ Tinytest.add("binary-heap - big test for min-max-heap", test => { shuffle(allNumbers); const secondarySets = [...allNumbers]; - allNumbers.forEach(n => { + allNumbers.forEach((n) => { heap.set(-n, n); heap._selfCheck(); heap._minHeap._selfCheck(); @@ -145,19 +145,20 @@ Tinytest.add("binary-heap - big test for min-max-heap", test => { const minId = heap.minElementId(); output.push(heap.get(minId)); heap.remove(minId); - heap._selfCheck(); heap._minHeap._selfCheck(); + heap._selfCheck(); + heap._minHeap._selfCheck(); }); test.equal(heap.size(), 0); allNumbers.sort((a, b) => a - b); - const initialTestText = `initial sets: ${initialSets.toString()}` + - `; secondary sets: ${secondarySets.toString()}`; + const initialTestText = + `initial sets: ${initialSets.toString()}` + `; secondary sets: ${secondarySets.toString()}`; test.equal(output, allNumbers, initialTestText); - initialSets.forEach(n => heap.set(n, n)); - secondarySets.forEach(n => heap.set(-n, n)); + initialSets.forEach((n) => heap.set(n, n)); + secondarySets.forEach((n) => heap.set(-n, n)); allNumbers.sort((a, b) => b - a); output = []; @@ -165,7 +166,8 @@ Tinytest.add("binary-heap - big test for min-max-heap", test => { const maxId = heap.maxElementId(); output.push(heap.get(maxId)); heap.remove(maxId); - heap._selfCheck(); heap._minHeap._selfCheck(); + heap._selfCheck(); + heap._minHeap._selfCheck(); }); test.equal(output, allNumbers, initialTestText); diff --git a/packages/binary-heap/binary-heap.js b/packages/binary-heap/binary-heap.js index 74fc89497e..b36d6f124d 100644 --- a/packages/binary-heap/binary-heap.js +++ b/packages/binary-heap/binary-heap.js @@ -1,3 +1,3 @@ -export { MaxHeap } from './max-heap.js'; -export { MinHeap } from './min-heap.js'; -export { MinMaxHeap } from './min-max-heap.js'; +export { MaxHeap } from "./max-heap.js"; +export { MinHeap } from "./min-heap.js"; +export { MinMaxHeap } from "./min-max-heap.js"; diff --git a/packages/binary-heap/max-heap.js b/packages/binary-heap/max-heap.js index 79eec0400e..4044820882 100644 --- a/packages/binary-heap/max-heap.js +++ b/packages/binary-heap/max-heap.js @@ -8,10 +8,10 @@ // each value is retained // - IdMap - Constructor - Optional - custom IdMap class to store id->index // mappings internally. Standard IdMap is used by default. -export class MaxHeap { +export class MaxHeap { constructor(comparator, options = {}) { - if (typeof comparator !== 'function') { - throw new Error('Passed comparator is invalid, should be a comparison function'); + if (typeof comparator !== "function") { + throw new Error("Passed comparator is invalid, should be a comparison function"); } // a C-style comparator that is given two values and returns a number, @@ -19,13 +19,13 @@ export class MaxHeap { // value is greater than the first and zero if they are equal. this._comparator = comparator; - if (! options.IdMap) { + if (!options.IdMap) { options.IdMap = IdMap; } // _heapIdx maps an id to an index in the Heap array the corresponding value // is located on. - this._heapIdx = new options.IdMap; + this._heapIdx = new options.IdMap(); // The Heap data-structure implemented as a 0-based contiguous array where // every item on index idx is a node in a complete binary tree. Every node can @@ -47,7 +47,7 @@ export class MaxHeap { data.forEach(({ id }, i) => this._heapIdx.set(id, i)); - if (! data.length) { + if (!data.length) { return; } @@ -84,7 +84,7 @@ export class MaxHeap { while (idx > 0) { const parent = parentIdx(idx); if (this._maxIndex(parent, idx) === idx) { - this._swap(parent, idx) + this._swap(parent, idx); idx = parent; } else { break; @@ -115,9 +115,7 @@ export class MaxHeap { } get(id) { - return this.has(id) ? - this._get(this._heapIdx.get(id)) : - null; + return this.has(id) ? this._get(this._heapIdx.get(id)) : null; } set(id, value) { @@ -176,7 +174,7 @@ export class MaxHeap { // iterate over values in no particular order forEach(iterator) { - this._heap.forEach(obj => iterator(obj.value, obj.id)); + this._heap.forEach((obj) => iterator(obj.value, obj.id)); } size() { @@ -204,12 +202,14 @@ export class MaxHeap { _selfCheck() { for (let i = 1; i < this._heap.length; i++) { if (this._maxIndex(parentIdx(i), i) !== parentIdx(i)) { - throw new Error(`An item with id ${this._heap[i].id} has a parent younger than it: ${this._heap[parentIdx(i)].id}`); + throw new Error( + `An item with id ${this._heap[i].id} has a parent younger than it: ${this._heap[parentIdx(i)].id}`, + ); } } } } -const leftChildIdx = i => i * 2 + 1; -const rightChildIdx = i => i * 2 + 2; -const parentIdx = i => (i - 1) >> 1; +const leftChildIdx = (i) => i * 2 + 1; +const rightChildIdx = (i) => i * 2 + 2; +const parentIdx = (i) => (i - 1) >> 1; diff --git a/packages/binary-heap/min-heap.js b/packages/binary-heap/min-heap.js index e57a445e58..87435762c5 100644 --- a/packages/binary-heap/min-heap.js +++ b/packages/binary-heap/min-heap.js @@ -1,4 +1,4 @@ -import { MaxHeap } from './max-heap.js'; +import { MaxHeap } from "./max-heap.js"; export class MinHeap extends MaxHeap { constructor(comparator, options) { @@ -12,4 +12,4 @@ export class MinHeap extends MaxHeap { minElementId() { return super.maxElementId(); } -}; +} diff --git a/packages/binary-heap/min-max-heap.js b/packages/binary-heap/min-max-heap.js index c40fb09fd1..d27c7cf88b 100644 --- a/packages/binary-heap/min-max-heap.js +++ b/packages/binary-heap/min-max-heap.js @@ -1,5 +1,5 @@ -import { MaxHeap } from './max-heap.js'; -import { MinHeap } from './min-heap.js'; +import { MaxHeap } from "./max-heap.js"; +import { MinHeap } from "./min-heap.js"; // This implementation of Min/Max-Heap is just a subclass of Max-Heap // with a Min-Heap as an encapsulated property. @@ -47,5 +47,4 @@ export class MinMaxHeap extends MaxHeap { minElementId() { return this._minHeap.minElementId(); } - -}; +} diff --git a/packages/binary-heap/package.js b/packages/binary-heap/package.js index c532928f9d..b39f6390b1 100644 --- a/packages/binary-heap/package.js +++ b/packages/binary-heap/package.js @@ -1,15 +1,15 @@ Package.describe({ summary: "Binary Heap datastructure implementation", - version: '1.0.12', + version: "1.0.12", }); -Package.onUse(api => { - api.export(['MaxHeap', 'MinHeap', 'MinMaxHeap']); - api.use(['id-map', 'ecmascript']); - api.mainModule('binary-heap.js'); +Package.onUse((api) => { + api.export(["MaxHeap", "MinHeap", "MinMaxHeap"]); + api.use(["id-map", "ecmascript"]); + api.mainModule("binary-heap.js"); }); -Package.onTest(api => { - api.use(['tinytest', 'binary-heap', 'ecmascript']); - api.addFiles('binary-heap-tests.js'); +Package.onTest((api) => { + api.use(["tinytest", "binary-heap", "ecmascript"]); + api.addFiles("binary-heap-tests.js"); }); diff --git a/packages/callback-hook/hook.js b/packages/callback-hook/hook.js index 256ffd83d3..a76c3e168f 100644 --- a/packages/callback-hook/hook.js +++ b/packages/callback-hook/hook.js @@ -65,12 +65,14 @@ export class Hook { } register(callback) { - const exceptionHandler = this.exceptionHandler || function (exception) { - // Note: this relies on the undocumented fact that if bindEnvironment's - // onException throws, and you are invoking the callback either in the - // browser or from within a Fiber in Node, the exception is propagated. - throw exception; - }; + const exceptionHandler = + this.exceptionHandler || + function (exception) { + // Note: this relies on the undocumented fact that if bindEnvironment's + // onException throws, and you are invoking the callback either in the + // browser or from within a Fiber in Node, the exception is propagated. + throw exception; + }; if (this.bindEnvironment) { callback = Meteor.bindEnvironment(callback, exceptionHandler); @@ -89,7 +91,7 @@ export class Hook { callback, stop: () => { delete this.callbacks[id]; - } + }, }; } @@ -110,14 +112,13 @@ export class Hook { * @param iterator */ forEach(iterator) { - const ids = Object.keys(this.callbacks); - for (let i = 0; i < ids.length; ++i) { + for (let i = 0; i < ids.length; ++i) { const id = ids[i]; // check to see if the callback was removed during iteration if (hasOwn.call(this.callbacks, id)) { const callback = this.callbacks[id]; - if (! iterator(callback)) { + if (!iterator(callback)) { break; } } @@ -134,12 +135,12 @@ export class Hook { */ async forEachAsync(iterator) { const ids = Object.keys(this.callbacks); - for (let i = 0; i < ids.length; ++i) { + for (let i = 0; i < ids.length; ++i) { const id = ids[i]; // check to see if the callback was removed during iteration if (hasOwn.call(this.callbacks, id)) { const callback = this.callbacks[id]; - if (!await iterator(callback)) { + if (!(await iterator(callback))) { break; } } @@ -157,13 +158,10 @@ export class Hook { // Copied from Meteor.bindEnvironment and removed all the env stuff. function dontBindEnvironment(func, onException, _this) { - if (!onException || typeof(onException) === 'string') { + if (!onException || typeof onException === "string") { const description = onException || "callback of async function"; onException = function (error) { - Meteor._debug( - `Exception in ${description}`, - error - ); + Meteor._debug(`Exception in ${description}`, error); }; } diff --git a/packages/callback-hook/hook_tests.js b/packages/callback-hook/hook_tests.js index 5000d1ca59..89f96ee069 100644 --- a/packages/callback-hook/hook_tests.js +++ b/packages/callback-hook/hook_tests.js @@ -1,13 +1,13 @@ Tinytest.add("callback-hook - binds to registrar's env by default", function (test) { const hook = new Hook(); - const envVar = new Meteor.EnvironmentVariable; - envVar.withValue("registrar's value", function() { - hook.register(function() { + const envVar = new Meteor.EnvironmentVariable(); + envVar.withValue("registrar's value", function () { + hook.register(function () { test.equal(envVar.get(), "registrar's value"); }); }); - envVar.withValue("invoker's value", function() { - hook.forEach(function(callback) { + envVar.withValue("invoker's value", function () { + hook.forEach(function (callback) { callback(); }); }); @@ -15,14 +15,14 @@ Tinytest.add("callback-hook - binds to registrar's env by default", function (te Tinytest.add("callback-hook - uses invoker's env with {bindEnvironment: false}", function (test) { const hook = new Hook({ bindEnvironment: false }); - const envVar = new Meteor.EnvironmentVariable; - envVar.withValue("registrar's value", function() { - hook.register(function() { + const envVar = new Meteor.EnvironmentVariable(); + envVar.withValue("registrar's value", function () { + hook.register(function () { test.equal(envVar.get(), "invoker's value"); }); }); - envVar.withValue("invoker's value", function() { - hook.each(function(callback) { + envVar.withValue("invoker's value", function () { + hook.each(function (callback) { callback(); }); }); @@ -30,26 +30,31 @@ Tinytest.add("callback-hook - uses invoker's env with {bindEnvironment: false}", Tinytest.add("callback-hook - exceptions unhandled with {bindEnvironment: false}", function (test) { const hook = new Hook({ bindEnvironment: false }); - hook.register(function() { + hook.register(function () { throw new Error("Test error"); }); - hook.forEach(function(callback) { + hook.forEach(function (callback) { test.throws(callback, "Test error"); }); }); -Tinytest.add("callback-hook - exceptionHandler used with {bindEnvironment: false}", function (test) { - const exToThrow = new Error("Test error"); - let thrownEx = null; - const hook = new Hook({ - bindEnvironment: false, - exceptionHandler: function (ex) { thrownEx = ex; } - }); - hook.register(function() { - throw exToThrow; - }); - hook.each(function(callback) { - callback(); - }); - test.equal(exToThrow, thrownEx); -}); +Tinytest.add( + "callback-hook - exceptionHandler used with {bindEnvironment: false}", + function (test) { + const exToThrow = new Error("Test error"); + let thrownEx = null; + const hook = new Hook({ + bindEnvironment: false, + exceptionHandler: function (ex) { + thrownEx = ex; + }, + }); + hook.register(function () { + throw exToThrow; + }); + hook.each(function (callback) { + callback(); + }); + test.equal(exToThrow, thrownEx); + }, +); diff --git a/packages/callback-hook/package.js b/packages/callback-hook/package.js index 6f9e9ac68a..b6fd5ead65 100644 --- a/packages/callback-hook/package.js +++ b/packages/callback-hook/package.js @@ -1,16 +1,16 @@ Package.describe({ summary: "Register callbacks on a hook", - version: '1.6.1', + version: "1.6.1", }); Package.onUse(function (api) { - api.use('ecmascript'); - api.mainModule('hook.js'); - api.export('Hook'); + api.use("ecmascript"); + api.mainModule("hook.js"); + api.export("Hook"); }); Package.onTest(function (api) { - api.use('callback-hook'); - api.use('tinytest'); - api.addFiles('hook_tests.js', 'server'); + api.use("callback-hook"); + api.use("tinytest"); + api.addFiles("hook_tests.js", "server"); }); diff --git a/packages/check/check.d.ts b/packages/check/check.d.ts index 975d8be9f9..0229937724 100644 --- a/packages/check/check.d.ts +++ b/packages/check/check.d.ts @@ -40,24 +40,16 @@ export namespace Match { * Matches either `undefined`, `null`, or pattern. If used in an object, matches only if the key is not set as opposed to the value being set to `undefined` or `null`. This set of conditions * was chosen because `undefined` arguments to Meteor Methods are converted to `null` when sent over the wire. */ - function Maybe( - pattern: T - ): Matcher | undefined | null>; + function Maybe(pattern: T): Matcher | undefined | null>; /** Behaves like `Match.Maybe` except it doesn’t accept `null`. If used in an object, the behavior is identical to `Match.Maybe`. */ - function Optional( - pattern: T - ): Matcher | undefined>; + function Optional(pattern: T): Matcher | undefined>; /** Matches an Object with the given keys; the value may also have other keys with arbitrary values. */ - function ObjectIncluding( - dico: T - ): Matcher>; + function ObjectIncluding(dico: T): Matcher>; /** Matches any value that matches at least one of the provided patterns. */ - function OneOf( - ...patterns: T - ): Matcher>; + function OneOf(...patterns: T): Matcher>; /** * Calls the function condition with the value as the argument. If condition returns true, this matches. If condition throws a `Match.Error` or returns false, this fails. If condition throws @@ -72,10 +64,7 @@ export namespace Match { * @param value The value to check * @param pattern The pattern to match `value` against */ - function test( - value: any, - pattern: T - ): value is PatternMatch; + function test(value: any, pattern: T): value is PatternMatch; } /** @@ -93,5 +82,5 @@ export namespace Match { export declare function check( value: any, pattern: T, - options?: { throwAllErrors?: boolean } + options?: { throwAllErrors?: boolean }, ): asserts value is Match.PatternMatch; diff --git a/packages/check/isPlainObject.js b/packages/check/isPlainObject.js index cdbbee772e..7502265265 100644 --- a/packages/check/isPlainObject.js +++ b/packages/check/isPlainObject.js @@ -12,10 +12,10 @@ const ObjectFunctionString = fnToString.call(Object); const getProto = Object.getPrototypeOf; -export const isPlainObject = obj => { +export const isPlainObject = (obj) => { // Detect obvious negatives // Use toString instead of jQuery.type to catch host objects - if (!obj || toString.call(obj) !== '[object Object]') { + if (!obj || toString.call(obj) !== "[object Object]") { return false; } @@ -27,7 +27,6 @@ export const isPlainObject = obj => { } // Objects with prototype are plain iff they were constructed by a global Object function - const Ctor = hasOwn.call(proto, 'constructor') && proto.constructor; - return typeof Ctor === 'function' && - fnToString.call(Ctor) === ObjectFunctionString; + const Ctor = hasOwn.call(proto, "constructor") && proto.constructor; + return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString; }; diff --git a/packages/check/match.js b/packages/check/match.js index 4fc072e539..68d419df80 100644 --- a/packages/check/match.js +++ b/packages/check/match.js @@ -1,13 +1,13 @@ // XXX docs -import { isPlainObject } from './isPlainObject'; +import { isPlainObject } from "./isPlainObject"; // Things we explicitly do NOT support: // - heterogenous arrays -const currentArgumentChecker = new Meteor.EnvironmentVariable; +const currentArgumentChecker = new Meteor.EnvironmentVariable(); const hasOwn = Object.prototype.hasOwnProperty; -const format = result => { +const format = (result) => { const err = new Match.Error(result.message); if (result.path) { err.message += ` in field ${result.path}`; @@ -15,7 +15,7 @@ const format = result => { } return err; -} +}; function nonEmptyStringCondition(value) { check(value, String); @@ -53,61 +53,61 @@ export function check(value, pattern, options = { throwAllErrors: false }) { if (result) { if (options.throwAllErrors) { - throw Array.isArray(result) ? result.map(r => format(r)) : [format(result)] + throw Array.isArray(result) ? result.map((r) => format(r)) : [format(result)]; } else { - throw format(result) + throw format(result); } } -}; +} /** * @namespace Match * @summary The namespace for all Match types and methods. */ export const Match = { - Optional: function(pattern) { + Optional: function (pattern) { return new Optional(pattern); }, - Maybe: function(pattern) { + Maybe: function (pattern) { return new Maybe(pattern); }, - OneOf: function(...args) { + OneOf: function (...args) { return new OneOf(args); }, - Any: ['__any__'], - Where: function(condition) { + Any: ["__any__"], + Where: function (condition) { return new Where(condition); }, - NonEmptyString: ['__NonEmptyString__'], + NonEmptyString: ["__NonEmptyString__"], - ObjectIncluding: function(pattern) { - return new ObjectIncluding(pattern) + ObjectIncluding: function (pattern) { + return new ObjectIncluding(pattern); }, - ObjectWithValues: function(pattern) { + ObjectWithValues: function (pattern) { return new ObjectWithValues(pattern); }, // Matches only signed 32-bit integers - Integer: ['__integer__'], + Integer: ["__integer__"], // XXX matchers should know how to describe themselves for errors - Error: Meteor.makeErrorType('Match.Error', function (msg) { + Error: Meteor.makeErrorType("Match.Error", function (msg) { this.message = `Match error: ${msg}`; // The path of the value that failed to match. Initially empty, this gets // populated by catching and rethrowing the exception as it goes back up the // stack. // E.g.: "vals[3].entity.created" - this.path = ''; + this.path = ""; // If this gets sent over DDP, don't give full internal details but at least // provide something better than 500 Internal server error. - this.sanitizedError = new Meteor.Error(400, 'Match failed'); + this.sanitizedError = new Meteor.Error(400, "Match failed"); }), // Tests to see if value matches pattern. Unlike check, it merely returns true @@ -132,15 +132,12 @@ export const Match = { // (using `description` in the message). _failIfArgumentsAreNotAllChecked(f, context, args, description) { const argChecker = new ArgumentChecker(args, description); - const result = currentArgumentChecker.withValue( - argChecker, - () => f.apply(context, args) - ); + const result = currentArgumentChecker.withValue(argChecker, () => f.apply(context, args)); // If f didn't itself throw, make sure it checked all of its arguments. argChecker.throwUnlessAllArgumentsHaveBeenChecked(); return result; - } + }, }; class Optional { @@ -158,7 +155,7 @@ class Maybe { class OneOf { constructor(choices) { if (!choices || choices.length === 0) { - throw new Error('Must provide at least one choice to Match.OneOf'); + throw new Error("Must provide at least one choice to Match.OneOf"); } this.choices = choices; @@ -184,26 +181,25 @@ class ObjectWithValues { } const stringForErrorMessage = (value, options = {}) => { - if ( value === null ) { - return 'null'; + if (value === null) { + return "null"; } - if ( options.onlyShowType ) { + if (options.onlyShowType) { return typeof value; } // Your average non-object things. Saves from doing the try/catch below for. - if ( typeof value !== 'object' ) { - return EJSON.stringify(value) + if (typeof value !== "object") { + return EJSON.stringify(value); } try { - // Find objects with circular references since EJSON doesn't support them yet (Issue #4778 + Unaccepted PR) // If the native stringify is going to choke, EJSON.stringify is going to choke too. JSON.stringify(value); } catch (stringifyError) { - if ( stringifyError.name === 'TypeError' ) { + if (stringifyError.name === "TypeError") { return typeof value; } } @@ -211,20 +207,19 @@ const stringForErrorMessage = (value, options = {}) => { return EJSON.stringify(value); }; - const typeofChecks = [ - [String, 'string'], - [Number, 'number'], - [Boolean, 'boolean'], + [String, "string"], + [Number, "number"], + [Boolean, "boolean"], // While we don't allow undefined/function in EJSON, this is good for optional // arguments with OneOf. - [Function, 'function'], - [undefined, 'undefined'], + [Function, "function"], + [undefined, "undefined"], ]; // Return `false` if it matches. Otherwise, returns an object with a `message` and a `path` field or an array of objects each with a `message` and a `path` field when collecting errors. -const testSubtree = (value, pattern, collectErrors = false, errors = [], path = '') => { +const testSubtree = (value, pattern, collectErrors = false, errors = [], path = "") => { // Match anything! if (pattern === Match.Any) { return false; @@ -240,7 +235,7 @@ const testSubtree = (value, pattern, collectErrors = false, errors = [], path = return { message: `Expected ${typeofChecks[i][1]}, got ${stringForErrorMessage(value, { onlyShowType: true })}`, - path: '', + path: "", }; } } @@ -252,38 +247,37 @@ const testSubtree = (value, pattern, collectErrors = false, errors = [], path = return { message: `Expected null, got ${stringForErrorMessage(value)}`, - path: '', + path: "", }; } // Strings, numbers, and booleans match literally. Goes well with Match.OneOf. - if (typeof pattern === 'string' || typeof pattern === 'number' || typeof pattern === 'boolean') { + if (typeof pattern === "string" || typeof pattern === "number" || typeof pattern === "boolean") { if (value === pattern) { return false; } return { message: `Expected ${pattern}, got ${stringForErrorMessage(value)}`, - path: '', + path: "", }; } // Match.Integer is special type encoded with array if (pattern === Match.Integer) { - // There is no consistent and reliable way to check if variable is a 64-bit // integer. One of the popular solutions is to get reminder of division by 1 // but this method fails on really large floats with big precision. // E.g.: 1.348192308491824e+23 % 1 === 0 in V8 // Bitwise operators work consistantly but always cast variable to 32-bit // signed integer according to JavaScript specs. - if (typeof value === 'number' && (value | 0) === value) { + if (typeof value === "number" && (value | 0) === value) { return false; } return { message: `Expected Integer, got ${stringForErrorMessage(value)}`, - path: '', + path: "", }; } @@ -292,7 +286,7 @@ const testSubtree = (value, pattern, collectErrors = false, errors = [], path = pattern = Match.ObjectIncluding({}); } // This must be invoked before pattern instanceof Array as strings are regarded as arrays - // We invoke the pattern as IIFE so that `pattern isntanceof Where` catches it + // We invoke the pattern as IIFE so that `pattern isntanceof Where` catches it if (pattern === Match.NonEmptyString) { pattern = new Where(nonEmptyStringCondition); } @@ -302,25 +296,24 @@ const testSubtree = (value, pattern, collectErrors = false, errors = [], path = if (pattern.length !== 1) { return { message: `Bad pattern: arrays must have one type element ${stringForErrorMessage(pattern)}`, - path: '', + path: "", }; } if (!Array.isArray(value) && !isArguments(value)) { return { message: `Expected array, got ${stringForErrorMessage(value)}`, - path: '', + path: "", }; } - for (let i = 0, length = value.length; i < length; i++) { - const arrPath = `${path}[${i}]` + const arrPath = `${path}[${i}]`; const result = testSubtree(value[i], pattern[0], collectErrors, errors, arrPath); if (result) { - result.path = _prependPath(collectErrors ? arrPath : i, result.path) + result.path = _prependPath(collectErrors ? arrPath : i, result.path); if (!collectErrors) return result; - if (typeof value[i] !== 'object' || result.message) errors.push(result) + if (typeof value[i] !== "object" || result.message) errors.push(result); } } @@ -341,7 +334,7 @@ const testSubtree = (value, pattern, collectErrors = false, errors = [], path = return { message: err.message, - path: err.path + path: err.path, }; } @@ -352,8 +345,8 @@ const testSubtree = (value, pattern, collectErrors = false, errors = [], path = // XXX this error is terrible return { - message: 'Failed Match.Where validation', - path: '', + message: "Failed Match.Where validation", + path: "", }; } @@ -367,7 +360,6 @@ const testSubtree = (value, pattern, collectErrors = false, errors = [], path = for (let i = 0; i < pattern.choices.length; ++i) { const result = testSubtree(value, pattern.choices[i]); if (!result) { - // No error? Yay, return. return false; } @@ -377,8 +369,8 @@ const testSubtree = (value, pattern, collectErrors = false, errors = [], path = // XXX this error is terrible return { - message: 'Failed Match.OneOf, Match.Maybe or Match.Optional validation', - path: '', + message: "Failed Match.OneOf, Match.Maybe or Match.Optional validation", + path: "", }; } @@ -390,8 +382,8 @@ const testSubtree = (value, pattern, collectErrors = false, errors = [], path = } return { - message: `Expected ${pattern.name || 'particular constructor'}`, - path: '', + message: `Expected ${pattern.name || "particular constructor"}`, + path: "", }; } @@ -405,47 +397,46 @@ const testSubtree = (value, pattern, collectErrors = false, errors = [], path = if (pattern instanceof ObjectWithValues) { unknownKeysAllowed = true; unknownKeyPattern = [pattern.pattern]; - pattern = {}; // no required keys + pattern = {}; // no required keys } - if (typeof pattern !== 'object') { + if (typeof pattern !== "object") { return { - message: 'Bad pattern: unknown pattern type', - path: '', + message: "Bad pattern: unknown pattern type", + path: "", }; } // An object, with required and optional keys. Note that this does NOT do // structural matches against objects of special types that happen to match // the pattern: this really needs to be a plain old {Object}! - if (typeof value !== 'object') { + if (typeof value !== "object") { return { message: `Expected object, got ${typeof value}`, - path: '', + path: "", }; } if (value === null) { return { message: `Expected object, got null`, - path: '', + path: "", }; } - if (! isPlainObject(value)) { + if (!isPlainObject(value)) { return { message: `Expected plain object`, - path: '', + path: "", }; } const requiredPatterns = Object.create(null); const optionalPatterns = Object.create(null); - Object.keys(pattern).forEach(key => { + Object.keys(pattern).forEach((key) => { const subPattern = pattern[key]; - if (subPattern instanceof Optional || - subPattern instanceof Maybe) { + if (subPattern instanceof Optional || subPattern instanceof Maybe) { optionalPatterns[key] = subPattern.pattern; } else { requiredPatterns[key] = subPattern; @@ -458,24 +449,23 @@ const testSubtree = (value, pattern, collectErrors = false, errors = [], path = if (hasOwn.call(requiredPatterns, key)) { const result = testSubtree(subValue, requiredPatterns[key], collectErrors, errors, objPath); if (result) { - result.path = _prependPath(collectErrors ? objPath : key, result.path) + result.path = _prependPath(collectErrors ? objPath : key, result.path); if (!collectErrors) return result; - if (typeof subValue !== 'object' || result.message) errors.push(result); + if (typeof subValue !== "object" || result.message) errors.push(result); } delete requiredPatterns[key]; } else if (hasOwn.call(optionalPatterns, key)) { const result = testSubtree(subValue, optionalPatterns[key], collectErrors, errors, objPath); if (result) { - result.path = _prependPath(collectErrors ? objPath : key, result.path) + result.path = _prependPath(collectErrors ? objPath : key, result.path); if (!collectErrors) return result; - if (typeof subValue !== 'object' || result.message) errors.push(result); + if (typeof subValue !== "object" || result.message) errors.push(result); } - } else { if (!unknownKeysAllowed) { const result = { - message: 'Unknown key', + message: "Unknown key", path: key, }; if (!collectErrors) return result; @@ -485,9 +475,9 @@ const testSubtree = (value, pattern, collectErrors = false, errors = [], path = if (unknownKeyPattern) { const result = testSubtree(subValue, unknownKeyPattern[0], collectErrors, errors, objPath); if (result) { - result.path = _prependPath(collectErrors ? objPath : key, result.path) + result.path = _prependPath(collectErrors ? objPath : key, result.path); if (!collectErrors) return result; - if (typeof subValue !== 'object' || result.message) errors.push(result); + if (typeof subValue !== "object" || result.message) errors.push(result); } } } @@ -495,9 +485,9 @@ const testSubtree = (value, pattern, collectErrors = false, errors = [], path = const keys = Object.keys(requiredPatterns); if (keys.length) { - const createMissingError = key => ({ + const createMissingError = (key) => ({ message: `Missing key '${key}'`, - path: collectErrors ? path : '', + path: collectErrors ? path : "", }); if (!collectErrors) { @@ -514,8 +504,7 @@ const testSubtree = (value, pattern, collectErrors = false, errors = [], path = }; class ArgumentChecker { - constructor (args, description) { - + constructor(args, description) { // Make a SHALLOW copy of the arguments. (We'll be doing identity checks // against its contents.) this.args = [...args]; @@ -542,13 +531,11 @@ class ArgumentChecker { _checkingOneValue(value) { for (let i = 0; i < this.args.length; ++i) { - // Is this value one of the arguments? (This can have a false positive if // the argument is an interned primitive, but it's still a good enough // check.) // (NaN is not === to itself, so we have to check specially.) - if (value === this.args[i] || - (Number.isNaN(value) && Number.isNaN(this.args[i]))) { + if (value === this.args[i] || (Number.isNaN(value) && Number.isNaN(this.args[i]))) { this.args.splice(i, 1); return true; } @@ -562,37 +549,81 @@ class ArgumentChecker { } } -const _jsKeywords = ['do', 'if', 'in', 'for', 'let', 'new', 'try', 'var', 'case', - 'else', 'enum', 'eval', 'false', 'null', 'this', 'true', 'void', 'with', - 'break', 'catch', 'class', 'const', 'super', 'throw', 'while', 'yield', - 'delete', 'export', 'import', 'public', 'return', 'static', 'switch', - 'typeof', 'default', 'extends', 'finally', 'package', 'private', 'continue', - 'debugger', 'function', 'arguments', 'interface', 'protected', 'implements', - 'instanceof']; +const _jsKeywords = [ + "do", + "if", + "in", + "for", + "let", + "new", + "try", + "var", + "case", + "else", + "enum", + "eval", + "false", + "null", + "this", + "true", + "void", + "with", + "break", + "catch", + "class", + "const", + "super", + "throw", + "while", + "yield", + "delete", + "export", + "import", + "public", + "return", + "static", + "switch", + "typeof", + "default", + "extends", + "finally", + "package", + "private", + "continue", + "debugger", + "function", + "arguments", + "interface", + "protected", + "implements", + "instanceof", +]; // Assumes the base of path is already escaped properly // returns key + base const _prependPath = (key, base) => { - if ((typeof key) === 'number' || key.match(/^[0-9]+$/)) { + if (typeof key === "number" || key.match(/^[0-9]+$/)) { key = `[${key}]`; - } else if (!key.match(/^[a-z_$][0-9a-z_$.[\]]*$/i) || - _jsKeywords.indexOf(key) >= 0) { + } else if (!key.match(/^[a-z_$][0-9a-z_$.[\]]*$/i) || _jsKeywords.indexOf(key) >= 0) { key = JSON.stringify([key]); } - if (base && base[0] !== '[') { + if (base && base[0] !== "[") { return `${key}.${base}`; } return key + base; -} +}; -const isObject = value => typeof value === 'object' && value !== null; +const isObject = (value) => typeof value === "object" && value !== null; -const baseIsArguments = item => - isObject(item) && - Object.prototype.toString.call(item) === '[object Arguments]'; +const baseIsArguments = (item) => + isObject(item) && Object.prototype.toString.call(item) === "[object Arguments]"; -const isArguments = baseIsArguments(function() { return arguments; }()) ? - baseIsArguments : - value => isObject(value) && typeof value.callee === 'function'; +const isArguments = baseIsArguments( + (function () { + return arguments; + })(), +) + ? baseIsArguments + : (value) => isObject(value) && typeof value.callee === "function"; diff --git a/packages/check/match_test.js b/packages/check/match_test.js index 37a4d54cfe..41b8abf4db 100644 --- a/packages/check/match_test.js +++ b/packages/check/match_test.js @@ -1,4 +1,4 @@ -Tinytest.add('check - check', test => { +Tinytest.add("check - check", (test) => { const matches = (value, pattern) => { let error; try { @@ -26,21 +26,21 @@ Tinytest.add('check - check', test => { // Atoms. const pairs = [ - ['foo', String], - ['', String], + ["foo", String], + ["", String], [0, Number], [42.59, Number], [NaN, Number], [Infinity, Number], [true, Boolean], [false, Boolean], - [function(){}, Function], + [function () {}, Function], [undefined, undefined], - [null, null] + [null, null], ]; - pairs.forEach(pair => { + pairs.forEach((pair) => { matches(pair[0], Match.Any); - [String, Number, Boolean, undefined, null].forEach(type => { + [String, Number, Boolean, undefined, null].forEach((type) => { if (type === pair[1]) { matches(pair[0], type); matches(pair[0], Match.Optional(type)); @@ -48,70 +48,81 @@ Tinytest.add('check - check', test => { matches(pair[0], Match.Maybe(type)); matches(undefined, Match.Maybe(type)); matches(null, Match.Maybe(type)); - matches(pair[0], Match.Where(() => { - check(pair[0], type); - return true; - })); - matches(pair[0], Match.Where(() => { - try { + matches( + pair[0], + Match.Where(() => { check(pair[0], type); return true; - } catch { - return false; - } - })); + }), + ); + matches( + pair[0], + Match.Where(() => { + try { + check(pair[0], type); + return true; + } catch { + return false; + } + }), + ); } else { fails(pair[0], type); matches(pair[0], Match.OneOf(type, pair[1])); matches(pair[0], Match.OneOf(pair[1], type)); - fails(pair[0], Match.Where(() => { - check(pair[0], type); - return true; - })); - fails(pair[0], Match.Where(() => { - try { + fails( + pair[0], + Match.Where(() => { check(pair[0], type); return true; - } catch { - return false; - } - })); + }), + ); + fails( + pair[0], + Match.Where(() => { + try { + check(pair[0], type); + return true; + } catch { + return false; + } + }), + ); } - if ( type !== null ) { - + if (type !== null) { // Optional doesn't allow null, but does match on null type fails(null, Match.Optional(type)); - } + } fails(pair[0], [type]); fails(pair[0], Object); }); }); fails(true, Match.OneOf(String, Number, undefined, null, [Boolean])); - fails(new String('foo'), String); + fails(new String("foo"), String); fails(new Boolean(true), Boolean); fails(new Number(123), Number); matches([1, 2, 3], [Number]); matches([], [Number]); - fails([1, 2, 3, '4'], [Number]); + fails([1, 2, 3, "4"], [Number]); fails([1, 2, 3, [4]], [Number]); - matches([1, 2, 3, '4'], [Match.OneOf(Number, String)]); + matches([1, 2, 3, "4"], [Match.OneOf(Number, String)]); matches({}, Object); matches({}, {}); - matches({foo: 42}, Object); - fails({foo: 42}, {}); - matches({a: 1, b:2}, {b: Number, a: Number}); - fails({a: 1, b:2}, {b: Number}); - matches({a: 1, b:2}, Match.ObjectIncluding({b: Number})); - fails({a: 1, b:2}, Match.ObjectIncluding({b: String})); - fails({a: 1, b:2}, Match.ObjectIncluding({c: String})); - fails({}, {a: Number}); - matches({nodeType: 1}, {nodeType: Match.Any}); - matches({nodeType: 1}, Match.ObjectIncluding({nodeType: Match.Any})); - fails({nodeType: 1}, {nodeType: String}); - fails({}, Match.ObjectIncluding({nodeType: Match.Any})); + matches({ foo: 42 }, Object); + fails({ foo: 42 }, {}); + matches({ a: 1, b: 2 }, { b: Number, a: Number }); + fails({ a: 1, b: 2 }, { b: Number }); + matches({ a: 1, b: 2 }, Match.ObjectIncluding({ b: Number })); + fails({ a: 1, b: 2 }, Match.ObjectIncluding({ b: String })); + fails({ a: 1, b: 2 }, Match.ObjectIncluding({ c: String })); + fails({}, { a: Number }); + matches({ nodeType: 1 }, { nodeType: Match.Any }); + matches({ nodeType: 1 }, Match.ObjectIncluding({ nodeType: Match.Any })); + fails({ nodeType: 1 }, { nodeType: String }); + fails({}, Match.ObjectIncluding({ nodeType: Match.Any })); // Match.Optional does not match on a null value, unless the allowed type is itself "null" fails(null, Match.Optional(String)); @@ -124,12 +135,12 @@ Tinytest.add('check - check', test => { matches(undefined, Match.Optional(null)); fails(true, Match.Optional(String)); // different should still fail - matches('String', Match.Optional(String)); // same should pass + matches("String", Match.Optional(String)); // same should pass - matches({}, {a: Match.Optional(Number)}); - matches({a: 1}, {a: Match.Optional(Number)}); - fails({a: true}, {a: Match.Optional(Number)}); - fails({a: undefined}, {a: Match.Optional(Number)}); + matches({}, { a: Match.Optional(Number) }); + matches({ a: 1 }, { a: Match.Optional(Number) }); + fails({ a: true }, { a: Match.Optional(Number) }); + fails({ a: undefined }, { a: Match.Optional(Number) }); // .Maybe requires undefined, null or the allowed type in order to match matches(null, Match.Maybe(String)); @@ -141,68 +152,76 @@ Tinytest.add('check - check', test => { matches(undefined, Match.Maybe(null)); fails(true, Match.Maybe(String)); // different should still fail - matches('String', Match.Maybe(String)); // same should pass + matches("String", Match.Maybe(String)); // same should pass - matches({}, {a: Match.Maybe(Number)}); - matches({a: 1}, {a: Match.Maybe(Number)}); - fails({a: true}, {a: Match.Maybe(Number)}); + matches({}, { a: Match.Maybe(Number) }); + matches({ a: 1 }, { a: Match.Maybe(Number) }); + fails({ a: true }, { a: Match.Maybe(Number) }); // Match.Optional means "or undefined" at the top level but "or absent" in // objects. // Match.Maybe should behave the same as Match.Optional in objects // including handling nulls - fails({a: undefined}, {a: Match.Maybe(Number)}); - fails({a: null}, {a: Match.Maybe(Number)}); + fails({ a: undefined }, { a: Match.Maybe(Number) }); + fails({ a: null }, { a: Match.Maybe(Number) }); const F = function () { this.x = 123; }; - fails(new F, { x: 123 }); + fails(new F(), { x: 123 }); matches({}, Match.ObjectWithValues(Number)); - matches({x: 1}, Match.ObjectWithValues(Number)); - matches({x: 1, y: 2}, Match.ObjectWithValues(Number)); - fails({x: 1, y: '2'}, Match.ObjectWithValues(Number)); + matches({ x: 1 }, Match.ObjectWithValues(Number)); + matches({ x: 1, y: 2 }, Match.ObjectWithValues(Number)); + fails({ x: 1, y: "2" }, Match.ObjectWithValues(Number)); - matches('asdf', 'asdf'); - fails('asdf', 'monkey'); + matches("asdf", "asdf"); + fails("asdf", "monkey"); matches(123, 123); fails(123, 456); - fails('123', 123); - fails(123, '123'); + fails("123", 123); + fails(123, "123"); matches(true, true); matches(false, false); fails(true, false); - fails(true, 'true'); - fails('false', false); - matches('xx', Match.NonEmptyString); - fails('', Match.NonEmptyString); + fails(true, "true"); + fails("false", false); + matches("xx", Match.NonEmptyString); + fails("", Match.NonEmptyString); matches(/foo/, RegExp); fails(/foo/, String); - matches(new Date, Date); - fails(new Date, Number); + matches(new Date(), Date); + fails(new Date(), Number); matches(EJSON.newBinary(42), Match.Where(EJSON.isBinary)); fails([], Match.Where(EJSON.isBinary)); - matches(42, Match.Where(x => x % 2 === 0)); - fails(43, Match.Where(x => x % 2 === 0)); - - matches({ - a: 'something', - b: [ - {x: 42, k: null}, - {x: 43, k: true, p: ['yay']}, - ], - }, { - a: String, - b: [ - Match.ObjectIncluding({ - x: Number, - k: Match.OneOf(null, Boolean) - }), - ], - }); + matches( + 42, + Match.Where((x) => x % 2 === 0), + ); + fails( + 43, + Match.Where((x) => x % 2 === 0), + ); + matches( + { + a: "something", + b: [ + { x: 42, k: null }, + { x: 43, k: true, p: ["yay"] }, + ], + }, + { + a: String, + b: [ + Match.ObjectIncluding({ + x: Number, + k: Match.OneOf(null, Boolean), + }), + ], + }, + ); // Match.Integer matches(-1, Match.Integer); @@ -211,28 +230,27 @@ Tinytest.add('check - check', test => { matches(-2147483648, Match.Integer); // INT_MIN matches(2147483647, Match.Integer); // INT_MAX fails(123.33, Match.Integer); - fails(.33, Match.Integer); - fails(1.348192308491824e+23, Match.Integer); + fails(0.33, Match.Integer); + fails(1.348192308491824e23, Match.Integer); fails(NaN, Match.Integer); fails(Infinity, Match.Integer); fails(-Infinity, Match.Integer); fails({}, Match.Integer); fails([], Match.Integer); fails(function () {}, Match.Integer); - fails(new Date, Match.Integer); - + fails(new Date(), Match.Integer); // Test non-plain objects. - const parentObj = {foo: 'bar'}; - const childObj = Object.assign(Object.create(parentObj), {bar: 'foo'}); + const parentObj = { foo: "bar" }; + const childObj = Object.assign(Object.create(parentObj), { bar: "foo" }); matches(parentObj, Object); - fails(parentObj, {foo: String, bar: String}); - fails(parentObj, {bar: String}); - matches(parentObj, {foo: String}); + fails(parentObj, { foo: String, bar: String }); + fails(parentObj, { bar: String }); + matches(parentObj, { foo: String }); fails(childObj, Object); - fails(childObj, {foo: String, bar: String}); - fails(childObj, {bar: String}); - fails(childObj, {foo: String}); + fails(childObj, { foo: String, bar: String }); + fails(childObj, { bar: String }); + fails(childObj, { foo: String }); // Functions const testFunction = () => {}; @@ -247,7 +265,7 @@ Tinytest.add('check - check', test => { this.child = child; }; - const testInstanceChild = new TestInstanceChild() + const testInstanceChild = new TestInstanceChild(); const testInstanceParent = new TestInstanceParent(testInstanceChild); matches(TestInstanceParent, Function); @@ -275,15 +293,15 @@ Tinytest.add('check - check', test => { const argumentsFails = function () { fails(arguments, [Number]); }; - argumentsFails('123'); - argumentsFails(1, '23'); + argumentsFails("123"); + argumentsFails(1, "23"); }); -Tinytest.add('check - check throw all errors', test => { +Tinytest.add("check - check throw all errors", (test) => { const matches = (value, pattern) => { let error; try { - check(value, pattern, {throwAllErrors: true}); + check(value, pattern, { throwAllErrors: true }); } catch (e) { error = e; } @@ -296,33 +314,33 @@ Tinytest.add('check - check throw all errors', test => { let error; try { - check(value, pattern, {throwAllErrors: true}); + check(value, pattern, { throwAllErrors: true }); } catch (e) { error = e; } test.isTrue(error); - error.every(e => test.instanceOf(e, Match.Error)); + error.every((e) => test.instanceOf(e, Match.Error)); test.isFalse(Match.test(value, pattern)); }; // Atoms. const pairs = [ - ['foo', String], - ['', String], + ["foo", String], + ["", String], [0, Number], [42.59, Number], [NaN, Number], [Infinity, Number], [true, Boolean], [false, Boolean], - [function(){}, Function], + [function () {}, Function], [undefined, undefined], - [null, null] + [null, null], ]; - pairs.forEach(pair => { + pairs.forEach((pair) => { matches(pair[0], Match.Any); - [String, Number, Boolean, undefined, null].forEach(type => { + [String, Number, Boolean, undefined, null].forEach((type) => { if (type === pair[1]) { matches(pair[0], type); matches(pair[0], Match.Optional(type)); @@ -330,70 +348,81 @@ Tinytest.add('check - check throw all errors', test => { matches(pair[0], Match.Maybe(type)); matches(undefined, Match.Maybe(type)); matches(null, Match.Maybe(type)); - matches(pair[0], Match.Where(() => { - check(pair[0], type); - return true; - })); - matches(pair[0], Match.Where(() => { - try { + matches( + pair[0], + Match.Where(() => { check(pair[0], type); return true; - } catch { - return false; - } - })); + }), + ); + matches( + pair[0], + Match.Where(() => { + try { + check(pair[0], type); + return true; + } catch { + return false; + } + }), + ); } else { fails(pair[0], type); matches(pair[0], Match.OneOf(type, pair[1])); matches(pair[0], Match.OneOf(pair[1], type)); - fails(pair[0], Match.Where(() => { - check(pair[0], type); - return true; - })); - fails(pair[0], Match.Where(() => { - try { + fails( + pair[0], + Match.Where(() => { check(pair[0], type); return true; - } catch { - return false; - } - })); + }), + ); + fails( + pair[0], + Match.Where(() => { + try { + check(pair[0], type); + return true; + } catch { + return false; + } + }), + ); } - if ( type !== null ) { - + if (type !== null) { // Optional doesn't allow null, but does match on null type fails(null, Match.Optional(type)); - } + } fails(pair[0], [type]); fails(pair[0], Object); }); }); fails(true, Match.OneOf(String, Number, undefined, null, [Boolean])); - fails(new String('foo'), String); + fails(new String("foo"), String); fails(new Boolean(true), Boolean); fails(new Number(123), Number); matches([1, 2, 3], [Number]); matches([], [Number]); - fails([1, 2, 3, '4'], [Number]); + fails([1, 2, 3, "4"], [Number]); fails([1, 2, 3, [4]], [Number]); - matches([1, 2, 3, '4'], [Match.OneOf(Number, String)]); + matches([1, 2, 3, "4"], [Match.OneOf(Number, String)]); matches({}, Object); matches({}, {}); - matches({foo: 42}, Object); - fails({foo: 42}, {}); - matches({a: 1, b:2}, {b: Number, a: Number}); - fails({a: 1, b:2}, {b: Number}); - matches({a: 1, b:2}, Match.ObjectIncluding({b: Number})); - fails({a: 1, b:2}, Match.ObjectIncluding({b: String})); - fails({a: 1, b:2}, Match.ObjectIncluding({c: String})); - fails({}, {a: Number}); - matches({nodeType: 1}, {nodeType: Match.Any}); - matches({nodeType: 1}, Match.ObjectIncluding({nodeType: Match.Any})); - fails({nodeType: 1}, {nodeType: String}); - fails({}, Match.ObjectIncluding({nodeType: Match.Any})); + matches({ foo: 42 }, Object); + fails({ foo: 42 }, {}); + matches({ a: 1, b: 2 }, { b: Number, a: Number }); + fails({ a: 1, b: 2 }, { b: Number }); + matches({ a: 1, b: 2 }, Match.ObjectIncluding({ b: Number })); + fails({ a: 1, b: 2 }, Match.ObjectIncluding({ b: String })); + fails({ a: 1, b: 2 }, Match.ObjectIncluding({ c: String })); + fails({}, { a: Number }); + matches({ nodeType: 1 }, { nodeType: Match.Any }); + matches({ nodeType: 1 }, Match.ObjectIncluding({ nodeType: Match.Any })); + fails({ nodeType: 1 }, { nodeType: String }); + fails({}, Match.ObjectIncluding({ nodeType: Match.Any })); // Match.Optional does not match on a null value, unless the allowed type is itself "null" fails(null, Match.Optional(String)); @@ -406,12 +435,12 @@ Tinytest.add('check - check throw all errors', test => { matches(undefined, Match.Optional(null)); fails(true, Match.Optional(String)); // different should still fail - matches('String', Match.Optional(String)); // same should pass + matches("String", Match.Optional(String)); // same should pass - matches({}, {a: Match.Optional(Number)}); - matches({a: 1}, {a: Match.Optional(Number)}); - fails({a: true}, {a: Match.Optional(Number)}); - fails({a: undefined}, {a: Match.Optional(Number)}); + matches({}, { a: Match.Optional(Number) }); + matches({ a: 1 }, { a: Match.Optional(Number) }); + fails({ a: true }, { a: Match.Optional(Number) }); + fails({ a: undefined }, { a: Match.Optional(Number) }); // .Maybe requires undefined, null or the allowed type in order to match matches(null, Match.Maybe(String)); @@ -423,67 +452,75 @@ Tinytest.add('check - check throw all errors', test => { matches(undefined, Match.Maybe(null)); fails(true, Match.Maybe(String)); // different should still fail - matches('String', Match.Maybe(String)); // same should pass + matches("String", Match.Maybe(String)); // same should pass - matches({}, {a: Match.Maybe(Number)}); - matches({a: 1}, {a: Match.Maybe(Number)}); - fails({a: true}, {a: Match.Maybe(Number)}); + matches({}, { a: Match.Maybe(Number) }); + matches({ a: 1 }, { a: Match.Maybe(Number) }); + fails({ a: true }, { a: Match.Maybe(Number) }); // Match.Optional means "or undefined" at the top level but "or absent" in // objects. // Match.Maybe should behave the same as Match.Optional in objects // including handling nulls - fails({a: undefined}, {a: Match.Maybe(Number)}); - fails({a: null}, {a: Match.Maybe(Number)}); + fails({ a: undefined }, { a: Match.Maybe(Number) }); + fails({ a: null }, { a: Match.Maybe(Number) }); const F = function () { this.x = 123; }; - fails(new F, { x: 123 }); + fails(new F(), { x: 123 }); matches({}, Match.ObjectWithValues(Number)); - matches({x: 1}, Match.ObjectWithValues(Number)); - matches({x: 1, y: 2}, Match.ObjectWithValues(Number)); - fails({x: 1, y: '2'}, Match.ObjectWithValues(Number)); + matches({ x: 1 }, Match.ObjectWithValues(Number)); + matches({ x: 1, y: 2 }, Match.ObjectWithValues(Number)); + fails({ x: 1, y: "2" }, Match.ObjectWithValues(Number)); - matches('asdf', 'asdf'); - fails('asdf', 'monkey'); + matches("asdf", "asdf"); + fails("asdf", "monkey"); matches(123, 123); fails(123, 456); - fails('123', 123); - fails(123, '123'); + fails("123", 123); + fails(123, "123"); matches(true, true); matches(false, false); fails(true, false); - fails(true, 'true'); - fails('false', false); + fails(true, "true"); + fails("false", false); matches(/foo/, RegExp); fails(/foo/, String); - matches(new Date, Date); - fails(new Date, Number); + matches(new Date(), Date); + fails(new Date(), Number); matches(EJSON.newBinary(42), Match.Where(EJSON.isBinary)); fails([], Match.Where(EJSON.isBinary)); - matches(42, Match.Where(x => x % 2 === 0)); - fails(43, Match.Where(x => x % 2 === 0)); - - matches({ - a: 'something', - b: [ - {x: 42, k: null}, - {x: 43, k: true, p: ['yay']}, - ], - }, { - a: String, - b: [ - Match.ObjectIncluding({ - x: Number, - k: Match.OneOf(null, Boolean) - }), - ], - }); + matches( + 42, + Match.Where((x) => x % 2 === 0), + ); + fails( + 43, + Match.Where((x) => x % 2 === 0), + ); + matches( + { + a: "something", + b: [ + { x: 42, k: null }, + { x: 43, k: true, p: ["yay"] }, + ], + }, + { + a: String, + b: [ + Match.ObjectIncluding({ + x: Number, + k: Match.OneOf(null, Boolean), + }), + ], + }, + ); // Match.Integer matches(-1, Match.Integer); @@ -492,28 +529,27 @@ Tinytest.add('check - check throw all errors', test => { matches(-2147483648, Match.Integer); // INT_MIN matches(2147483647, Match.Integer); // INT_MAX fails(123.33, Match.Integer); - fails(.33, Match.Integer); - fails(1.348192308491824e+23, Match.Integer); + fails(0.33, Match.Integer); + fails(1.348192308491824e23, Match.Integer); fails(NaN, Match.Integer); fails(Infinity, Match.Integer); fails(-Infinity, Match.Integer); fails({}, Match.Integer); fails([], Match.Integer); fails(function () {}, Match.Integer); - fails(new Date, Match.Integer); - + fails(new Date(), Match.Integer); // Test non-plain objects. - const parentObj = {foo: 'bar'}; - const childObj = Object.assign(Object.create(parentObj), {bar: 'foo'}); + const parentObj = { foo: "bar" }; + const childObj = Object.assign(Object.create(parentObj), { bar: "foo" }); matches(parentObj, Object); - fails(parentObj, {foo: String, bar: String}); - fails(parentObj, {bar: String}); - matches(parentObj, {foo: String}); + fails(parentObj, { foo: String, bar: String }); + fails(parentObj, { bar: String }); + matches(parentObj, { foo: String }); fails(childObj, Object); - fails(childObj, {foo: String, bar: String}); - fails(childObj, {bar: String}); - fails(childObj, {foo: String}); + fails(childObj, { foo: String, bar: String }); + fails(childObj, { bar: String }); + fails(childObj, { foo: String }); // Functions const testFunction = () => {}; @@ -528,7 +564,7 @@ Tinytest.add('check - check throw all errors', test => { this.child = child; }; - const testInstanceChild = new TestInstanceChild() + const testInstanceChild = new TestInstanceChild(); const testInstanceParent = new TestInstanceParent(testInstanceChild); matches(TestInstanceParent, Function); @@ -556,38 +592,102 @@ Tinytest.add('check - check throw all errors', test => { const argumentsFails = function () { fails(arguments, [Number]); }; - argumentsFails('123'); - argumentsFails(1, '23'); + argumentsFails("123"); + argumentsFails(1, "23"); }); -Tinytest.add('check - check throw all errors deeply nested', test => { +Tinytest.add("check - check throw all errors deeply nested", (test) => { let error; const value = { text: 1, - emails: ['2', 3, 4], - things: [{id: '1', num: 1}, {id: 2, num: 2}, {id: 3, num: '3'}], - stuff: {foo: 'true', bar: 3, items: [{x: 1, y: '1'}, {x: '2', y: 2}, {x: '3', y: '3'}]}, - any: { a: 'a' }, - maybe: { m: 'm', a: [1, '2'], b: [{x: 1, y: '1'}, {x: '2', y: 2}, {x: '3', y: '3'}] }, - opt: { o: 'm', a: [1, '2'], b: [{x: 1, y: '1'}, {x: '2', y: 2}, {x: '3', y: '3'}] }, - int: { i: 1.2, a: [1, '2'], b: [{x: 1, y: '1'}, {x: '2', y: 2}, {x: '3', y: '3'}] }, - oneOf: { f: 'm', a: [1, '2'], b: [{x: 1, y: '1'}, {x: '2', y: 2}, {x: '3', y: '3'}] }, - where: { w: 'a', a: [1, '2'], b: [{x: 1, y: '1'}, {x: '2', y: 2}, {x: '3', y: '3'}] }, + emails: ["2", 3, 4], + things: [ + { id: "1", num: 1 }, + { id: 2, num: 2 }, + { id: 3, num: "3" }, + ], + stuff: { + foo: "true", + bar: 3, + items: [ + { x: 1, y: "1" }, + { x: "2", y: 2 }, + { x: "3", y: "3" }, + ], + }, + any: { a: "a" }, + maybe: { + m: "m", + a: [1, "2"], + b: [ + { x: 1, y: "1" }, + { x: "2", y: 2 }, + { x: "3", y: "3" }, + ], + }, + opt: { + o: "m", + a: [1, "2"], + b: [ + { x: 1, y: "1" }, + { x: "2", y: 2 }, + { x: "3", y: "3" }, + ], + }, + int: { + i: 1.2, + a: [1, "2"], + b: [ + { x: 1, y: "1" }, + { x: "2", y: 2 }, + { x: "3", y: "3" }, + ], + }, + oneOf: { + f: "m", + a: [1, "2"], + b: [ + { x: 1, y: "1" }, + { x: "2", y: 2 }, + { x: "3", y: "3" }, + ], + }, + where: { + w: "a", + a: [1, "2"], + b: [ + { x: 1, y: "1" }, + { x: "2", y: 2 }, + { x: "3", y: "3" }, + ], + }, whereArr: [1, 2, 3], - embedded: { thing: '1' } + embedded: { thing: "1" }, }; const pattern = { text: String, emails: [String], - things: [{id: String, num: Number}], - stuff: {foo: Boolean, bar: String, items: [{ x: String, y: Number }]}, + things: [{ id: String, num: Number }], + stuff: { foo: Boolean, bar: String, items: [{ x: String, y: Number }] }, any: Match.Any, - maybe: { m: Match.Maybe(Number), a: [Match.Maybe(Number)], b: Match.Maybe([{ x: String, y: Number }]) }, - opt: { m: Match.Optional(Number), a: [Match.Optional(Number)], b: Match.Optional([{ x: String, y: Number }]) }, - int: { i: Match.Integer, a: [Match.Integer], b: [{ x: Match.Integer, y: Number }]}, - oneOf: { f: Match.OneOf(Number, Boolean), a: [Match.OneOf(Boolean, Function)], b: [{ x: Match.OneOf(String, Number), y: Match.OneOf(Boolean, null) }]}, + maybe: { + m: Match.Maybe(Number), + a: [Match.Maybe(Number)], + b: Match.Maybe([{ x: String, y: Number }]), + }, + opt: { + m: Match.Optional(Number), + a: [Match.Optional(Number)], + b: Match.Optional([{ x: String, y: Number }]), + }, + int: { i: Match.Integer, a: [Match.Integer], b: [{ x: Match.Integer, y: Number }] }, + oneOf: { + f: Match.OneOf(Number, Boolean), + a: [Match.OneOf(Boolean, Function)], + b: [{ x: Match.OneOf(String, Number), y: Match.OneOf(Boolean, null) }], + }, where: { w: Match.Where((x) => { check(x, String); @@ -596,7 +696,7 @@ Tinytest.add('check - check throw all errors deeply nested', test => { a: Match.Where((x) => { check(x, [Number]); return x > 1; - }) + }), }, whereArr: Match.Where((x) => { check(x, [String]); @@ -604,90 +704,114 @@ Tinytest.add('check - check throw all errors deeply nested', test => { }), missing1: String, missing2: String, - embedded: { thing: String, another: String } - } + embedded: { thing: String, another: String }, + }; try { - check(value, pattern, {throwAllErrors: true}); + check(value, pattern, { throwAllErrors: true }); } catch (e) { error = e; } test.isTrue(error); test.equal(error.length, 40); - test.equal(error.filter(e => e.message.includes('Missing key')).map(e => e.message), [`Match error: Missing key 'another' in field embedded`, `Match error: Missing key 'missing1'`, `Match error: Missing key 'missing2'`]); - error.every(e => test.instanceOf(e, Match.Error)); + test.equal( + error.filter((e) => e.message.includes("Missing key")).map((e) => e.message), + [ + `Match error: Missing key 'another' in field embedded`, + `Match error: Missing key 'missing1'`, + `Match error: Missing key 'missing2'`, + ], + ); + error.every((e) => test.instanceOf(e, Match.Error)); test.isFalse(Match.test(value, pattern)); -}) +}); -Tinytest.add('check - argument checker', test => { +Tinytest.add("check - argument checker", (test) => { const checksAllArguments = (f, ...args) => - Match._failIfArgumentsAreNotAllChecked(f, {}, args, 'test'); + Match._failIfArgumentsAreNotAllChecked(f, {}, args, "test"); checksAllArguments(() => {}); - checksAllArguments(x => check(x, Match.Any), undefined); - checksAllArguments(x => check(x, Match.Any), null); - checksAllArguments(x => check(x, Match.Any), false); - checksAllArguments(x => check(x, Match.Any), true); - checksAllArguments(x => check(x, Match.Any), 0); - checksAllArguments((a, b, c) => { - check(a, String); - check(b, Boolean); - check(c, Match.Optional(Number)); - }, 'foo', true); + checksAllArguments((x) => check(x, Match.Any), undefined); + checksAllArguments((x) => check(x, Match.Any), null); + checksAllArguments((x) => check(x, Match.Any), false); + checksAllArguments((x) => check(x, Match.Any), true); + checksAllArguments((x) => check(x, Match.Any), 0); + checksAllArguments( + (a, b, c) => { + check(a, String); + check(b, Boolean); + check(c, Match.Optional(Number)); + }, + "foo", + true, + ); checksAllArguments((...args) => check(args, [Number]), 1, 2, 4); - checksAllArguments((x, ...args) => { - check(x, Number); - check(args, [String]); - }, 1, 'foo', 'bar', 'baz'); + checksAllArguments( + (x, ...args) => { + check(x, Number); + check(args, [String]); + }, + 1, + "foo", + "bar", + "baz", + ); // NaN values - checksAllArguments(x => check(x, Number), NaN); + checksAllArguments((x) => check(x, Number), NaN); const doesntCheckAllArguments = (f, ...args) => { try { - Match._failIfArgumentsAreNotAllChecked(f, {}, args, 'test'); - test.fail({message: 'expected _failIfArgumentsAreNotAllChecked to throw'}); + Match._failIfArgumentsAreNotAllChecked(f, {}, args, "test"); + test.fail({ message: "expected _failIfArgumentsAreNotAllChecked to throw" }); } catch (e) { - test.equal(e.message, 'Did not check() all arguments during test'); + test.equal(e.message, "Did not check() all arguments during test"); } }; doesntCheckAllArguments(() => {}, undefined); doesntCheckAllArguments(() => {}, null); doesntCheckAllArguments(() => {}, 1); - doesntCheckAllArguments((_x, ...args) => check(args, [String]), 1, 'asdf', 'foo'); + doesntCheckAllArguments((_x, ...args) => check(args, [String]), 1, "asdf", "foo"); doesntCheckAllArguments((x, _y) => check(x, Boolean), true, false); // One "true" check doesn't count for all. doesntCheckAllArguments((x, _y) => check(x, Boolean), true, true); // For non-primitives, we really do require that each arg gets checked. - doesntCheckAllArguments((x, _y) => { - check(x, [Boolean]); - check(x, [Boolean]); - }, [true], [true]); + doesntCheckAllArguments( + (x, _y) => { + check(x, [Boolean]); + check(x, [Boolean]); + }, + [true], + [true], + ); // In an ideal world this test would fail, but we currently can't // differentiate between "two calls to check x, both of which are true" and // "check x and check y, both of which are true" (for any interned primitive // type). - checksAllArguments((x, _y) => { - check(x, Boolean); - check(x, Boolean); - }, true, true); + checksAllArguments( + (x, _y) => { + check(x, Boolean); + check(x, Boolean); + }, + true, + true, + ); }); -Tinytest.add('check - Match error path', test => { +Tinytest.add("check - Match error path", (test) => { const match = (value, pattern, expectedPath) => { try { check(value, pattern); } catch (err) { - // XXX just for FF 3.6, its JSON stringification prefers "\u000a" to "\n" - err.path = err.path.replace(/\\u000a/, '\\n'); + err.path = err.path.replace(/\\u000a/, "\\n"); if (err.path != expectedPath) { test.fail({ - type: 'match-error-path', + type: "match-error-path", message: "The path of Match.Error doesn't match.", pattern: JSON.stringify(pattern), value: JSON.stringify(value), @@ -698,38 +822,43 @@ Tinytest.add('check - Match error path', test => { } }; - match({ foo: [ { bar: 3 }, { bar: 'something' } ] }, { foo: [{ bar: Number }] }, 'foo[1].bar'); + match({ foo: [{ bar: 3 }, { bar: "something" }] }, { foo: [{ bar: Number }] }, "foo[1].bar"); // Complicated case with arrays, $, whitespace and quotes! - match([{ $FoO: { "bar baz\n\"'": 3 } }], [{ $FoO: { "bar baz\n\"'": String } }], "[0].$FoO[\"bar baz\\n\\\"'\"]"); + match( + [{ $FoO: { "bar baz\n\"'": 3 } }], + [{ $FoO: { "bar baz\n\"'": String } }], + '[0].$FoO["bar baz\\n\\"\'"]', + ); // Numbers only, can be accessed w/o quotes - match({ '1231': 123 }, { '1231': String }, '[1231]'); - match({ '1234abcd': 123 }, { '1234abcd': String }, '["1234abcd"]'); - match({ $set: { people: 'nice' } }, { $set: { people: [String] } }, '$set.people'); - match({ _underscore: 'should work' }, { _underscore: Number }, '_underscore'); + match({ 1231: 123 }, { 1231: String }, "[1231]"); + match({ "1234abcd": 123 }, { "1234abcd": String }, '["1234abcd"]'); + match({ $set: { people: "nice" } }, { $set: { people: [String] } }, "$set.people"); + match({ _underscore: "should work" }, { _underscore: Number }, "_underscore"); // Nested array looks nice - match([[['something', 'here'], []], [['string', 123]]], [[[String]]], '[1][0][1]'); + match([[["something", "here"], []], [["string", 123]]], [[[String]]], "[1][0][1]"); // Object nested in arrays should look nice, too! - match([[[{ foo: 'something' }, { foo: 'here'}], - [{ foo: 'asdf' }]], - [[{ foo: 123 }]]], - [[[{ foo: String }]]], '[1][0][0].foo'); + match( + [[[{ foo: "something" }, { foo: "here" }], [{ foo: "asdf" }]], [[{ foo: 123 }]]], + [[[{ foo: String }]]], + "[1][0][0].foo", + ); // JS keyword - match({ 'return': 0 }, { 'return': String }, '["return"]'); + match({ return: 0 }, { return: String }, '["return"]'); }); -Tinytest.add('check - Match error message', test => { +Tinytest.add("check - Match error message", (test) => { const match = (value, pattern, expectedMessage) => { try { check(value, pattern); } catch (err) { if (err.message !== `Match error: ${expectedMessage}`) { test.fail({ - type: 'match-error-message', + type: "match-error-message", message: "The message of Match.Error doesn't match.", pattern: JSON.stringify(pattern), value: JSON.stringify(value), @@ -740,21 +869,21 @@ Tinytest.add('check - Match error message', test => { } }; - match(2, String, 'Expected string, got number'); - match({ key: 0 }, Number, 'Expected number, got object'); - match(null, Boolean, 'Expected boolean, got null'); - match('string', undefined, 'Expected undefined, got string'); - match(true, null, 'Expected null, got true'); + match(2, String, "Expected string, got number"); + match({ key: 0 }, Number, "Expected number, got object"); + match(null, Boolean, "Expected boolean, got null"); + match("string", undefined, "Expected undefined, got string"); + match(true, null, "Expected null, got true"); match({}, Match.ObjectIncluding({ bar: String }), "Missing key 'bar'"); - match(null, Object, 'Expected object, got null'); - match(null, Function, 'Expected function, got null'); - match('bar', 'foo', 'Expected foo, got "bar"'); - match(3.14, Match.Integer, 'Expected Integer, got 3.14'); - match(false, [Boolean], 'Expected array, got false'); - match([null, null], [String], 'Expected string, got null in field [0]'); - match(2, { key: 2 }, 'Expected object, got number'); - match(null, { key: 2 }, 'Expected object, got null'); - match(new Date, { key: 2 }, 'Expected plain object'); + match(null, Object, "Expected object, got null"); + match(null, Function, "Expected function, got null"); + match("bar", "foo", 'Expected foo, got "bar"'); + match(3.14, Match.Integer, "Expected Integer, got 3.14"); + match(false, [Boolean], "Expected array, got false"); + match([null, null], [String], "Expected string, got null in field [0]"); + match(2, { key: 2 }, "Expected object, got number"); + match(null, { key: 2 }, "Expected object, got null"); + match(new Date(), { key: 2 }, "Expected plain object"); const TestInstanceChild = function () {}; const TestInstanceParent = function (child) { @@ -762,23 +891,24 @@ Tinytest.add('check - Match error message', test => { this.child = child; }; - const testInstanceChild = new TestInstanceChild() + const testInstanceChild = new TestInstanceChild(); new TestInstanceParent(testInstanceChild); - match(testInstanceChild, TestInstanceParent, `Expected ${(TestInstanceParent.name || 'particular constructor')}`); + match( + testInstanceChild, + TestInstanceParent, + `Expected ${TestInstanceParent.name || "particular constructor"}`, + ); const circleFoo = {}; const circleBar = {}; circleFoo.bar = circleBar; circleBar.foo = circleFoo; - match(circleFoo, null, 'Expected null, got object'); - + match(circleFoo, null, "Expected null, got object"); }); Tinytest.add( - 'check - Match methods that return class instances can be called as ' + - 'constructors', - test => { - + "check - Match methods that return class instances can be called as " + "constructors", + (test) => { // Existing code sometimes uses these properties as constructors, so we can't // switch them to arrow functions or method shorthand. test.equal(new Match.Optional(), Match.Optional()); @@ -787,5 +917,5 @@ Tinytest.add( test.equal(new Match.Where(), Match.Where()); test.equal(new Match.ObjectIncluding(), Match.ObjectIncluding()); test.equal(new Match.ObjectWithValues(), Match.ObjectWithValues()); - } -); \ No newline at end of file + }, +); diff --git a/packages/check/package.js b/packages/check/package.js index a095044bbb..972bf211f0 100644 --- a/packages/check/package.js +++ b/packages/check/package.js @@ -1,22 +1,22 @@ Package.describe({ - summary: 'Check whether a value matches a pattern', - version: '1.5.0', + summary: "Check whether a value matches a pattern", + version: "1.5.0", }); -Package.onUse(api => { - api.use('ecmascript'); - api.use('ejson'); +Package.onUse((api) => { + api.use("ecmascript"); + api.use("ejson"); - api.addAssets('check.d.ts', 'server'); + api.addAssets("check.d.ts", "server"); - api.mainModule('match.js'); + api.mainModule("match.js"); - api.export('check'); - api.export('Match'); + api.export("check"); + api.export("Match"); }); -Package.onTest(api => { - api.use(['check', 'tinytest', 'ejson', 'ecmascript'], ['client', 'server']); +Package.onTest((api) => { + api.use(["check", "tinytest", "ejson", "ecmascript"], ["client", "server"]); - api.addFiles('match_test.js', ['client', 'server']); + api.addFiles("match_test.js", ["client", "server"]); }); diff --git a/packages/diff-sequence/diff.js b/packages/diff-sequence/diff.js index 0893a923c0..27d26e37ef 100644 --- a/packages/diff-sequence/diff.js +++ b/packages/diff-sequence/diff.js @@ -15,18 +15,12 @@ function isObjEmpty(obj) { // old_results and new_results: collections of documents. // if ordered, they are arrays. // if unordered, they are IdMaps -DiffSequence.diffQueryChanges = function (ordered, oldResults, newResults, - observer, options) { - if (ordered) - DiffSequence.diffQueryOrderedChanges( - oldResults, newResults, observer, options); - else - DiffSequence.diffQueryUnorderedChanges( - oldResults, newResults, observer, options); +DiffSequence.diffQueryChanges = function (ordered, oldResults, newResults, observer, options) { + if (ordered) DiffSequence.diffQueryOrderedChanges(oldResults, newResults, observer, options); + else DiffSequence.diffQueryUnorderedChanges(oldResults, newResults, observer, options); }; -DiffSequence.diffQueryUnorderedChanges = function (oldResults, newResults, - observer, options) { +DiffSequence.diffQueryUnorderedChanges = function (oldResults, newResults, observer, options) { options = options || {}; const projectionFn = options.projectionFn || EJSON.clone; @@ -40,9 +34,8 @@ DiffSequence.diffQueryUnorderedChanges = function (oldResults, newResults, if (observer.changed && !EJSON.equals(oldDoc, newDoc)) { const projectedNew = projectionFn(newDoc); const projectedOld = projectionFn(oldDoc); - const changedFields = - DiffSequence.makeChangedFields(projectedNew, projectedOld); - if (! isObjEmpty(changedFields)) { + const changedFields = DiffSequence.makeChangedFields(projectedNew, projectedOld); + if (!isObjEmpty(changedFields)) { observer.changed(id, changedFields); } } @@ -55,28 +48,24 @@ DiffSequence.diffQueryUnorderedChanges = function (oldResults, newResults, if (observer.removed) { oldResults.forEach(function (oldDoc, id) { - if (!newResults.has(id)) - observer.removed(id); + if (!newResults.has(id)) observer.removed(id); }); } }; -DiffSequence.diffQueryOrderedChanges = function (old_results, new_results, - observer, options) { +DiffSequence.diffQueryOrderedChanges = function (old_results, new_results, observer, options) { options = options || {}; const projectionFn = options.projectionFn || EJSON.clone; const new_presence_of_id = {}; new_results.forEach(function (doc) { - if (new_presence_of_id[doc._id]) - Meteor._debug("Duplicate _id in new_results"); + if (new_presence_of_id[doc._id]) Meteor._debug("Duplicate _id in new_results"); new_presence_of_id[doc._id] = true; }); const old_index_of_id = {}; old_results.forEach(function (doc, i) { - if (doc._id in old_index_of_id) - Meteor._debug("Duplicate _id in old_results"); + if (doc._id in old_index_of_id) Meteor._debug("Duplicate _id in old_results"); old_index_of_id[doc._id] = i; }); @@ -106,7 +95,6 @@ DiffSequence.diffQueryOrderedChanges = function (old_results, new_results, // Asymptotically: O(N k) where k is number of ops, or potentially // O(N log N) if inner loop of LCS were made to be binary search. - //////// LCS (longest common sequence, with respect to _id) // (see Wikipedia article on Longest Increasing Subsequence, // where the LIS is taken of the sequence of old indices of the @@ -126,12 +114,12 @@ DiffSequence.diffQueryOrderedChanges = function (old_results, new_results, // ptr[n] is -1. const ptrs = Array.from({ length: N }); // virtual sequence of old indices of new results - const old_idx_seq = function(i_new) { + const old_idx_seq = function (i_new) { return old_index_of_id[new_results[i_new]._id]; }; // for each item in new_results, use it to extend a common subsequence // of length j <= max_seq_len - for(let i=0; i 0) { - if (old_idx_seq(seq_ends[j-1]) < old_idx_seq(i)) - break; + if (old_idx_seq(seq_ends[j - 1]) < old_idx_seq(i)) break; j--; } - ptrs[i] = (j === 0 ? -1 : seq_ends[j-1]); + ptrs[i] = j === 0 ? -1 : seq_ends[j - 1]; seq_ends[j] = i; - if (j+1 > max_seq_len) - max_seq_len = j+1; + if (j + 1 > max_seq_len) max_seq_len = j + 1; } } // pull out the LCS/LIS into unmoved - let idx = (max_seq_len === 0 ? -1 : seq_ends[max_seq_len-1]); + let idx = max_seq_len === 0 ? -1 : seq_ends[max_seq_len - 1]; while (idx >= 0) { unmoved.push(idx); idx = ptrs[idx]; @@ -206,13 +192,10 @@ DiffSequence.diffQueryOrderedChanges = function (old_results, new_results, if (observer.changed) observer.changed(newDoc._id, fields); } } - startOfGroup = endOfGroup+1; + startOfGroup = endOfGroup + 1; }); - - }; - // General helper for diff-ing two objects. // callbacks is an object like so: // { leftOnly: function (key, leftValue) {...}, @@ -220,7 +203,7 @@ DiffSequence.diffQueryOrderedChanges = function (old_results, new_results, // both: function (key, leftValue, rightValue) {...}, // } DiffSequence.diffObjects = function (left, right, callbacks) { - Object.keys(left).forEach(key => { + Object.keys(left).forEach((key) => { const leftValue = left[key]; if (hasOwn.call(right, key)) { if (callbacks.both) callbacks.both(key, leftValue, right[key]); @@ -230,9 +213,9 @@ DiffSequence.diffObjects = function (left, right, callbacks) { }); if (callbacks.rightOnly) { - Object.keys(right).forEach(key => { + Object.keys(right).forEach((key) => { const rightValue = right[key]; - if (! hasOwn.call(left, key)) { + if (!hasOwn.call(left, key)) { callbacks.rightOnly(key, rightValue); } }); @@ -241,7 +224,7 @@ DiffSequence.diffObjects = function (left, right, callbacks) { DiffSequence.diffMaps = function (left, right, callbacks) { left.forEach(function (leftValue, key) { - if (right.has(key)){ + if (right.has(key)) { if (callbacks.both) callbacks.both(key, leftValue, right.get(key)); } else { if (callbacks.leftOnly) callbacks.leftOnly(key, leftValue); @@ -250,14 +233,13 @@ DiffSequence.diffMaps = function (left, right, callbacks) { if (callbacks.rightOnly) { right.forEach(function (rightValue, key) { - if (!left.has(key)){ + if (!left.has(key)) { callbacks.rightOnly(key, rightValue); } }); } }; - DiffSequence.makeChangedFields = function (newDoc, oldDoc) { const fields = {}; DiffSequence.diffObjects(oldDoc, newDoc, { @@ -268,15 +250,14 @@ DiffSequence.makeChangedFields = function (newDoc, oldDoc) { fields[key] = value; }, both: function (key, leftValue, rightValue) { - if (!EJSON.equals(leftValue, rightValue)) - fields[key] = rightValue; - } + if (!EJSON.equals(leftValue, rightValue)) fields[key] = rightValue; + }, }); return fields; }; DiffSequence.applyChanges = function (doc, changeFields) { - Object.keys(changeFields).forEach(key => { + Object.keys(changeFields).forEach((key) => { const value = changeFields[key]; if (typeof value === "undefined") { delete doc[key]; @@ -285,4 +266,3 @@ DiffSequence.applyChanges = function (doc, changeFields) { } }); }; - diff --git a/packages/diff-sequence/package.js b/packages/diff-sequence/package.js index 92882442dc..df02c23ff2 100644 --- a/packages/diff-sequence/package.js +++ b/packages/diff-sequence/package.js @@ -1,24 +1,19 @@ Package.describe({ summary: "An implementation of a diff algorithm on arrays and objects.", - version: '1.1.3', - documentation: null + version: "1.1.3", + documentation: null, }); Package.onUse(function (api) { - api.use('ecmascript'); - api.use('ejson'); - api.mainModule('diff.js'); - api.export('DiffSequence'); + api.use("ecmascript"); + api.use("ejson"); + api.mainModule("diff.js"); + api.export("DiffSequence"); }); Package.onTest(function (api) { - api.use([ - 'tinytest', - 'ejson' - ]); + api.use(["tinytest", "ejson"]); - api.use('diff-sequence'); - api.addFiles([ - 'tests.js' - ]); + api.use("diff-sequence"); + api.addFiles(["tests.js"]); }); diff --git a/packages/diff-sequence/tests.js b/packages/diff-sequence/tests.js index dc8e71a724..5c605abd6c 100644 --- a/packages/diff-sequence/tests.js +++ b/packages/diff-sequence/tests.js @@ -1,21 +1,22 @@ Tinytest.add("diff-sequence - diff changes ordering", function (test) { const makeDocs = function (ids) { - return ids.map(function (id) { return {_id: id};}); + return ids.map(function (id) { + return { _id: id }; + }); }; const testMutation = function (a, b) { const aa = makeDocs(a); const bb = makeDocs(b); const aaCopy = EJSON.clone(aa); DiffSequence.diffQueryOrderedChanges(aa, bb, { - addedBefore: function (id, doc, before) { if (before === null) { - aaCopy.push( Object.assign({_id: id}, doc)); + aaCopy.push(Object.assign({ _id: id }, doc)); return; } for (let i = 0; i < aaCopy.length; i++) { if (aaCopy[i]._id === before) { - aaCopy.splice(i, 0, Object.assign({_id: id}, doc)); + aaCopy.splice(i, 0, Object.assign({ _id: id }, doc)); return; } } @@ -29,12 +30,12 @@ Tinytest.add("diff-sequence - diff changes ordering", function (test) { } } if (before === null) { - aaCopy.push( Object.assign({_id: id}, found)); + aaCopy.push(Object.assign({ _id: id }, found)); return; } for (let i = 0; i < aaCopy.length; i++) { if (aaCopy[i]._id === before) { - aaCopy.splice(i, 0, Object.assign({_id: id}, found)); + aaCopy.splice(i, 0, Object.assign({ _id: id }, found)); return; } } @@ -45,7 +46,7 @@ Tinytest.add("diff-sequence - diff changes ordering", function (test) { aaCopy.splice(i, 1); } } - } + }, }); test.equal(aaCopy, bb); }; @@ -57,55 +58,54 @@ Tinytest.add("diff-sequence - diff changes ordering", function (test) { testBothWays(["a", "b", "c"], ["c", "b", "a"]); testBothWays(["a", "b", "c"], []); - testBothWays(["a", "b", "c"], ["e","f"]); + testBothWays(["a", "b", "c"], ["e", "f"]); testBothWays(["a", "b", "c", "d"], ["c", "b", "a"]); - testBothWays(['A','B','C','D','E','F','G','H','I'], - ['A','B','F','G','C','D','I','L','M','N','H']); - testBothWays(['A','B','C','D','E','F','G','H','I'],['A','B','C','D','F','G','H','E','I']); + testBothWays( + ["A", "B", "C", "D", "E", "F", "G", "H", "I"], + ["A", "B", "F", "G", "C", "D", "I", "L", "M", "N", "H"], + ); + testBothWays( + ["A", "B", "C", "D", "E", "F", "G", "H", "I"], + ["A", "B", "C", "D", "F", "G", "H", "E", "I"], + ); }); Tinytest.add("diff-sequence - diff", function (test) { - // test correctness - const diffTest = function(origLen, newOldIdx) { + const diffTest = function (origLen, newOldIdx) { const oldResults = Array.from({ length: origLen }); - for (let i = 1; i <= origLen; i++) - oldResults[i-1] = {_id: i}; + for (let i = 1; i <= origLen; i++) oldResults[i - 1] = { _id: i }; - const newResults = newOldIdx.map(function(n) { - const doc = {_id: Math.abs(n)}; - if (n < 0) - doc.changed = true; + const newResults = newOldIdx.map(function (n) { + const doc = { _id: Math.abs(n) }; + if (n < 0) doc.changed = true; return doc; }); const find = function (arr, id) { for (let i = 0; i < arr.length; i++) { - if (EJSON.equals(arr[i]._id, id)) - return i; + if (EJSON.equals(arr[i]._id, id)) return i; } return -1; }; const results = [...oldResults]; const observer = { - addedBefore: function(id, fields, before) { + addedBefore: function (id, fields, before) { let before_idx; - if (before === null) - before_idx = results.length; - else - before_idx = find (results, before); - const doc = Object.assign({_id: id}, fields); + if (before === null) before_idx = results.length; + else before_idx = find(results, before); + const doc = Object.assign({ _id: id }, fields); test.isFalse(before_idx < 0 || before_idx > results.length); results.splice(before_idx, 0, doc); }, - removed: function(id) { - const at_idx = find (results, id); + removed: function (id) { + const at_idx = find(results, id); test.isFalse(at_idx < 0 || at_idx >= results.length); results.splice(at_idx, 1); }, - changed: function(id, fields) { - const at_idx = find (results, id); + changed: function (id, fields) { + const at_idx = find(results, id); const oldDoc = results[at_idx]; const doc = EJSON.clone(oldDoc); DiffSequence.applyChanges(doc, fields); @@ -113,19 +113,16 @@ Tinytest.add("diff-sequence - diff", function (test) { test.equal(doc._id, oldDoc._id); results[at_idx] = doc; }, - movedBefore: function(id, before) { + movedBefore: function (id, before) { const old_idx = find(results, id); let new_idx; - if (before === null) - new_idx = results.length; - else - new_idx = find (results, before); - if (new_idx > old_idx) - new_idx--; + if (before === null) new_idx = results.length; + else new_idx = find(results, before); + if (new_idx > old_idx) new_idx--; test.isFalse(old_idx < 0 || old_idx >= results.length); test.isFalse(new_idx < 0 || new_idx >= results.length); results.splice(new_idx, 0, results.splice(old_idx, 1)[0]); - } + }, }; DiffSequence.diffQueryOrderedChanges(oldResults, newResults, observer); diff --git a/packages/ejson/custom_models_for_tests.js b/packages/ejson/custom_models_for_tests.js index c60a6b3caf..7faa22ee65 100644 --- a/packages/ejson/custom_models_for_tests.js +++ b/packages/ejson/custom_models_for_tests.js @@ -1,4 +1,4 @@ -import { EJSON } from './ejson'; +import { EJSON } from "./ejson"; class Address { constructor(city, state) { @@ -7,7 +7,7 @@ class Address { } typeName() { - return 'Address'; + return "Address"; } toJSONValue() { @@ -18,7 +18,7 @@ class Address { } } -EJSON.addType('Address', value => new Address(value.city, value.state)); +EJSON.addType("Address", (value) => new Address(value.city, value.state)); class Person { constructor(name, dob, address) { @@ -28,7 +28,7 @@ class Person { } typeName() { - return 'Person'; + return "Person"; } toJSONValue() { @@ -41,12 +41,9 @@ class Person { } EJSON.addType( - 'Person', - value => new Person( - value.name, - EJSON.fromJSONValue(value.dob), - EJSON.fromJSONValue(value.address) - ) + "Person", + (value) => + new Person(value.name, EJSON.fromJSONValue(value.dob), EJSON.fromJSONValue(value.address)), ); class Holder { @@ -55,7 +52,7 @@ class Holder { } typeName() { - return 'Holder'; + return "Holder"; } toJSONValue() { @@ -63,7 +60,7 @@ class Holder { } } -EJSON.addType('Holder', value => new Holder(value)); +EJSON.addType("Holder", (value) => new Holder(value)); const EJSONTest = { Address, diff --git a/packages/ejson/ejson.d.ts b/packages/ejson/ejson.d.ts index 53533f16d0..f2601743d6 100644 --- a/packages/ejson/ejson.d.ts +++ b/packages/ejson/ejson.d.ts @@ -39,17 +39,14 @@ export interface JSONable { export interface EJSON extends EJSONable {} export namespace EJSON { - function addType( - name: string, - factory: (val: JSONable) => EJSONableCustomType - ): void; + function addType(name: string, factory: (val: JSONable) => EJSONableCustomType): void; function clone(val: T): T; function equals( a: EJSON, b: EJSON, - options?: { keyOrderSensitive?: boolean | undefined } + options?: { keyOrderSensitive?: boolean | undefined }, ): boolean; function fromJSONValue(val: JSONable): any; @@ -64,7 +61,7 @@ export namespace EJSON { options?: { indent?: boolean | number | string | undefined; canonical?: boolean | undefined; - } + }, ): string; function toJSONValue(val: EJSON): JSONable; diff --git a/packages/ejson/ejson.js b/packages/ejson/ejson.js index 76ef364651..33ae7294eb 100644 --- a/packages/ejson/ejson.js +++ b/packages/ejson/ejson.js @@ -8,8 +8,8 @@ import { isArguments, isInfOrNaN, handleError, -} from './utils'; -import canonicalStringify from './stringify'; +} from "./utils"; +import canonicalStringify from "./stringify"; /** * @namespace @@ -96,25 +96,25 @@ EJSON.addType = (name, factory) => { }; const builtinConverters = [ - { // Date + { + // Date matchJSONValue(obj) { - return hasOwn(obj, '$date') && lengthOf(obj) === 1; + return hasOwn(obj, "$date") && lengthOf(obj) === 1; }, matchObject(obj) { return obj instanceof Date; }, toJSONValue(obj) { - return {$date: obj.getTime()}; + return { $date: obj.getTime() }; }, fromJSONValue(obj) { return new Date(obj.$date); }, }, - { // RegExp + { + // RegExp matchJSONValue(obj) { - return hasOwn(obj, '$regexp') - && hasOwn(obj, '$flags') - && lengthOf(obj) === 2; + return hasOwn(obj, "$regexp") && hasOwn(obj, "$flags") && lengthOf(obj) === 2; }, matchObject(obj) { return obj instanceof RegExp; @@ -122,7 +122,7 @@ const builtinConverters = [ toJSONValue(regexp) { return { $regexp: regexp.source, - $flags: regexp.flags + $flags: regexp.flags, }; }, fromJSONValue(obj) { @@ -132,15 +132,16 @@ const builtinConverters = [ obj.$flags // Cut off flags at 50 chars to avoid abusing RegExp for DOS. .slice(0, 50) - .replace(/[^gimuy]/g,'') - .replace(/(.)(?=.*\1)/g, '') + .replace(/[^gimuy]/g, "") + .replace(/(.)(?=.*\1)/g, ""), ); }, }, - { // NaN, Inf, -Inf. (These are the only objects with typeof !== 'object' + { + // NaN, Inf, -Inf. (These are the only objects with typeof !== 'object' // which we match.) matchJSONValue(obj) { - return hasOwn(obj, '$InfNaN') && lengthOf(obj) === 1; + return hasOwn(obj, "$InfNaN") && lengthOf(obj) === 1; }, matchObject: isInfOrNaN, toJSONValue(obj) { @@ -152,68 +153,71 @@ const builtinConverters = [ } else { sign = -1; } - return {$InfNaN: sign}; + return { $InfNaN: sign }; }, fromJSONValue(obj) { return obj.$InfNaN / 0; }, }, - { // Binary + { + // Binary matchJSONValue(obj) { - return hasOwn(obj, '$binary') && lengthOf(obj) === 1; + return hasOwn(obj, "$binary") && lengthOf(obj) === 1; }, matchObject(obj) { - return typeof Uint8Array !== 'undefined' && obj instanceof Uint8Array - || (obj && hasOwn(obj, '$Uint8ArrayPolyfill')); + return ( + (typeof Uint8Array !== "undefined" && obj instanceof Uint8Array) || + (obj && hasOwn(obj, "$Uint8ArrayPolyfill")) + ); }, toJSONValue(obj) { - return {$binary: Base64.encode(obj)}; + return { $binary: Base64.encode(obj) }; }, fromJSONValue(obj) { return Base64.decode(obj.$binary); }, }, - { // Escaping one level + { + // Escaping one level matchJSONValue(obj) { - return hasOwn(obj, '$escape') && lengthOf(obj) === 1; + return hasOwn(obj, "$escape") && lengthOf(obj) === 1; }, matchObject(obj) { let match = false; if (obj) { const keyCount = lengthOf(obj); if (keyCount === 1 || keyCount === 2) { - match = - builtinConverters.some(converter => converter.matchJSONValue(obj)); + match = builtinConverters.some((converter) => converter.matchJSONValue(obj)); } } return match; }, toJSONValue(obj) { const newObj = {}; - keysOf(obj).forEach(key => { + keysOf(obj).forEach((key) => { newObj[key] = EJSON.toJSONValue(obj[key]); }); - return {$escape: newObj}; + return { $escape: newObj }; }, fromJSONValue(obj) { const newObj = {}; - keysOf(obj.$escape).forEach(key => { + keysOf(obj.$escape).forEach((key) => { newObj[key] = EJSON.fromJSONValue(obj.$escape[key]); }); return newObj; }, }, - { // Custom + { + // Custom matchJSONValue(obj) { - return hasOwn(obj, '$type') - && hasOwn(obj, '$value') && lengthOf(obj) === 2; + return hasOwn(obj, "$type") && hasOwn(obj, "$value") && lengthOf(obj) === 2; }, matchObject(obj) { return EJSON._isCustomType(obj); }, toJSONValue(obj) { const jsonValue = Meteor._noYieldsAllowed(() => obj.toJSONValue()); - return {$type: obj.typeName(), $value: jsonValue}; + return { $type: obj.typeName(), $value: jsonValue }; }, fromJSONValue(obj) { const typeName = obj.$type; @@ -226,20 +230,17 @@ const builtinConverters = [ }, ]; -EJSON._isCustomType = (obj) => ( - obj && - isFunction(obj.toJSONValue) && - isFunction(obj.typeName) && - customTypes.has(obj.typeName()) -); +EJSON._isCustomType = (obj) => + obj && isFunction(obj.toJSONValue) && isFunction(obj.typeName) && customTypes.has(obj.typeName()); -EJSON._getTypes = (isOriginal = false) => (isOriginal ? customTypes : convertMapToObject(customTypes)); +EJSON._getTypes = (isOriginal = false) => + isOriginal ? customTypes : convertMapToObject(customTypes); EJSON._getConverters = () => builtinConverters; // Either return the JSON-compatible version of the argument, or undefined (if // the item isn't itself replaceable, but maybe some fields in it are) -const toJSONValueHelper = item => { +const toJSONValueHelper = (item) => { for (let i = 0; i < builtinConverters.length; i++) { const converter = builtinConverters[i]; if (converter.matchObject(item)) { @@ -250,7 +251,7 @@ const toJSONValueHelper = item => { }; // for both arrays and objects, in-place modification. -const adjustTypesToJSONValue = obj => { +const adjustTypesToJSONValue = (obj) => { // Is it an atom that we need to adjust? if (obj === null) { return null; @@ -267,10 +268,9 @@ const adjustTypesToJSONValue = obj => { } // Iterate over array or object structure. - keysOf(obj).forEach(key => { + keysOf(obj).forEach((key) => { const value = obj[key]; - if (!isObject(value) && value !== undefined && - !isInfOrNaN(value)) { + if (!isObject(value) && value !== undefined && !isInfOrNaN(value)) { return; // continue } @@ -294,7 +294,7 @@ EJSON._adjustTypesToJSONValue = adjustTypesToJSONValue; * @locus Anywhere * @param {EJSON} val A value to serialize to plain JSON. */ -EJSON.toJSONValue = item => { +EJSON.toJSONValue = (item) => { const changed = toJSONValueHelper(item); if (changed !== undefined) { return changed; @@ -312,11 +312,10 @@ EJSON.toJSONValue = item => { // rep of itself (the Object version) or the argument itself. // DOES NOT RECURSE. For actually getting the fully-changed value, use // EJSON.fromJSONValue -const fromJSONValueHelper = value => { +const fromJSONValueHelper = (value) => { if (isObject(value) && value !== null) { const keys = keysOf(value); - if (keys.length <= 2 - && keys.every(k => typeof k === 'string' && k.substr(0, 1) === '$')) { + if (keys.length <= 2 && keys.every((k) => typeof k === "string" && k.substr(0, 1) === "$")) { for (let i = 0; i < builtinConverters.length; i++) { const converter = builtinConverters[i]; if (converter.matchJSONValue(value)) { @@ -331,7 +330,7 @@ const fromJSONValueHelper = value => { // for both arrays and objects. Tries its best to just // use the object you hand it, but may return something // different if the object you hand it itself needs changing. -const adjustTypesFromJSONValue = obj => { +const adjustTypesFromJSONValue = (obj) => { if (obj === null) { return null; } @@ -346,7 +345,7 @@ const adjustTypesFromJSONValue = obj => { return obj; } - keysOf(obj).forEach(key => { + keysOf(obj).forEach((key) => { const value = obj[key]; if (isObject(value)) { const changed = fromJSONValueHelper(value); @@ -369,7 +368,7 @@ EJSON._adjustTypesFromJSONValue = adjustTypesFromJSONValue; * @locus Anywhere * @param {JSONCompatible} val A value to deserialize into EJSON. */ -EJSON.fromJSONValue = item => { +EJSON.fromJSONValue = (item) => { let changed = fromJSONValueHelper(item); if (changed === item && isObject(item)) { changed = EJSON.clone(item); @@ -409,9 +408,9 @@ EJSON.stringify = handleError((item, options) => { * @locus Anywhere * @param {String} str A string to parse into an EJSON value. */ -EJSON.parse = item => { - if (typeof item !== 'string') { - throw new Error('EJSON.parse argument should be a string'); +EJSON.parse = (item) => { + if (typeof item !== "string") { + throw new Error("EJSON.parse argument should be a string"); } return EJSON.fromJSONValue(JSON.parse(item)); }; @@ -422,9 +421,11 @@ EJSON.parse = item => { * @param {Object} x The variable to check. * @locus Anywhere */ -EJSON.isBinary = obj => { - return !!((typeof Uint8Array !== 'undefined' && obj instanceof Uint8Array) || - (obj && obj.$Uint8ArrayPolyfill)); +EJSON.isBinary = (obj) => { + return !!( + (typeof Uint8Array !== "undefined" && obj instanceof Uint8Array) || + (obj && obj.$Uint8ArrayPolyfill) + ); }; /** @@ -509,8 +510,10 @@ EJSON.equals = (a, b, options) => { // fallback for custom types that don't implement their own equals switch (EJSON._isCustomType(a) + EJSON._isCustomType(b)) { - case 1: return false; - case 2: return EJSON.equals(EJSON.toJSONValue(a), EJSON.toJSONValue(b)); + case 1: + return false; + case 2: + return EJSON.equals(EJSON.toJSONValue(a), EJSON.toJSONValue(b)); default: // Do nothing } @@ -520,7 +523,7 @@ EJSON.equals = (a, b, options) => { const bKeys = keysOf(b); if (keyOrderSensitive) { i = 0; - ret = aKeys.every(key => { + ret = aKeys.every((key) => { if (i >= bKeys.length) { return false; } @@ -535,7 +538,7 @@ EJSON.equals = (a, b, options) => { }); } else { i = 0; - ret = aKeys.every(key => { + ret = aKeys.every((key) => { if (!hasOwn(b, key)) { return false; } @@ -554,7 +557,7 @@ EJSON.equals = (a, b, options) => { * @locus Anywhere * @param {EJSON} val A value to copy. */ -EJSON.clone = v => { +EJSON.clone = (v) => { let ret; if (!isObject(v)) { return v; diff --git a/packages/ejson/ejson_tests.js b/packages/ejson/ejson_tests.js index cdb08168f6..3490f3e73f 100644 --- a/packages/ejson/ejson_tests.js +++ b/packages/ejson/ejson_tests.js @@ -1,69 +1,86 @@ -import { EJSON } from './ejson'; -import EJSONTest from './custom_models_for_tests'; +import { EJSON } from "./ejson"; +import EJSONTest from "./custom_models_for_tests"; -Tinytest.add('ejson - keyOrderSensitive', test => { - test.isTrue(EJSON.equals({ - a: {b: 1, c: 2}, - d: {e: 3, f: 4}, - }, { - d: {f: 4, e: 3}, - a: {c: 2, b: 1}, - })); +Tinytest.add("ejson - keyOrderSensitive", (test) => { + test.isTrue( + EJSON.equals( + { + a: { b: 1, c: 2 }, + d: { e: 3, f: 4 }, + }, + { + d: { f: 4, e: 3 }, + a: { c: 2, b: 1 }, + }, + ), + ); - test.isFalse(EJSON.equals({ - a: {b: 1, c: 2}, - d: {e: 3, f: 4}, - }, { - d: {f: 4, e: 3}, - a: {c: 2, b: 1}, - }, {keyOrderSensitive: true})); + test.isFalse( + EJSON.equals( + { + a: { b: 1, c: 2 }, + d: { e: 3, f: 4 }, + }, + { + d: { f: 4, e: 3 }, + a: { c: 2, b: 1 }, + }, + { keyOrderSensitive: true }, + ), + ); - test.isFalse(EJSON.equals({ - a: {b: 1, c: 2}, - d: {e: 3, f: 4}, - }, { - a: {c: 2, b: 1}, - d: {f: 4, e: 3}, - }, {keyOrderSensitive: true})); - test.isFalse(EJSON.equals({a: {}}, {a: {b: 2}}, {keyOrderSensitive: true})); - test.isFalse(EJSON.equals({a: {b: 2}}, {a: {}}, {keyOrderSensitive: true})); + test.isFalse( + EJSON.equals( + { + a: { b: 1, c: 2 }, + d: { e: 3, f: 4 }, + }, + { + a: { c: 2, b: 1 }, + d: { f: 4, e: 3 }, + }, + { keyOrderSensitive: true }, + ), + ); + test.isFalse(EJSON.equals({ a: {} }, { a: { b: 2 } }, { keyOrderSensitive: true })); + test.isFalse(EJSON.equals({ a: { b: 2 } }, { a: {} }, { keyOrderSensitive: true })); }); -Tinytest.add('ejson - nesting and literal', test => { +Tinytest.add("ejson - nesting and literal", (test) => { const d = new Date(); - const obj = {$date: d}; + const obj = { $date: d }; const eObj = EJSON.toJSONValue(obj); const roundTrip = EJSON.fromJSONValue(eObj); test.equal(obj, roundTrip); }); -Tinytest.add('ejson - some equality tests', test => { - test.isTrue(EJSON.equals({a: 1, b: 2, c: 3}, {a: 1, c: 3, b: 2})); - test.isFalse(EJSON.equals({a: 1, b: 2}, {a: 1, c: 3, b: 2})); - test.isFalse(EJSON.equals({a: 1, b: 2, c: 3}, {a: 1, b: 2})); - test.isFalse(EJSON.equals({a: 1, b: 2, c: 3}, {a: 1, c: 3, b: 4})); - test.isFalse(EJSON.equals({a: {}}, {a: {b: 2}})); - test.isFalse(EJSON.equals({a: {b: 2}}, {a: {}})); +Tinytest.add("ejson - some equality tests", (test) => { + test.isTrue(EJSON.equals({ a: 1, b: 2, c: 3 }, { a: 1, c: 3, b: 2 })); + test.isFalse(EJSON.equals({ a: 1, b: 2 }, { a: 1, c: 3, b: 2 })); + test.isFalse(EJSON.equals({ a: 1, b: 2, c: 3 }, { a: 1, b: 2 })); + test.isFalse(EJSON.equals({ a: 1, b: 2, c: 3 }, { a: 1, c: 3, b: 4 })); + test.isFalse(EJSON.equals({ a: {} }, { a: { b: 2 } })); + test.isFalse(EJSON.equals({ a: { b: 2 } }, { a: {} })); // XXX: Object and Array were previously mistaken, which is why // we add some extra tests for them here test.isTrue(EJSON.equals([1, 2, 3, 4, 5], [1, 2, 3, 4, 5])); test.isFalse(EJSON.equals([1, 2, 3, 4, 5], [1, 2, 3, 4])); - test.isFalse(EJSON.equals([1,2,3,4], {0: 1, 1: 2, 2: 3, 3: 4})); - test.isFalse(EJSON.equals({0: 1, 1: 2, 2: 3, 3: 4}, [1,2,3,4])); + test.isFalse(EJSON.equals([1, 2, 3, 4], { 0: 1, 1: 2, 2: 3, 3: 4 })); + test.isFalse(EJSON.equals({ 0: 1, 1: 2, 2: 3, 3: 4 }, [1, 2, 3, 4])); test.isFalse(EJSON.equals({}, [])); test.isFalse(EJSON.equals([], {})); }); -Tinytest.add('ejson - equality and falsiness', test => { +Tinytest.add("ejson - equality and falsiness", (test) => { test.isTrue(EJSON.equals(null, null)); test.isTrue(EJSON.equals(undefined, undefined)); - test.isFalse(EJSON.equals({foo: 'foo'}, null)); - test.isFalse(EJSON.equals(null, {foo: 'foo'})); - test.isFalse(EJSON.equals(undefined, {foo: 'foo'})); - test.isFalse(EJSON.equals({foo: 'foo'}, undefined)); + test.isFalse(EJSON.equals({ foo: "foo" }, null)); + test.isFalse(EJSON.equals(null, { foo: "foo" })); + test.isFalse(EJSON.equals(undefined, { foo: "foo" })); + test.isFalse(EJSON.equals({ foo: "foo" }, undefined)); }); -Tinytest.add('ejson - NaN and Inf', test => { +Tinytest.add("ejson - NaN and Inf", (test) => { test.equal(EJSON.parse('{"$InfNaN": 1}'), Infinity); test.equal(EJSON.parse('{"$InfNaN": -1}'), -Infinity); test.isTrue(Number.isNaN(EJSON.parse('{"$InfNaN": 0}'))); @@ -78,17 +95,11 @@ Tinytest.add('ejson - NaN and Inf', test => { test.isFalse(EJSON.equals(Infinity, 0)); test.isFalse(EJSON.equals(NaN, 0)); - test.isTrue(EJSON.equals( - EJSON.parse('{"a": {"$InfNaN": 1}}'), - {a: Infinity} - )); - test.isTrue(EJSON.equals( - EJSON.parse('{"a": {"$InfNaN": 0}}'), - {a: NaN} - )); + test.isTrue(EJSON.equals(EJSON.parse('{"a": {"$InfNaN": 1}}'), { a: Infinity })); + test.isTrue(EJSON.equals(EJSON.parse('{"a": {"$InfNaN": 0}}'), { a: NaN })); }); -Tinytest.add('ejson - clone', test => { +Tinytest.add("ejson - clone", (test) => { const cloneTest = (x, identical) => { const y = EJSON.clone(x); test.isTrue(EJSON.equals(x, y)); @@ -97,114 +108,92 @@ Tinytest.add('ejson - clone', test => { cloneTest(null, true); cloneTest(undefined, true); cloneTest(42, true); - cloneTest('asdf', true); + cloneTest("asdf", true); cloneTest([1, 2, 3]); - cloneTest([1, 'fasdf', {foo: 42}]); - cloneTest({x: 42, y: 'asdf'}); + cloneTest([1, "fasdf", { foo: 42 }]); + cloneTest({ x: 42, y: "asdf" }); function testCloneArgs(/*arguments*/) { const clonedArgs = EJSON.clone(arguments); - test.equal(clonedArgs, [1, 2, 'foo', [4]]); - }; - testCloneArgs(1, 2, 'foo', [4]); + test.equal(clonedArgs, [1, 2, "foo", [4]]); + } + testCloneArgs(1, 2, "foo", [4]); }); -Tinytest.add('ejson - stringify', test => { - test.equal(EJSON.stringify(null), 'null'); - test.equal(EJSON.stringify(true), 'true'); - test.equal(EJSON.stringify(false), 'false'); - test.equal(EJSON.stringify(123), '123'); - test.equal(EJSON.stringify('abc'), '"abc"'); +Tinytest.add("ejson - stringify", (test) => { + test.equal(EJSON.stringify(null), "null"); + test.equal(EJSON.stringify(true), "true"); + test.equal(EJSON.stringify(false), "false"); + test.equal(EJSON.stringify(123), "123"); + test.equal(EJSON.stringify("abc"), '"abc"'); - test.equal(EJSON.stringify([1, 2, 3]), - '[1,2,3]' - ); - test.equal(EJSON.stringify([1, 2, 3], {indent: true}), - '[\n 1,\n 2,\n 3\n]' - ); - test.equal(EJSON.stringify([1, 2, 3], {canonical: false}), - '[1,2,3]' - ); - test.equal(EJSON.stringify([1, 2, 3], {indent: true, canonical: false}), - '[\n 1,\n 2,\n 3\n]' + test.equal(EJSON.stringify([1, 2, 3]), "[1,2,3]"); + test.equal(EJSON.stringify([1, 2, 3], { indent: true }), "[\n 1,\n 2,\n 3\n]"); + test.equal(EJSON.stringify([1, 2, 3], { canonical: false }), "[1,2,3]"); + test.equal( + EJSON.stringify([1, 2, 3], { indent: true, canonical: false }), + "[\n 1,\n 2,\n 3\n]", ); - test.equal(EJSON.stringify([1, 2, 3], {indent: 4}), - '[\n 1,\n 2,\n 3\n]' - ); - test.equal(EJSON.stringify([1, 2, 3], {indent: '--'}), - '[\n--1,\n--2,\n--3\n]' - ); + test.equal(EJSON.stringify([1, 2, 3], { indent: 4 }), "[\n 1,\n 2,\n 3\n]"); + test.equal(EJSON.stringify([1, 2, 3], { indent: "--" }), "[\n--1,\n--2,\n--3\n]"); test.equal( - EJSON.stringify( - {b: [2, {d: 4, c: 3}], a: 1}, - {canonical: true} - ), - '{"a":1,"b":[2,{"c":3,"d":4}]}' + EJSON.stringify({ b: [2, { d: 4, c: 3 }], a: 1 }, { canonical: true }), + '{"a":1,"b":[2,{"c":3,"d":4}]}', ); test.equal( EJSON.stringify( - {b: [2, {d: 4, c: 3}], a: 1}, + { b: [2, { d: 4, c: 3 }], a: 1 }, { indent: true, canonical: true, - } + }, ), - '{\n' + - ' "a": 1,\n' + - ' "b": [\n' + - ' 2,\n' + - ' {\n' + - ' "c": 3,\n' + - ' "d": 4\n' + - ' }\n' + - ' ]\n' + - '}' + "{\n" + + ' "a": 1,\n' + + ' "b": [\n' + + " 2,\n" + + " {\n" + + ' "c": 3,\n' + + ' "d": 4\n' + + " }\n" + + " ]\n" + + "}", ); test.equal( - EJSON.stringify( - {b: [2, {d: 4, c: 3}], a: 1}, - {canonical: false} - ), - '{"b":[2,{"d":4,"c":3}],"a":1}' + EJSON.stringify({ b: [2, { d: 4, c: 3 }], a: 1 }, { canonical: false }), + '{"b":[2,{"d":4,"c":3}],"a":1}', ); test.equal( - EJSON.stringify( - {b: [2, {d: 4, c: 3}], a: 1}, - {indent: true, canonical: false} - ), - '{\n' + - ' "b": [\n' + - ' 2,\n' + - ' {\n' + - ' "d": 4,\n' + - ' "c": 3\n' + - ' }\n' + - ' ],\n' + - ' "a": 1\n' + - '}' + EJSON.stringify({ b: [2, { d: 4, c: 3 }], a: 1 }, { indent: true, canonical: false }), + "{\n" + + ' "b": [\n' + + " 2,\n" + + " {\n" + + ' "d": 4,\n' + + ' "c": 3\n' + + " }\n" + + " ],\n" + + ' "a": 1\n' + + "}", ); - test.throws( - () => { - const col = new Mongo.Collection('test'); - EJSON.stringify(col) - }, - /Converting circular structure to JSON/ - ); + test.throws(() => { + const col = new Mongo.Collection("test"); + EJSON.stringify(col); + }, /Converting circular structure to JSON/); }); -Tinytest.add('ejson - parse', test => { - test.equal(EJSON.parse('[1,2,3]'), [1, 2, 3]); - test.throws( - () => { EJSON.parse(null); }, - /argument should be a string/ - ); +Tinytest.add("ejson - parse", (test) => { + test.equal(EJSON.parse("[1,2,3]"), [1, 2, 3]); + test.throws(() => { + EJSON.parse(null); + }, /argument should be a string/); }); -Tinytest.add("ejson - regexp", test => { - test.equal(EJSON.stringify(/foo/gi), "{\"$regexp\":\"foo\",\"$flags\":\"gi\"}"); +Tinytest.add("ejson - regexp", (test) => { + test.equal(EJSON.stringify(/foo/gi), '{"$regexp":"foo","$flags":"gi"}'); const obj = { $regexp: "foo", $flags: "gi" }; const eObj = EJSON.toJSONValue(obj); @@ -212,11 +201,11 @@ Tinytest.add("ejson - regexp", test => { test.equal(obj, roundTrip); }); -Tinytest.add('ejson - custom types', test => { +Tinytest.add("ejson - custom types", (test) => { const testSameConstructors = (someObj, compareWith) => { test.equal(someObj.constructor, compareWith.constructor); - if (typeof someObj === 'object') { - Object.keys(someObj).forEach(key => { + if (typeof someObj === "object") { + Object.keys(someObj).forEach((key) => { const value = someObj[key]; testSameConstructors(value, compareWith[key]); }); @@ -239,11 +228,11 @@ Tinytest.add('ejson - custom types', test => { testReallyEqual(someObj, EJSON.clone(someObj)); }; - const a = new EJSONTest.Address('Montreal', 'Quebec'); - testCustomObject( {address: a} ); + const a = new EJSONTest.Address("Montreal", "Quebec"); + testCustomObject({ address: a }); // Test that difference is detected even if they // have similar toJSONValue results: - const nakedA = {city: 'Montreal', state: 'Quebec'}; + const nakedA = { city: "Montreal", state: "Quebec" }; test.notEqual(nakedA, a); test.notEqual(a, nakedA); const holder = new EJSONTest.Holder(nakedA); @@ -252,18 +241,18 @@ Tinytest.add('ejson - custom types', test => { test.notEqual(a, holder); const d = new Date(); - const obj = new EJSONTest.Person('John Doe', d, a); - testCustomObject( obj ); + const obj = new EJSONTest.Person("John Doe", d, a); + testCustomObject(obj); // Test clone is deep: const clone = EJSON.clone(obj); - clone.address.city = 'Sherbrooke'; - test.notEqual( obj, clone ); + clone.address.city = "Sherbrooke"; + test.notEqual(obj, clone); }); // Verify objects with a property named "length" can be handled by the EJSON // API properly (see https://github.com/meteor/meteor/issues/5175). -Tinytest.add('ejson - handle objects with properties named "length"', test => { +Tinytest.add('ejson - handle objects with properties named "length"', (test) => { class Widget { constructor() { this.length = 10; diff --git a/packages/ejson/package.js b/packages/ejson/package.js index 7cb875555d..d1dc95b13a 100644 --- a/packages/ejson/package.js +++ b/packages/ejson/package.js @@ -1,17 +1,17 @@ Package.describe({ - summary: 'Extended and Extensible JSON library', - version: '1.1.5', + summary: "Extended and Extensible JSON library", + version: "1.1.5", }); Package.onUse(function onUse(api) { - api.use(['ecmascript', 'base64']); - api.addAssets('ejson.d.ts', 'server'); - api.mainModule('ejson.js'); - api.export('EJSON'); + api.use(["ecmascript", "base64"]); + api.addAssets("ejson.d.ts", "server"); + api.mainModule("ejson.js"); + api.export("EJSON"); }); Package.onTest(function onTest(api) { - api.use(['ecmascript', 'tinytest', 'mongo']); - api.use('ejson'); - api.mainModule('ejson_tests.js'); + api.use(["ecmascript", "tinytest", "mongo"]); + api.use("ejson"); + api.mainModule("ejson_tests.js"); }); diff --git a/packages/ejson/stringify.js b/packages/ejson/stringify.js index 3d6f8a4ea9..f267df4166 100644 --- a/packages/ejson/stringify.js +++ b/packages/ejson/stringify.js @@ -16,74 +16,73 @@ const str = (key, holder, singleIndent, outerIndent, canonical) => { // What happens next depends on the value's type. switch (typeof value) { - case 'string': - return quote(value); - case 'number': - // JSON numbers must be finite. Encode non-finite numbers as null. - return isFinite(value) ? String(value) : 'null'; - case 'boolean': - return String(value); - // If the type is 'object', we might be dealing with an object or an array or - // null. - case 'object': { - // Due to a specification blunder in ECMAScript, typeof null is 'object', - // so watch out for that case. - if (!value) { - return 'null'; - } - // Make an array to hold the partial results of stringifying this object - // value. - const innerIndent = outerIndent + singleIndent; - const partial = []; - let v; + case "string": + return quote(value); + case "number": + // JSON numbers must be finite. Encode non-finite numbers as null. + return isFinite(value) ? String(value) : "null"; + case "boolean": + return String(value); + // If the type is 'object', we might be dealing with an object or an array or + // null. + case "object": { + // Due to a specification blunder in ECMAScript, typeof null is 'object', + // so watch out for that case. + if (!value) { + return "null"; + } + // Make an array to hold the partial results of stringifying this object + // value. + const innerIndent = outerIndent + singleIndent; + const partial = []; + let v; - // Is the value an array? - if (Array.isArray(value) || ({}).hasOwnProperty.call(value, 'callee')) { - // The value is an array. Stringify every element. Use null as a - // placeholder for non-JSON values. - const length = value.length; - for (let i = 0; i < length; i += 1) { - partial[i] = - str(i, value, singleIndent, innerIndent, canonical) || 'null'; + // Is the value an array? + if (Array.isArray(value) || {}.hasOwnProperty.call(value, "callee")) { + // The value is an array. Stringify every element. Use null as a + // placeholder for non-JSON values. + const length = value.length; + for (let i = 0; i < length; i += 1) { + partial[i] = str(i, value, singleIndent, innerIndent, canonical) || "null"; + } + + // Join all of the elements together, separated with commas, and wrap + // them in brackets. + if (partial.length === 0) { + v = "[]"; + } else if (innerIndent) { + v = `[\n${innerIndent}${partial.join(`,\n${innerIndent}`)}\n${outerIndent}]`; + } else { + v = `[${partial.join(",")}]`; + } + return v; } - // Join all of the elements together, separated with commas, and wrap - // them in brackets. + // Iterate through all of the keys in the object. + let keys = Object.keys(value); + if (canonical) { + keys = keys.sort(); + } + keys.forEach((k) => { + v = str(k, value, singleIndent, innerIndent, canonical); + if (v) { + partial.push(quote(k) + (innerIndent ? ": " : ":") + v); + } + }); + + // Join all of the member texts together, separated with commas, + // and wrap them in braces. if (partial.length === 0) { - v = '[]'; + v = "{}"; } else if (innerIndent) { - v = `[\n${innerIndent}${partial.join(`,\n${innerIndent}`)}\n${outerIndent}]`; + v = `{\n${innerIndent}${partial.join(`,\n${innerIndent}`)}\n${outerIndent}}`; } else { - v = `[${partial.join(',')}]`; + v = `{${partial.join(",")}}`; } return v; } - // Iterate through all of the keys in the object. - let keys = Object.keys(value); - if (canonical) { - keys = keys.sort(); - } - keys.forEach(k => { - v = str(k, value, singleIndent, innerIndent, canonical); - if (v) { - partial.push(quote(k) + (innerIndent ? ': ' : ':') + v); - } - }); - - // Join all of the member texts together, separated with commas, - // and wrap them in braces. - if (partial.length === 0) { - v = '{}'; - } else if (innerIndent) { - v = `{\n${innerIndent}${partial.join(`,\n${innerIndent}`)}\n${outerIndent}}`; - } else { - v = `{${partial.join(',')}}`; - } - return v; - } - - default: // Do nothing + default: // Do nothing } }; @@ -91,20 +90,23 @@ const str = (key, holder, singleIndent, outerIndent, canonical) => { const canonicalStringify = (value, options) => { // Make a fake root object containing our value under the key of ''. // Return the result of stringifying the value. - const allOptions = Object.assign({ - indent: '', - canonical: false, - }, options); + const allOptions = Object.assign( + { + indent: "", + canonical: false, + }, + options, + ); if (allOptions.indent === true) { - allOptions.indent = ' '; - } else if (typeof allOptions.indent === 'number') { - let newIndent = ''; + allOptions.indent = " "; + } else if (typeof allOptions.indent === "number") { + let newIndent = ""; for (let i = 0; i < allOptions.indent; i++) { - newIndent += ' '; + newIndent += " "; } allOptions.indent = newIndent; } - return str('', {'': value}, allOptions.indent, '', allOptions.canonical); + return str("", { "": value }, allOptions.indent, "", allOptions.canonical); }; export default canonicalStringify; diff --git a/packages/ejson/utils.js b/packages/ejson/utils.js index 6d71313fcd..8052bc4f38 100644 --- a/packages/ejson/utils.js +++ b/packages/ejson/utils.js @@ -1,6 +1,6 @@ -export const isFunction = (fn) => typeof fn === 'function'; +export const isFunction = (fn) => typeof fn === "function"; -export const isObject = (fn) => typeof fn === 'object'; +export const isObject = (fn) => typeof fn === "object"; export const keysOf = (obj) => Object.keys(obj); @@ -8,29 +8,30 @@ export const lengthOf = (obj) => Object.keys(obj).length; export const hasOwn = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop); -export const convertMapToObject = (map) => Array.from(map).reduce((acc, [key, value]) => { - // reassign to not create new object - acc[key] = value; - return acc; -}, {}); +export const convertMapToObject = (map) => + Array.from(map).reduce((acc, [key, value]) => { + // reassign to not create new object + acc[key] = value; + return acc; + }, {}); -export const isArguments = obj => obj != null && hasOwn(obj, 'callee'); +export const isArguments = (obj) => obj != null && hasOwn(obj, "callee"); -export const isInfOrNaN = - obj => Number.isNaN(obj) || obj === Infinity || obj === -Infinity; +export const isInfOrNaN = (obj) => Number.isNaN(obj) || obj === Infinity || obj === -Infinity; export const checkError = { - maxStack: (msgError) => new RegExp('Maximum call stack size exceeded', 'g').test(msgError), + maxStack: (msgError) => new RegExp("Maximum call stack size exceeded", "g").test(msgError), }; -export const handleError = (fn) => function() { - try { - return fn.apply(this, arguments); - } catch (error) { - const isMaxStack = checkError.maxStack(error.message); - if (isMaxStack) { - throw new Error('Converting circular structure to JSON') +export const handleError = (fn) => + function () { + try { + return fn.apply(this, arguments); + } catch (error) { + const isMaxStack = checkError.maxStack(error.message); + if (isMaxStack) { + throw new Error("Converting circular structure to JSON"); + } + throw error; } - throw error; - } -}; + }; diff --git a/packages/id-map/id-map.js b/packages/id-map/id-map.js index ab26c3ff42..17c9e6d7ff 100644 --- a/packages/id-map/id-map.js +++ b/packages/id-map/id-map.js @@ -1,4 +1,3 @@ - export class IdMap { constructor(idStringify, idParse) { this._map = new Map(); @@ -6,10 +5,10 @@ export class IdMap { this._idParse = idParse || JSON.parse; } -// Some of these methods are designed to match methods on OrderedDict, since -// (eg) ObserveMultiplex and _CachingChangeObserver use them interchangeably. -// (Conceivably, this should be replaced with "UnorderedDict" with a specific -// set of methods that overlap between the two.) + // Some of these methods are designed to match methods on OrderedDict, since + // (eg) ObserveMultiplex and _CachingChangeObserver use them interchangeably. + // (Conceivably, this should be replaced with "UnorderedDict" with a specific + // set of methods that overlap between the two.) get(id) { const key = this._idStringify(id); @@ -42,12 +41,8 @@ export class IdMap { // Iterates over the items in the map. Return `false` to break the loop. forEach(iterator) { // don't use _.each, because we can't break out of it. - for (const [key, value] of this._map){ - const breakIfFalse = iterator.call( - null, - value, - this._idParse(key) - ); + for (const [key, value] of this._map) { + const breakIfFalse = iterator.call(null, value, this._idParse(key)); if (breakIfFalse === false) { return; } @@ -55,12 +50,8 @@ export class IdMap { } async forEachAsync(iterator) { - for (const [key, value] of this._map){ - const breakIfFalse = await iterator.call( - null, - value, - this._idParse(key) - ); + for (const [key, value] of this._map) { + const breakIfFalse = await iterator.call(null, value, this._idParse(key)); if (breakIfFalse === false) { return; } @@ -85,7 +76,7 @@ export class IdMap { clone() { const clone = new IdMap(this._idStringify, this._idParse); // copy directly to avoid stringify/parse overhead - this._map.forEach(function(value, key){ + this._map.forEach(function (value, key) { clone._map.set(key, EJSON.clone(value)); }); return clone; diff --git a/packages/id-map/package.js b/packages/id-map/package.js index a79970c701..a32c703213 100644 --- a/packages/id-map/package.js +++ b/packages/id-map/package.js @@ -1,11 +1,11 @@ Package.describe({ summary: "Dictionary data structure allowing non-string keys", - version: '1.2.0', + version: "1.2.0", }); Package.onUse(function (api) { - api.use('ecmascript'); - api.use('ejson'); - api.mainModule('id-map.js'); - api.export('IdMap'); + api.use("ecmascript"); + api.use("ejson"); + api.mainModule("id-map.js"); + api.export("IdMap"); }); diff --git a/packages/logging/logging.d.ts b/packages/logging/logging.d.ts index 5441ba3808..57f21a1c18 100644 --- a/packages/logging/logging.d.ts +++ b/packages/logging/logging.d.ts @@ -9,7 +9,7 @@ type LogInput = string | LogJSONInput; type formatInput = { message: string; time: Date; - level: 'debug' | 'info' | 'warn' | 'error' + level: "debug" | "info" | "warn" | "error"; timeInexact?: boolean; file: string; line: number; @@ -23,23 +23,23 @@ type formatInput = { export declare function Log(input: LogInput, ...optionalParams: any[]): void; export declare namespace Log { - var outputFormat: 'json' | 'colored-text'; + var outputFormat: "json" | "colored-text"; var showTime: boolean; function _intercept(count: number): void; function _suppress(count: number): void; function _intercepted(): string[]; function _getCallerDetails(): { line: number; file: string }; - function parse(line: object | string): object + function parse(line: object | string): object; function format(object: formatInput, options: { color: true }): object | string; function objFromText( line: string, - override: object + override: object, ): { - message: string - level: 'info' - time: Date - timeInexact: true - } + message: string; + level: "info"; + time: Date; + timeInexact: true; + }; function debug(input: LogInput, ...optionalParams: any[]): void; function info(input: LogInput, ...optionalParams: any[]): void; diff --git a/packages/logging/logging.js b/packages/logging/logging.js index 609c6db5f3..5984ac6d6c 100644 --- a/packages/logging/logging.js +++ b/packages/logging/logging.js @@ -1,4 +1,4 @@ -import { Meteor } from 'meteor/meteor'; +import { Meteor } from "meteor/meteor"; const hasOwn = Object.prototype.hasOwnProperty; @@ -42,7 +42,7 @@ Log._intercepted = () => { // When this is set to 'colored-text', call 'Log.format' before printing. // This should be used for logging from within satellite, since there is no // other process that will be reading its standard output. -Log.outputFormat = 'json'; +Log.outputFormat = "json"; // Defaults to true for local development and for backwards compatibility. // for cloud environments is interesting to leave it false as most of them have the timestamp in the console. @@ -50,39 +50,48 @@ Log.outputFormat = 'json'; Log.showTime = true; const LEVEL_COLORS = { - debug: 'green', + debug: "green", // leave info as the default color - warn: 'magenta', - error: 'red' + warn: "magenta", + error: "red", }; -const META_COLOR = 'blue'; +const META_COLOR = "blue"; // Default colors cause readability problems on Windows Powershell, // switch to bright variants. While still capable of millions of // operations per second, the benchmark showed a 25%+ increase in // ops per second (on Node 8) by caching "process.platform". -const isWin32 = typeof process === 'object' && process.platform === 'win32'; +const isWin32 = typeof process === "object" && process.platform === "win32"; const platformColor = (color) => { - if (isWin32 && typeof color === 'string' && !color.endsWith('Bright')) { + if (isWin32 && typeof color === "string" && !color.endsWith("Bright")) { return `${color}Bright`; } return color; }; // XXX package -const RESTRICTED_KEYS = ['time', 'timeInexact', 'level', 'file', 'line', - 'program', 'originApp', 'satellite', 'stderr']; +const RESTRICTED_KEYS = [ + "time", + "timeInexact", + "level", + "file", + "line", + "program", + "originApp", + "satellite", + "stderr", +]; -const FORMATTED_KEYS = [...RESTRICTED_KEYS, 'app', 'message']; +const FORMATTED_KEYS = [...RESTRICTED_KEYS, "app", "message"]; -const logInBrowser = obj => { +const logInBrowser = (obj) => { const str = Log.format(obj); // XXX Some levels should be probably be sent to the server const level = obj.level; - if ((typeof console !== 'undefined') && console[level]) { + if (typeof console !== "undefined" && console[level]) { console[level](str); } else { // IE doesn't have console.log.apply, it's not a real Object. @@ -91,7 +100,6 @@ const logInBrowser = obj => { if (typeof console.log.apply === "function") { // Most browsers console.log.apply(console, [str]); - } else if (typeof Function.prototype.bind === "function") { // IE9 const log = Function.prototype.bind.call(console.log, console); @@ -106,7 +114,7 @@ Log._getCallerDetails = () => { // We do NOT use Error.prepareStackTrace here (a V8 extension that gets us a // pre-parsed stack) since it's impossible to compose it with the use of // Error.prepareStackTrace used on the server for source maps. - const err = new Error; + const err = new Error(); const stack = err.stack; return stack; }; @@ -118,10 +126,10 @@ Log._getCallerDetails = () => { // looking for the first line outside the logging package (or an // eval if we find that first) let line; - const lines = stack.split('\n').slice(1); + const lines = stack.split("\n").slice(1); for (line of lines) { if (line.match(/^\s*(at eval \(eval)|(eval:)/)) { - return {file: "eval"}; + return { file: "eval" }; } if (!line.match(/packages\/(?:local-test[:_])?logging(?:\/|\.js)/)) { @@ -140,84 +148,85 @@ Log._getCallerDetails = () => { } // in case the matched block here is line:column - details.line = match[2].split(':')[0]; + details.line = match[2].split(":")[0]; // Possible format: https://foo.bar.com/scripts/file.js?random=foobar // XXX: if you can write the following in better way, please do it // XXX: what about evals? - details.file = match[1].split('/').slice(-1)[0].split('?')[0]; + details.file = match[1].split("/").slice(-1)[0].split("?")[0]; return details; }; -['debug', 'info', 'warn', 'error'].forEach((level) => { - // @param arg {String|Object} - Log[level] = (arg) => { - if (suppress) { - suppress--; - return; - } - - let intercepted = false; - if (intercept) { - intercept--; - intercepted = true; - } - - let obj = (arg === Object(arg) - && !(arg instanceof RegExp) - && !(arg instanceof Date)) - ? arg - : { message: new String(arg).toString() }; - - RESTRICTED_KEYS.forEach(key => { - if (obj[key]) { - throw new Error(`Can't set '${key}' in log message`); +["debug", "info", "warn", "error"].forEach((level) => { + // @param arg {String|Object} + Log[level] = (arg) => { + if (suppress) { + suppress--; + return; } - }); - if (hasOwn.call(obj, 'message') && typeof obj.message !== 'string') { - throw new Error("The 'message' field in log objects must be a string"); - } + let intercepted = false; + if (intercept) { + intercept--; + intercepted = true; + } - if (!obj.omitCallerDetails) { - obj = { ...Log._getCallerDetails(), ...obj }; - } + let obj = + arg === Object(arg) && !(arg instanceof RegExp) && !(arg instanceof Date) + ? arg + : { message: new String(arg).toString() }; - obj.time = new Date(); - obj.level = level; + RESTRICTED_KEYS.forEach((key) => { + if (obj[key]) { + throw new Error(`Can't set '${key}' in log message`); + } + }); - // If we are in production don't write out debug logs. - if (level === 'debug' && Meteor.isProduction) { - return; - } + if (hasOwn.call(obj, "message") && typeof obj.message !== "string") { + throw new Error("The 'message' field in log objects must be a string"); + } - if (intercepted) { - interceptedLines.push(EJSON.stringify(obj)); - } else if (Meteor.isServer) { - if (Log.outputFormat === 'colored-text') { - console.log(Log.format(obj, {color: true})); - } else if (Log.outputFormat === 'json') { - console.log(EJSON.stringify(obj)); + if (!obj.omitCallerDetails) { + obj = { ...Log._getCallerDetails(), ...obj }; + } + + obj.time = new Date(); + obj.level = level; + + // If we are in production don't write out debug logs. + if (level === "debug" && Meteor.isProduction) { + return; + } + + if (intercepted) { + interceptedLines.push(EJSON.stringify(obj)); + } else if (Meteor.isServer) { + if (Log.outputFormat === "colored-text") { + console.log(Log.format(obj, { color: true })); + } else if (Log.outputFormat === "json") { + console.log(EJSON.stringify(obj)); + } else { + throw new Error(`Unknown logging output format: ${Log.outputFormat}`); + } } else { - throw new Error(`Unknown logging output format: ${Log.outputFormat}`); + logInBrowser(obj); } - } else { - logInBrowser(obj); - } -}; + }; }); - // tries to parse line as EJSON. returns object if parse is successful, or null if not Log.parse = (line) => { let obj = null; - if (line && line.startsWith('{')) { // might be json generated from calling 'Log' - try { obj = EJSON.parse(line); } catch { } + if (line && line.startsWith("{")) { + // might be json generated from calling 'Log' + try { + obj = EJSON.parse(line); + } catch {} } // XXX should probably check fields other than 'time' - if (obj && obj.time && (obj.time instanceof Date)) { + if (obj && obj.time && obj.time instanceof Date) { return obj; } else { return null; @@ -230,44 +239,43 @@ Log.format = (obj, options = {}) => { const { time, timeInexact, - level = 'info', + level = "info", file, line: lineNumber, - app: appName = '', + app: appName = "", originApp, - program = '', - satellite = '', - stderr = '', - } = obj; - let { - message = '', + program = "", + satellite = "", + stderr = "", } = obj; + let { message = "" } = obj; if (!(time instanceof Date)) { throw new Error("'time' must be a Date object"); } - FORMATTED_KEYS.forEach((key) => { delete obj[key]; }); + FORMATTED_KEYS.forEach((key) => { + delete obj[key]; + }); if (Object.keys(obj).length > 0) { if (message) { - message += ' '; + message += " "; } message += EJSON.stringify(obj); } - const pad2 = n => n.toString().padStart(2, '0'); - const pad3 = n => n.toString().padStart(3, '0'); + const pad2 = (n) => n.toString().padStart(2, "0"); + const pad3 = (n) => n.toString().padStart(3, "0"); - const dateStamp = time.getFullYear().toString() + - pad2(time.getMonth() + 1 /*0-based*/) + - pad2(time.getDate()); + const dateStamp = + time.getFullYear().toString() + pad2(time.getMonth() + 1 /*0-based*/) + pad2(time.getDate()); const timeStamp = `${pad2(time.getHours())}:${pad2(time.getMinutes())}:${pad2(time.getSeconds())}.${pad3(time.getMilliseconds())}`; // eg in San Francisco in June this will be '(-7)' - const utcOffsetStr = `(${(-(new Date().getTimezoneOffset() / 60))})`; + const utcOffsetStr = `(${-(new Date().getTimezoneOffset() / 60)})`; - let appInfo = ''; + let appInfo = ""; if (appName) { appInfo += appName; } @@ -289,30 +297,30 @@ Log.format = (obj, options = {}) => { sourceInfoParts.push(lineNumber); } - let sourceInfo = !sourceInfoParts.length ? - '' : `(${sourceInfoParts.join(':')}) `; + let sourceInfo = !sourceInfoParts.length ? "" : `(${sourceInfoParts.join(":")}) `; - if (satellite) - sourceInfo += `[${satellite}]`; + if (satellite) sourceInfo += `[${satellite}]`; - const stderrIndicator = stderr ? '(STDERR) ' : ''; + const stderrIndicator = stderr ? "(STDERR) " : ""; const timeString = Log.showTime - ? `${dateStamp}-${timeStamp}${utcOffsetStr}${timeInexact ? '? ' : ' '}` - : ' '; - - + ? `${dateStamp}-${timeStamp}${utcOffsetStr}${timeInexact ? "? " : " "}` + : " "; const metaPrefix = [ level.charAt(0).toUpperCase(), timeString, appInfo, sourceInfo, - stderrIndicator].join(''); + stderrIndicator, + ].join(""); - - return Formatter.prettify(metaPrefix, options.color && platformColor(options.metaColor || META_COLOR)) + - Formatter.prettify(message, options.color && platformColor(LEVEL_COLORS[level])); + return ( + Formatter.prettify( + metaPrefix, + options.color && platformColor(options.metaColor || META_COLOR), + ) + Formatter.prettify(message, options.color && platformColor(LEVEL_COLORS[level])) + ); }; // Turn a line of text into a loggable object. @@ -321,10 +329,10 @@ Log.format = (obj, options = {}) => { Log.objFromText = (line, override) => { return { message: line, - level: 'info', + level: "info", time: new Date(), timeInexact: true, - ...override + ...override, }; }; diff --git a/packages/logging/logging_browser.js b/packages/logging/logging_browser.js index 6d727d66bb..9191e5c460 100644 --- a/packages/logging/logging_browser.js +++ b/packages/logging/logging_browser.js @@ -1,4 +1,4 @@ Formatter = {}; -Formatter.prettify = function(line, _color){ - return line; +Formatter.prettify = function (line, _color) { + return line; }; diff --git a/packages/logging/logging_cordova.js b/packages/logging/logging_cordova.js index 7ac528befb..aea785314f 100644 --- a/packages/logging/logging_cordova.js +++ b/packages/logging/logging_cordova.js @@ -1,13 +1,13 @@ // Log all uncaught errors so they can be printed to the developer. // But since Android's adb catalog already prints the uncaught exceptions, we // can disable it for Android. -if (! /Android/i.test(navigator.userAgent)) { +if (!/Android/i.test(navigator.userAgent)) { window.onerror = function (msg, url, line) { // Cut off the url prefix, the meaningful part always starts at 'www/' in // Cordova apps. - url = url.replace(/^.*?\/www\//, ''); + url = url.replace(/^.*?\/www\//, ""); console.log(`Uncaught Error: ${msg}:${line}:${url}`); }; } -export * from './logging.js'; +export * from "./logging.js"; diff --git a/packages/logging/logging_server.js b/packages/logging/logging_server.js index dc01ce0ceb..0b082506b0 100644 --- a/packages/logging/logging_server.js +++ b/packages/logging/logging_server.js @@ -1,5 +1,5 @@ Formatter = {}; -Formatter.prettify = function(line, color){ - if(!color) return line; - return require("chalk")[color](line); +Formatter.prettify = function (line, color) { + if (!color) return line; + return require("chalk")[color](line); }; diff --git a/packages/logging/logging_test.js b/packages/logging/logging_test.js index 612770e733..70aedb0eb6 100644 --- a/packages/logging/logging_test.js +++ b/packages/logging/logging_test.js @@ -21,7 +21,7 @@ Tinytest.add("logging - _getCallerDetails", function (test) { // for the minified filename test.matches( eval(code), // oxlint-disable-line no-eval -- intentional eval for testing caller details - /^(?:eval|local-test_logging\.js|[a-f0-9]{40}\.js)/ + /^(?:eval|local-test_logging\.js|[a-f0-9]{40}\.js)/, ); } }); @@ -64,11 +64,7 @@ Tinytest.add("logging - log", function (test) { [0, "0", "falsy - 0"], [null, "null", "falsy - null"], [undefined, "undefined", "falsy - undefined"], - [ - new Date("2013-06-13T01:15:16.000Z"), - new Date("2013-06-13T01:15:16.000Z"), - "date", - ], + [new Date("2013-06-13T01:15:16.000Z"), new Date("2013-06-13T01:15:16.000Z"), "date"], [/[^regexp]{0,1}/g, "/[^regexp]{0,1}/g", "regexp"], [true, "true", "boolean - true"], [false, "false", "boolean - false"], @@ -94,8 +90,7 @@ Tinytest.add("logging - log", function (test) { if ( expected && expected.toString && - (expected.toString() === "NaN" || - expected.toString() === "Invalid Date") + (expected.toString() === "NaN" || expected.toString() === "Invalid Date") ) { return; } @@ -116,9 +111,7 @@ Tinytest.add("logging - log", function (test) { test.throws(function () { log({ level: "not the right level" }); }); - ["file", "line", "program", "originApp", "satellite"].forEach(function ( - restrictedKey - ) { + ["file", "line", "program", "originApp", "satellite"].forEach(function (restrictedKey) { test.throws(function () { const obj = {}; obj[restrictedKey] = "usage of restricted key"; @@ -177,9 +170,7 @@ Tinytest.add("logging - format", function (test) { ["debug", "info", "warn", "error"].forEach(function (level) { test.equal( Log.format({ message: "message", time, level }), - `${level - .charAt(0) - .toUpperCase()}20120908-07:06:05.004${utcOffsetStr} message` + `${level.charAt(0).toUpperCase()}20120908-07:06:05.004${utcOffsetStr} message`, ); test.equal( @@ -189,23 +180,19 @@ Tinytest.add("logging - format", function (test) { timeInexact: true, level, }), - `${level - .charAt(0) - .toUpperCase()}20120908-07:06:05.004${utcOffsetStr}? message` + `${level.charAt(0).toUpperCase()}20120908-07:06:05.004${utcOffsetStr}? message`, ); test.equal( Log.format({ foo1: "bar1", foo2: "bar2", time, level }), `${level .charAt(0) - .toUpperCase()}20120908-07:06:05.004${utcOffsetStr} {"foo1":"bar1","foo2":"bar2"}` + .toUpperCase()}20120908-07:06:05.004${utcOffsetStr} {"foo1":"bar1","foo2":"bar2"}`, ); test.equal( Log.format({ message: "message", foo: "bar", time, level }), - `${level - .charAt(0) - .toUpperCase()}20120908-07:06:05.004${utcOffsetStr} message {"foo":"bar"}` + `${level.charAt(0).toUpperCase()}20120908-07:06:05.004${utcOffsetStr} message {"foo":"bar"}`, ); // Has everything except stderr field @@ -223,7 +210,7 @@ Tinytest.add("logging - format", function (test) { }), `${level .charAt(0) - .toUpperCase()}20120908-07:06:05.004${utcOffsetStr} [myApp via proxy] (server:app.js:42) message {"foo":"bar"}` + .toUpperCase()}20120908-07:06:05.004${utcOffsetStr} [myApp via proxy] (server:app.js:42) message {"foo":"bar"}`, ); // stderr @@ -236,7 +223,7 @@ Tinytest.add("logging - format", function (test) { }), `${level .charAt(0) - .toUpperCase()}20120908-07:06:05.004${utcOffsetStr} (STDERR) message from stderr` + .toUpperCase()}20120908-07:06:05.004${utcOffsetStr} (STDERR) message from stderr`, ); // app/originApp @@ -248,9 +235,7 @@ Tinytest.add("logging - format", function (test) { app: "app", originApp: "app", }), - `${level - .charAt(0) - .toUpperCase()}20120908-07:06:05.004${utcOffsetStr} [app] message` + `${level.charAt(0).toUpperCase()}20120908-07:06:05.004${utcOffsetStr} [app] message`, ); test.equal( Log.format({ @@ -262,7 +247,7 @@ Tinytest.add("logging - format", function (test) { }), `${level .charAt(0) - .toUpperCase()}20120908-07:06:05.004${utcOffsetStr} [app via proxy] message` + .toUpperCase()}20120908-07:06:05.004${utcOffsetStr} [app via proxy] message`, ); // source info @@ -277,7 +262,7 @@ Tinytest.add("logging - format", function (test) { }), `${level .charAt(0) - .toUpperCase()}20120908-07:06:05.004${utcOffsetStr} (server:app.js:42) message` + .toUpperCase()}20120908-07:06:05.004${utcOffsetStr} (server:app.js:42) message`, ); test.equal( Log.format({ @@ -287,9 +272,7 @@ Tinytest.add("logging - format", function (test) { file: "app.js", line: 42, }), - `${level - .charAt(0) - .toUpperCase()}20120908-07:06:05.004${utcOffsetStr} (app.js:42) message` + `${level.charAt(0).toUpperCase()}20120908-07:06:05.004${utcOffsetStr} (app.js:42) message`, ); }); @@ -302,9 +285,9 @@ Tinytest.add("logging - format", function (test) { }, { color: true, - } + }, ), - /oyez/ + /oyez/, ); }); @@ -316,7 +299,7 @@ Tinytest.add("logging - formats - without time", function (test) { for (const level of levels) { test.equal( Log.format({ message: "message", time, level }), - `${level.charAt(0).toUpperCase()} message` + `${level.charAt(0).toUpperCase()} message`, ); test.equal( @@ -326,17 +309,17 @@ Tinytest.add("logging - formats - without time", function (test) { timeInexact: true, level, }), - `${level.charAt(0).toUpperCase()} message` + `${level.charAt(0).toUpperCase()} message`, ); test.equal( Log.format({ foo1: "bar1", foo2: "bar2", time, level }), - `${level.charAt(0).toUpperCase()} {"foo1":"bar1","foo2":"bar2"}` + `${level.charAt(0).toUpperCase()} {"foo1":"bar1","foo2":"bar2"}`, ); test.equal( Log.format({ message: "message", foo: "bar", time, level }), - `${level.charAt(0).toUpperCase()} message {"foo":"bar"}` + `${level.charAt(0).toUpperCase()} message {"foo":"bar"}`, ); // Has everything except stderr field @@ -352,9 +335,7 @@ Tinytest.add("logging - formats - without time", function (test) { originApp: "proxy", program: "server", }), - `${level - .charAt(0) - .toUpperCase()} [myApp via proxy] (server:app.js:42) message {"foo":"bar"}` + `${level.charAt(0).toUpperCase()} [myApp via proxy] (server:app.js:42) message {"foo":"bar"}`, ); // stderr @@ -365,7 +346,7 @@ Tinytest.add("logging - formats - without time", function (test) { level, stderr: true, }), - `${level.charAt(0).toUpperCase()} (STDERR) message from stderr` + `${level.charAt(0).toUpperCase()} (STDERR) message from stderr`, ); // app/originApp @@ -377,7 +358,7 @@ Tinytest.add("logging - formats - without time", function (test) { app: "app", originApp: "app", }), - `${level.charAt(0).toUpperCase()} [app] message` + `${level.charAt(0).toUpperCase()} [app] message`, ); test.equal( Log.format({ @@ -387,7 +368,7 @@ Tinytest.add("logging - formats - without time", function (test) { app: "app", originApp: "proxy", }), - `${level.charAt(0).toUpperCase()} [app via proxy] message` + `${level.charAt(0).toUpperCase()} [app via proxy] message`, ); // source info @@ -400,7 +381,7 @@ Tinytest.add("logging - formats - without time", function (test) { line: 42, program: "server", }), - `${level.charAt(0).toUpperCase()} (server:app.js:42) message` + `${level.charAt(0).toUpperCase()} (server:app.js:42) message`, ); test.equal( Log.format({ @@ -410,7 +391,7 @@ Tinytest.add("logging - formats - without time", function (test) { file: "app.js", line: 42, }), - `${level.charAt(0).toUpperCase()} (app.js:42) message` + `${level.charAt(0).toUpperCase()} (app.js:42) message`, ); } Log.showTime = true; // reset diff --git a/packages/logging/package.js b/packages/logging/package.js index 51b58c8a3a..e953ba6d82 100644 --- a/packages/logging/package.js +++ b/packages/logging/package.js @@ -1,33 +1,33 @@ Package.describe({ - summary: 'Logging facility.', - version: '1.3.6', + summary: "Logging facility.", + version: "1.3.6", }); Npm.depends({ - 'chalk': '4.1.2', - '@babel/runtime': '7.20.7', + chalk: "4.1.2", + "@babel/runtime": "7.20.7", }); Npm.strip({ - 'es5-ext': ['test/'] + "es5-ext": ["test/"], }); Package.onUse(function (api) { - api.export('Log'); + api.export("Log"); // The `ecmascript-runtime-client` package is explicitly depended upon // here due to this package's dependency on // `String.prototype.padRight` which is polyfilled only in // `ecmascript-runtime-client@0.6.2` or newer. - api.use(['ejson', 'ecmascript', 'typescript', 'ecmascript-runtime-client']); - api.mainModule('logging.js'); - api.addFiles('logging_server.js', 'server'); - api.addFiles('logging_browser.js', 'client'); - api.mainModule('logging_cordova.js', 'web.cordova'); - api.addAssets('logging.d.ts', 'server'); + api.use(["ejson", "ecmascript", "typescript", "ecmascript-runtime-client"]); + api.mainModule("logging.js"); + api.addFiles("logging_server.js", "server"); + api.addFiles("logging_browser.js", "client"); + api.mainModule("logging_cordova.js", "web.cordova"); + api.addAssets("logging.d.ts", "server"); }); Package.onTest(function (api) { - api.use(['tinytest', 'ejson', 'ecmascript']); - api.use('logging', ['client', 'server']); - api.mainModule('logging_test.js', ['server', 'client']); + api.use(["tinytest", "ejson", "ecmascript"]); + api.use("logging", ["client", "server"]); + api.mainModule("logging_test.js", ["server", "client"]); }); diff --git a/packages/ordered-dict/ordered_dict.js b/packages/ordered-dict/ordered_dict.js index c55a6bfc74..db2b273be1 100644 --- a/packages/ordered-dict/ordered_dict.js +++ b/packages/ordered-dict/ordered_dict.js @@ -12,7 +12,7 @@ function element(key, value, next, prev) { key: key, value: value, next: next, - prev: prev + prev: prev, }; } @@ -23,13 +23,15 @@ export class OrderedDict { this._last = null; this._size = 0; - if (typeof args[0] === 'function') { + if (typeof args[0] === "function") { this._stringify = args.shift(); } else { - this._stringify = function (x) { return x; }; + this._stringify = function (x) { + return x; + }; } - args.forEach(kv => this.putBefore(kv[0], kv[1], null)); + args.forEach((kv) => this.putBefore(kv[0], kv[1], null)); } // the "prefix keys with a space" thing comes from here @@ -49,36 +51,26 @@ export class OrderedDict { _linkEltIn(elt) { if (!elt.next) { elt.prev = this._last; - if (this._last) - this._last.next = elt; + if (this._last) this._last.next = elt; this._last = elt; } else { elt.prev = elt.next.prev; elt.next.prev = elt; - if (elt.prev) - elt.prev.next = elt; + if (elt.prev) elt.prev.next = elt; } - if (this._first === null || this._first === elt.next) - this._first = elt; + if (this._first === null || this._first === elt.next) this._first = elt; } _linkEltOut(elt) { - if (elt.next) - elt.next.prev = elt.prev; - if (elt.prev) - elt.prev.next = elt.next; - if (elt === this._last) - this._last = elt.prev; - if (elt === this._first) - this._first = elt.next; + if (elt.next) elt.next.prev = elt.prev; + if (elt.prev) elt.prev.next = elt.next; + if (elt === this._last) this._last = elt.prev; + if (elt === this._first) this._first = elt.next; } putBefore(key, item, before) { - if (this._dict[this._k(key)]) - throw new Error(`Item ${key} already present in OrderedDict`); - const elt = before ? - element(key, item, this._dict[this._k(before)]) : - element(key, item, null); + if (this._dict[this._k(key)]) throw new Error(`Item ${key} already present in OrderedDict`); + const elt = before ? element(key, item, this._dict[this._k(before)]) : element(key, item, null); if (typeof elt.next === "undefined") throw new Error("could not find item to put this one before"); this._linkEltIn(elt); @@ -92,8 +84,7 @@ export class OrderedDict { remove(key) { const elt = this._dict[this._k(key)]; - if (typeof elt === "undefined") - throw new Error(`Item ${key} not present in OrderedDict`); + if (typeof elt === "undefined") throw new Error(`Item ${key} not present in OrderedDict`); this._linkEltOut(elt); this._size--; delete this._dict[this._k(key)]; @@ -107,10 +98,7 @@ export class OrderedDict { } has(key) { - return Object.prototype.hasOwnProperty.call( - this._dict, - this._k(key) - ); + return Object.prototype.hasOwnProperty.call(this._dict, this._k(key)); } // Iterate through the items in this dictionary in order, calling @@ -170,8 +158,7 @@ export class OrderedDict { prev(key) { if (this.has(key)) { const elt = this._dict[this._k(key)]; - if (elt.prev) - return elt.prev.key; + if (elt.prev) return elt.prev.key; } return null; } @@ -179,8 +166,7 @@ export class OrderedDict { next(key) { if (this.has(key)) { const elt = this._dict[this._k(key)]; - if (elt.next) - return elt.next.key; + if (elt.next) return elt.next.key; } return null; } @@ -194,7 +180,8 @@ export class OrderedDict { if (typeof eltBefore === "undefined") { throw new Error("Could not find element to move this one before"); } - if (eltBefore === elt.next) // no moving necessary + if (eltBefore === elt.next) + // no moving necessary return; // remove from its old place this._linkEltOut(elt); @@ -217,7 +204,7 @@ export class OrderedDict { } _checkRep() { - Object.keys(this._dict).forEach(k => { + Object.keys(this._dict).forEach((k) => { const v = this._dict[k]; if (v.next === v) { throw new Error("Next is a loop"); @@ -229,4 +216,4 @@ export class OrderedDict { } } -OrderedDict.BREAK = {"break": true}; +OrderedDict.BREAK = { break: true }; diff --git a/packages/ordered-dict/package.js b/packages/ordered-dict/package.js index b83fe76255..333b3b6486 100644 --- a/packages/ordered-dict/package.js +++ b/packages/ordered-dict/package.js @@ -1,11 +1,11 @@ Package.describe({ summary: "Ordered traversable dictionary with a mutable ordering", - version: '1.2.0', - documentation: null + version: "1.2.0", + documentation: null, }); Package.onUse(function (api) { - api.use('ecmascript'); - api.mainModule('ordered_dict.js'); - api.export('OrderedDict'); + api.use("ecmascript"); + api.mainModule("ordered_dict.js"); + api.export("OrderedDict"); }); diff --git a/packages/random/AbstractRandomGenerator.js b/packages/random/AbstractRandomGenerator.js index 2190bb6bc0..ef920c0a7d 100644 --- a/packages/random/AbstractRandomGenerator.js +++ b/packages/random/AbstractRandomGenerator.js @@ -7,10 +7,8 @@ // window.crypto.getRandomValues() or alea, the primitive is fraction and we use // that to construct hex string. - -const UNMISTAKABLE_CHARS = '23456789ABCDEFGHJKLMNPQRSTWXYZabcdefghijkmnopqrstuvwxyz'; -const BASE64_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + - '0123456789-_'; +const UNMISTAKABLE_CHARS = "23456789ABCDEFGHJKLMNPQRSTWXYZabcdefghijkmnopqrstuvwxyz"; +const BASE64_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789-_"; // `type` is one of `RandomGenerator.Type` as defined below. // @@ -19,13 +17,12 @@ const BASE64_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + // whose items will be `toString`ed and used as the seed to the Alea // algorithm export default class RandomGenerator { - /** * @name Random.fraction * @summary Return a number between 0 and 1, like `Math.random`. * @locus Anywhere */ - fraction () { + fraction() { throw new Error(`Unknown random generator type`); } @@ -35,13 +32,13 @@ export default class RandomGenerator { * @locus Anywhere * @param {Number} n Length of the string */ - hexString (digits) { - return this._randomString(digits, '0123456789abcdef'); + hexString(digits) { + return this._randomString(digits, "0123456789abcdef"); } - _randomString (charsCount, alphabet) { - let result = ''; - for (let i = 0; i < charsCount; i++) { + _randomString(charsCount, alphabet) { + let result = ""; + for (let i = 0; i < charsCount; i++) { result += this.choice(alphabet); } return result; @@ -55,7 +52,7 @@ export default class RandomGenerator { * @param {Number} [n] Optional length of the identifier in characters * (defaults to 17) */ - id (charsCount) { + id(charsCount) { // 17 characters is around 96 bits of entropy, which is the amount of // state in the Alea PRNG. if (charsCount === undefined) { @@ -74,7 +71,7 @@ export default class RandomGenerator { * @param {Number} [n] Optional length of the secret string (defaults to 43 * characters, or 256 bits of entropy) */ - secret (charsCount) { + secret(charsCount) { // Default to 256 bits of entropy, or 43 characters at 6 bits per // character. if (charsCount === undefined) { @@ -90,9 +87,9 @@ export default class RandomGenerator { * @locus Anywhere * @param {Array|String} arrayOrString Array or string to choose from */ - choice (arrayOrString) { + choice(arrayOrString) { const index = Math.floor(this.fraction() * arrayOrString.length); - if (typeof arrayOrString === 'string') { + if (typeof arrayOrString === "string") { return arrayOrString.substr(index, 1); } return arrayOrString[index]; diff --git a/packages/random/AleaRandomGenerator.js b/packages/random/AleaRandomGenerator.js index 8338b6a6a0..7e7eb827e6 100644 --- a/packages/random/AleaRandomGenerator.js +++ b/packages/random/AleaRandomGenerator.js @@ -1,4 +1,4 @@ -import RandomGenerator from './AbstractRandomGenerator'; +import RandomGenerator from "./AbstractRandomGenerator"; // Alea PRNG, which is not cryptographically strong // see http://baagoe.org/en/wiki/Better_random_numbers_for_javascript @@ -22,7 +22,7 @@ function Alea(seeds) { return (n >>> 0) * 2.3283064365386963e-10; // 2^-32 }; - mash.version = 'Mash 0.9'; + mash.version = "Mash 0.9"; return mash; } @@ -31,12 +31,12 @@ function Alea(seeds) { let s2 = 0; let c = 1; if (seeds.length === 0) { - seeds = [+new Date]; + seeds = [+new Date()]; } let mash = Mash(); - s0 = mash(' '); - s1 = mash(' '); - s2 = mash(' '); + s0 = mash(" "); + s1 = mash(" "); + s2 = mash(" "); for (let i = 0; i < seeds.length; i++) { s0 -= mash(seeds[i]); @@ -55,17 +55,17 @@ function Alea(seeds) { mash = null; const random = () => { - const t = (2091639 * s0) + (c * 2.3283064365386963e-10); // 2^-32 + const t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32 s0 = s1; s1 = s2; - return s2 = t - (c = t | 0); + return (s2 = t - (c = t | 0)); }; random.uint32 = () => random() * 0x100000000; // 2^32 // oxlint-disable-next-line oxc/erasing-op -- intentional bitwise OR for float-to-int (Alea PRNG algorithm) - random.fract53 = () => random() + ((random() * 0x200000 | 0) * 1.1102230246251565e-16); // 2^-53 + random.fract53 = () => random() + ((random() * 0x200000) | 0) * 1.1102230246251565e-16; // 2^-53 - random.version = 'Alea 0.9'; + random.version = "Alea 0.9"; random.args = seeds; return random; } @@ -75,10 +75,10 @@ function Alea(seeds) { // whose items will be `toString`ed and used as the seed to the Alea // algorithm export default class AleaRandomGenerator extends RandomGenerator { - constructor ({ seeds = [] } = {}) { + constructor({ seeds = [] } = {}) { super(); if (!seeds) { - throw new Error('No seeds were provided for Alea PRNG'); + throw new Error("No seeds were provided for Alea PRNG"); } this.alea = Alea(seeds); } @@ -88,7 +88,7 @@ export default class AleaRandomGenerator extends RandomGenerator { * @summary Return a number between 0 and 1, like `Math.random`. * @locus Anywhere */ - fraction () { + fraction() { return this.alea(); } } diff --git a/packages/random/BrowserRandomGenerator.js b/packages/random/BrowserRandomGenerator.js index 03c22dc7b4..17c4b3353f 100644 --- a/packages/random/BrowserRandomGenerator.js +++ b/packages/random/BrowserRandomGenerator.js @@ -1,4 +1,4 @@ -import RandomGenerator from './AbstractRandomGenerator'; +import RandomGenerator from "./AbstractRandomGenerator"; // cryptographically strong PRNGs available in modern browsers export default class BrowserRandomGenerator extends RandomGenerator { @@ -7,7 +7,7 @@ export default class BrowserRandomGenerator extends RandomGenerator { * @summary Return a number between 0 and 1, like `Math.random`. * @locus Anywhere */ - fraction () { + fraction() { const array = new Uint32Array(1); window.crypto.getRandomValues(array); return array[0] * 2.3283064365386963e-10; // 2^-32 diff --git a/packages/random/NodeRandomGenerator.js b/packages/random/NodeRandomGenerator.js index f2cb8ff7a4..440bce0d38 100644 --- a/packages/random/NodeRandomGenerator.js +++ b/packages/random/NodeRandomGenerator.js @@ -1,5 +1,5 @@ -import crypto from 'crypto'; -import RandomGenerator from './AbstractRandomGenerator'; +import crypto from "crypto"; +import RandomGenerator from "./AbstractRandomGenerator"; export default class NodeRandomGenerator extends RandomGenerator { /** @@ -7,7 +7,7 @@ export default class NodeRandomGenerator extends RandomGenerator { * @summary Return a number between 0 and 1, like `Math.random`. * @locus Anywhere */ - fraction () { + fraction() { const numerator = Number.parseInt(this.hexString(8), 16); return numerator * 2.3283064365386963e-10; // 2^-3; } @@ -18,7 +18,7 @@ export default class NodeRandomGenerator extends RandomGenerator { * @locus Anywhere * @param {Number} n Length of the string */ - hexString (digits) { + hexString(digits) { const numBytes = Math.ceil(digits / 2); let bytes; // Try to get cryptographically strong randomness. Fall back to @@ -29,7 +29,7 @@ export default class NodeRandomGenerator extends RandomGenerator { // XXX should re-throw any error except insufficient entropy bytes = crypto.pseudoRandomBytes(numBytes); } - const result = bytes.toString('hex'); + const result = bytes.toString("hex"); // If the number of digits is odd, we'll have generated an extra 4 bits // of randomness, so we need to trim the last digit. return result.substring(0, digits); diff --git a/packages/random/createAleaGenerator.js b/packages/random/createAleaGenerator.js index 49bca69840..f992be5761 100644 --- a/packages/random/createAleaGenerator.js +++ b/packages/random/createAleaGenerator.js @@ -1,31 +1,29 @@ -import AleaRandomGenerator from './AleaRandomGenerator'; +import AleaRandomGenerator from "./AleaRandomGenerator"; // instantiate RNG. Heuristically collect entropy from various sources when a // cryptographic PRNG isn't available. // client sources -const height = (typeof window !== 'undefined' && window.innerHeight) || - (typeof document !== 'undefined' - && document.documentElement - && document.documentElement.clientHeight) || - (typeof document !== 'undefined' - && document.body - && document.body.clientHeight) || - 1; +const height = + (typeof window !== "undefined" && window.innerHeight) || + (typeof document !== "undefined" && + document.documentElement && + document.documentElement.clientHeight) || + (typeof document !== "undefined" && document.body && document.body.clientHeight) || + 1; -const width = (typeof window !== 'undefined' && window.innerWidth) || - (typeof document !== 'undefined' - && document.documentElement - && document.documentElement.clientWidth) || - (typeof document !== 'undefined' - && document.body - && document.body.clientWidth) || - 1; +const width = + (typeof window !== "undefined" && window.innerWidth) || + (typeof document !== "undefined" && + document.documentElement && + document.documentElement.clientWidth) || + (typeof document !== "undefined" && document.body && document.body.clientWidth) || + 1; -const agent = (typeof navigator !== 'undefined' && navigator.userAgent) || ''; +const agent = (typeof navigator !== "undefined" && navigator.userAgent) || ""; export default function createAleaGenerator() { return new AleaRandomGenerator({ - seeds: [new Date, height, width, agent, Math.random()], + seeds: [new Date(), height, width, agent, Math.random()], }); } diff --git a/packages/random/createRandom.js b/packages/random/createRandom.js index a1702641ca..de40e8a337 100644 --- a/packages/random/createRandom.js +++ b/packages/random/createRandom.js @@ -1,12 +1,12 @@ -import AleaRandomGenerator from './AleaRandomGenerator' -import createAleaGeneratorWithGeneratedSeed from './createAleaGenerator'; +import AleaRandomGenerator from "./AleaRandomGenerator"; +import createAleaGeneratorWithGeneratedSeed from "./createAleaGenerator"; export default function createRandom(generator) { // Create a non-cryptographically secure PRNG with a given seed (using // the Alea algorithm) generator.createWithSeeds = (...seeds) => { if (seeds.length === 0) { - throw new Error('No seeds were provided'); + throw new Error("No seeds were provided"); } return new AleaRandomGenerator({ seeds }); }; diff --git a/packages/random/main_client.js b/packages/random/main_client.js index 3c00042b43..88c53e25de 100644 --- a/packages/random/main_client.js +++ b/packages/random/main_client.js @@ -1,17 +1,16 @@ // We use cryptographically strong PRNGs (window.crypto.getRandomValues()) -// when available. If these PRNGs fail, we fall back to the Alea PRNG, which is -// not cryptographically strong, and we seed it with various sources +// when available. If these PRNGs fail, we fall back to the Alea PRNG, which is +// not cryptographically strong, and we seed it with various sources // such as the date, Math.random, and window size on the client. -// When using window.crypto.getRandomValues() or alea, the primitive is fraction +// When using window.crypto.getRandomValues() or alea, the primitive is fraction // and we use that to construct hex string. -import BrowserRandomGenerator from './BrowserRandomGenerator'; -import createAleaGeneratorWithGeneratedSeed from './createAleaGenerator'; -import createRandom from './createRandom'; +import BrowserRandomGenerator from "./BrowserRandomGenerator"; +import createAleaGeneratorWithGeneratedSeed from "./createAleaGenerator"; +import createRandom from "./createRandom"; let generator; -if (typeof window !== 'undefined' && window.crypto && - window.crypto.getRandomValues) { +if (typeof window !== "undefined" && window.crypto && window.crypto.getRandomValues) { generator = new BrowserRandomGenerator(); } else { // On IE 10 and below, there's no browser crypto API @@ -22,5 +21,4 @@ if (typeof window !== 'undefined' && window.crypto && generator = createAleaGeneratorWithGeneratedSeed(); } - export const Random = createRandom(generator); diff --git a/packages/random/main_server.js b/packages/random/main_server.js index 1a85f61f1a..7bb0a74bb4 100644 --- a/packages/random/main_server.js +++ b/packages/random/main_server.js @@ -2,7 +2,7 @@ // When using crypto.getRandomValues(), our primitive is hexString(), // from which we construct fraction(). -import NodeRandomGenerator from './NodeRandomGenerator'; -import createRandom from './createRandom'; +import NodeRandomGenerator from "./NodeRandomGenerator"; +import createRandom from "./createRandom"; export const Random = createRandom(new NodeRandomGenerator()); diff --git a/packages/random/package.js b/packages/random/package.js index 69c806c7c4..40c8466a63 100644 --- a/packages/random/package.js +++ b/packages/random/package.js @@ -1,19 +1,19 @@ Package.describe({ - summary: 'Random number generator and utilities', - version: '1.2.2', + summary: "Random number generator and utilities", + version: "1.2.2", }); Package.onUse(function (api) { - api.use('ecmascript'); - api.export('Random'); - api.mainModule('main_client.js', 'client'); - api.mainModule('main_server.js', 'server'); - api.addAssets('random.d.ts', 'server'); + api.use("ecmascript"); + api.export("Random"); + api.mainModule("main_client.js", "client"); + api.mainModule("main_server.js", "server"); + api.addAssets("random.d.ts", "server"); }); Package.onTest(function (api) { - api.use('random'); - api.use('ecmascript'); - api.use('tinytest'); - api.mainModule('random_tests.js'); + api.use("random"); + api.use("ecmascript"); + api.use("tinytest"); + api.mainModule("random_tests.js"); }); diff --git a/packages/random/random_tests.js b/packages/random/random_tests.js index 8586ccb529..8140e7de49 100644 --- a/packages/random/random_tests.js +++ b/packages/random/random_tests.js @@ -1,7 +1,7 @@ -import { Meteor } from 'meteor/meteor'; -import { Random } from 'meteor/random'; +import { Meteor } from "meteor/meteor"; +import { Random } from "meteor/random"; -Tinytest.add('random', function (test) { +Tinytest.add("random", function (test) { // Deterministic with a specified seed, which should generate the // same sequence in all environments. // @@ -11,15 +11,15 @@ Tinytest.add('random', function (test) { // sequence for a seed, as long as the sequence is consistent for // a particular release. const random = Random.createWithSeeds(0); - test.equal(random.id(), 'cp9hWvhg8GSvuZ9os'); - test.equal(random.id(), '3f3k6Xo7rrHCifQhR'); - test.equal(random.id(), 'shxDnjWWmnKPEoLhM'); - test.equal(random.id(), '6QTjB8C5SEqhmz4ni'); + test.equal(random.id(), "cp9hWvhg8GSvuZ9os"); + test.equal(random.id(), "3f3k6Xo7rrHCifQhR"); + test.equal(random.id(), "shxDnjWWmnKPEoLhM"); + test.equal(random.id(), "6QTjB8C5SEqhmz4ni"); }); // node crypto and window.crypto.getRandomValues() don't let us specify a seed, // but at least test that the output is in the right format. -Tinytest.add('random - format', function (test) { +Tinytest.add("random - format", function (test) { const idLen = 17; test.equal(Random.id().length, idLen); test.equal(Random.id(29).length, 29); @@ -35,18 +35,21 @@ Tinytest.add('random - format', function (test) { test.equal(Random.secret(13).length, 13); }); -Tinytest.add('random - Alea is last resort', function (test) { +Tinytest.add("random - Alea is last resort", function (test) { if (Meteor.isServer) { test.isTrue(Random.alea === undefined); } if (Meteor.isClient) { - const useGetRandomValues = !!(typeof window !== 'undefined' && - window.crypto && window.crypto.getRandomValues); + const useGetRandomValues = !!( + typeof window !== "undefined" && + window.crypto && + window.crypto.getRandomValues + ); test.equal(Random.alea === undefined, useGetRandomValues); } }); -Tinytest.add('random - createWithSeeds requires parameters', function (test) { +Tinytest.add("random - createWithSeeds requires parameters", function (test) { test.throws(function () { Random.createWithSeeds(); }); diff --git a/packages/rate-limit/package.js b/packages/rate-limit/package.js index 8b5f453c01..2edc5bbe1c 100644 --- a/packages/rate-limit/package.js +++ b/packages/rate-limit/package.js @@ -1,29 +1,29 @@ Package.describe({ - name: 'rate-limit', - version: '1.1.2', + name: "rate-limit", + version: "1.1.2", // Brief, one-line summary of the package. - summary: 'An algorithm for rate limiting anything', + summary: "An algorithm for rate limiting anything", // URL to the Git repository containing the source code for this package. - git: '', + git: "", // By default, Meteor will default to using README.md for documentation. // To avoid submitting documentation, set this field to null. - documentation: 'README.md', + documentation: "README.md", }); -Package.onUse(function(api) { - api.use('random'); - api.use('ecmascript'); - api.mainModule('rate-limit.js'); - api.export('RateLimiter'); +Package.onUse(function (api) { + api.use("random"); + api.use("ecmascript"); + api.mainModule("rate-limit.js"); + api.export("RateLimiter"); }); -Package.onTest(function(api) { - api.use('test-helpers', ['client', 'server']); - api.use('ecmascript'); - api.use('random'); - api.use('ddp-rate-limiter'); - api.use('tinytest'); - api.use('rate-limit'); - api.use('ddp-common'); - api.mainModule('rate-limit-tests.js'); +Package.onTest(function (api) { + api.use("test-helpers", ["client", "server"]); + api.use("ecmascript"); + api.use("random"); + api.use("ddp-rate-limiter"); + api.use("tinytest"); + api.use("rate-limit"); + api.use("ddp-common"); + api.mainModule("rate-limit-tests.js"); }); diff --git a/packages/rate-limit/rate-limit-tests.js b/packages/rate-limit/rate-limit-tests.js index 5d22a58ae6..bd67cbad5f 100644 --- a/packages/rate-limit/rate-limit-tests.js +++ b/packages/rate-limit/rate-limit-tests.js @@ -19,44 +19,39 @@ // XXX These tests should be refactored to use Tinytest.add instead of // testAsyncMulti as they're all on the server. Any future tests should be // written that way. -import { Meteor } from 'meteor/meteor'; -import { RateLimiter } from 'meteor/rate-limit'; -import { DDPCommon } from 'meteor/ddp-common'; +import { Meteor } from "meteor/meteor"; +import { RateLimiter } from "meteor/rate-limit"; +import { DDPCommon } from "meteor/ddp-common"; -Tinytest.add('rate limit tests - Check empty constructor creation', +Tinytest.add("rate limit tests - Check empty constructor creation", function (test) { + const r = new RateLimiter(); + test.equal(r.rules, {}); +}); + +Tinytest.add( + "rate limit tests - Check single rule with multiple " + "invocations, only 1 that matches", function (test) { const r = new RateLimiter(); - test.equal(r.rules, {}); + const userIdOne = 1; + const restrictJustUserIdOneRule = { + userId: userIdOne, + IPAddr: null, + method: null, + }; + r.addRule(restrictJustUserIdOneRule, 1, 1000); + const connectionHandle = createTempConnectionHandle(123, "127.0.0.1"); + const methodInvc1 = createTempMethodInvocation(userIdOne, connectionHandle, "login"); + const methodInvc2 = createTempMethodInvocation(2, connectionHandle, "login"); + for (let i = 0; i < 2; i++) { + r.increment(methodInvc1); + r.increment(methodInvc2); + } + test.equal(r.check(methodInvc1).allowed, false); + test.equal(r.check(methodInvc2).allowed, true); }, ); -Tinytest.add('rate limit tests - Check single rule with multiple ' + - 'invocations, only 1 that matches', -function (test) { - const r = new RateLimiter(); - const userIdOne = 1; - const restrictJustUserIdOneRule = { - userId: userIdOne, - IPAddr: null, - method: null, - }; - r.addRule(restrictJustUserIdOneRule, 1, 1000); - const connectionHandle = createTempConnectionHandle(123, '127.0.0.1'); - const methodInvc1 = createTempMethodInvocation(userIdOne, connectionHandle, - 'login'); - const methodInvc2 = createTempMethodInvocation(2, connectionHandle, - 'login'); - for (let i = 0; i < 2; i++) { - r.increment(methodInvc1); - r.increment(methodInvc2); - } - test.equal(r.check(methodInvc1).allowed, false); - test.equal(r.check(methodInvc2).allowed, true); -}, -); - -testAsyncMulti('rate limit tests - Run multiple invocations and wait for one' + - ' to reset', [ +testAsyncMulti("rate limit tests - Run multiple invocations and wait for one" + " to reset", [ function (test, expect) { this.r = new RateLimiter(); this.userIdOne = 1; @@ -67,18 +62,19 @@ testAsyncMulti('rate limit tests - Run multiple invocations and wait for one' + method: null, }; this.r.addRule(this.restrictJustUserIdOneRule, 1, 500); - this.connectionHandle = createTempConnectionHandle(123, '127.0.0.1') - this.methodInvc1 = createTempMethodInvocation(this.userIdOne, - this.connectionHandle, 'login'); - this.methodInvc2 = createTempMethodInvocation(this.userIdTwo, - this.connectionHandle, 'login'); + this.connectionHandle = createTempConnectionHandle(123, "127.0.0.1"); + this.methodInvc1 = createTempMethodInvocation(this.userIdOne, this.connectionHandle, "login"); + this.methodInvc2 = createTempMethodInvocation(this.userIdTwo, this.connectionHandle, "login"); for (let i = 0; i < 2; i++) { this.r.increment(this.methodInvc1); this.r.increment(this.methodInvc2); } test.equal(this.r.check(this.methodInvc1).allowed, false); test.equal(this.r.check(this.methodInvc2).allowed, true); - Meteor.setTimeout(expect(function () { }), 1000); + Meteor.setTimeout( + expect(function () {}), + 1000, + ); }, function (test) { for (let i = 0; i < 100; i++) { @@ -89,74 +85,73 @@ testAsyncMulti('rate limit tests - Run multiple invocations and wait for one' + }, ]); -Tinytest.add('rate limit tests - Check two rules that affect same methodInvc' + - ' still throw', function (test) { - const r = new RateLimiter(); - const loginMethodRule = { - userId: null, - IPAddr: null, - method: 'login', - }; - const onlyLimitEvenUserIdRule = { - userId: userId => userId % 2 === 0, - IPAddr: null, - method: null, - }; - r.addRule(loginMethodRule, 10, 100); - r.addRule(onlyLimitEvenUserIdRule, 4, 100); - const connectionHandle = createTempConnectionHandle(1234, '127.0.0.1'); - const methodInvc1 = createTempMethodInvocation(1, connectionHandle, - 'login'); - const methodInvc2 = createTempMethodInvocation(2, connectionHandle, - 'login'); - const methodInvc3 = createTempMethodInvocation(3, connectionHandle, - 'test'); - for (let i = 0; i < 5; i++) { +Tinytest.add( + "rate limit tests - Check two rules that affect same methodInvc" + " still throw", + function (test) { + const r = new RateLimiter(); + const loginMethodRule = { + userId: null, + IPAddr: null, + method: "login", + }; + const onlyLimitEvenUserIdRule = { + userId: (userId) => userId % 2 === 0, + IPAddr: null, + method: null, + }; + r.addRule(loginMethodRule, 10, 100); + r.addRule(onlyLimitEvenUserIdRule, 4, 100); + const connectionHandle = createTempConnectionHandle(1234, "127.0.0.1"); + const methodInvc1 = createTempMethodInvocation(1, connectionHandle, "login"); + const methodInvc2 = createTempMethodInvocation(2, connectionHandle, "login"); + const methodInvc3 = createTempMethodInvocation(3, connectionHandle, "test"); + for (let i = 0; i < 5; i++) { + r.increment(methodInvc1); + r.increment(methodInvc2); + r.increment(methodInvc3); + } + // After for loop runs, we only have 10 runs, so that's under the limit + test.equal(r.check(methodInvc1).allowed, true); + // However, this triggers userId rule since this userId is even + test.equal(r.check(methodInvc2).allowed, false); + test.equal(r.check(methodInvc2).allowed, false); + // Running one more test causes it to be false, since we're at 11 now. r.increment(methodInvc1); - r.increment(methodInvc2); - r.increment(methodInvc3); - } - // After for loop runs, we only have 10 runs, so that's under the limit - test.equal(r.check(methodInvc1).allowed, true); - // However, this triggers userId rule since this userId is even - test.equal(r.check(methodInvc2).allowed, false); - test.equal(r.check(methodInvc2).allowed, false); - // Running one more test causes it to be false, since we're at 11 now. - r.increment(methodInvc1); - test.equal(r.check(methodInvc1).allowed, false); - // 3rd Method Invocation isn't affected by either rules. - test.equal(r.check(methodInvc3).allowed, true); -}); + test.equal(r.check(methodInvc1).allowed, false); + // 3rd Method Invocation isn't affected by either rules. + test.equal(r.check(methodInvc3).allowed, true); + }, +); -Tinytest.add('rate limit tests - Check one rule affected by two different ' + - 'invocations', function (test) { - const r = new RateLimiter(); - const loginMethodRule = { - userId: null, - IPAddr: null, - method: 'login', - }; - r.addRule(loginMethodRule, 10, 10000); +Tinytest.add( + "rate limit tests - Check one rule affected by two different " + "invocations", + function (test) { + const r = new RateLimiter(); + const loginMethodRule = { + userId: null, + IPAddr: null, + method: "login", + }; + r.addRule(loginMethodRule, 10, 10000); - const connectionHandle = createTempConnectionHandle(1234, '127.0.0.1'); - const methodInvc1 = createTempMethodInvocation(1, connectionHandle, - 'login'); - const methodInvc2 = createTempMethodInvocation(2, connectionHandle, - 'login'); + const connectionHandle = createTempConnectionHandle(1234, "127.0.0.1"); + const methodInvc1 = createTempMethodInvocation(1, connectionHandle, "login"); + const methodInvc2 = createTempMethodInvocation(2, connectionHandle, "login"); - for (let i = 0; i < 5; i++) { + for (let i = 0; i < 5; i++) { + r.increment(methodInvc1); + r.increment(methodInvc2); + } + // This throws us over the limit since both increment the login rule + // counter r.increment(methodInvc1); - r.increment(methodInvc2); - } - // This throws us over the limit since both increment the login rule - // counter - r.increment(methodInvc1); - test.equal(r.check(methodInvc1).allowed, false); - test.equal(r.check(methodInvc2).allowed, false); -}); + test.equal(r.check(methodInvc1).allowed, false); + test.equal(r.check(methodInvc2).allowed, false); + }, +); -Tinytest.add('rate limit tests - add global rule', function (test) { +Tinytest.add("rate limit tests - add global rule", function (test) { const r = new RateLimiter(); const globalRule = { userId: null, @@ -165,15 +160,12 @@ Tinytest.add('rate limit tests - add global rule', function (test) { }; r.addRule(globalRule, 1, 10000); - const connectionHandle = createTempConnectionHandle(1234, '127.0.0.1'); - const connectionHandle2 = createTempConnectionHandle(1234, '127.0.0.2'); + const connectionHandle = createTempConnectionHandle(1234, "127.0.0.1"); + const connectionHandle2 = createTempConnectionHandle(1234, "127.0.0.2"); - const methodInvc1 = createTempMethodInvocation(1, connectionHandle, - 'login'); - const methodInvc2 = createTempMethodInvocation(2, connectionHandle2, - 'test'); - const methodInvc3 = createTempMethodInvocation(3, connectionHandle, - 'user-accounts'); + const methodInvc1 = createTempMethodInvocation(1, connectionHandle, "login"); + const methodInvc2 = createTempMethodInvocation(2, connectionHandle2, "test"); + const methodInvc3 = createTempMethodInvocation(3, connectionHandle, "user-accounts"); // First invocation, all methods would still be allowed. r.increment(methodInvc2); @@ -187,55 +179,52 @@ Tinytest.add('rate limit tests - add global rule', function (test) { test.equal(r.check(methodInvc3).allowed, false); }); -Tinytest.add('rate limit tests - Fuzzy rule match does not trigger rate limit', - function (test) { - const r = new RateLimiter(); - const rule = { - a: inp => inp % 3 === 0, - b: 5, - c: 'hi', - }; - r.addRule(rule, 1, 10000); - const input = { - a: 3, - b: 5, - }; - for (let i = 0; i < 5; i++) { - r.increment(input); - } - test.equal(r.check(input).allowed, true); - const matchingInput = { - a: 3, - b: 5, - c: 'hi', - d: 1, - }; - r.increment(matchingInput); - r.increment(matchingInput); - // Past limit so should be false - test.equal(r.check(matchingInput).allowed, false); - - // Add secondary rule and check that longer time is returned when multiple - // rules limits are hit - const newRule = { - a: inp => inp % 3 === 0, - b: 5, - c: 'hi', - d: 1, - }; - r.addRule(newRule, 1, 10); - // First rule should still throw while second rule will trigger as well, - // causing us to return longer time to reset to user - r.increment(matchingInput); - r.increment(matchingInput); - test.equal(r.check(matchingInput).timeToReset > 50, true); - }, -); +Tinytest.add("rate limit tests - Fuzzy rule match does not trigger rate limit", function (test) { + const r = new RateLimiter(); + const rule = { + a: (inp) => inp % 3 === 0, + b: 5, + c: "hi", + }; + r.addRule(rule, 1, 10000); + const input = { + a: 3, + b: 5, + }; + for (let i = 0; i < 5; i++) { + r.increment(input); + } + test.equal(r.check(input).allowed, true); + const matchingInput = { + a: 3, + b: 5, + c: "hi", + d: 1, + }; + r.increment(matchingInput); + r.increment(matchingInput); + // Past limit so should be false + test.equal(r.check(matchingInput).allowed, false); + // Add secondary rule and check that longer time is returned when multiple + // rules limits are hit + const newRule = { + a: (inp) => inp % 3 === 0, + b: 5, + c: "hi", + d: 1, + }; + r.addRule(newRule, 1, 10); + // First rule should still throw while second rule will trigger as well, + // causing us to return longer time to reset to user + r.increment(matchingInput); + r.increment(matchingInput); + test.equal(r.check(matchingInput).timeToReset > 50, true); +}); /****** Test Our Helper Methods *****/ -Tinytest.add('rate limit tests - test matchRule method', function (test) { +Tinytest.add("rate limit tests - test matchRule method", function (test) { const r = new RateLimiter(); const globalRule = { userId: null, @@ -247,9 +236,9 @@ Tinytest.add('rate limit tests - test matchRule method', function (test) { const rateLimiterInput = { userId: 1023, - IPAddr: '127.0.0.1', - type: 'sub', - name: 'getSubLists', + IPAddr: "127.0.0.1", + type: "sub", + name: "getSubLists", }; test.equal(r.rules[globalRuleId].match(rateLimiterInput), true); @@ -269,54 +258,52 @@ Tinytest.add('rate limit tests - test matchRule method', function (test) { const notCompleteInput = { userId: 102, - IPAddr: '127.0.0.1', + IPAddr: "127.0.0.1", }; test.equal(r.rules[globalRuleId].match(notCompleteInput), true); test.equal(r.rules[oneNotNullId].match(notCompleteInput), false); }); -Tinytest.add('rate limit tests - test generateMethodKey string', - function (test) { - const r = new RateLimiter(); - const globalRule = { - userId: null, - IPAddr: null, - type: null, - name: null, - }; - const globalRuleId = r.addRule(globalRule); +Tinytest.add("rate limit tests - test generateMethodKey string", function (test) { + const r = new RateLimiter(); + const globalRule = { + userId: null, + IPAddr: null, + type: null, + name: null, + }; + const globalRuleId = r.addRule(globalRule); - const rateLimiterInput = { - userId: 1023, - IPAddr: '127.0.0.1', - type: 'sub', - name: 'getSubLists', - }; + const rateLimiterInput = { + userId: 1023, + IPAddr: "127.0.0.1", + type: "sub", + name: "getSubLists", + }; - test.equal(r.rules[globalRuleId]._generateKeyString(rateLimiterInput), ''); - globalRule.userId = 1023; + test.equal(r.rules[globalRuleId]._generateKeyString(rateLimiterInput), ""); + globalRule.userId = 1023; - test.equal(r.rules[globalRuleId]._generateKeyString(rateLimiterInput), - 'userId1023'); + test.equal(r.rules[globalRuleId]._generateKeyString(rateLimiterInput), "userId1023"); - const ruleWithFuncs = { - userId: input => input % 2 === 0, - IPAddr: null, - type: null, - }; - const funcRuleId = r.addRule(ruleWithFuncs); - test.equal(r.rules[funcRuleId]._generateKeyString(rateLimiterInput), ''); - rateLimiterInput.userId = 1024; - test.equal(r.rules[funcRuleId]._generateKeyString(rateLimiterInput), - 'userId1024'); + const ruleWithFuncs = { + userId: (input) => input % 2 === 0, + IPAddr: null, + type: null, + }; + const funcRuleId = r.addRule(ruleWithFuncs); + test.equal(r.rules[funcRuleId]._generateKeyString(rateLimiterInput), ""); + rateLimiterInput.userId = 1024; + test.equal(r.rules[funcRuleId]._generateKeyString(rateLimiterInput), "userId1024"); - const multipleRules = ruleWithFuncs; - multipleRules.IPAddr = '127.0.0.1'; - const multipleRuleId = r.addRule(multipleRules); - test.equal(r.rules[multipleRuleId]._generateKeyString(rateLimiterInput), - 'userId1024IPAddr127.0.0.1'); - }, -); + const multipleRules = ruleWithFuncs; + multipleRules.IPAddr = "127.0.0.1"; + const multipleRuleId = r.addRule(multipleRules); + test.equal( + r.rules[multipleRuleId]._generateKeyString(rateLimiterInput), + "userId1024IPAddr127.0.0.1", + ); +}); function createTempConnectionHandle(id, clientIP) { return { @@ -325,7 +312,7 @@ function createTempConnectionHandle(id, clientIP) { this.close(); }, onClose(fn) { - const cb = Meteor.bindEnvironment(fn, 'connection onClose callback'); + const cb = Meteor.bindEnvironment(fn, "connection onClose callback"); if (this.inQueue) { this._closeCallbacks.push(cb); } else { diff --git a/packages/rate-limit/rate-limit.js b/packages/rate-limit/rate-limit.js index 73f0578d9e..a533d71693 100644 --- a/packages/rate-limit/rate-limit.js +++ b/packages/rate-limit/rate-limit.js @@ -1,5 +1,5 @@ -import { Meteor } from 'meteor/meteor'; -import { Random } from 'meteor/random'; +import { Meteor } from "meteor/meteor"; +import { Random } from "meteor/random"; // Default time interval (in milliseconds) to reset rate limit counters const DEFAULT_INTERVAL_TIME_IN_MILLISECONDS = 1000; @@ -39,22 +39,20 @@ class Rule { // rule.matchers. If the match fails, search short circuits instead of // iterating through all matchers. match(input) { - return Object - .entries(this._matchers) - .every(([key, matcher]) => { - if (matcher !== null) { - if (!hasOwn.call(input, key)) { - return false; - } else if (typeof matcher === 'function') { - if (!(matcher(input[key]))) { - return false; - } - } else if (matcher !== input[key]) { + return Object.entries(this._matchers).every(([key, matcher]) => { + if (matcher !== null) { + if (!hasOwn.call(input, key)) { + return false; + } else if (typeof matcher === "function") { + if (!matcher(input[key])) { return false; } + } else if (matcher !== input[key]) { + return false; } - return true; - }); + } + return true; + }); } // Generates unique key string for provided input by concatenating all the @@ -64,7 +62,7 @@ class Rule { return Object.entries(this._matchers) .filter(([key]) => this._matchers[key] !== null) .reduce((returnString, [key, matcher]) => { - if (typeof matcher === 'function') { + if (typeof matcher === "function") { if (matcher(input[key])) { returnString += key + input[key]; } @@ -72,7 +70,7 @@ class Rule { returnString += key + input[key]; } return returnString; - }, ''); + }, ""); } // Applies the provided input and returns the key string, time since counters @@ -120,19 +118,19 @@ class RateLimiter { } /** - * Checks if this input has exceeded any rate limits. - * @param {object} input dictionary containing key-value pairs of attributes - * that match to rules - * @return {object} Returns object of following structure - * { 'allowed': boolean - is this input allowed - * 'timeToReset': integer | Infinity - returns time until counters are reset - * in milliseconds - * 'numInvocationsLeft': integer | Infinity - returns number of calls left - * before limit is reached - * } - * If multiple rules match, the least number of invocations left is returned. - * If the rate limit has been reached, the longest timeToReset is returned. - */ + * Checks if this input has exceeded any rate limits. + * @param {object} input dictionary containing key-value pairs of attributes + * that match to rules + * @return {object} Returns object of following structure + * { 'allowed': boolean - is this input allowed + * 'timeToReset': integer | Infinity - returns time until counters are reset + * in milliseconds + * 'numInvocationsLeft': integer | Infinity - returns number of calls left + * before limit is reached + * } + * If multiple rules match, the least number of invocations left is returned. + * If the rate limit has been reached, the longest timeToReset is returned. + */ check(input) { const reply = { allowed: true, @@ -148,8 +146,7 @@ class RateLimiter { if (ruleResult.timeToNextReset < 0) { // Reset all the counters since the rule has reset rule.resetCounter(); - ruleResult.timeSinceLastReset = new Date().getTime() - - rule._lastResetTime; + ruleResult.timeSinceLastReset = new Date().getTime() - rule._lastResetTime; ruleResult.timeToNextReset = rule.options.intervalTime; numInvocations = 0; } @@ -169,11 +166,12 @@ class RateLimiter { } else { // If this is an allowed attempt and we haven't failed on any of the // other rules that match, update the reply field. - if (rule.options.numRequestsAllowed - numInvocations < - reply.numInvocationsLeft && reply.allowed) { + if ( + rule.options.numRequestsAllowed - numInvocations < reply.numInvocationsLeft && + reply.allowed + ) { reply.timeToReset = ruleResult.timeToNextReset; - reply.numInvocationsLeft = rule.options.numRequestsAllowed - - numInvocations; + reply.numInvocationsLeft = rule.options.numRequestsAllowed - numInvocations; } reply.ruleId = rule.id; rule._executeCallback(reply, input); @@ -183,31 +181,31 @@ class RateLimiter { } /** - * Adds a rule to dictionary of rules that are checked against on every call. - * Only inputs that pass all of the rules will be allowed. Returns unique rule - * id that can be passed to `removeRule`. - * @param {object} rule Input dictionary defining certain attributes and - * rules associated with them. - * Each attribute's value can either be a value, a function or null. All - * functions must return a boolean of whether the input is matched by that - * attribute's rule or not - * @param {integer} numRequestsAllowed Optional. Number of events allowed per - * interval. Default = 10. - * @param {integer} intervalTime Optional. Number of milliseconds before - * rule's counters are reset. Default = 1000. - * @param {function} callback Optional. Function to be called after a - * rule is executed. Two objects will be passed to this function. - * The first one is the result of RateLimiter.prototype.check - * The second is the input object of the rule, it has the following structure: - * { - * 'type': string - either 'method' or 'subscription' - * 'name': string - the name of the method or subscription being called - * 'userId': string - the user ID attempting the method or subscription - * 'connectionId': string - a string representing the user's DDP connection - * 'clientAddress': string - the IP address of the user - * } - * @return {string} Returns unique rule id - */ + * Adds a rule to dictionary of rules that are checked against on every call. + * Only inputs that pass all of the rules will be allowed. Returns unique rule + * id that can be passed to `removeRule`. + * @param {object} rule Input dictionary defining certain attributes and + * rules associated with them. + * Each attribute's value can either be a value, a function or null. All + * functions must return a boolean of whether the input is matched by that + * attribute's rule or not + * @param {integer} numRequestsAllowed Optional. Number of events allowed per + * interval. Default = 10. + * @param {integer} intervalTime Optional. Number of milliseconds before + * rule's counters are reset. Default = 1000. + * @param {function} callback Optional. Function to be called after a + * rule is executed. Two objects will be passed to this function. + * The first one is the result of RateLimiter.prototype.check + * The second is the input object of the rule, it has the following structure: + * { + * 'type': string - either 'method' or 'subscription' + * 'name': string - the name of the method or subscription being called + * 'userId': string - the user ID attempting the method or subscription + * 'connectionId': string - a string representing the user's DDP connection + * 'clientAddress': string - the IP address of the user + * } + * @return {string} Returns unique rule id + */ addRule(rule, numRequestsAllowed, intervalTime, callback) { const options = { numRequestsAllowed: numRequestsAllowed || DEFAULT_REQUESTS_PER_INTERVAL, @@ -221,10 +219,10 @@ class RateLimiter { } /** - * Increment counters in every rule that match to this input - * @param {object} input Dictionary object containing attributes that may - * match to rules - */ + * Increment counters in every rule that match to this input + * @param {object} input Dictionary object containing attributes that may + * match to rules + */ increment(input) { // Only increment rule counters that match this input const matchedRules = this._findAllMatchingRules(input); @@ -248,7 +246,7 @@ class RateLimiter { // Returns an array of all rules that apply to provided input _findAllMatchingRules(input) { - return Object.values(this.rules).filter(rule => rule.match(input)); + return Object.values(this.rules).filter((rule) => rule.match(input)); } /** diff --git a/packages/reload-safetybelt/package.js b/packages/reload-safetybelt/package.js index 8861d31df8..01afcb09c0 100644 --- a/packages/reload-safetybelt/package.js +++ b/packages/reload-safetybelt/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Reload safety belt for multi-server deployments", - version: '2.0.0', + version: "2.0.0", }); Package.onUse(function (api) { diff --git a/packages/reload-safetybelt/reload-safety-belt-tests.js b/packages/reload-safetybelt/reload-safety-belt-tests.js index b09d9e1206..d193d2eae2 100644 --- a/packages/reload-safetybelt/reload-safety-belt-tests.js +++ b/packages/reload-safetybelt/reload-safety-belt-tests.js @@ -1,11 +1,11 @@ -await (async () => { +await(async () => { const script = await Assets.getTextAsync("safetybelt.js"); Tinytest.add("reload-safetybelt - safety belt is added", function (test) { test.isTrue( - Object.values(WebAppInternals.additionalStaticJs).some( function (js, _pathname) { + Object.values(WebAppInternals.additionalStaticJs).some(function (js, _pathname) { return js === script; - }) + }), ); }); })(); diff --git a/packages/reload-safetybelt/reload-safety-belt.js b/packages/reload-safetybelt/reload-safety-belt.js index cc641b6bad..544f8c5b89 100644 --- a/packages/reload-safetybelt/reload-safety-belt.js +++ b/packages/reload-safetybelt/reload-safety-belt.js @@ -3,6 +3,6 @@ // chance of hitting an old server for the HTML and the new server for the JS or // CSS. This prevents you from displaying the page in that case, and instead // reloads it, presumably all on the new version now. -await (async () => { +await(async () => { WebAppInternals.addStaticJs(await Assets.getTextAsync("safetybelt.js")); })(); diff --git a/packages/reload-safetybelt/safetybelt.js b/packages/reload-safetybelt/safetybelt.js index 56f311b824..896b720d27 100644 --- a/packages/reload-safetybelt/safetybelt.js +++ b/packages/reload-safetybelt/safetybelt.js @@ -1,6 +1,8 @@ -if (typeof Package === 'undefined' || - ! Package.webapp || - ! Package.webapp.WebApp || - ! Package.webapp.WebApp._isCssLoaded()) { +if ( + typeof Package === "undefined" || + !Package.webapp || + !Package.webapp.WebApp || + !Package.webapp.WebApp._isCssLoaded() +) { window.location.reload(); } diff --git a/packages/retry/package.js b/packages/retry/package.js index fbcf575997..d4dccf54fe 100644 --- a/packages/retry/package.js +++ b/packages/retry/package.js @@ -1,11 +1,11 @@ Package.describe({ summary: "Retry logic with exponential backoff", - version: '1.1.1', + version: "1.1.1", }); Package.onUse(function (api) { - api.use('ecmascript'); - api.use('random'); - api.mainModule('retry.js'); - api.export('Retry'); + api.use("ecmascript"); + api.use("random"); + api.mainModule("retry.js"); + api.export("Retry"); }); diff --git a/packages/retry/retry.js b/packages/retry/retry.js index b510509ede..53c588e07b 100644 --- a/packages/retry/retry.js +++ b/packages/retry/retry.js @@ -45,12 +45,9 @@ export class Retry { // fuzz the timeout randomly, to avoid reconnect storms when a // server goes down. - const timeout = Math.min( - this.maxTimeout, - this.baseTimeout * Math.pow(this.exponent, count) - ) * ( - Random.fraction() * this.fuzz + (1 - this.fuzz / 2) - ); + const timeout = + Math.min(this.maxTimeout, this.baseTimeout * Math.pow(this.exponent, count)) * + (Random.fraction() * this.fuzz + (1 - this.fuzz / 2)); return timeout; } @@ -58,8 +55,7 @@ export class Retry { // Call `fn` after a delay, based on the `count` of which retry this is. retryLater(count, fn) { const timeout = this._timeout(count); - if (this.retryTimer) - clearTimeout(this.retryTimer); + if (this.retryTimer) clearTimeout(this.retryTimer); this.retryTimer = Meteor.setTimeout(fn, timeout); return timeout; } diff --git a/packages/session/package.js b/packages/session/package.js index 7bf3a9d266..a705802969 100644 --- a/packages/session/package.js +++ b/packages/session/package.js @@ -1,26 +1,26 @@ Package.describe({ summary: "Session variable", - version: '1.2.2', + version: "1.2.2", }); Package.onUse(function (api) { - api.use(['ecmascript', 'reactive-dict', 'ejson'], 'client'); + api.use(["ecmascript", "reactive-dict", "ejson"], "client"); // Session can work with or without reload, but if reload is present // it should load first so we can detect it at startup and populate // the session. - api.use('reload', 'client', {weak: true}); + api.use("reload", "client", { weak: true }); - api.export('Session', 'client'); - api.mainModule('session.js', 'client'); - api.addAssets('session.d.ts', 'server'); + api.export("Session", "client"); + api.mainModule("session.js", "client"); + api.addAssets("session.d.ts", "server"); }); Package.onTest(function (api) { - api.use('ecmascript'); - api.use('tinytest'); - api.use('session', 'client'); - api.use('tracker'); - api.use('mongo'); - api.addFiles('session_tests.js', 'client'); + api.use("ecmascript"); + api.use("tinytest"); + api.use("session", "client"); + api.use("tracker"); + api.use("mongo"); + api.addFiles("session_tests.js", "client"); }); diff --git a/packages/session/session.d.ts b/packages/session/session.d.ts index 6e5afd4420..96c5b5cd49 100644 --- a/packages/session/session.d.ts +++ b/packages/session/session.d.ts @@ -1,4 +1,4 @@ -import { EJSONable } from 'meteor/ejson'; +import { EJSONable } from "meteor/ejson"; export namespace Session { /** diff --git a/packages/session/session.js b/packages/session/session.js index 3eda8d7edd..790d9054ba 100644 --- a/packages/session/session.js +++ b/packages/session/session.js @@ -1,6 +1,6 @@ -import { ReactiveDict } from 'meteor/reactive-dict'; +import { ReactiveDict } from "meteor/reactive-dict"; -export const Session = new ReactiveDict('session'); +export const Session = new ReactiveDict("session"); // Documentation here is really awkward because the methods are defined // elsewhere diff --git a/packages/session/session_tests.js b/packages/session/session_tests.js index 82eb6da69a..c1d06fa7dc 100644 --- a/packages/session/session_tests.js +++ b/packages/session/session_tests.js @@ -1,194 +1,193 @@ -import { Session } from 'meteor/session'; +import { Session } from "meteor/session"; -Tinytest.add('session - setDefault', function (test) { - Session.setDefault('def', "argyle"); - test.equal(Session.get('def'), "argyle"); - Session.set('def', "noodle"); - test.equal(Session.get('def'), "noodle"); - Session.set('nondef', "potato"); - test.equal(Session.get('nondef'), "potato"); - Session.setDefault('nondef', "eggs"); - test.equal(Session.get('nondef'), "potato"); +Tinytest.add("session - setDefault", function (test) { + Session.setDefault("def", "argyle"); + test.equal(Session.get("def"), "argyle"); + Session.set("def", "noodle"); + test.equal(Session.get("def"), "noodle"); + Session.set("nondef", "potato"); + test.equal(Session.get("nondef"), "potato"); + Session.setDefault("nondef", "eggs"); + test.equal(Session.get("nondef"), "potato"); // This is so the test passes the next time, after hot code push. I know it // doesn't return it to the completely untouched state, but we don't have // Session.clear() yet. When we do, this should be that. - delete Session.keys['def']; - delete Session.keys['nondef']; + delete Session.keys["def"]; + delete Session.keys["nondef"]; }); -Tinytest.add('session - get/set/equals types', function (test) { - test.equal(Session.get('u'), undefined); - test.isTrue(Session.equals('u', undefined)); - test.isFalse(Session.equals('u', null)); - test.isFalse(Session.equals('u', 0)); - test.isFalse(Session.equals('u', '')); +Tinytest.add("session - get/set/equals types", function (test) { + test.equal(Session.get("u"), undefined); + test.isTrue(Session.equals("u", undefined)); + test.isFalse(Session.equals("u", null)); + test.isFalse(Session.equals("u", 0)); + test.isFalse(Session.equals("u", "")); - Session.set('u', undefined); - test.equal(Session.get('u'), undefined); - test.isTrue(Session.equals('u', undefined)); - test.isFalse(Session.equals('u', null)); - test.isFalse(Session.equals('u', 0)); - test.isFalse(Session.equals('u', '')); - test.isFalse(Session.equals('u', 'undefined')); - test.isFalse(Session.equals('u', 'null')); + Session.set("u", undefined); + test.equal(Session.get("u"), undefined); + test.isTrue(Session.equals("u", undefined)); + test.isFalse(Session.equals("u", null)); + test.isFalse(Session.equals("u", 0)); + test.isFalse(Session.equals("u", "")); + test.isFalse(Session.equals("u", "undefined")); + test.isFalse(Session.equals("u", "null")); - Session.set('n', null); - test.equal(Session.get('n'), null); - test.isFalse(Session.equals('n', undefined)); - test.isTrue(Session.equals('n', null)); - test.isFalse(Session.equals('n', 0)); - test.isFalse(Session.equals('n', '')); - test.isFalse(Session.equals('n', 'undefined')); - test.isFalse(Session.equals('n', 'null')); + Session.set("n", null); + test.equal(Session.get("n"), null); + test.isFalse(Session.equals("n", undefined)); + test.isTrue(Session.equals("n", null)); + test.isFalse(Session.equals("n", 0)); + test.isFalse(Session.equals("n", "")); + test.isFalse(Session.equals("n", "undefined")); + test.isFalse(Session.equals("n", "null")); - Session.set('t', true); - test.equal(Session.get('t'), true); - test.isTrue(Session.equals('t', true)); - test.isFalse(Session.equals('t', false)); - test.isFalse(Session.equals('t', 1)); - test.isFalse(Session.equals('t', 'true')); + Session.set("t", true); + test.equal(Session.get("t"), true); + test.isTrue(Session.equals("t", true)); + test.isFalse(Session.equals("t", false)); + test.isFalse(Session.equals("t", 1)); + test.isFalse(Session.equals("t", "true")); - Session.set('f', false); - test.equal(Session.get('f'), false); - test.isFalse(Session.equals('f', true)); - test.isTrue(Session.equals('f', false)); - test.isFalse(Session.equals('f', 1)); - test.isFalse(Session.equals('f', 'false')); + Session.set("f", false); + test.equal(Session.get("f"), false); + test.isFalse(Session.equals("f", true)); + test.isTrue(Session.equals("f", false)); + test.isFalse(Session.equals("f", 1)); + test.isFalse(Session.equals("f", "false")); - Session.set('num', 0); - test.equal(Session.get('num'), 0); - test.isTrue(Session.equals('num', 0)); - test.isFalse(Session.equals('num', false)); - test.isFalse(Session.equals('num', '0')); - test.isFalse(Session.equals('num', 1)); + Session.set("num", 0); + test.equal(Session.get("num"), 0); + test.isTrue(Session.equals("num", 0)); + test.isFalse(Session.equals("num", false)); + test.isFalse(Session.equals("num", "0")); + test.isFalse(Session.equals("num", 1)); - Session.set('str', 'true'); - test.equal(Session.get('str'), 'true'); - test.isTrue(Session.equals('str', 'true')); - test.isFalse(Session.equals('str', true)); + Session.set("str", "true"); + test.equal(Session.get("str"), "true"); + test.isTrue(Session.equals("str", "true")); + test.isFalse(Session.equals("str", true)); - Session.set('arr', [1, 2, {a: 1, b: [5, 6]}]); - test.equal(Session.get('arr'), [1, 2, {b: [5, 6], a: 1}]); - test.isFalse(Session.equals('arr', 1)); - test.isFalse(Session.equals('arr', '[1,2,{"a":1,"b":[5,6]}]')); + Session.set("arr", [1, 2, { a: 1, b: [5, 6] }]); + test.equal(Session.get("arr"), [1, 2, { b: [5, 6], a: 1 }]); + test.isFalse(Session.equals("arr", 1)); + test.isFalse(Session.equals("arr", '[1,2,{"a":1,"b":[5,6]}]')); test.throws(function () { - Session.equals('arr', [1, 2, {a: 1, b: [5, 6]}]); + Session.equals("arr", [1, 2, { a: 1, b: [5, 6] }]); }); - Session.set('obj', {a: 1, b: [5, 6]}); - test.equal(Session.get('obj'), {b: [5, 6], a: 1}); - test.isFalse(Session.equals('obj', 1)); - test.isFalse(Session.equals('obj', '{"a":1,"b":[5,6]}')); - test.throws(function() { Session.equals('obj', {a: 1, b: [5, 6]}); }); + Session.set("obj", { a: 1, b: [5, 6] }); + test.equal(Session.get("obj"), { b: [5, 6], a: 1 }); + test.isFalse(Session.equals("obj", 1)); + test.isFalse(Session.equals("obj", '{"a":1,"b":[5,6]}')); + test.throws(function () { + Session.equals("obj", { a: 1, b: [5, 6] }); + }); + Session.set("date", new Date(1234)); + test.equal(Session.get("date"), new Date(1234)); + test.isFalse(Session.equals("date", new Date(3455))); + test.isTrue(Session.equals("date", new Date(1234))); - Session.set('date', new Date(1234)); - test.equal(Session.get('date'), new Date(1234)); - test.isFalse(Session.equals('date', new Date(3455))); - test.isTrue(Session.equals('date', new Date(1234))); - - Session.set('oid', new Mongo.ObjectID('ffffffffffffffffffffffff')); - test.equal(Session.get('oid'), new Mongo.ObjectID('ffffffffffffffffffffffff')); - test.isFalse(Session.equals('oid', new Mongo.ObjectID('fffffffffffffffffffffffa'))); - test.isTrue(Session.equals('oid', new Mongo.ObjectID('ffffffffffffffffffffffff'))); + Session.set("oid", new Mongo.ObjectID("ffffffffffffffffffffffff")); + test.equal(Session.get("oid"), new Mongo.ObjectID("ffffffffffffffffffffffff")); + test.isFalse(Session.equals("oid", new Mongo.ObjectID("fffffffffffffffffffffffa"))); + test.isTrue(Session.equals("oid", new Mongo.ObjectID("ffffffffffffffffffffffff"))); }); -Tinytest.add('session - objects are cloned', function (test) { - Session.set('frozen-array', [1, 2, 3]); - Session.get('frozen-array')[1] = 42; - test.equal(Session.get('frozen-array'), [1, 2, 3]); +Tinytest.add("session - objects are cloned", function (test) { + Session.set("frozen-array", [1, 2, 3]); + Session.get("frozen-array")[1] = 42; + test.equal(Session.get("frozen-array"), [1, 2, 3]); - Session.set('frozen-object', {a: 1, b: 2}); - Session.get('frozen-object').a = 43; - test.equal(Session.get('frozen-object'), {a: 1, b: 2}); + Session.set("frozen-object", { a: 1, b: 2 }); + Session.get("frozen-object").a = 43; + test.equal(Session.get("frozen-object"), { a: 1, b: 2 }); }); -Tinytest.add('session - context invalidation for get', function (test) { +Tinytest.add("session - context invalidation for get", function (test) { let xGetExecutions = 0; Tracker.autorun(function () { ++xGetExecutions; - Session.get('x'); + Session.get("x"); }); test.equal(xGetExecutions, 1); - Session.set('x', 1); + Session.set("x", 1); // Invalidation shouldn't happen until flush time. test.equal(xGetExecutions, 1); Tracker.flush(); test.equal(xGetExecutions, 2); // Setting to the same value doesn't re-run. - Session.set('x', 1); + Session.set("x", 1); Tracker.flush(); test.equal(xGetExecutions, 2); - Session.set('x', '1'); + Session.set("x", "1"); Tracker.flush(); test.equal(xGetExecutions, 3); }); -Tinytest.add('session - context invalidation for equals', function (test) { +Tinytest.add("session - context invalidation for equals", function (test) { let xEqualsExecutions = 0; Tracker.autorun(function () { ++xEqualsExecutions; - Session.equals('x', 5); + Session.equals("x", 5); }); test.equal(xEqualsExecutions, 1); - Session.set('x', 1); + Session.set("x", 1); Tracker.flush(); // Changing undefined -> 1 shouldn't affect equals(5). test.equal(xEqualsExecutions, 1); - Session.set('x', 5); + Session.set("x", 5); // Invalidation shouldn't happen until flush time. test.equal(xEqualsExecutions, 1); Tracker.flush(); test.equal(xEqualsExecutions, 2); - Session.set('x', 5); + Session.set("x", 5); Tracker.flush(); // Setting to the same value doesn't re-run. test.equal(xEqualsExecutions, 2); - Session.set('x', '5'); + Session.set("x", "5"); test.equal(xEqualsExecutions, 2); Tracker.flush(); test.equal(xEqualsExecutions, 3); - Session.set('x', 5); + Session.set("x", 5); test.equal(xEqualsExecutions, 3); Tracker.flush(); test.equal(xEqualsExecutions, 4); }); -Tinytest.add( - 'session - context invalidation for equals with undefined', - function (test) { - // Make sure the special casing for equals undefined works. - let yEqualsExecutions = 0; - Tracker.autorun(function () { - ++yEqualsExecutions; - Session.equals('y', undefined); - }); - test.equal(yEqualsExecutions, 1); - Session.set('y', undefined); - Tracker.flush(); - test.equal(yEqualsExecutions, 1); - Session.set('y', 5); - test.equal(yEqualsExecutions, 1); - Tracker.flush(); - test.equal(yEqualsExecutions, 2); - Session.set('y', 3); - Tracker.flush(); - test.equal(yEqualsExecutions, 2); - Session.set('y', 'undefined'); - Tracker.flush(); - test.equal(yEqualsExecutions, 2); - Session.set('y', undefined); - test.equal(yEqualsExecutions, 2); - Tracker.flush(); - test.equal(yEqualsExecutions, 3); +Tinytest.add("session - context invalidation for equals with undefined", function (test) { + // Make sure the special casing for equals undefined works. + let yEqualsExecutions = 0; + Tracker.autorun(function () { + ++yEqualsExecutions; + Session.equals("y", undefined); }); - -Tinytest.add('session - parse an object of key/value pairs', function (test) { - Session._setObject({fruit: 'apple', vegetable: 'potato'}); - - test.equal(Session.get('fruit'), 'apple'); - test.equal(Session.get('vegetable'), 'potato'); - - delete Session.keys['fruit']; - delete Session.keys['vegetable']; + test.equal(yEqualsExecutions, 1); + Session.set("y", undefined); + Tracker.flush(); + test.equal(yEqualsExecutions, 1); + Session.set("y", 5); + test.equal(yEqualsExecutions, 1); + Tracker.flush(); + test.equal(yEqualsExecutions, 2); + Session.set("y", 3); + Tracker.flush(); + test.equal(yEqualsExecutions, 2); + Session.set("y", "undefined"); + Tracker.flush(); + test.equal(yEqualsExecutions, 2); + Session.set("y", undefined); + test.equal(yEqualsExecutions, 2); + Tracker.flush(); + test.equal(yEqualsExecutions, 3); +}); + +Tinytest.add("session - parse an object of key/value pairs", function (test) { + Session._setObject({ fruit: "apple", vegetable: "potato" }); + + test.equal(Session.get("fruit"), "apple"); + test.equal(Session.get("vegetable"), "potato"); + + delete Session.keys["fruit"]; + delete Session.keys["vegetable"]; }); diff --git a/packages/test-helpers/async_multi.js b/packages/test-helpers/async_multi.js index 86c46e20d7..a6d6384976 100644 --- a/packages/test-helpers/async_multi.js +++ b/packages/test-helpers/async_multi.js @@ -57,25 +57,20 @@ Object.assign(ExpectationManager.prototype, { const self = this; let expected; - if (typeof arguments[0] === "function") - expected = arguments[0]; - else - expected = Array.from(arguments); + if (typeof arguments[0] === "function") expected = arguments[0]; + else expected = Array.from(arguments); - if (self.closed) - throw new Error("Too late to add more expectations to the test"); + if (self.closed) throw new Error("Too late to add more expectations to the test"); self.outstanding++; return async function (/* arguments */) { - if (self.dead) - return; + if (self.dead) return; if (typeof expected === "function") { try { await expected.apply({}, arguments); } catch (e) { - if (self.cancel()) - self.test.exception(e); + if (self.cancel()) self.test.exception(e); } } else { self.test.equal(Array.from(arguments), expected); @@ -94,7 +89,7 @@ Object.assign(ExpectationManager.prototype, { cancel: function () { const self = this; - if (! self.dead) { + if (!self.dead) { self.dead = true; return true; } @@ -107,7 +102,7 @@ Object.assign(ExpectationManager.prototype, { self.dead = true; self.onComplete(); } - } + }, }); testAsyncMulti = function (name, funcs, { isOnly = false } = {}) { @@ -116,7 +111,7 @@ testAsyncMulti = function (name, funcs, { isOnly = false } = {}) { const addFunction = isOnly ? Tinytest.onlyAsync : Tinytest.addAsync; addFunction(name, function (test, onComplete) { - const remaining = [...funcs] + const remaining = [...funcs]; const context = {}; let i = 0; @@ -125,8 +120,7 @@ testAsyncMulti = function (name, funcs, { isOnly = false } = {}) { if (!func) { delete test.extraDetails.asyncBlock; onComplete(); - } - else { + } else { const em = new ExpectationManager(test, function () { clearTimeout(timer); runNext(); @@ -134,7 +128,7 @@ testAsyncMulti = function (name, funcs, { isOnly = false } = {}) { const timer = setTimeout(function () { if (em.cancel()) { - test.fail({type: "timeout", message: "Async batch timed out"}); + test.fail({ type: "timeout", message: "Async batch timed out" }); onComplete(); } return; @@ -142,22 +136,25 @@ testAsyncMulti = function (name, funcs, { isOnly = false } = {}) { test.extraDetails.asyncBlock = i++; - new Promise(resolve => { + new Promise((resolve) => { const result = func.apply(context, [test, em.expect.bind(em)]); if (result && typeof result.then === "function") { - return result.then((r) => resolve(r)) + return result.then((r) => resolve(r)); } return resolve(result); - }).then(() => { - em.done(); - }, exception => { - if (em.cancel()) { - test.exception(exception); - // Because we called test.exception, we're not to call onComplete. - } - clearTimeout(timer); - }); + }).then( + () => { + em.done(); + }, + (exception) => { + if (em.cancel()) { + test.exception(exception); + // Because we called test.exception, we're not to call onComplete. + } + clearTimeout(timer); + }, + ); } }; @@ -170,7 +167,7 @@ testAsyncMulti = function (name, funcs, { isOnly = false } = {}) { simplePoll = function (fn, success, failed, timeout, step) { timeout = timeout || 10000; step = step || 100; - const start = (new Date()).valueOf(); + const start = new Date().valueOf(); let timeOutId; const helper = function () { if (fn()) { @@ -178,7 +175,7 @@ simplePoll = function (fn, success, failed, timeout, step) { Meteor.clearTimeout(timeOutId); return; } - if (start + timeout < (new Date()).valueOf()) { + if (start + timeout < new Date().valueOf()) { failed(); Meteor.clearTimeout(timeOutId); return; @@ -194,10 +191,14 @@ pollUntil = function (expect, f, timeout, step, noFail) { const expectation = expect(true); simplePoll( f, - function () { expectation(true) }, - function () { expectation(noFail) }, + function () { + expectation(true); + }, + function () { + expectation(noFail); + }, timeout, - step + step, ); }; diff --git a/packages/test-helpers/callback_logger.js b/packages/test-helpers/callback_logger.js index 0bfd86ea8a..5e63bc1902 100644 --- a/packages/test-helpers/callback_logger.js +++ b/packages/test-helpers/callback_logger.js @@ -1,4 +1,4 @@ -import isEqual from 'lodash.isequal'; +import isEqual from "lodash.isequal"; // This file allows you to write tests that expect certain callbacks to be // called in certain orders, or optionally in groups where the order does not @@ -56,8 +56,8 @@ CallbackLogger.prototype.expectResultOnly = async function (callbackName, args) CallbackLogger.prototype._waitForLengthOrTimeout = function (len) { const self = this; - const timeoutControl = { executionTime: 0 }; - return new Promise(resolve => { + const timeoutControl = { executionTime: 0 }; + return new Promise((resolve) => { const waitFunc = () => { if (timeoutControl.executionTime < TIMEOUT && self._log.length < len) { timeoutControl.executionTime += 100; @@ -89,8 +89,7 @@ CallbackLogger.prototype.expectResultUnordered = async function (list) { break; } } - if (!found) - self._test.fail([`Found unexpected result: ${JSON.stringify(dequeued)}`]); + if (!found) self._test.fail([`Found unexpected result: ${JSON.stringify(dequeued)}`]); i--; } }; diff --git a/packages/test-helpers/canonicalize_html.js b/packages/test-helpers/canonicalize_html.js index 142ab5372c..5dde0fbd09 100644 --- a/packages/test-helpers/canonicalize_html.js +++ b/packages/test-helpers/canonicalize_html.js @@ -1,37 +1,39 @@ -canonicalizeHtml = function(html) { +canonicalizeHtml = function (html) { let h = html; // kill IE-specific comments inserted by DomRange - h = h.replace(//g, ''); - h = h.replace(//g, ''); + h = h.replace(//g, ""); + h = h.replace(//g, ""); // ignore exact text of comments - h = h.replace(//g, ''); + h = h.replace(//g, ""); // make all tags lowercase - h = h.replace(/<\/?(\w+)/g, function(m) { - return m.toLowerCase(); }); + h = h.replace(/<\/?(\w+)/g, function (m) { + return m.toLowerCase(); + }); // replace whitespace sequences with spaces - h = h.replace(/\s+/g, ' '); + h = h.replace(/\s+/g, " "); // Trim leading and trailing whitespace - h = h.replace(/^\s+|\s+$/g, ''); + h = h.replace(/^\s+|\s+$/g, ""); // remove whitespace before and after tags h = h.replace(/\s*(<\/?\w.*?>)\s*/g, function (m, tag) { - return tag; }); + return tag; + }); // make tag attributes uniform - h = h.replace(/<(\w+)\s+(.*?)\s*>/g, function(m, tagName, attrs) { + h = h.replace(/<(\w+)\s+(.*?)\s*>/g, function (m, tagName, attrs) { // Drop expando property used by Sizzle (part of jQuery) which leaks into // attributes in IE8. Note that its value always contains spaces. - attrs = attrs.replace(/sizcache[0-9]+="[^"]*"/g, ' '); + attrs = attrs.replace(/sizcache[0-9]+="[^"]*"/g, " "); // Similarly for expando properties used by jQuery to track data. - attrs = attrs.replace(/jQuery[0-9]+="[0-9]+"/g, ' '); + attrs = attrs.replace(/jQuery[0-9]+="[0-9]+"/g, " "); // Similarly for expando properties used to DOMBackend to keep // track of callbacks to fire when an element is removed - attrs = attrs.replace(/\$blaze_teardown_callbacks="[^"]*"/g, ' '); + attrs = attrs.replace(/\$blaze_teardown_callbacks="[^"]*"/g, " "); // And by DOMRange to keep track of the element's DOMRange - attrs = attrs.replace(/\$blaze_range="[^"]*"/g, ' '); + attrs = attrs.replace(/\$blaze_range="[^"]*"/g, " "); - attrs = attrs.replace(/\s*=\s*/g, '='); - attrs = attrs.replace(/^\s+/g, ''); - attrs = attrs.replace(/\s+$/g, ''); - attrs = attrs.replace(/\s+/g, ' '); + attrs = attrs.replace(/\s*=\s*/g, "="); + attrs = attrs.replace(/^\s+/g, ""); + attrs = attrs.replace(/\s+$/g, ""); + attrs = attrs.replace(/\s+/g, " "); // quote unquoted attribute values, as in `type=checkbox`. This // will do the wrong thing if there's an `=` in an attribute value. attrs = attrs.replace(/(\w)=([^'" >/]+)/g, '$1="$2"'); @@ -48,22 +50,19 @@ canonicalizeHtml = function(html) { const tagContents = [tagName]; - for(let i=0; i`; + return `<${tagContents.join(" ")}>`; }); return h; }; diff --git a/packages/test-helpers/connection_client.js b/packages/test-helpers/connection_client.js index 828a36857b..7c89d6885a 100644 --- a/packages/test-helpers/connection_client.js +++ b/packages/test-helpers/connection_client.js @@ -1,5 +1,5 @@ captureConnectionMessagesClient = async function () { - const messages = [] + const messages = []; const conn = DDP.connect(Meteor.absoluteUrl()); @@ -11,19 +11,19 @@ captureConnectionMessagesClient = async function () { messages.push(EJSON.parse(args[0])); } send.apply(this, args); - } + }; - conn._stream.on('message', message => { + conn._stream.on("message", (message) => { return messages.push(EJSON.parse(message)); }); function cleanup() { - conn._stream.send = send + conn._stream.send = send; } return { conn, messages, - cleanup - } -}; \ No newline at end of file + cleanup, + }; +}; diff --git a/packages/test-helpers/connection_server.js b/packages/test-helpers/connection_server.js index d760936a38..4c2fa5ecf0 100644 --- a/packages/test-helpers/connection_server.js +++ b/packages/test-helpers/connection_server.js @@ -10,7 +10,7 @@ makeTestConnection = function (test, succeeded, failed) { // Add incoming connections to `serverConns`. const onConnectionHandle = Meteor.onConnection(function (serverConn) { - test.isTrue(typeof serverConn.id === 'string', "connection handle id exists and is a string"); + test.isTrue(typeof serverConn.id === "string", "connection handle id exists and is a string"); if (serverConns[serverConn.id]) { test.fail("onConnection callback called multiple times for same session id"); failed(); @@ -23,13 +23,13 @@ makeTestConnection = function (test, succeeded, failed) { // Disable retries so that when the connection is closed we don't // automatically keep reconnecting on the client side. // The connection from the client side. - const clientConn = DDP.connect(Meteor.absoluteUrl(), {retry: false}); + const clientConn = DDP.connect(Meteor.absoluteUrl(), { retry: false }); // We've succeeded when we get the session id on the client side. const onClientSessionId = function (sessionId) { test.isTrue(clientConn.status().connected); const serverConn = serverConns[sessionId]; - if (! serverConn) { + if (!serverConn) { test.fail("No onConnection received server side for connected client"); failed(); } else { @@ -47,7 +47,7 @@ makeTestConnection = function (test, succeeded, failed) { function () { test.fail("client side of connection did not receive a session id"); failed(); - } + }, ); }; @@ -58,7 +58,7 @@ createTestConnectionPromise = function (test) { }; captureConnectionMessages = async function (test) { - const messages = [] + const messages = []; const conn = await createTestConnectionPromise(test); @@ -67,17 +67,17 @@ captureConnectionMessages = async function (test) { conn._stream.send = function (...args) { send.apply(this, args); messages.push(EJSON.parse(args[0])); - } + }; - conn._stream.on('message', message => messages.push(EJSON.parse(message))); + conn._stream.on("message", (message) => messages.push(EJSON.parse(message))); function cleanup() { - conn._stream.send = send + conn._stream.send = send; } return { conn, messages, - cleanup - } -}; \ No newline at end of file + cleanup, + }; +}; diff --git a/packages/test-helpers/current_style.js b/packages/test-helpers/current_style.js index c6d2e6e76d..b45e8ca121 100644 --- a/packages/test-helpers/current_style.js +++ b/packages/test-helpers/current_style.js @@ -1,10 +1,12 @@ // Cross-browser implementation of getting the computed style of an element. -getStyleProperty = function(n, prop) { +getStyleProperty = function (n, prop) { if (n.currentStyle) { // camelCase it for IE - return n.currentStyle[prop.replace( - /-([a-z])/g, - function(x,y) { return y.toUpperCase(); })]; + return n.currentStyle[ + prop.replace(/-([a-z])/g, function (x, y) { + return y.toUpperCase(); + }) + ]; } else { return window.getComputedStyle(n, null).getPropertyValue(prop); } diff --git a/packages/test-helpers/domutils.js b/packages/test-helpers/domutils.js index e1bbe3e0e9..6c50ab8675 100644 --- a/packages/test-helpers/domutils.js +++ b/packages/test-helpers/domutils.js @@ -9,7 +9,7 @@ testSelectDiv.firstChild.setAttribute("name", "myname"); // Tests that, if true, indicate browser quirks present. const quirks = { // IE loses initial whitespace when setting innerHTML. - leadingWhitespaceKilled: (testDiv.firstChild.nodeType !== 3), + leadingWhitespaceKilled: testDiv.firstChild.nodeType !== 3, // IE may insert an empty tbody tag in a table. tbodyInsertion: testDiv.getElementsByTagName("tbody").length > 0, @@ -18,15 +18,15 @@ const quirks = { tagsLost: testDiv.getElementsByTagName("link").length === 0, // IE <= 9 loses HTML comments in "; // Need to wrap in a div rather than directly creating SELECT to avoid // *another* IE bug. -const testSelectDiv = document.createElement("div"); +var testSelectDiv = document.createElement("div"); testSelectDiv.innerHTML = ""; testSelectDiv.firstChild.setAttribute("name", "myname"); // Tests that, if true, indicate browser quirks present. -const quirks = { +var quirks = { // IE loses initial whitespace when setting innerHTML. - leadingWhitespaceKilled: testDiv.firstChild.nodeType !== 3, + leadingWhitespaceKilled: (testDiv.firstChild.nodeType !== 3), // IE may insert an empty tbody tag in a table. tbodyInsertion: testDiv.getElementsByTagName("tbody").length > 0, @@ -18,15 +18,15 @@ const quirks = { tagsLost: testDiv.getElementsByTagName("link").length === 0, // IE <= 9 loses HTML comments in