From b760e699c6124ae12e0e21a6afe36ea5a08b11d4 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Thu, 24 Dec 2020 09:36:34 +0100 Subject: [PATCH 001/357] remove explicitly setting process.env.DEBUG --- src/node/utils/Settings.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index c4245b77c..ed514c98a 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -683,7 +683,6 @@ exports.reloadSettings = function reloadSettings() { log4js.configure(exports.logconfig);// Configure the logging appenders log4js.setGlobalLogLevel(exports.loglevel);// set loglevel - process.env.DEBUG = `socket.io:${exports.loglevel}`; // Used by SocketIO for Debug log4js.replaceConsole(); if (!exports.skinName) { From e051f2f2f7b17a35cdfafc43da892a0b7c464118 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 26 Dec 2020 20:39:37 +0000 Subject: [PATCH 002/357] lint: scroll.js --- src/static/js/scroll.js | 139 ++++++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 62 deletions(-) diff --git a/src/static/js/scroll.js b/src/static/js/scroll.js index 064831fe7..063e55513 100644 --- a/src/static/js/scroll.js +++ b/src/static/js/scroll.js @@ -1,3 +1,5 @@ +'use strict'; + /* This file handles scroll on edition or when user presses arrow keys. In this file we have two representations of line (browser and rep line). @@ -17,23 +19,26 @@ function Scroll(outerWin) { this.rootDocument = parent.parent.document; } -Scroll.prototype.scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary = function (rep, isScrollableEvent, innerHeight) { +Scroll.prototype.scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary = + function (rep, isScrollableEvent, innerHeight) { // are we placing the caret on the line at the bottom of viewport? // And if so, do we need to scroll the editor, as defined on the settings.json? - const shouldScrollWhenCaretIsAtBottomOfViewport = this.scrollSettings.scrollWhenCaretIsInTheLastLineOfViewport; - if (shouldScrollWhenCaretIsAtBottomOfViewport) { - // avoid scrolling when selection includes multiple lines -- user can potentially be selecting more lines - // than it fits on viewport - const multipleLinesSelected = rep.selStart[0] !== rep.selEnd[0]; + const shouldScrollWhenCaretIsAtBottomOfViewport = + this.scrollSettings.scrollWhenCaretIsInTheLastLineOfViewport; + if (shouldScrollWhenCaretIsAtBottomOfViewport) { + // avoid scrolling when selection includes multiple lines -- + // user can potentially be selecting more lines + // than it fits on viewport + const multipleLinesSelected = rep.selStart[0] !== rep.selEnd[0]; - // avoid scrolling when pad loads - if (isScrollableEvent && !multipleLinesSelected && this._isCaretAtTheBottomOfViewport(rep)) { - // when scrollWhenFocusLineIsOutOfViewport.percentage is 0, pixelsToScroll is 0 - const pixelsToScroll = this._getPixelsRelativeToPercentageOfViewport(innerHeight); - this._scrollYPage(pixelsToScroll); + // avoid scrolling when pad loads + if (isScrollableEvent && !multipleLinesSelected && this._isCaretAtTheBottomOfViewport(rep)) { + // when scrollWhenFocusLineIsOutOfViewport.percentage is 0, pixelsToScroll is 0 + const pixelsToScroll = this._getPixelsRelativeToPercentageOfViewport(innerHeight); + this._scrollYPage(pixelsToScroll); + } } - } -}; + }; Scroll.prototype.scrollWhenPressArrowKeys = function (arrowUp, rep, innerHeight) { // if percentageScrollArrowUp is 0, let the scroll to be handled as default, put the previous @@ -60,8 +65,10 @@ Scroll.prototype._isCaretAtTheBottomOfViewport = function (rep) { const caretLine = rep.selStart[0]; const lineAfterCaretLine = caretLine + 1; const firstLineVisibleAfterCaretLine = caretPosition.getNextVisibleLine(lineAfterCaretLine, rep); - const caretLineIsPartiallyVisibleOnViewport = this._isLinePartiallyVisibleOnViewport(caretLine, rep); - const lineAfterCaretLineIsPartiallyVisibleOnViewport = this._isLinePartiallyVisibleOnViewport(firstLineVisibleAfterCaretLine, rep); + const caretLineIsPartiallyVisibleOnViewport = + this._isLinePartiallyVisibleOnViewport(caretLine, rep); + const lineAfterCaretLineIsPartiallyVisibleOnViewport = + this._isLinePartiallyVisibleOnViewport(firstLineVisibleAfterCaretLine, rep); if (caretLineIsPartiallyVisibleOnViewport || lineAfterCaretLineIsPartiallyVisibleOnViewport) { // check if the caret is in the bottom of the viewport const caretLinePosition = caretPosition.getPosition(); @@ -99,9 +106,11 @@ Scroll.prototype._getViewPortTopBottom = function () { const doc = this.doc; const height = doc.documentElement.clientHeight; // includes padding - // we have to get the exactly height of the viewport. So it has to subtract all the values which changes + // we have to get the exactly height of the viewport. + // So it has to subtract all the values which changes // the viewport height (E.g. padding, position top) - const viewportExtraSpacesAndPosition = this._getEditorPositionTop() + this._getPaddingTopAddedWhenPageViewIsEnable(); + const viewportExtraSpacesAndPosition = + this._getEditorPositionTop() + this._getPaddingTopAddedWhenPageViewIsEnable(); return { top: theTop, bottom: (theTop + height - viewportExtraSpacesAndPosition), @@ -162,9 +171,12 @@ Scroll.prototype.setScrollXY = function (x, y) { Scroll.prototype._isCaretAtTheTopOfViewport = function (rep) { const caretLine = rep.selStart[0]; const linePrevCaretLine = caretLine - 1; - const firstLineVisibleBeforeCaretLine = caretPosition.getPreviousVisibleLine(linePrevCaretLine, rep); - const caretLineIsPartiallyVisibleOnViewport = this._isLinePartiallyVisibleOnViewport(caretLine, rep); - const lineBeforeCaretLineIsPartiallyVisibleOnViewport = this._isLinePartiallyVisibleOnViewport(firstLineVisibleBeforeCaretLine, rep); + const firstLineVisibleBeforeCaretLine = + caretPosition.getPreviousVisibleLine(linePrevCaretLine, rep); + const caretLineIsPartiallyVisibleOnViewport = + this._isLinePartiallyVisibleOnViewport(caretLine, rep); + const lineBeforeCaretLineIsPartiallyVisibleOnViewport = + this._isLinePartiallyVisibleOnViewport(firstLineVisibleBeforeCaretLine, rep); if (caretLineIsPartiallyVisibleOnViewport || lineBeforeCaretLineIsPartiallyVisibleOnViewport) { const caretLinePosition = caretPosition.getPosition(); // get the position of the browser line const viewportPosition = this._getViewPortTopBottom(); @@ -172,7 +184,8 @@ Scroll.prototype._isCaretAtTheTopOfViewport = function (rep) { const viewportBottom = viewportPosition.bottom; const caretLineIsBelowViewportTop = caretLinePosition.bottom >= viewportTop; const caretLineIsAboveViewportBottom = caretLinePosition.top < viewportBottom; - const caretLineIsInsideOfViewport = caretLineIsBelowViewportTop && caretLineIsAboveViewportBottom; + const caretLineIsInsideOfViewport = + caretLineIsBelowViewportTop && caretLineIsAboveViewportBottom; if (caretLineIsInsideOfViewport) { const prevLineTop = caretPosition.getPositionTopOfPreviousBrowserLine(caretLinePosition, rep); const previousLineIsAboveViewportTop = prevLineTop < viewportTop; @@ -185,14 +198,15 @@ Scroll.prototype._isCaretAtTheTopOfViewport = function (rep) { // By default, when user makes an edition in a line out of viewport, this line goes // to the edge of viewport. This function gets the extra pixels necessary to get the // caret line in a position X relative to Y% viewport. -Scroll.prototype._getPixelsRelativeToPercentageOfViewport = function (innerHeight, aboveOfViewport) { - let pixels = 0; - const scrollPercentageRelativeToViewport = this._getPercentageToScroll(aboveOfViewport); - if (scrollPercentageRelativeToViewport > 0 && scrollPercentageRelativeToViewport <= 1) { - pixels = parseInt(innerHeight * scrollPercentageRelativeToViewport); - } - return pixels; -}; +Scroll.prototype._getPixelsRelativeToPercentageOfViewport = + function (innerHeight, aboveOfViewport) { + let pixels = 0; + const scrollPercentageRelativeToViewport = this._getPercentageToScroll(aboveOfViewport); + if (scrollPercentageRelativeToViewport > 0 && scrollPercentageRelativeToViewport <= 1) { + pixels = parseInt(innerHeight * scrollPercentageRelativeToViewport); + } + return pixels; + }; // we use different percentages when change selection. It depends on if it is // either above the top or below the bottom of the page @@ -226,30 +240,34 @@ Scroll.prototype._scrollYPageWithoutAnimation = function (pixelsToScroll) { this.outerWin.scrollBy(0, pixelsToScroll); }; -Scroll.prototype._scrollYPageWithAnimation = function (pixelsToScroll, durationOfAnimationToShowFocusline) { - const outerDocBody = this.doc.getElementById('outerdocbody'); +Scroll.prototype._scrollYPageWithAnimation = + function (pixelsToScroll, durationOfAnimationToShowFocusline) { + const outerDocBody = this.doc.getElementById('outerdocbody'); - // it works on later versions of Chrome - const $outerDocBody = $(outerDocBody); - this._triggerScrollWithAnimation($outerDocBody, pixelsToScroll, durationOfAnimationToShowFocusline); + // it works on later versions of Chrome + const $outerDocBody = $(outerDocBody); + this._triggerScrollWithAnimation( + $outerDocBody, pixelsToScroll, durationOfAnimationToShowFocusline); - // it works on Firefox and earlier versions of Chrome - const $outerDocBodyParent = $outerDocBody.parent(); - this._triggerScrollWithAnimation($outerDocBodyParent, pixelsToScroll, durationOfAnimationToShowFocusline); -}; + // it works on Firefox and earlier versions of Chrome + const $outerDocBodyParent = $outerDocBody.parent(); + this._triggerScrollWithAnimation( + $outerDocBodyParent, pixelsToScroll, durationOfAnimationToShowFocusline); + }; -// using a custom queue and clearing it, we avoid creating a queue of scroll animations. So if this function -// is called twice quickly, only the last one runs. -Scroll.prototype._triggerScrollWithAnimation = function ($elem, pixelsToScroll, durationOfAnimationToShowFocusline) { - // clear the queue of animation - $elem.stop('scrollanimation'); - $elem.animate({ - scrollTop: `+=${pixelsToScroll}`, - }, { - duration: durationOfAnimationToShowFocusline, - queue: 'scrollanimation', - }).dequeue('scrollanimation'); -}; +// using a custom queue and clearing it, we avoid creating a queue of scroll animations. +// So if this function is called twice quickly, only the last one runs. +Scroll.prototype._triggerScrollWithAnimation = + function ($elem, pixelsToScroll, durationOfAnimationToShowFocusline) { + // clear the queue of animation + $elem.stop('scrollanimation'); + $elem.animate({ + scrollTop: `+=${pixelsToScroll}`, + }, { + duration: durationOfAnimationToShowFocusline, + queue: 'scrollanimation', + }).dequeue('scrollanimation'); + }; // scrollAmountWhenFocusLineIsOutOfViewport is set to 0 (default), scroll it the minimum distance // needed to be completely in view. If the value is greater than 0 and less than or equal to 1, @@ -257,7 +275,6 @@ Scroll.prototype._triggerScrollWithAnimation = function ($elem, pixelsToScroll, // (viewport height * scrollAmountWhenFocusLineIsOutOfViewport) pixels Scroll.prototype.scrollNodeVerticallyIntoView = function (rep, innerHeight) { const viewport = this._getViewPortTopBottom(); - const isPartOfRepLineOutOfViewport = this._partOfRepLineIsOutOfViewport(viewport, rep); // when the selection changes outside of the viewport the browser automatically scrolls the line // to inside of the viewport. Tested on IE, Firefox, Chrome in releases from 2015 until now @@ -269,10 +286,12 @@ Scroll.prototype.scrollNodeVerticallyIntoView = function (rep, innerHeight) { const caretIsAboveOfViewport = distanceOfTopOfViewport < 0; const caretIsBelowOfViewport = distanceOfBottomOfViewport < 0; if (caretIsAboveOfViewport) { - var pixelsToScroll = distanceOfTopOfViewport - this._getPixelsRelativeToPercentageOfViewport(innerHeight, true); + const pixelsToScroll = + distanceOfTopOfViewport - this._getPixelsRelativeToPercentageOfViewport(innerHeight, true); this._scrollYPage(pixelsToScroll); } else if (caretIsBelowOfViewport) { - var pixelsToScroll = -distanceOfBottomOfViewport + this._getPixelsRelativeToPercentageOfViewport(innerHeight); + const pixelsToScroll = -distanceOfBottomOfViewport + + this._getPixelsRelativeToPercentageOfViewport(innerHeight); this._scrollYPage(pixelsToScroll); } else { this.scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary(rep, true, innerHeight); @@ -311,12 +330,10 @@ Scroll.prototype.getVisibleLineRange = function (rep) { const obj = {}; const self = this; const start = rep.lines.search((e) => self._getLineEntryTopBottom(e, obj).bottom > viewport.top); - let end = rep.lines.search((e) => - // return the first line that the top position is greater or equal than - // the viewport. That is the first line that is below the viewport bottom. - // So the line that is in the bottom of the viewport is the very previous one. - self._getLineEntryTopBottom(e, obj).top >= viewport.bottom - ); + // return the first line that the top position is greater or equal than + // the viewport. That is the first line that is below the viewport bottom. + // So the line that is in the bottom of the viewport is the very previous one. + let end = rep.lines.search((e) => self._getLineEntryTopBottom(e, obj).top >= viewport.bottom); if (end < start) end = start; // unlikely // top.console.log(start+","+(end -1)); return [start, end - 1]; @@ -327,6 +344,4 @@ Scroll.prototype.getVisibleCharRange = function (rep) { return [rep.lines.offsetOfIndex(lineRange[0]), rep.lines.offsetOfIndex(lineRange[1])]; }; -exports.init = function (outerWin) { - return new Scroll(outerWin); -}; +exports.init = (outerWin) => new Scroll(outerWin); From 38c98271614e4ee2187bdd5fc19c0213c07b6b9f Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 26 Dec 2020 21:05:08 +0000 Subject: [PATCH 003/357] Feature: Scroll to Line number based on Hash IE http://foo.com/p/bar#L10 will scroll to line 10. (#4554) Includes test coverage Co-authored-by: webzwo0i --- doc/api/embed_parameters.md | 11 ++++-- src/static/css/iframe_editor.css | 1 + src/static/js/ace2_inner.js | 1 + src/static/js/pad_editor.js | 59 ++++++++++++++++++++++++++++++-- tests/frontend/helper.js | 28 +++++++++------ tests/frontend/specs/scrollTo.js | 43 +++++++++++++++++++++++ 6 files changed, 128 insertions(+), 15 deletions(-) create mode 100755 tests/frontend/specs/scrollTo.js diff --git a/doc/api/embed_parameters.md b/doc/api/embed_parameters.md index 79b60f214..d6f27af05 100644 --- a/doc/api/embed_parameters.md +++ b/doc/api/embed_parameters.md @@ -3,10 +3,10 @@ You can easily embed your etherpad-lite into any webpage by using iframes. You c Example: -Cut and paste the following code into any webpage to embed a pad. The parameters below will hide the chat and the line numbers. +Cut and paste the following code into any webpage to embed a pad. The parameters below will hide the chat and the line numbers and will auto-focus on Line 4. ``` - + ``` ## showLineNumbers @@ -66,3 +66,10 @@ Example: `lang=ar` (translates the interface into Arabic) Default: true Displays pad text from right to left. +## #L + * Int + +Default: 0 +Focuses pad at specific line number and places caret at beginning of this line +Special note: Is not a URL parameter but instead of a Hash value + diff --git a/src/static/css/iframe_editor.css b/src/static/css/iframe_editor.css index 7267375a4..42951f486 100644 --- a/src/static/css/iframe_editor.css +++ b/src/static/css/iframe_editor.css @@ -101,6 +101,7 @@ body.mozilla, body.safari { font-size: 9px; padding: 0 14px 0 10px; font-family: monospace; + cursor: pointer; } .plugin-ep_author_neat #sidedivinner.authorColors .line-number { padding-right: 10px; diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index fc339ab78..34b7e79a1 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -676,6 +676,7 @@ function Ace2Inner() { editorInfo.ace_doReturnKey = doReturnKey; editorInfo.ace_isBlockElement = isBlockElement; editorInfo.ace_getLineListType = getLineListType; + editorInfo.ace_setSelection = setSelection; editorInfo.ace_callWithAce = function (fn, callStack, normalize) { let wrapper = function () { diff --git a/src/static/js/pad_editor.js b/src/static/js/pad_editor.js index b7b94f720..70afc0e09 100644 --- a/src/static/js/pad_editor.js +++ b/src/static/js/pad_editor.js @@ -1,5 +1,4 @@ 'use strict'; - /** * This code is mostly from the old Etherpad. Please help us to comment this code. * This helps other people to understand this code better and helps them to improve it. @@ -44,6 +43,15 @@ const padeditor = (() => { $('#editorloadingbox').hide(); if (readyFunc) { readyFunc(); + + // Listen for clicks on sidediv items + const $outerdoc = $('iframe[name="ace_outer"]').contents().find('#outerdocbody'); + $outerdoc.find('#sidedivinner').on('click', 'div', function () { + const targetLineNumber = $(this).index() + 1; + window.location.hash = `L${targetLineNumber}`; + }); + + exports.focusOnLine(self.ace); } }; @@ -55,7 +63,6 @@ const padeditor = (() => { } self.initViewOptions(); self.setViewOptions(initialViewOptions); - // view bar $('#viewbarcontents').show(); }, @@ -89,6 +96,7 @@ const padeditor = (() => { html10n.bind('localized', () => { $('#languagemenu').val(html10n.getLanguage()); // translate the value of 'unnamed' and 'Enter your name' textboxes in the userlist + // this does not interfere with html10n's normal value-setting because // html10n just ingores s // also, a value which has been set by the user will be not overwritten @@ -166,3 +174,50 @@ const padeditor = (() => { })(); exports.padeditor = padeditor; + +exports.focusOnLine = (ace) => { + // If a number is in the URI IE #L124 go to that line number + const lineNumber = window.location.hash.substr(1); + if (lineNumber) { + if (lineNumber[0] === 'L') { + const $outerdoc = $('iframe[name="ace_outer"]').contents().find('#outerdocbody'); + const lineNumberInt = parseInt(lineNumber.substr(1)); + if (lineNumberInt) { + const $inner = $('iframe[name="ace_outer"]').contents().find('iframe') + .contents().find('#innerdocbody'); + const line = $inner.find(`div:nth-child(${lineNumberInt})`); + if (line.length !== 0) { + let offsetTop = line.offset().top; + offsetTop += parseInt($outerdoc.css('padding-top').replace('px', '')); + const hasMobileLayout = $('body').hasClass('mobile-layout'); + if (!hasMobileLayout) { + offsetTop += parseInt($inner.css('padding-top').replace('px', '')); + } + const $outerdocHTML = $('iframe[name="ace_outer"]').contents() + .find('#outerdocbody').parent(); + $outerdoc.css({top: `${offsetTop}px`}); // Chrome + $outerdocHTML.animate({scrollTop: offsetTop}); // needed for FF + const node = line[0]; + ace.callWithAce((ace) => { + const selection = { + startPoint: { + index: 0, + focusAtStart: true, + maxIndex: 1, + node, + }, + endPoint: { + index: 0, + focusAtStart: true, + maxIndex: 1, + node, + }, + }; + ace.ace_setSelection(selection); + }); + } + } + } + } + // End of setSelection / set Y position of editor +}; diff --git a/tests/frontend/helper.js b/tests/frontend/helper.js index b49d32eb8..37c5af3b1 100644 --- a/tests/frontend/helper.js +++ b/tests/frontend/helper.js @@ -1,4 +1,5 @@ -var helper = {}; +'use strict'; +const helper = {}; // eslint-disable-line (function () { let $iframe; const @@ -29,10 +30,9 @@ var helper = {}; const getFrameJQuery = function ($iframe) { /* - I tried over 9000 ways to inject javascript into iframes. + I tried over 9001 ways to inject javascript into iframes. This is the only way I found that worked in IE 7+8+9, FF and Chrome */ - const win = $iframe[0].contentWindow; const doc = win.document; @@ -68,7 +68,8 @@ var helper = {}; // I don't fully understand it, but this function seems to properly simulate // padCookie.setPref in the client code helper.setPadPrefCookie = function (prefs) { - helper.padChrome$.document.cookie = (`prefsHttp=${escape(JSON.stringify(prefs))};expires=Thu, 01 Jan 3000 00:00:00 GMT`); + helper.padChrome$.document.cookie = + (`prefsHttp=${escape(JSON.stringify(prefs))};expires=Thu, 01 Jan 3000 00:00:00 GMT`); }; // Functionality for knowing what key event type is required for tests @@ -102,8 +103,13 @@ var helper = {}; } // if opts.params is set we manipulate the URL to include URL parameters IE ?foo=Bah. + let encodedParams; if (opts.params) { - var encodedParams = `?${$.param(opts.params)}`; + encodedParams = `?${$.param(opts.params)}`; + } + let hash; + if (opts.hash) { + hash = `#${opts.hash}`; } // clear cookies @@ -112,8 +118,7 @@ var helper = {}; } if (!padName) padName = `FRONTEND_TEST_${helper.randomString(20)}`; - $iframe = $(``); - + $iframe = $(``); // needed for retry const origPadName = padName; @@ -132,7 +137,8 @@ var helper = {}; if (opts.padPrefs) { helper.setPadPrefCookie(opts.padPrefs); } - helper.waitFor(() => !$iframe.contents().find('#editorloadingbox').is(':visible'), 10000).done(() => { + helper.waitFor(() => !$iframe.contents().find('#editorloadingbox') + .is(':visible'), 10000).done(() => { helper.padOuter$ = getFrameJQuery(helper.padChrome$('iframe[name="ace_outer"]')); helper.padInner$ = getFrameJQuery(helper.padOuter$('iframe[name="ace_inner"]')); @@ -175,7 +181,7 @@ var helper = {}; }; helper.waitFor = function (conditionFunc, timeoutTime = 1900, intervalTime = 10) { - const deferred = $.Deferred(); + const deferred = $.Deferred(); // eslint-disable-line const _fail = deferred.fail.bind(deferred); let listenForFail = false; @@ -245,7 +251,7 @@ var helper = {}; selection.addRange(range); }; - var getTextNodeAndOffsetOf = function ($targetLine, targetOffsetAtLine) { + const getTextNodeAndOffsetOf = function ($targetLine, targetOffsetAtLine) { const $textNodes = $targetLine.find('*').contents().filter(function () { return this.nodeType === Node.TEXT_NODE; }); @@ -268,7 +274,7 @@ var helper = {}; }); // edge cases - if (textNodeWhereOffsetIs === null) { + if (textNodeWhereOffsetIs == null) { // there was no text node inside $targetLine, so it is an empty line (
). // Use beginning of line textNodeWhereOffsetIs = $targetLine.get(0); diff --git a/tests/frontend/specs/scrollTo.js b/tests/frontend/specs/scrollTo.js new file mode 100755 index 000000000..47fe1ca7e --- /dev/null +++ b/tests/frontend/specs/scrollTo.js @@ -0,0 +1,43 @@ +'use strict'; + +describe('scrolls to line', function () { + // create a new pad with URL hash set before each test run + beforeEach(function (cb) { + helper.newPad({ + hash: 'L4', + cb, + }); + this.timeout(10000); + }); + + it('Scrolls down to Line 4', async function () { + this.timeout(10000); + const chrome$ = helper.padChrome$; + await helper.waitForPromise(() => { + const topOffset = parseInt(chrome$('iframe').first('iframe') + .contents().find('#outerdocbody').css('top')); + return (topOffset >= 100); + }); + }); +}); + +describe('doesnt break on weird hash input', function () { + // create a new pad with URL hash set before each test run + beforeEach(function (cb) { + helper.newPad({ + hash: '#DEEZ123123NUTS', + cb, + }); + this.timeout(10000); + }); + + it('Does NOT change scroll', async function () { + this.timeout(10000); + const chrome$ = helper.padChrome$; + await helper.waitForPromise(() => { + const topOffset = parseInt(chrome$('iframe').first('iframe') + .contents().find('#outerdocbody').css('top')); + return (!topOffset); // no css top should be set. + }); + }); +}); From 951c93fa6ddb4f6580e320ea45c54cb3e3d6455d Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 27 Dec 2020 00:13:03 +0100 Subject: [PATCH 004/357] change_user_name test: refactor --- tests/frontend/helper/ui.js | 44 +++++++++++++- tests/frontend/specs/change_user_name.js | 75 ++++++------------------ 2 files changed, 62 insertions(+), 57 deletions(-) diff --git a/tests/frontend/helper/ui.js b/tests/frontend/helper/ui.js index d83cbee97..0f3e64169 100644 --- a/tests/frontend/helper/ui.js +++ b/tests/frontend/helper/ui.js @@ -77,7 +77,49 @@ helper.settingsMenu = function () { return helper.padChrome$('#settings'); }; * * @returns {HTMLElement} the settings button */ -helper.settingsButton = function () { return helper.padChrome$("button[data-l10n-id='pad.toolbar.settings.title']"); }; +helper.settingsButton = function () { + return helper.padChrome$("button[data-l10n-id='pad.toolbar.settings.title']"); +}; + +/** + * Toggles user list + */ +helper.toggleUserList = async function () { + const isVisible = helper.userListShown(); + const button = helper.padChrome$("button[data-l10n-id='pad.toolbar.showusers.title']"); + button.click(); + await helper.waitForPromise(() => !isVisible); +}; + +/** + * Gets the user name input field + * + * @returns {HTMLElement} user name input field + */ +helper.usernameField = function () { + return helper.padChrome$("input[data-l10n-id='pad.userlist.entername']"); +}; + +/** + * Is the user list popup shown? + * + * @returns {boolean} + */ +helper.userListShown = function () { + return helper.padChrome$('div#users').hasClass('popup-show'); +}; + +/** + * Sets the user name + * + */ +helper.setUserName = async (name) => { + const userElement = helper.usernameField(); + userElement.click(); + userElement.val(name); + userElement.blur(); + await helper.waitForPromise(() => !helper.usernameField().hasClass('editactive')); +}; /** * Gets the titlecross icon diff --git a/tests/frontend/specs/change_user_name.js b/tests/frontend/specs/change_user_name.js index e144a2340..0b9132f80 100644 --- a/tests/frontend/specs/change_user_name.js +++ b/tests/frontend/specs/change_user_name.js @@ -2,70 +2,33 @@ describe('change username value', function () { // create a new pad before each test run beforeEach(function (cb) { helper.newPad(cb); - this.timeout(60000); }); - it('Remembers the user name after a refresh', function (done) { - this.timeout(60000); - const chrome$ = helper.padChrome$; + it('Remembers the user name after a refresh', async function () { + helper.toggleUserList(); + helper.setUserName('😃'); - // click on the settings button to make settings visible - const $userButton = chrome$('.buttonicon-showusers'); - $userButton.click(); + helper.newPad({ // get a new pad, but don't clear the cookies + clearCookies: false, + cb() { + helper.toggleUserList(); - const $usernameInput = chrome$('#myusernameedit'); - $usernameInput.click(); - - $usernameInput.val('John McLear'); - $usernameInput.blur(); - - setTimeout(() => { // give it a second to save the username on the server side - helper.newPad({ // get a new pad, but don't clear the cookies - clearCookies: false, - cb() { - const chrome$ = helper.padChrome$; - - // click on the settings button to make settings visible - const $userButton = chrome$('.buttonicon-showusers'); - $userButton.click(); - - const $usernameInput = chrome$('#myusernameedit'); - expect($usernameInput.val()).to.be('John McLear'); - done(); - }, - }); - }, 1000); + expect(helper.usernameField().val()).to.be('😃'); + }, + }); }); + it('Own user name is shown when you enter a chat', async function () { + helper.toggleUserList(); + helper.setUserName('😃'); - it('Own user name is shown when you enter a chat', function (done) { - const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; + helper.showChat(); + helper.sendChatMessage('O hi{enter}'); - // click on the settings button to make settings visible - const $userButton = chrome$('.buttonicon-showusers'); - $userButton.click(); - - const $usernameInput = chrome$('#myusernameedit'); - $usernameInput.click(); - - $usernameInput.val('John McLear'); - $usernameInput.blur(); - - // click on the chat button to make chat visible - const $chatButton = chrome$('#chaticon'); - $chatButton.click(); - const $chatInput = chrome$('#chatinput'); - $chatInput.sendkeys('O hi'); // simulate a keypress of typing JohnMcLear - $chatInput.sendkeys('{enter}'); // simulate a keypress of enter actually does evt.which = 10 not 13 - - // check if chat shows up - helper.waitFor(() => chrome$('#chattext').children('p').length !== 0 // wait until the chat message shows up - ).done(() => { - const $firstChatMessage = chrome$('#chattext').children('p'); - const containsJohnMcLear = $firstChatMessage.text().indexOf('John McLear') !== -1; // does the string contain John McLear - expect(containsJohnMcLear).to.be(true); // expect the first chat message to contain JohnMcLear - done(); + await helper.waitForPromise(() => { + // username:hours:minutes text + const chatText = helper.chatTextParagraphs().text(); + return chatText.indexOf('😃') === 0; }); }); }); From ca405c1685800b2b3c5c67a92263535282b45897 Mon Sep 17 00:00:00 2001 From: webzwo0i Date: Sun, 27 Dec 2020 23:32:09 +0100 Subject: [PATCH 005/357] send the test files with the correct content-type header --- src/node/hooks/express/tests.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/node/hooks/express/tests.js b/src/node/hooks/express/tests.js index 7b32a322d..87edc2a09 100644 --- a/src/node/hooks/express/tests.js +++ b/src/node/hooks/express/tests.js @@ -17,7 +17,7 @@ exports.expressCreateServer = function (hook_name, args, cb) { files = files.filter((f) => f.endsWith('.js')); console.debug('Sent browser the following test specs:', files); - res.setHeader('content-type', 'text/javascript'); + res.setHeader('content-type', 'application/javascript'); res.end(`var specs_list = ${JSON.stringify(files)};\n`); }); @@ -49,6 +49,8 @@ exports.expressCreateServer = function (hook_name, args, cb) { content = `describe(${JSON.stringify(specFileName)}, function(){ ${content} });`; + if(!specFilePath.endsWith('index.html')) res.setHeader('content-type', 'application/javascript'); + res.send(content); }); }); From 6d626c90d3ad0bf96aada324c9d574bb5481a564 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Tue, 29 Dec 2020 08:50:16 +0100 Subject: [PATCH 006/357] Localisation updates from https://translatewiki.net. --- src/locales/fi.json | 3 +++ src/locales/fr.json | 58 ++++++++++++++++++++++----------------------- src/locales/it.json | 2 +- src/locales/uk.json | 42 +++++++++++++++++++++++++++++--- 4 files changed, 72 insertions(+), 33 deletions(-) diff --git a/src/locales/fi.json b/src/locales/fi.json index cdba94ec9..2af040a3d 100644 --- a/src/locales/fi.json +++ b/src/locales/fi.json @@ -18,6 +18,7 @@ "VezonThunder" ] }, + "admin_plugins.available_install.value": "Lataa", "admin_plugins.available_search.placeholder": "Etsi asennettavia laajennuksia", "admin_plugins.description": "Kuvaus", "admin_plugins.installed": "Asennetut laajennukset", @@ -41,6 +42,8 @@ "admin_settings": "Asetukset", "admin_settings.current": "Nykyinen kokoonpano", "admin_settings.current_example-devel": "Esimerkki kehitysasetusten mallista", + "admin_settings.current_save.value": "Tallenna Asetukset", + "admin_settings.page-title": "asetukset - Etherpad", "index.newPad": "Uusi muistio", "index.createOpenPad": "tai luo tai avaa muistio nimellä:", "pad.toolbar.bold.title": "Lihavointi (Ctrl-B)", diff --git a/src/locales/fr.json b/src/locales/fr.json index 68c03225b..f72a887ce 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -30,29 +30,29 @@ ] }, "admin.page-title": "Tableau de bord administrateur — Etherpad", - "admin_plugins": "Gestionnaire de compléments", - "admin_plugins.available": "Compléments disponibles", - "admin_plugins.available_not-found": "Aucun complément trouvé.", - "admin_plugins.available_fetching": "Récupération…", + "admin_plugins": "Gestionnaire de greffons", + "admin_plugins.available": "Greffons disponibles", + "admin_plugins.available_not-found": "Aucun greffon trouvé.", + "admin_plugins.available_fetching": "Récupération en cours...", "admin_plugins.available_install.value": "Installer", - "admin_plugins.available_search.placeholder": "Rechercher des compléments à installer", + "admin_plugins.available_search.placeholder": "Rechercher des greffons à installer", "admin_plugins.description": "Description", - "admin_plugins.installed": "Compléments installés", - "admin_plugins.installed_fetching": "Récupération des compléments installés…", - "admin_plugins.installed_nothing": "Vous n’avez pas encore installé de complément.", + "admin_plugins.installed": "Greffons installés", + "admin_plugins.installed_fetching": "Récupération des greffons installés en cours...", + "admin_plugins.installed_nothing": "Vous n’avez encore installé aucun greffon.", "admin_plugins.installed_uninstall.value": "Désinstaller", "admin_plugins.last-update": "Dernière mise à jour", "admin_plugins.name": "Nom", - "admin_plugins.page-title": "Gestionnaire de compléments — Etherpad", + "admin_plugins.page-title": "Gestionnaire de greffons — Etherpad", "admin_plugins.version": "Version", - "admin_plugins_info": "Information de résolution de problème", + "admin_plugins_info": "Informations de résolution de problème", "admin_plugins_info.hooks": "Crochets installés", "admin_plugins_info.hooks_client": "Crochets côté client", "admin_plugins_info.hooks_server": "Crochets côté serveur", "admin_plugins_info.parts": "Parties installées", - "admin_plugins_info.plugins": "Compléments installés", - "admin_plugins_info.page-title": "Information de complément — Etherpad", - "admin_plugins_info.version": "Version Etherpad", + "admin_plugins_info.plugins": "Greffons installés", + "admin_plugins_info.page-title": "Informations du greffon — Etherpad", + "admin_plugins_info.version": "Version d’Etherpad", "admin_plugins_info.version_latest": "Dernière version disponible", "admin_plugins_info.version_number": "Numéro de version", "admin_settings": "Paramètres", @@ -64,7 +64,7 @@ "admin_settings.page-title": "Paramètres — Etherpad", "index.newPad": "Nouveau bloc-notes", "index.createOpenPad": "ou créer/ouvrir un bloc-notes intitulé :", - "index.openPad": "ouvrir un Pad existant avec le nom :", + "index.openPad": "ouvrir un bloc-note existant avec le nom :", "pad.toolbar.bold.title": "Gras (Ctrl+B)", "pad.toolbar.italic.title": "Italique (Ctrl+I)", "pad.toolbar.underline.title": "Souligné (Ctrl+U)", @@ -76,7 +76,7 @@ "pad.toolbar.undo.title": "Annuler (Ctrl+Z)", "pad.toolbar.redo.title": "Rétablir (Ctrl+Y)", "pad.toolbar.clearAuthorship.title": "Effacer le surlignage par auteur (Ctrl+Shift+C)", - "pad.toolbar.import_export.title": "Importer de/Exporter vers un format de fichier différent", + "pad.toolbar.import_export.title": "Importer/Exporter des formats de fichiers différents", "pad.toolbar.timeslider.title": "Historique dynamique", "pad.toolbar.savedRevision.title": "Enregistrer la révision", "pad.toolbar.settings.title": "Paramètres", @@ -85,7 +85,7 @@ "pad.colorpicker.save": "Enregistrer", "pad.colorpicker.cancel": "Annuler", "pad.loading": "Chargement...", - "pad.noCookie": "Un cookie n’a pas pu être trouvé. Veuillez autoriser les fichiers témoins (ou cookies) dans votre navigateur ! Votre session et vos paramètres ne seront pas enregistrés entre les visites. Cela peut être dû au fait qu’Etehrpad est inclus dans un iFrame dans certains navigateurs. Veuillez vous assurer que Etherpad est dans le même sous-domaine/domaine que son iFrame parent", + "pad.noCookie": "Un fichier témoin (ou ''cookie'') n’a pas pu être trouvé. Veuillez autoriser les fichiers témoins dans votre navigateur ! Votre session et vos paramètres ne seront pas enregistrés entre les visites. Cela peut être dû au fait qu’Etherpad est inclus dans un ''iFrame'' dans certains navigateurs. Veuillez vous assurer qu’Etherpad est dans le même sous-domaine/domaine que son ''iFrame'' parent.", "pad.permissionDenied": "Vous n’êtes pas autorisé à accéder à ce bloc-notes", "pad.settings.padSettings": "Paramètres du bloc-notes", "pad.settings.myView": "Ma vue", @@ -94,11 +94,11 @@ "pad.settings.colorcheck": "Surlignage par auteur", "pad.settings.linenocheck": "Numéros de lignes", "pad.settings.rtlcheck": "Le contenu doit-il être lu de droite à gauche ?", - "pad.settings.fontType": "Police :", + "pad.settings.fontType": "Type de police :", "pad.settings.fontType.normal": "Normal", "pad.settings.language": "Langue :", "pad.settings.about": "À propos", - "pad.settings.poweredBy": "Fourni par", + "pad.settings.poweredBy": "Propulsé par", "pad.importExport.import_export": "Importer/Exporter", "pad.importExport.import": "Charger un texte ou un document", "pad.importExport.importSuccessful": "Réussi !", @@ -111,13 +111,13 @@ "pad.importExport.exportopen": "ODF (Open Document Format)", "pad.importExport.abiword.innerHTML": "Vous ne pouvez importer que des formats texte brut ou HTML. Pour des fonctionnalités d’importation plus évoluées, veuillez installer AbiWord ou LibreOffice.", "pad.modals.connected": "Connecté.", - "pad.modals.reconnecting": "Reconnexion à votre bloc-notes...", + "pad.modals.reconnecting": "Reconnexion à votre bloc-notes en cours...", "pad.modals.forcereconnect": "Forcer la reconnexion", "pad.modals.reconnecttimer": "Essai de reconnexion", "pad.modals.cancel": "Annuler", "pad.modals.userdup": "Ouvert dans une autre fenêtre", "pad.modals.userdup.explanation": "Ce bloc-notes semble être ouvert dans plusieurs fenêtres sur cet ordinateur.", - "pad.modals.userdup.advice": "Se reconnecter en utilisant cette fenêtre.", + "pad.modals.userdup.advice": "Se reconnecter en utilisant plutôt cette fenêtre.", "pad.modals.unauth": "Non autorisé", "pad.modals.unauth.explanation": "Vos autorisations ont été changées lors de l’affichage de cette page. Essayez de vous reconnecter.", "pad.modals.looping.explanation": "Nous éprouvons des problèmes de communication au serveur de synchronisation.", @@ -133,10 +133,10 @@ "pad.modals.corruptPad.cause": "Cela peut être dû à une mauvaise configuration du serveur ou à un autre comportement inattendu. Veuillez contacter l’administrateur du service.", "pad.modals.deleted": "Supprimé.", "pad.modals.deleted.explanation": "Ce bloc-notes a été supprimé.", - "pad.modals.rateLimited": "Taux limité.", - "pad.modals.rateLimited.explanation": "Vous avez envoyé trop de messages à ce bloc, il vous a donc déconnecté.", + "pad.modals.rateLimited": "Flot limité.", + "pad.modals.rateLimited.explanation": "Vous avez envoyé trop de messages à ce bloc-notes, il vous a donc déconnecté.", "pad.modals.rejected.explanation": "Le serveur a rejeté un message qui a été envoyé par votre navigateur.", - "pad.modals.rejected.cause": "Le serveur peut avoir été mis à jour pendant que vous regardiez le bloc, ou il y a peut-être un bogue dans Etherpad. Essayez de recharger la page.", + "pad.modals.rejected.cause": "Le serveur peut avoir été mis à jour pendant que vous regardiez le bloc-notes, ou il y a peut-être une anomalie dans Etherpad. Essayez de recharger la page.", "pad.modals.disconnected": "Vous avez été déconnecté.", "pad.modals.disconnected.explanation": "La connexion au serveur a échoué.", "pad.modals.disconnected.cause": "Il se peut que le serveur soit indisponible. Si le problème persiste, veuillez en informer l’administrateur du service.", @@ -149,7 +149,7 @@ "pad.chat.loadmessages": "Charger davantage de messages", "pad.chat.stick.title": "Ancrer la discussion sur l’écran", "pad.chat.writeMessage.placeholder": "Entrez votre message ici", - "timeslider.followContents": "Suivre les mises à jour de contenu du bloc", + "timeslider.followContents": "Suivre les mises à jour de contenu du bloc-notes", "timeslider.pageTitle": "Historique dynamique de {{appTitle}}", "timeslider.toolbar.returnbutton": "Retourner au bloc-notes", "timeslider.toolbar.authors": "Auteurs :", @@ -158,7 +158,7 @@ "timeslider.exportCurrent": "Exporter la version actuelle sous :", "timeslider.version": "Version {{version}}", "timeslider.saved": "Enregistré le {{day}} {{month}} {{year}}", - "timeslider.playPause": "Lecture / Pause des contenus du bloc-notes", + "timeslider.playPause": "Lecture / Pause des contenus du bloc-notes", "timeslider.backRevision": "Reculer d’une révision dans ce bloc-notes", "timeslider.forwardRevision": "Avancer d’une révision dans ce bloc-notes", "timeslider.dateformat": "{{day}}/{{month}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", @@ -184,10 +184,10 @@ "pad.impexp.importing": "Import en cours...", "pad.impexp.confirmimport": "Importer un fichier écrasera le contenu actuel du bloc-notes. Êtes-vous sûr de vouloir le faire ?", "pad.impexp.convertFailed": "Nous ne pouvons pas importer ce fichier. Veuillez utiliser un autre format de document ou faire manuellement un copier/coller du texte brut", - "pad.impexp.padHasData": "Nous n’avons pas pu importer ce fichier parce que ce bloc-notes a déjà été modifié ; veuillez l’importer vers un nouveau bloc-notes", - "pad.impexp.uploadFailed": "Le téléversement a échoué, veuillez réessayer", - "pad.impexp.importfailed": "Échec de l’importation", + "pad.impexp.padHasData": "Nous n’avons pas pu importer ce fichier parce que ce bloc-notes a déjà été modifié ; veuillez l’importer vers un nouveau bloc-notes.", + "pad.impexp.uploadFailed": "Le téléversement a échoué, veuillez réessayer.", + "pad.impexp.importfailed": "Échec de l’import", "pad.impexp.copypaste": "Veuillez copier-coller", "pad.impexp.exportdisabled": "L’exportation au format {{type}} est désactivée. Veuillez contacter votre administrateur système pour plus de détails.", - "pad.impexp.maxFileSize": "Fichier trop gros. Contactez votre administrateur de site pour augmenter la taille maximale des fichiers importés" + "pad.impexp.maxFileSize": "Fichier trop gros. Contactez votre administrateur de site pour augmenter la taille maximale des fichiers importés." } diff --git a/src/locales/it.json b/src/locales/it.json index 963b04982..0b3914b47 100644 --- a/src/locales/it.json +++ b/src/locales/it.json @@ -86,7 +86,7 @@ "pad.modals.disconnected.cause": "Il server potrebbe essere non disponibile. Informa l'amministrazione del servizio se il problema persiste.", "pad.share": "Condividi questo Pad", "pad.share.readonly": "Sola lettura", - "pad.share.link": "Link", + "pad.share.link": "Collegamento", "pad.share.emebdcode": "Incorpora URL", "pad.chat": "Chat", "pad.chat.title": "Apri la chat per questo Pad.", diff --git a/src/locales/uk.json b/src/locales/uk.json index 240302de7..3604e38b9 100644 --- a/src/locales/uk.json +++ b/src/locales/uk.json @@ -10,11 +10,40 @@ "Piramidion", "Steve.rusyn", "SteveR", + "Ата", "Григорій Пугач" ] }, + "admin.page-title": "Адміністративна панель — Etherpad", + "admin_plugins": "Менеджер плагінів", + "admin_plugins.available": "Доступні плагіни", + "admin_plugins.available_not-found": "Плагінів не знайдено.", + "admin_plugins.available_fetching": "Отримується…", + "admin_plugins.available_install.value": "Встановити", + "admin_plugins.available_search.placeholder": "Шукати плагіни для встановлення", + "admin_plugins.description": "Опис", + "admin_plugins.installed": "Встановлені плагіни", + "admin_plugins.installed_fetching": "Отримуються встановлені плагіни…", + "admin_plugins.installed_nothing": "Ви ще не встановили жодних плагінів.", + "admin_plugins.installed_uninstall.value": "Видалити", + "admin_plugins.last-update": "Останнє оновлення", + "admin_plugins.name": "Назва", + "admin_plugins.page-title": "Менеджер плагінів — Etherpad", + "admin_plugins.version": "Версія", + "admin_plugins_info": "Інформація щодо виправлення неполадок", + "admin_plugins_info.plugins": "Встановлені плагіни", + "admin_plugins_info.page-title": "Інформація про плагіни — Etherpad", + "admin_plugins_info.version": "Версія Etherpad", + "admin_plugins_info.version_latest": "Найсвіжіша доступна версія", + "admin_plugins_info.version_number": "Номер версії", + "admin_settings": "Налаштування", + "admin_settings.current": "Поточна конфігурація", + "admin_settings.current_restart.value": "Перезапустити Etherpad", + "admin_settings.current_save.value": "Зберегти налаштування", + "admin_settings.page-title": "Налаштування — Etherpad", "index.newPad": "Створити", "index.createOpenPad": "або створити/відкрити документ з назвою:", + "index.openPad": "відкрити наявний документ з назвою:", "pad.toolbar.bold.title": "Напівжирний (Ctrl-B)", "pad.toolbar.italic.title": "Курсив (Ctrl-I)", "pad.toolbar.underline.title": "Підкреслення (Ctrl-U)", @@ -22,7 +51,7 @@ "pad.toolbar.ol.title": "Упорядкований список (Ctrl+Shift+N)", "pad.toolbar.ul.title": "Неупорядкований список (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Відступ (TAB)", - "pad.toolbar.unindent.title": "Виступ (Shift+TAB)", + "pad.toolbar.unindent.title": "Відступ (Shift+TAB)", "pad.toolbar.undo.title": "Скасувати (Ctrl-Z)", "pad.toolbar.redo.title": "Повторити (Ctrl-Y)", "pad.toolbar.clearAuthorship.title": "Очистити кольори авторства (Ctrl+Shift+C)", @@ -35,7 +64,7 @@ "pad.colorpicker.save": "Зберегти", "pad.colorpicker.cancel": "Скасувати", "pad.loading": "Завантаження…", - "pad.noCookie": "Реп'яшки не знайдено. Будь-ласка, увімкніть реп'яшки у вашому браузері!", + "pad.noCookie": "Реп'яшки не знайдено. Будь ласка, увімкніть реп'яшки у вашому браузері! Ваша сесія та налаштування не зберігатимуться між візитами. Це може спричинятися тим, що Etherpad у деяких браузерах включений через iFrame. Будь ласка, переконайтеся, що iFrame міститься на тому ж піддомені/домені, що й батьківський iFrame", "pad.permissionDenied": "У Вас немає дозволу для доступу до цього документа", "pad.settings.padSettings": "Налаштування документа", "pad.settings.myView": "Мій Вигляд", @@ -47,6 +76,8 @@ "pad.settings.fontType": "Тип шрифту:", "pad.settings.fontType.normal": "Звичайний", "pad.settings.language": "Мова:", + "pad.settings.about": "Про програму", + "pad.settings.poweredBy": "Працює на", "pad.importExport.import_export": "Імпорт/Експорт", "pad.importExport.import": "Завантажити будь-який текстовий файл або документ", "pad.importExport.importSuccessful": "Успішно!", @@ -59,7 +90,7 @@ "pad.importExport.exportopen": "ODF (документ OpenOffice)", "pad.importExport.abiword.innerHTML": "Ви можете імпортувати лише у форматі простого тексту або HTML. Для більш просунутих способів імпорту встановіть AbiWord або LibreOffice.", "pad.modals.connected": "З'єднано.", - "pad.modals.reconnecting": "Перепідлючення до Вашого документа..", + "pad.modals.reconnecting": "Перепідключення до Вашого документа…", "pad.modals.forcereconnect": "Примусове перепідключення", "pad.modals.reconnecttimer": "Триває спроба відновлення з'єднання", "pad.modals.cancel": "Скасувати", @@ -81,6 +112,10 @@ "pad.modals.corruptPad.cause": "Це може бути через неправильну конфігурацію сервера або іншу непередбачувану поведінку. Зверніться до адміністратора служби.", "pad.modals.deleted": "Вилучено.", "pad.modals.deleted.explanation": "Цей документ було вилучено.", + "pad.modals.rateLimited": "Швидкість обмежено.", + "pad.modals.rateLimited.explanation": "Ви надіслали надто багато повідомлень у цей документ, тому він вас від'єднав.", + "pad.modals.rejected.explanation": "Сервер відхилив повідомлення, надіслане вашим браузером.", + "pad.modals.rejected.cause": "Сервер міг оновитися, поки ви переглядали документ, а може це баг в Etherpad'і. Спробуйте перезавантажити сторінку.", "pad.modals.disconnected": "Вас було від'єднано.", "pad.modals.disconnected.explanation": "З'єднання з сервером втрачено", "pad.modals.disconnected.cause": "Сервер, можливо, недоступний. Будь ласка, повідомте адміністратора служби, якщо це повторюватиметься.", @@ -93,6 +128,7 @@ "pad.chat.loadmessages": "Завантажити більше повідомлень", "pad.chat.stick.title": "Закріпити чат на екрані", "pad.chat.writeMessage.placeholder": "Напишіть своє повідомлення сюди", + "timeslider.followContents": "Слідкувати за оновленнями вмісту документа", "timeslider.pageTitle": "Часова шкала {{appTitle}}", "timeslider.toolbar.returnbutton": "Повернутись до документа", "timeslider.toolbar.authors": "Автори:", From 68ca3fcdbd37fcdba932d81c8b2d2948370a49dd Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 4 Jan 2021 14:43:02 +0100 Subject: [PATCH 007/357] Localisation updates from https://translatewiki.net. --- src/locales/be-tarask.json | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/locales/be-tarask.json b/src/locales/be-tarask.json index 2d3e33a52..a759eda7e 100644 --- a/src/locales/be-tarask.json +++ b/src/locales/be-tarask.json @@ -7,6 +7,13 @@ "Wizardist" ] }, + "admin.page-title": "Адміністрацыйная панэль — Etherpad", + "admin_plugins": "Кіраўнік плагінаў", + "admin_plugins.available": "Даступныя плагіны", + "admin_plugins.available_not-found": "Плагіны ня знойдзеныя.", + "admin_plugins.available_fetching": "Атрымліваем…", + "admin_plugins.available_install.value": "Усталяваць", + "admin_settings.page-title": "Налады — Etherpad", "index.newPad": "Стварыць", "index.createOpenPad": "ці тварыць/адкрыць дакумэнт з назвай:", "index.openPad": "адкрыць існы Нататнік з назваю:", @@ -17,7 +24,7 @@ "pad.toolbar.ol.title": "Упарадкаваны сьпіс (Ctrl+Shift+N)", "pad.toolbar.ul.title": "Неўпарадкаваны сьпіс (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Водступ (TAB)", - "pad.toolbar.unindent.title": "Выступ (Shift+TAB)", + "pad.toolbar.unindent.title": "Водступ (Shift+TAB)", "pad.toolbar.undo.title": "Скасаваць(Ctrl-Z)", "pad.toolbar.redo.title": "Вярнуць (Ctrl-Y)", "pad.toolbar.clearAuthorship.title": "Прыбраць колер дакумэнту (Ctrl+Shift+C)", @@ -42,6 +49,7 @@ "pad.settings.fontType": "Тып шрыфту:", "pad.settings.fontType.normal": "Звычайны", "pad.settings.language": "Мова:", + "pad.settings.about": "Пра", "pad.importExport.import_export": "Імпарт/Экспарт", "pad.importExport.import": "Загрузіжайце любыя тэкставыя файлы або дакумэнты", "pad.importExport.importSuccessful": "Пасьпяхова!", @@ -54,7 +62,7 @@ "pad.importExport.exportopen": "ODF (Open Document Format)", "pad.importExport.abiword.innerHTML": "Вы можаце імпартаваць толькі з звычайнага тэксту або HTML. Дзеля больш пашыраных магчымасьцяў імпарту, калі ласка, усталюйце AbiWord альбо LibreOffice.", "pad.modals.connected": "Падлучыліся.", - "pad.modals.reconnecting": "Перападлучэньне да вашага дакумэнта...", + "pad.modals.reconnecting": "Перападлучэньне да вашага дакумэнта…", "pad.modals.forcereconnect": "Прымусовае перападлучэньне", "pad.modals.reconnecttimer": "Спрабуем перападключыцца праз", "pad.modals.cancel": "Скасаваць", From 6012a9a480731c384950b47ad20c6795ce244783 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 5 Jan 2021 16:45:14 +0000 Subject: [PATCH 008/357] scripts: include v in release number to fix rleease script docs --- bin/release.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/release.js b/bin/release.js index 281434409..36b1194e6 100644 --- a/bin/release.js +++ b/bin/release.js @@ -56,7 +56,7 @@ child_process.execSync(`git push origin release/${newVersion}`); child_process.execSync(`make docs`); child_process.execSync(`git clone git@github.com:ether/ether.github.com.git`); -child_process.execSync(`cp -R out/doc/ ether.github.com/doc/${newVersion}`); +child_process.execSync(`cp -R out/doc/ ether.github.com/doc/v${newVersion}`); console.log('Once merged into master please run the following commands'); console.log(`git tag -a ${newVersion} -m ${newVersion} && git push origin master`); From 3373f059e6ee74bfeb9b52f77edc541be86b717a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 5 Jan 2021 16:11:29 -0500 Subject: [PATCH 009/357] Plugin publish workflow: Run `git push` before `npm publish` --- bin/plugins/lib/npmpublish.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/bin/plugins/lib/npmpublish.yml b/bin/plugins/lib/npmpublish.yml index 8d94ce88a..2e0cf2405 100644 --- a/bin/plugins/lib/npmpublish.yml +++ b/bin/plugins/lib/npmpublish.yml @@ -64,10 +64,20 @@ jobs: - run: git config user.email '41898282+github-actions[bot]@users.noreply.github.com' - run: npm ci - run: npm version patch + - run: git push --follow-tags + # `npm publish` must come after `git push` otherwise there is a race + # condition: If two PRs are merged back-to-back then master/main will be + # updated with the commits from the second PR before the first PR's + # workflow has a chance to push the commit generated by `npm version + # patch`. This causes the first PR's `git push` step to fail after the + # package has already been published, which in turn will cause all future + # workflow runs to fail because they will all attempt to use the same + # already-used version number. By running `npm publish` after `git push`, + # back-to-back merges will cause the first merge's workflow to fail but + # the second's will succeed. - run: npm publish env: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} - - run: git push --follow-tags -##ETHERPAD_NPM_V=1 +##ETHERPAD_NPM_V=2 ## NPM configuration automatically created using bin/plugins/updateAllPluginsScript.sh From 04962bfe39fcf5f95710d42f93054b00cfa64cdc Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 7 Jan 2021 09:16:13 +0000 Subject: [PATCH 010/357] update ueberdb to 1.1.7 (#4633) --- src/package-lock.json | 1804 +++++++++++++++++++++++++---------------- src/package.json | 2 +- 2 files changed, 1104 insertions(+), 702 deletions(-) diff --git a/src/package-lock.json b/src/package-lock.json index c203c98e9..f82019554 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -15,14 +15,14 @@ } }, "@apidevtools/openapi-schemas": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.0.3.tgz", - "integrity": "sha512-QoPaxGXfgqgGpK1p21FJ400z56hV681a8DOcZt3J5z0WIHgFeaIZ4+6bX5ATqmOoCpRCsH4ITEwKaOyFMz7wOA==" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.0.4.tgz", + "integrity": "sha512-ob5c4UiaMYkb24pNhvfSABShAwpREvUGCkqjiz/BX9gKZ32y/S22M+ALIHftTAuv9KsFVSpVdIDzi9ZzFh5TCA==" }, "@apidevtools/swagger-methods": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.1.tgz", - "integrity": "sha512-1Vlm18XYW6Yg7uHunroXeunWz5FShPFAdxBbPy8H6niB2Elz9QQsCoYHMbcc11EL1pTxaIr9HXz2An/mHXlX1Q==" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" }, "@apidevtools/swagger-parser": { "version": "9.0.1", @@ -39,75 +39,98 @@ } }, "@azure/ms-rest-azure-env": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@azure/ms-rest-azure-env/-/ms-rest-azure-env-1.1.2.tgz", - "integrity": "sha512-l7z0DPCi2Hp88w12JhDTtx5d0Y3+vhfE7JKJb9O7sEz71Cwp053N8piTtTnnk/tUor9oZHgEKi/p3tQQmLPjvA==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-azure-env/-/ms-rest-azure-env-2.0.0.tgz", + "integrity": "sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw==" }, "@azure/ms-rest-js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-1.9.0.tgz", - "integrity": "sha512-cB4Z2Mg7eBmet1rfbf0QSO1XbhfknRW7B+mX3IHJq0KGHaGJvCPoVTgdsJdCkazEMK1jtANFNEDDzSQacxyzbA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.1.0.tgz", + "integrity": "sha512-4BXLVImYRt+jcUmEJ5LUWglI8RBNVQndY6IcyvQ4U8O4kIXdmlRz3cJdA/RpXf5rKT38KOoTO2T6Z1f6Z1HDBg==", "requires": { - "@types/tunnel": "0.0.0", - "axios": "^0.19.0", - "form-data": "^2.3.2", - "tough-cookie": "^2.4.3", - "tslib": "^1.9.2", + "@types/node-fetch": "^2.3.7", + "@types/tunnel": "0.0.1", + "abort-controller": "^3.0.0", + "form-data": "^2.5.0", + "node-fetch": "^2.6.0", + "tough-cookie": "^3.0.1", + "tslib": "^1.10.0", "tunnel": "0.0.6", - "uuid": "^3.2.1", + "uuid": "^3.3.2", "xml2js": "^0.4.19" + }, + "dependencies": { + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "requires": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } } }, "@azure/ms-rest-nodeauth": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@azure/ms-rest-nodeauth/-/ms-rest-nodeauth-2.0.2.tgz", - "integrity": "sha512-KmNNICOxt3EwViAJI3iu2VH8t8BQg5J2rSAyO4IUYLF9ZwlyYsP419pdvl4NBUhluAP2cgN7dfD2V6E6NOMZlQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-nodeauth/-/ms-rest-nodeauth-3.0.6.tgz", + "integrity": "sha512-2twuzsXHdKMzEFI2+Sr82o6yS4ppNGZceYwil8PFo+rJxOZIoBm9e0//YC+dKilV/3F+6K/HuW8LdskDrJEQWA==", "requires": { - "@azure/ms-rest-azure-env": "^1.1.2", - "@azure/ms-rest-js": "^1.8.7", + "@azure/ms-rest-azure-env": "^2.0.0", + "@azure/ms-rest-js": "^2.0.4", "adal-node": "^0.1.28" } }, "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", "dev": true, "requires": { - "@babel/highlight": "^7.8.3" + "@babel/highlight": "^7.10.4" } }, "@babel/core": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.6.tgz", - "integrity": "sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", + "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.6", - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helpers": "^7.9.6", - "@babel/parser": "^7.9.6", - "@babel/template": "^7.8.6", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6", + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.10", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.10", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.10", + "@babel/types": "^7.12.10", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", "json5": "^2.1.2", - "lodash": "^4.17.13", - "resolve": "^1.3.2", + "lodash": "^4.17.19", "semver": "^5.4.1", "source-map": "^0.5.0" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -116,15 +139,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -134,14 +148,13 @@ } }, "@babel/generator": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.6.tgz", - "integrity": "sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", + "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", "dev": true, "requires": { - "@babel/types": "^7.9.6", + "@babel/types": "^7.12.11", "jsesc": "^2.5.1", - "lodash": "^4.17.13", "source-map": "^0.5.0" }, "dependencies": { @@ -154,200 +167,176 @@ } }, "@babel/helper-function-name": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz", - "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", + "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.9.5" + "@babel/helper-get-function-arity": "^7.12.10", + "@babel/template": "^7.12.7", + "@babel/types": "^7.12.11" } }, "@babel/helper-get-function-arity": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", - "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", + "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.12.10" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", - "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", + "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.12.7" } }, "@babel/helper-module-imports": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", - "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.12.5" } }, "@babel/helper-module-transforms": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", - "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-simple-access": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/template": "^7.8.6", - "@babel/types": "^7.9.0", - "lodash": "^4.17.13" + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "lodash": "^4.17.19" } }, "@babel/helper-optimise-call-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", - "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", + "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.12.10" } }, "@babel/helper-replace-supers": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz", - "integrity": "sha512-qX+chbxkbArLyCImk3bWV+jB5gTNU/rsze+JlcF6Nf8tVTigPJSI1o1oBow/9Resa1yehUO9lIipsmu9oG4RzA==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", + "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6" + "@babel/helper-member-expression-to-functions": "^7.12.7", + "@babel/helper-optimise-call-expression": "^7.12.10", + "@babel/traverse": "^7.12.10", + "@babel/types": "^7.12.11" } }, "@babel/helper-simple-access": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", - "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", "dev": true, "requires": { - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/types": "^7.12.1" } }, "@babel/helper-split-export-declaration": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", - "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", + "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.12.11" } }, "@babel/helper-validator-identifier": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", - "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", "dev": true }, "@babel/helpers": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.6.tgz", - "integrity": "sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", "dev": true, "requires": { - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6" + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" } }, "@babel/highlight": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", - "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.9.0", + "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } } }, "@babel/parser": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.6.tgz", - "integrity": "sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==", + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", + "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", "dev": true }, "@babel/template": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", - "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", + "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6" + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.12.7", + "@babel/types": "^7.12.7" } }, "@babel/traverse": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.6.tgz", - "integrity": "sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg==", + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", + "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.6", - "@babel/helper-function-name": "^7.9.5", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.9.6", - "@babel/types": "^7.9.6", + "@babel/code-frame": "^7.12.11", + "@babel/generator": "^7.12.11", + "@babel/helper-function-name": "^7.12.11", + "@babel/helper-split-export-declaration": "^7.12.11", + "@babel/parser": "^7.12.11", + "@babel/types": "^7.12.12", "debug": "^4.1.0", "globals": "^11.1.0", - "lodash": "^4.17.13" + "lodash": "^4.17.19" }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -357,13 +346,13 @@ } }, "@babel/types": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.6.tgz", - "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==", + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", + "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.9.5", - "lodash": "^4.17.13", + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } }, @@ -406,15 +395,6 @@ "ms": "2.1.2" } }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -430,13 +410,14 @@ } }, "@istanbuljs/load-nyc-config": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", - "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "requires": { "camelcase": "^5.3.1", "find-up": "^4.1.0", + "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" }, @@ -474,6 +455,12 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true } } }, @@ -483,10 +470,15 @@ "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", "dev": true }, + "@js-joda/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-3.2.0.tgz", + "integrity": "sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg==" + }, "@jsdevtools/ono": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.2.tgz", - "integrity": "sha512-qS/a24RA5FEoiJS9wiv6Pwg2c/kiUo3IVUQcfeM9JvsR6pM8Yx+yl/6xWYLckZCT5jpLNhslgjiA8p/XcGyMRQ==" + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" }, "@kwsites/file-exists": { "version": "1.1.1", @@ -545,9 +537,9 @@ } }, "@sinonjs/samsam": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.2.0.tgz", - "integrity": "sha512-CaIcyX5cDsjcW/ab7HposFWzV1kC++4HNsfnEdFJa7cP1QIuILAKV+BgfeqRXhcnSAc76r/Rh/O5C+300BwUIw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.0.tgz", + "integrity": "sha512-hXpcfx3aq+ETVBwPlRFICld5EnrkexXuXDwqUNhDdr5L8VjvMeSRwyOa0qL7XFmR+jVWR4rUZtnxlG7RX72sBg==", "dev": true, "requires": { "@sinonjs/commons": "^1.6.0", @@ -566,29 +558,35 @@ "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==" }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, "@types/long": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" }, "@types/node": { - "version": "14.14.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.13.tgz", - "integrity": "sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ==" + "version": "14.14.20", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz", + "integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==" }, - "@types/readable-stream": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.9.tgz", - "integrity": "sha512-sqsgQqFT7HmQz/V5jH1O0fvQQnXAJO46Gg9LRO/JPfjmVmGUlcx831TZZO3Y3HtWhIkzf3kTsNT0Z0kzIhIvZw==", + "@types/node-fetch": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz", + "integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==", "requires": { "@types/node": "*", - "safe-buffer": "*" + "form-data": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } } }, "@types/request": { @@ -620,9 +618,9 @@ "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==" }, "@types/tunnel": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.0.tgz", - "integrity": "sha512-FGDp0iBRiBdPjOgjJmn1NH0KDLN+Z8fRmo+9J7XGBhubq1DPrGrbmG4UTlGzrpbCpesMqD0sWkzi27EYkOMHyg==", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.1.tgz", + "integrity": "sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A==", "requires": { "@types/node": "*" } @@ -632,6 +630,20 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==" }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -695,9 +707,9 @@ } }, "aggregate-error": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", - "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "requires": { "clean-stack": "^2.0.0", @@ -751,6 +763,12 @@ "default-require-extensions": "^3.0.0" } }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "optional": true + }, "archiver": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/archiver/-/archiver-3.1.1.tgz", @@ -867,6 +885,54 @@ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "optional": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -904,9 +970,9 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" }, "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, "async": { @@ -939,14 +1005,6 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" }, - "axios": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", - "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", - "requires": { - "follow-redirects": "1.5.10" - } - }, "backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", @@ -963,14 +1021,14 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", + "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=" }, "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true }, "base64id": { @@ -1030,6 +1088,15 @@ "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "optional": true, + "requires": { + "inherits": "~2.0.0" + } + }, "bluebird": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", @@ -1103,14 +1170,19 @@ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" }, + "bson": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", + "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==" + }, "buffer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "dev": true, "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, "buffer-crc32": { @@ -1197,9 +1269,9 @@ } }, "ccount": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.5.tgz", - "integrity": "sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", + "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==" }, "chai": { "version": "4.2.0", @@ -1282,6 +1354,12 @@ "readdirp": "~3.2.0" } }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "optional": true + }, "clean-css": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", @@ -1375,6 +1453,12 @@ } } }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "optional": true + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1482,6 +1566,12 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "optional": true + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -1520,9 +1610,9 @@ } }, "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" }, "cookie-parser": { "version": "1.4.5", @@ -1531,13 +1621,6 @@ "requires": { "cookie": "0.4.0", "cookie-signature": "1.0.6" - }, - "dependencies": { - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - } } }, "cookie-signature": { @@ -1576,9 +1659,9 @@ } }, "cross-spawn": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", - "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -1647,6 +1730,12 @@ "type-detect": "^4.0.0" } }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "optional": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -1675,10 +1764,16 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "optional": true + }, "denque": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", - "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", + "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" }, "depd": { "version": "1.1.2", @@ -1690,6 +1785,12 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "optional": true + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -1851,6 +1952,11 @@ "ws": "^7.1.2" }, "dependencies": { + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -1860,25 +1966,25 @@ } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, "engine.io-client": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.3.tgz", - "integrity": "sha512-0NGY+9hioejTEJCaSJZfWZLk4FPI9dN+1H1C4+wj2iuFba47UgZbJzfWs4aNFajnX/qAaYKbe2lLTfEEWzCmcw==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.4.tgz", + "integrity": "sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ==", "requires": { "component-emitter": "~1.3.0", "component-inherit": "0.0.3", - "debug": "~4.1.0", + "debug": "~3.1.0", "engine.io-parser": "~2.2.0", "has-cors": "1.1.0", "indexof": "0.0.1", - "parseqs": "0.0.5", - "parseuri": "0.0.5", + "parseqs": "0.0.6", + "parseuri": "0.0.6", "ws": "~6.1.0", "xmlhttprequest-ssl": "~1.5.4", "yeast": "0.1.2" @@ -1890,17 +1996,22 @@ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "requires": { - "ms": "^2.1.1" + "ms": "2.0.0" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" + }, + "parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" }, "ws": { "version": "6.1.4", @@ -1913,13 +2024,13 @@ } }, "engine.io-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz", - "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz", + "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==", "requires": { "after": "0.8.2", "arraybuffer.slice": "~0.0.7", - "base64-arraybuffer": "0.1.5", + "base64-arraybuffer": "0.1.4", "blob": "0.0.5", "has-binary2": "~1.0.2" } @@ -2010,9 +2121,9 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.15.0.tgz", - "integrity": "sha512-Vr64xFDT8w30wFll643e7cGrIkPEU50yIiI36OdSIDoSGguIeaLzBo0vpGvzo9RECUqq7htURfwEtKqwytkqzA==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.17.0.tgz", + "integrity": "sha512-zJk08MiBgwuGoxes5sSQhOtibZ75pz0J35XTRlZOk9xMffhpA9BTbQZxoXZzOl5zMbleShbGwtw+1kGferfFwQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -2049,7 +2160,7 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^5.2.3", + "table": "^6.0.4", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -2097,15 +2208,6 @@ "ms": "2.1.2" } }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2145,9 +2247,9 @@ } }, "eslint-config-etherpad": { - "version": "1.0.20", - "resolved": "https://registry.npmjs.org/eslint-config-etherpad/-/eslint-config-etherpad-1.0.20.tgz", - "integrity": "sha512-dDEmWphxOmYe7XC0Uevzb0lK7o1jDBGwYMMCdNeZlgo2EfJljnijPgodlimM4R+4OsnfegEMY6rdWoXjzdd5Rw==", + "version": "1.0.21", + "resolved": "https://registry.npmjs.org/eslint-config-etherpad/-/eslint-config-etherpad-1.0.21.tgz", + "integrity": "sha512-kJjTElXtgWqAtJNyyhl8k5g/phSg3PUpNSb1PHZBC72q7ibP3uB22hbIxZrg9PkB3dwl2w8GCBp8uLI+3OX7SA==", "dev": true }, "eslint-plugin-es": { @@ -2380,6 +2482,11 @@ "resolved": "https://registry.npmjs.org/etherpad-yajsml/-/etherpad-yajsml-0.0.2.tgz", "integrity": "sha1-HCTSaLCUduY30EnN2xxt+McptG4=" }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, "express": { "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", @@ -2417,11 +2524,6 @@ "vary": "~1.1.2" }, "dependencies": { - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -2449,11 +2551,6 @@ "uid-safe": "~2.1.5" }, "dependencies": { - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -2566,24 +2663,6 @@ "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==", "dev": true }, - "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "requires": { - "debug": "=3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - } - } - }, "foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -2625,9 +2704,9 @@ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, "fromentries": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.2.0.tgz", - "integrity": "sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", + "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", "dev": true }, "fs-constants": { @@ -2636,6 +2715,15 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "dev": true }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "optional": true, + "requires": { + "minipass": "^2.6.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2647,6 +2735,29 @@ "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", "optional": true }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "optional": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "optional": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -2658,10 +2769,63 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, "get-caller-file": { @@ -2675,15 +2839,21 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" }, "get-intrinsic": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", - "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz", + "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1" } }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -2714,10 +2884,13 @@ } }, "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } }, "graceful-fs": { "version": "4.2.4", @@ -2796,10 +2969,16 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "optional": true + }, "hasha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.0.tgz", - "integrity": "sha512-2W+jKdQbAdSIrggA8Q35Br8qKadTrqCTC8+XZvBWepKDK6m9XkX6Iz1a2yh2KP01kzAR/dpuMeUnocoLYDcskw==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", + "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", "dev": true, "requires": { "is-stream": "^2.0.0", @@ -2812,13 +2991,6 @@ "integrity": "sha512-JQMW+TJe0UAIXZMjCJ4Wf6ayDV9Yv3PBDPsHD4ExBpAspJ6MOcCX+nzVF+UJVv7OqPcg852WEMSHQPoRA+FVSw==", "requires": { "hast-util-is-element": "^1.1.0" - }, - "dependencies": { - "hast-util-is-element": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz", - "integrity": "sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ==" - } } }, "hast-util-from-parse5": { @@ -2834,14 +3006,14 @@ } }, "hast-util-is-element": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.0.4.tgz", - "integrity": "sha512-NFR6ljJRvDcyPP5SbV7MyPBgF47X3BsskLnmw1U34yL+X6YC0MoBx9EyMg8Jtx4FzGH95jw8+c1VPLHaRA0wDQ==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz", + "integrity": "sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ==" }, "hast-util-parse-selector": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.4.tgz", - "integrity": "sha512-gW3sxfynIvZApL4L07wryYF4+C9VvH3AUi7LAnVXV4MneGEgwOByXvFo18BgmTWnm7oHAe874jKbIB1YhHSIzA==" + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==" }, "hast-util-to-html": { "version": "6.1.0", @@ -2951,9 +3123,9 @@ } }, "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true }, "ignore": { @@ -2962,22 +3134,23 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "ignore-walk": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, "import-fresh": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", - "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - } } }, "imurmurhash": { @@ -3011,6 +3184,17 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "optional": true + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -3049,9 +3233,9 @@ "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" }, "is-core-module": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", - "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", "dev": true, "requires": { "has": "^1.0.3" @@ -3234,9 +3418,9 @@ "dev": true }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -3256,12 +3440,12 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -3632,6 +3816,14 @@ "dev": true, "requires": { "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, "make-dir": { @@ -3665,6 +3857,12 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -3706,6 +3904,25 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "optional": true, + "requires": { + "minipass": "^2.9.0" + } + }, "mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -3718,7 +3935,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz", "integrity": "sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==", - "dev": true, "requires": { "ansi-colors": "3.2.3", "browser-stdout": "1.3.1", @@ -3750,7 +3966,6 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, "requires": { "ms": "^2.1.1" } @@ -3758,14 +3973,12 @@ "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, "supports-color": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -3787,19 +4000,84 @@ "openapi-types": "^1.3.2" } }, + "mongodb": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.3.tgz", + "integrity": "sha512-rOZuR0QkodZiM+UbQE5kDsJykBqWi0CL4Ec2i1nrGrUI3KO11r6Fbxskqmq3JK2NH7aW4dcccBuUujAP0ERl5w==", + "requires": { + "bl": "^2.2.1", + "bson": "^1.1.4", + "denque": "^1.4.1", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" + }, + "dependencies": { + "bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "mssql": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/mssql/-/mssql-6.3.0.tgz", - "integrity": "sha512-6/BK/3J8Oe4t6BYnmdCCORHhyBtBI/Fh0Sh6l1hPzb/hKtxDrsaSDGIpck1u8bzkLzev39TH5W2nz+ffeRz7gg==", + "version": "7.0.0-alpha.4", + "resolved": "https://registry.npmjs.org/mssql/-/mssql-7.0.0-alpha.4.tgz", + "integrity": "sha512-vt/OGFG2tw86TiEIAldy0LEkmoPqR9ZsBjxlWeriTs6CuPrE+o2LfXRZ7rI6Awx8QvckZbCShZgRGZoELUQ9ZQ==", "requires": { - "debug": "^4.3.1", - "tarn": "^1.1.5", - "tedious": "^6.7.0" + "debug": "^4", + "tarn": "^3.0.1", + "tedious": "^9.2.3" }, "dependencies": { "debug": { @@ -3900,6 +4178,34 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "needle": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz", + "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==", + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "optional": true + } + } + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -3929,6 +4235,12 @@ } } }, + "node-addon-api": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.0.tgz", + "integrity": "sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA==", + "optional": true + }, "node-environment-flags": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", @@ -3945,6 +4257,102 @@ } } }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "optional": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "optional": true + } + } + }, + "node-pre-gyp": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", + "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + }, + "dependencies": { + "nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + } + } + }, "node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -3963,6 +4371,15 @@ "promise": "~1.3.0" } }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "optional": true, + "requires": { + "abbrev": "1" + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -7051,6 +7468,44 @@ } } }, + "npm-bundled": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "optional": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "optional": true + }, + "npm-packlist": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", @@ -7059,6 +7514,12 @@ "boolbase": "~1.0.0" } }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "optional": true + }, "nyc": { "version": "15.0.1", "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.0.1.tgz", @@ -7093,19 +7554,12 @@ "yargs": "^15.0.2" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, @@ -7135,12 +7589,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -7165,12 +7613,6 @@ "path-is-absolute": "^1.0.0" } }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -7195,25 +7637,11 @@ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true }, "wrap-ansi": { "version": "6.2.0", @@ -7227,9 +7655,9 @@ } }, "yargs": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "requires": { "cliui": "^6.0.0", @@ -7242,7 +7670,7 @@ "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^18.1.1" + "yargs-parser": "^18.1.2" } }, "yargs-parser": { @@ -7262,6 +7690,12 @@ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "optional": true + }, "object-component": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", @@ -7338,13 +7772,6 @@ "openapi-types": "^1.3.4", "qs": "^6.6.0", "swagger-parser": "^9.0.1" - }, - "dependencies": { - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - } } }, "openapi-schema-validation": { @@ -7363,9 +7790,9 @@ "integrity": "sha512-11oi4zYorsgvg5yBarZplAqbpev5HkuVNPlZaPTknPDzAynq+lnJdXAmruGWP0s+dNYZS7bjM+xrTpJw7184Fg==" }, "optional-js": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/optional-js/-/optional-js-2.1.1.tgz", - "integrity": "sha512-mUS4bDngcD5kKzzRUd1HVQkr9Lzzby3fSrrPR9wOHhQiyYo+hDS5NVli5YQzGjQRQ15k5Sno4xH9pfykJdeEUA==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/optional-js/-/optional-js-2.3.0.tgz", + "integrity": "sha512-B0LLi+Vg+eko++0z/b8zIv57kp7HKEzaPJo7LowJXMUKYdf+3XJGu/cw03h/JhIOsLnP+cG5QnTHAuicjA5fMw==" }, "optionator": { "version": "0.9.1", @@ -7381,6 +7808,28 @@ "word-wrap": "^1.2.3" } }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "optional": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -7662,9 +8111,9 @@ } }, "property-information": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.5.0.tgz", - "integrity": "sha512-RgEbCx2HLa1chNgvChcx+rrCWD0ctBmGSE0M7lVm1yyv4UbvbrWoXp/BkVLZefzjrRBGW8/Js6uh/BnlHXFyjA==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", "requires": { "xtend": "^4.0.0" } @@ -7759,6 +8208,18 @@ } } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -7868,11 +8329,6 @@ "es6-error": "^4.0.1" } }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" - }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -7912,20 +8368,42 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" + }, + "dependencies": { + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + } + } + }, "resolve": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" }, "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, "ret": { @@ -7959,6 +8437,15 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -8058,13 +8545,12 @@ "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, "simple-git": { - "version": "2.27.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-2.27.0.tgz", - "integrity": "sha512-/Q4aolzErYrIx6SgyH421jmtv5l1DaAw+KYWMWy229+isW6yld/nHGxJ2xUR/aeX3SuYJnbucyUigERwaw4Xow==", + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-2.31.0.tgz", + "integrity": "sha512-/+rmE7dYZMbRAfEmn8EUIOwlM2G7UdzpkC60KF86YAfXGnmGtsPrKsym0hKvLBdFLLW019C+aZld1+6iIVy5xA==", "requires": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", @@ -8125,20 +8611,38 @@ } }, "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" }, "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true } } @@ -8170,9 +8674,9 @@ } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, @@ -8202,6 +8706,11 @@ "to-array": "0.1.4" }, "dependencies": { + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -8216,20 +8725,25 @@ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "socket.io-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", - "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz", + "integrity": "sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==", "requires": { - "component-emitter": "1.2.1", + "component-emitter": "~1.3.0", "debug": "~3.1.0", "isarray": "2.0.1" }, "dependencies": { + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -8271,9 +8785,9 @@ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" } } }, @@ -8296,6 +8810,15 @@ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==" }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "optional": true, + "requires": { + "memory-pager": "^1.0.2" + } + }, "spawn-wrap": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", @@ -8334,6 +8857,17 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, + "sqlite3": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.0.tgz", + "integrity": "sha512-rjvqHFUaSGnzxDy2AHCwhHy6Zp6MNJzCPGYju4kD8yi6bze4d1/zMTg6C7JI49b7/EM7jKMTvyfN/4ylBKdwfw==", + "optional": true, + "requires": { + "node-addon-api": "2.0.0", + "node-gyp": "3.x", + "node-pre-gyp": "^0.11.0" + } + }, "sqlstring": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", @@ -8446,9 +8980,9 @@ }, "dependencies": { "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { "ms": "^2.1.1" @@ -8461,9 +8995,9 @@ "dev": true }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "readable-stream": { @@ -8506,77 +9040,6 @@ "requires": { "methods": "^1.1.2", "superagent": "^3.8.3" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "superagent": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", - "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", - "dev": true, - "requires": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.2.0", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.3.5" - } - } } }, "supports-color": { @@ -8606,64 +9069,55 @@ "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" }, "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", + "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", "dev": true, "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" + "ajv": "^7.0.2", + "lodash": "^4.17.20", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { + "ajv": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.0.3.tgz", + "integrity": "sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" } }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true } } }, + "tar": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", + "optional": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.12", + "inherits": "2" + } + }, "tar-stream": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz", - "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "dev": true, "requires": { - "bl": "^4.0.1", + "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", @@ -8671,33 +9125,28 @@ } }, "tarn": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/tarn/-/tarn-1.1.5.tgz", - "integrity": "sha512-PMtJ3HCLAZeedWjJPgGnCvcphbCOMbtZpjKgLq3qM5Qq9aQud+XHrL0WlrlgnTyS8U+jrjGbEXprFcQrxPy52g==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.1.tgz", + "integrity": "sha512-6usSlV9KyHsspvwu2duKH+FMUhqJnAh6J5J/4MITl8s94iSUQTLkJggdiewKv4RyARQccnigV48Z+khiuVZDJw==" }, "tedious": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/tedious/-/tedious-6.7.0.tgz", - "integrity": "sha512-8qr7+sB0h4SZVQBRWUgHmYuOEflAOl2eihvxk0fVNvpvGJV4V5UC/YmSvebyfgyfwWcPO22/AnSbYVZZqf9wuQ==", + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/tedious/-/tedious-9.2.3.tgz", + "integrity": "sha512-+mI2r/5mqxpTHKBZ/SW+NNH2MK5i3Pwwkw0gF5ZrS2wf2uT/03bLSss8nm7xh604abJXyjx0sirhwH63H328qA==", "requires": { - "@azure/ms-rest-nodeauth": "2.0.2", - "@types/node": "^12.12.17", - "@types/readable-stream": "^2.3.5", + "@azure/ms-rest-nodeauth": "^3.0.6", + "@js-joda/core": "^3.1.0", + "adal-node": "^0.1.28", "bl": "^3.0.0", "depd": "^2.0.0", - "iconv-lite": "^0.5.0", - "jsbi": "^3.1.1", + "iconv-lite": "^0.6.2", + "jsbi": "^3.1.3", "native-duplexpair": "^1.0.0", "punycode": "^2.1.0", - "readable-stream": "^3.4.0", + "readable-stream": "^3.6.0", "sprintf-js": "^1.1.2" }, "dependencies": { - "@types/node": { - "version": "12.19.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.9.tgz", - "integrity": "sha512-yj0DOaQeUrk3nJ0bd3Y5PeDRJ6W0r+kilosLA+dzF3dola/o9hxhMSg2sFvVcA2UHS5JSOsZp4S0c1OEXc4m1Q==" - }, "bl": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.1.tgz", @@ -8712,11 +9161,11 @@ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, "iconv-lite": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", - "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sprintf-js": { @@ -8782,11 +9231,11 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "ms": { @@ -8909,9 +9358,9 @@ } }, "ueberdb2": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/ueberdb2/-/ueberdb2-0.5.6.tgz", - "integrity": "sha512-stLhNkWlxUMAO33JjEh8JCRuZvHYeDQjbo6K1C3I7R37AlMKNu9GWXSZm1wQDnAqpXAXeMVh3owBsAdj0YvOrg==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/ueberdb2/-/ueberdb2-1.1.7.tgz", + "integrity": "sha512-Fxvpei4MVYOlCWZ67I5iibYyaHIb40ZLwN0OsjPEuMpcWYBXuwkl+svFyA2nYgk9/1Nk9eL/zdsXbwm7BLMKsg==", "requires": { "async": "^3.2.0", "cassandra-driver": "^4.5.1", @@ -8921,7 +9370,8 @@ "dirty": "^1.1.0", "elasticsearch": "^16.7.1", "mocha": "^7.1.2", - "mssql": "^6.2.3", + "mongodb": "^3.6.3", + "mssql": "7.0.0-alpha.4", "mysql": "2.18.1", "nano": "^8.2.2", "pg": "^8.0.3", @@ -8929,61 +9379,9 @@ "redis": "^3.0.2", "rethinkdb": "^2.4.2", "rimraf": "^3.0.2", - "simple-git": "^2.4.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "mocha": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz", - "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==", - "requires": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.5", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "requires": { - "has-flag": "^3.0.0" - } - } + "simple-git": "^2.4.0", + "sqlite3": "^5.0.0", + "wtfnode": "^0.8.4" } }, "uid-safe": { @@ -9000,9 +9398,9 @@ "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" }, "unified": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/unified/-/unified-9.0.0.tgz", - "integrity": "sha512-ssFo33gljU3PdlWLjNp15Inqb77d6JnJSfyplGJPT/a+fNRNyCBeveBAYJdO5khKdF6WVHa/yYCC7Xl6BDwZUQ==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", + "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", "requires": { "bail": "^1.0.0", "extend": "^3.0.0", @@ -9091,13 +9489,12 @@ } }, "vfile": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.1.1.tgz", - "integrity": "sha512-lRjkpyDGjVlBA7cDQhQ+gNcvB1BGaTHYuSOcY3S7OhDmBtnzX95FhtZZDecSTDm6aajFymyve6S5DN4ZHGezdQ==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", "requires": { "@types/unist": "^2.0.0", "is-buffer": "^2.0.0", - "replace-ext": "1.0.0", "unist-util-stringify-position": "^2.0.0", "vfile-message": "^2.0.0" } @@ -9311,9 +9708,14 @@ } }, "ws": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz", - "integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w==" + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", + "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==" + }, + "wtfnode": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/wtfnode/-/wtfnode-0.8.4.tgz", + "integrity": "sha512-64GEKtMt/MUBuAm+8kHqP74ojjafzu00aT0JKsmkIwYmjRQ/odO0yhbzKLm+Z9v1gMla+8dwITRKzTAlHsB+Og==" }, "xml2js": { "version": "0.4.23", @@ -9350,15 +9752,15 @@ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" }, "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "optional": true }, "yargs": { "version": "13.3.2", diff --git a/src/package.json b/src/package.json index c98c23389..fcf73751b 100644 --- a/src/package.json +++ b/src/package.json @@ -70,7 +70,7 @@ "threads": "^1.4.0", "tiny-worker": "^2.3.0", "tinycon": "0.0.1", - "ueberdb2": "^0.5.6", + "ueberdb2": "^1.1.7", "underscore": "1.8.3", "unorm": "1.4.1" }, From fd3004faadca9586566b7904580037bb33759747 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 8 Jan 2021 02:52:45 +0000 Subject: [PATCH 011/357] checkPlugins: various minor updates (#4635) Co-authored-by: Richard Hansen --- bin/plugins/checkPlugin.js | 108 ++++++++++++++++++++++++------------- 1 file changed, 70 insertions(+), 38 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index fd31c148e..3649e7f01 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -1,3 +1,5 @@ +'use strict'; + /* * * Usage -- see README.md @@ -10,7 +12,7 @@ node bin/plugins/checkPlugins.js ep_whatever autofix autocommit */ const fs = require('fs'); -const {exec} = require('child_process'); +const childProcess = require('child_process'); // get plugin name & path from user input const pluginName = process.argv[2]; @@ -24,15 +26,16 @@ const pluginPath = `node_modules/${pluginName}`; console.log(`Checking the plugin: ${pluginName}`); +const optArgs = process.argv.slice(3, 0); + // Should we autofix? -if (process.argv[3] && process.argv[3] === 'autofix') var autoFix = true; +const autoFix = optArgs.indexOf('autofix') !== -1; // Should we update files where possible? -if (process.argv[5] && process.argv[5] === 'autoupdate') var autoUpdate = true; +const autoUpdate = optArgs.indexOf('autoupdate') !== -1; // Should we automcommit and npm publish?! -if (process.argv[4] && process.argv[4] === 'autocommit') var autoCommit = true; - +const autoCommit = optArgs.indexOf('autocommit') !== -1; if (autoCommit) { console.warn('Auto commit is enabled, I hope you know what you are doing...'); @@ -62,18 +65,33 @@ fs.readdir(pluginPath, (err, rootFiles) => { process.exit(1); } - // do a git pull... - var child_process = require('child_process'); try { - child_process.execSync('git pull ', {cwd: `${pluginPath}/`}); + childProcess.execSync('git pull ', {cwd: `${pluginPath}/`}); } catch (e) { console.error('Error git pull', e); } + try { + const packages = [ + 'eslint', + 'eslint-config-etherpad', + 'eslint-plugin-eslint-comments', + 'eslint-plugin-mocha', + 'eslint-plugin-node', + 'eslint-plugin-prefer-arrow', + 'eslint-plugin-promise', + 'eslint-plugin-you-dont-need-lodash-underscore', + ]; + childProcess.execSync(`npm install --save-dev ${packages.join(' ')}`, {cwd: `${pluginPath}/`}); + } catch (e) { + console.error('Error npm updating pull', e); + } + try { const path = `${pluginPath}/.github/workflows/npmpublish.yml`; if (!fs.existsSync(path)) { - console.log('no .github/workflows/npmpublish.yml, create one and set npm secret to auto publish to npm on commit'); + console.log('no .github/workflows/npmpublish.yml'); + console.log('create one and set npm secret to auto publish to npm on commit'); if (autoFix) { const npmpublish = fs.readFileSync('bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'}); @@ -89,11 +107,14 @@ fs.readdir(pluginPath, (err, rootFiles) => { // checkVersion takes two file paths and checks for a version string in them. const currVersionFile = fs.readFileSync(path, {encoding: 'utf8', flag: 'r'}); const existingConfigLocation = currVersionFile.indexOf('##ETHERPAD_NPM_V='); - const existingValue = parseInt(currVersionFile.substr(existingConfigLocation + 17, existingConfigLocation.length)); + const existingValue = parseInt( + currVersionFile.substr(existingConfigLocation + 17, existingConfigLocation.length)); - const reqVersionFile = fs.readFileSync('bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'}); + const reqVersionFile = + fs.readFileSync('bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'}); const reqConfigLocation = reqVersionFile.indexOf('##ETHERPAD_NPM_V='); - const reqValue = parseInt(reqVersionFile.substr(reqConfigLocation + 17, reqConfigLocation.length)); + const reqValue = + parseInt(reqVersionFile.substr(reqConfigLocation + 17, reqConfigLocation.length)); if (!existingValue || (reqValue > existingValue)) { const npmpublish = @@ -111,7 +132,8 @@ fs.readdir(pluginPath, (err, rootFiles) => { try { const path = `${pluginPath}/.github/workflows/backend-tests.yml`; if (!fs.existsSync(path)) { - console.log('no .github/workflows/backend-tests.yml, create one and set npm secret to auto publish to npm on commit'); + console.log('no .github/workflows/backend-tests.yml'); + console.log('create one and set npm secret to auto publish to npm on commit'); if (autoFix) { const backendTests = fs.readFileSync('bin/plugins/lib/backend-tests.yml', {encoding: 'utf8', flag: 'r'}); @@ -124,11 +146,14 @@ fs.readdir(pluginPath, (err, rootFiles) => { // checkVersion takes two file paths and checks for a version string in them. const currVersionFile = fs.readFileSync(path, {encoding: 'utf8', flag: 'r'}); const existingConfigLocation = currVersionFile.indexOf('##ETHERPAD_NPM_V='); - const existingValue = parseInt(currVersionFile.substr(existingConfigLocation + 17, existingConfigLocation.length)); + const existingValue = parseInt( + currVersionFile.substr(existingConfigLocation + 17, existingConfigLocation.length)); - const reqVersionFile = fs.readFileSync('bin/plugins/lib/backend-tests.yml', {encoding: 'utf8', flag: 'r'}); + const reqVersionFile = + fs.readFileSync('bin/plugins/lib/backend-tests.yml', {encoding: 'utf8', flag: 'r'}); const reqConfigLocation = reqVersionFile.indexOf('##ETHERPAD_NPM_V='); - const reqValue = parseInt(reqVersionFile.substr(reqConfigLocation + 17, reqConfigLocation.length)); + const reqValue = + parseInt(reqVersionFile.substr(reqConfigLocation + 17, reqConfigLocation.length)); if (!existingValue || (reqValue > existingValue)) { const backendTests = @@ -147,7 +172,8 @@ fs.readdir(pluginPath, (err, rootFiles) => { } if (files.indexOf('package.json') !== -1) { - const packageJSON = fs.readFileSync(`${pluginPath}/package.json`, {encoding: 'utf8', flag: 'r'}); + const packageJSON = + fs.readFileSync(`${pluginPath}/package.json`, {encoding: 'utf8', flag: 'r'}); const parsedPackageJSON = JSON.parse(packageJSON); if (autoFix) { let updatedPackageJSON = false; @@ -167,7 +193,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { if (packageJSON.toLowerCase().indexOf('repository') === -1) { console.warn('No repository in package.json'); if (autoFix) { - console.warn('Repository not detected in package.json. Please add repository section manually.'); + console.warn('Repository not detected in package.json. Add repository section.'); } } else { // useful for creating README later. @@ -175,8 +201,10 @@ fs.readdir(pluginPath, (err, rootFiles) => { } // include lint config - if (packageJSON.toLowerCase().indexOf('devdependencies') === -1 || !parsedPackageJSON.devDependencies.eslint) { - console.warn('Missing eslint reference in devDependencies'); + if (packageJSON.toLowerCase().indexOf('devdependencies') === -1 || + !parsedPackageJSON.devDependencies.eslint || + !parsedPackageJSON.devDependencies['eslint-plugin-you-dont-need-lodash-underscore']) { + console.warn('Missing an eslint reference in devDependencies'); if (autoFix) { const devDependencies = { 'eslint': '^7.14.0', @@ -185,14 +213,14 @@ fs.readdir(pluginPath, (err, rootFiles) => { 'eslint-plugin-node': '^11.1.0', 'eslint-plugin-prefer-arrow': '^1.2.2', 'eslint-plugin-promise': '^4.2.1', + 'eslint-plugin-you-dont-need-lodash-underscore': '^6.10.0', }; hasAutoFixed = true; parsedPackageJSON.devDependencies = devDependencies; fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2)); - const child_process = require('child_process'); try { - child_process.execSync('npm install', {cwd: `${pluginPath}/`}); + childProcess.execSync('npm install', {cwd: `${pluginPath}/`}); hasAutoFixed = true; } catch (e) { console.error('Failed to create package-lock.json'); @@ -210,9 +238,9 @@ fs.readdir(pluginPath, (err, rootFiles) => { hasAutoFixed = true; parsedPackageJSON.peerDependencies = peerDependencies; fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2)); - const child_process = require('child_process'); try { - child_process.execSync('npm install --no-save ep_etherpad-lite@file:../../src', {cwd: `${pluginPath}/`}); + childProcess.execSync( + 'npm install --no-save ep_etherpad-lite@file:../../src', {cwd: `${pluginPath}/`}); hasAutoFixed = true; } catch (e) { console.error('Failed to create package-lock.json'); @@ -260,11 +288,11 @@ fs.readdir(pluginPath, (err, rootFiles) => { } if (files.indexOf('package-lock.json') === -1) { - console.warn('package-lock.json file not found. Please run npm install in the plugin folder and commit the package-lock.json file.'); + console.warn('package-lock.json file not found.'); + console.warn('Run npm install in the plugin folder and commit the package-lock.json file.'); if (autoFix) { - var child_process = require('child_process'); try { - child_process.execSync('npm install', {cwd: `${pluginPath}/`}); + childProcess.execSync('npm install', {cwd: `${pluginPath}/`}); console.log('Making package-lock.json'); hasAutoFixed = true; } catch (e) { @@ -276,7 +304,8 @@ fs.readdir(pluginPath, (err, rootFiles) => { if (files.indexOf('readme') === -1 && files.indexOf('readme.md') === -1) { console.warn('README.md file not found, please create'); if (autoFix) { - console.log('Autofixing missing README.md file, please edit the README.md file further to include plugin specific details.'); + console.log('Autofixing missing README.md file'); + console.log('please edit the README.md file further to include plugin specific details.'); let readme = fs.readFileSync('bin/plugins/lib/README.md', {encoding: 'utf8', flag: 'r'}); readme = readme.replace(/\[plugin_name\]/g, pluginName); if (repository) { @@ -317,7 +346,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { if (autoFix) { hasAutoFixed = true; console.log('Autofixing missing LICENSE.md file, including Apache 2 license.'); - exec('git config user.name', (error, name, stderr) => { + childProcess.exec('git config user.name', (error, name, stderr) => { if (error) { console.log(`error: ${error.message}`); return; @@ -422,14 +451,9 @@ fs.readdir(pluginPath, (err, rootFiles) => { } // linting begins - if (autoFix) { - var lintCmd = 'npm run lint:fix'; - } else { - var lintCmd = 'npm run lint'; - } - try { - child_process.execSync(lintCmd, {cwd: `${pluginPath}/`}); + const lintCmd = autoFix ? 'npx eslint --fix .' : 'npx eslint'; + childProcess.execSync(lintCmd, {cwd: `${pluginPath}/`}); console.log('Linting...'); if (autoFix) { // todo: if npm run lint doesn't do anything no need for... @@ -444,11 +468,19 @@ fs.readdir(pluginPath, (err, rootFiles) => { if (hasAutoFixed) { console.log('Fixes applied, please check git diff then run the following command:\n\n'); // bump npm Version + const cmd = [ + `cd node_modules/${pluginName}`, + 'git rm -rf node_modules --ignore-unmatch', + 'git add -A', + 'git commit --allow-empty -m "autofixes from Etherpad checkPlugins.js"', + 'git push', + 'cd ../..', + ].join(' && '); if (autoCommit) { // holy shit you brave. console.log('Attempting autocommit and auto publish to npm'); // github should push to npm for us :) - exec(`cd node_modules/${pluginName} && git rm -rf node_modules --ignore-unmatch && git add -A && git commit --allow-empty -m 'autofixes from Etherpad checkPlugins.js' && git push && cd ../..`, (error, name, stderr) => { + childProcess.exec(cmd, (error, name, stderr) => { if (error) { console.log(`error: ${error.message}`); return; @@ -461,7 +493,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { process.exit(0); }); } else { - console.log(`cd node_modules/${pluginName} && git add -A && git commit --allow-empty -m 'autofixes from Etherpad checkPlugins.js' && npm version patch && git add package.json && git commit --allow-empty -m 'bump version' && git push && npm publish && cd ../..`); + console.log(cmd); } } From ba1c468865194f47c5c65e08c3daeef8f4ae49b6 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 8 Jan 2021 18:07:55 -0500 Subject: [PATCH 012/357] checkPlugin: Fix command-line argument parsing bug --- bin/plugins/checkPlugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 3649e7f01..43fe64ee0 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -26,7 +26,7 @@ const pluginPath = `node_modules/${pluginName}`; console.log(`Checking the plugin: ${pluginName}`); -const optArgs = process.argv.slice(3, 0); +const optArgs = process.argv.slice(3); // Should we autofix? const autoFix = optArgs.indexOf('autofix') !== -1; From 6890bfada1ac2fdd0e3afffd45b09232c945d36d Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 8 Jan 2021 18:24:42 -0500 Subject: [PATCH 013/357] checkPlugin: Bump eslint, eslint-config-etherpad versions --- bin/plugins/checkPlugin.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 43fe64ee0..3220875ec 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -207,8 +207,8 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.warn('Missing an eslint reference in devDependencies'); if (autoFix) { const devDependencies = { - 'eslint': '^7.14.0', - 'eslint-config-etherpad': '^1.0.13', + 'eslint': '^7.17.0', + 'eslint-config-etherpad': '^1.0.22', 'eslint-plugin-mocha': '^8.0.0', 'eslint-plugin-node': '^11.1.0', 'eslint-plugin-prefer-arrow': '^1.2.2', From a0d21f75c49c8acce2280f3305344baba6db26ec Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 8 Jan 2021 18:42:10 -0500 Subject: [PATCH 014/357] checkPlugin: Add missing lint dependency --- bin/plugins/checkPlugin.js | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 3220875ec..9e1d86f1e 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -209,6 +209,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { const devDependencies = { 'eslint': '^7.17.0', 'eslint-config-etherpad': '^1.0.22', + 'eslint-plugin-eslint-comments': '^3.2.0', 'eslint-plugin-mocha': '^8.0.0', 'eslint-plugin-node': '^11.1.0', 'eslint-plugin-prefer-arrow': '^1.2.2', From 44118c0e7b2dec056da97804c7d762e1973914fc Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 8 Jan 2021 18:45:27 -0500 Subject: [PATCH 015/357] checkPlugin: Refine installation of lint dependencies --- bin/plugins/checkPlugin.js | 68 +++++++++++++++----------------------- 1 file changed, 27 insertions(+), 41 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 9e1d86f1e..ea0e7e2be 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -71,22 +71,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.error('Error git pull', e); } - try { - const packages = [ - 'eslint', - 'eslint-config-etherpad', - 'eslint-plugin-eslint-comments', - 'eslint-plugin-mocha', - 'eslint-plugin-node', - 'eslint-plugin-prefer-arrow', - 'eslint-plugin-promise', - 'eslint-plugin-you-dont-need-lodash-underscore', - ]; - childProcess.execSync(`npm install --save-dev ${packages.join(' ')}`, {cwd: `${pluginPath}/`}); - } catch (e) { - console.error('Error npm updating pull', e); - } - try { const path = `${pluginPath}/.github/workflows/npmpublish.yml`; if (!fs.existsSync(path)) { @@ -201,31 +185,33 @@ fs.readdir(pluginPath, (err, rootFiles) => { } // include lint config - if (packageJSON.toLowerCase().indexOf('devdependencies') === -1 || - !parsedPackageJSON.devDependencies.eslint || - !parsedPackageJSON.devDependencies['eslint-plugin-you-dont-need-lodash-underscore']) { - console.warn('Missing an eslint reference in devDependencies'); - if (autoFix) { - const devDependencies = { - 'eslint': '^7.17.0', - 'eslint-config-etherpad': '^1.0.22', - 'eslint-plugin-eslint-comments': '^3.2.0', - 'eslint-plugin-mocha': '^8.0.0', - 'eslint-plugin-node': '^11.1.0', - 'eslint-plugin-prefer-arrow': '^1.2.2', - 'eslint-plugin-promise': '^4.2.1', - 'eslint-plugin-you-dont-need-lodash-underscore': '^6.10.0', - }; - hasAutoFixed = true; - parsedPackageJSON.devDependencies = devDependencies; - fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2)); - - try { - childProcess.execSync('npm install', {cwd: `${pluginPath}/`}); - hasAutoFixed = true; - } catch (e) { - console.error('Failed to create package-lock.json'); - } + const lintDeps = { + 'eslint': '^7.17.0', + 'eslint-config-etherpad': '^1.0.22', + 'eslint-plugin-eslint-comments': '^3.2.0', + 'eslint-plugin-mocha': '^8.0.0', + 'eslint-plugin-node': '^11.1.0', + 'eslint-plugin-prefer-arrow': '^1.2.2', + 'eslint-plugin-promise': '^4.2.1', + 'eslint-plugin-you-dont-need-lodash-underscore': '^6.10.0', + }; + const {devDependencies = {}} = parsedPackageJSON; + let lintDepsNeedUpdating = false; + for (const [pkg, ver] of Object.entries(lintDeps)) { + if (devDependencies[pkg] !== ver) { + console.warn(`Missing/outdated ESLint dependency: '${pkg}': '${ver}' ` + + `(current: ${devDependencies[pkg]})`); + lintDepsNeedUpdating = true; + } + } + if (lintDepsNeedUpdating && autoFix) { + hasAutoFixed = true; + parsedPackageJSON.devDependencies = Object.assign(devDependencies, lintDeps); + fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2)); + try { + childProcess.execSync('npm install', {cwd: `${pluginPath}/`}); + } catch (err) { + console.error(`Failed to create package-lock.json: ${err.stack || err}`); } } From a55dd73f2bd118b1b7d7b808b683164a07988aff Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 8 Jan 2021 18:49:30 -0500 Subject: [PATCH 016/357] Typo fix: `checkPlugins.js` -> `checkPlugin.js` --- CHANGELOG.md | 2 +- bin/plugins/README.md | 4 ++-- bin/plugins/checkPlugin.js | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b63f571b9..5863b69c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,7 +40,7 @@ content in `.etherpad` exports * New `expressCloseServer` hook to close Express when required * The `padUpdate` hook context now includes `revs` and `changeset` -* `checkPlugins.js` has various improvements to help plugin developers +* `checkPlugin.js` has various improvements to help plugin developers * The HTTP request object (and therefore the express-session state) is now accessible from within most `eejsBlock_*` hooks * Users without a `password` or `hash` property in `settings.json` are no longer diff --git a/bin/plugins/README.md b/bin/plugins/README.md index 81d5a4298..fbadf98ca 100755 --- a/bin/plugins/README.md +++ b/bin/plugins/README.md @@ -15,12 +15,12 @@ node bin/plugins/checkPlugin.js ep_webrtc ## Autofixing - will autofix any issues it can ``` -node bin/plugins/checkPlugins.js ep_whatever autofix +node bin/plugins/checkPlugin.js ep_whatever autofix ``` ## Autocommitting, push, npm minor patch and npm publish (highly dangerous) ``` -node bin/plugins/checkPlugins.js ep_whatever autofix autocommit +node bin/plugins/checkPlugin.js ep_whatever autofix autocommit ``` # All the plugins diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index ea0e7e2be..5449a2f0e 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -4,10 +4,10 @@ * * Usage -- see README.md * -* Normal usage: node bin/plugins/checkPlugins.js ep_whatever -* Auto fix the things it can: node bin/plugins/checkPlugins.js ep_whatever autofix +* Normal usage: node bin/plugins/checkPlugin.js ep_whatever +* Auto fix the things it can: node bin/plugins/checkPlugin.js ep_whatever autofix * Auto commit, push and publish(to npm) * highly dangerous: -node bin/plugins/checkPlugins.js ep_whatever autofix autocommit +node bin/plugins/checkPlugin.js ep_whatever autofix autocommit */ @@ -459,7 +459,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { `cd node_modules/${pluginName}`, 'git rm -rf node_modules --ignore-unmatch', 'git add -A', - 'git commit --allow-empty -m "autofixes from Etherpad checkPlugins.js"', + 'git commit --allow-empty -m "autofixes from Etherpad checkPlugin.js"', 'git push', 'cd ../..', ].join(' && '); From d816bb2b38f21ae7143097ab0489f4f8503418f7 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 8 Jan 2021 23:04:11 -0500 Subject: [PATCH 017/357] checkPlugin: New `execSync()` convenience function --- bin/plugins/checkPlugin.js | 52 ++++++++++++-------------------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 5449a2f0e..3473cb184 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -37,6 +37,11 @@ const autoUpdate = optArgs.indexOf('autoupdate') !== -1; // Should we automcommit and npm publish?! const autoCommit = optArgs.indexOf('autocommit') !== -1; +const execSync = (cmd, opts = {}) => (childProcess.execSync(cmd, { + cwd: `${pluginPath}/`, + ...opts, +}) || '').toString().replace(/\n+$/, ''); + if (autoCommit) { console.warn('Auto commit is enabled, I hope you know what you are doing...'); } @@ -66,7 +71,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { } try { - childProcess.execSync('git pull ', {cwd: `${pluginPath}/`}); + execSync('git pull'); } catch (e) { console.error('Error git pull', e); } @@ -209,7 +214,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { parsedPackageJSON.devDependencies = Object.assign(devDependencies, lintDeps); fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2)); try { - childProcess.execSync('npm install', {cwd: `${pluginPath}/`}); + execSync('npm install'); } catch (err) { console.error(`Failed to create package-lock.json: ${err.stack || err}`); } @@ -226,8 +231,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { parsedPackageJSON.peerDependencies = peerDependencies; fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2)); try { - childProcess.execSync( - 'npm install --no-save ep_etherpad-lite@file:../../src', {cwd: `${pluginPath}/`}); + execSync('npm install --no-save ep_etherpad-lite@file:../../src'); hasAutoFixed = true; } catch (e) { console.error('Failed to create package-lock.json'); @@ -279,7 +283,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.warn('Run npm install in the plugin folder and commit the package-lock.json file.'); if (autoFix) { try { - childProcess.execSync('npm install', {cwd: `${pluginPath}/`}); + execSync('npm install'); console.log('Making package-lock.json'); hasAutoFixed = true; } catch (e) { @@ -333,20 +337,10 @@ fs.readdir(pluginPath, (err, rootFiles) => { if (autoFix) { hasAutoFixed = true; console.log('Autofixing missing LICENSE.md file, including Apache 2 license.'); - childProcess.exec('git config user.name', (error, name, stderr) => { - if (error) { - console.log(`error: ${error.message}`); - return; - } - if (stderr) { - console.log(`stderr: ${stderr}`); - return; - } - let license = fs.readFileSync('bin/plugins/lib/LICENSE.md', {encoding: 'utf8', flag: 'r'}); - license = license.replace('[yyyy]', new Date().getFullYear()); - license = license.replace('[name of copyright owner]', name); - fs.writeFileSync(`${pluginPath}/LICENSE.md`, license); - }); + let license = fs.readFileSync('bin/plugins/lib/LICENSE.md', {encoding: 'utf8', flag: 'r'}); + license = license.replace('[yyyy]', new Date().getFullYear()); + license = license.replace('[name of copyright owner]', execSync('git config user.name')); + fs.writeFileSync(`${pluginPath}/LICENSE.md`, license); } } @@ -440,7 +434,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { // linting begins try { const lintCmd = autoFix ? 'npx eslint --fix .' : 'npx eslint'; - childProcess.execSync(lintCmd, {cwd: `${pluginPath}/`}); + execSync(lintCmd); console.log('Linting...'); if (autoFix) { // todo: if npm run lint doesn't do anything no need for... @@ -456,31 +450,17 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.log('Fixes applied, please check git diff then run the following command:\n\n'); // bump npm Version const cmd = [ - `cd node_modules/${pluginName}`, 'git rm -rf node_modules --ignore-unmatch', 'git add -A', 'git commit --allow-empty -m "autofixes from Etherpad checkPlugin.js"', 'git push', - 'cd ../..', ].join(' && '); if (autoCommit) { // holy shit you brave. console.log('Attempting autocommit and auto publish to npm'); - // github should push to npm for us :) - childProcess.exec(cmd, (error, name, stderr) => { - if (error) { - console.log(`error: ${error.message}`); - return; - } - if (stderr) { - console.log(`stderr: ${stderr}`); - return; - } - console.log("I think she's got it! By George she's got it!"); - process.exit(0); - }); + execSync(cmd); } else { - console.log(cmd); + console.log(`(cd node_modules/${pluginName} && ${cmd})`); } } From 102c01f723c98f0a09f87c4e73301554193a75f3 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 9 Jan 2021 00:59:20 -0500 Subject: [PATCH 018/357] checkPlugin: Move log messages where they make more sense --- bin/plugins/checkPlugin.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 3473cb184..060d1e2ba 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -433,9 +433,9 @@ fs.readdir(pluginPath, (err, rootFiles) => { // linting begins try { + console.log('Linting...'); const lintCmd = autoFix ? 'npx eslint --fix .' : 'npx eslint'; execSync(lintCmd); - console.log('Linting...'); if (autoFix) { // todo: if npm run lint doesn't do anything no need for... hasAutoFixed = true; @@ -447,7 +447,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { // linting ends. if (hasAutoFixed) { - console.log('Fixes applied, please check git diff then run the following command:\n\n'); // bump npm Version const cmd = [ 'git rm -rf node_modules --ignore-unmatch', @@ -460,6 +459,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.log('Attempting autocommit and auto publish to npm'); execSync(cmd); } else { + console.log('Fixes applied, please check git diff then run the following command:'); console.log(`(cd node_modules/${pluginName} && ${cmd})`); } } From 1a9bfc8d4f9a6db05b61db711fda14267fbad07a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 9 Jan 2021 00:59:56 -0500 Subject: [PATCH 019/357] checkPlugin: Don't capture stdout when unnecessary --- bin/plugins/checkPlugin.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 060d1e2ba..21ebbde4f 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -71,7 +71,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { } try { - execSync('git pull'); + execSync('git pull', {stdio: 'inherit'}); } catch (e) { console.error('Error git pull', e); } @@ -214,7 +214,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { parsedPackageJSON.devDependencies = Object.assign(devDependencies, lintDeps); fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2)); try { - execSync('npm install'); + execSync('npm install', {stdio: 'inherit'}); } catch (err) { console.error(`Failed to create package-lock.json: ${err.stack || err}`); } @@ -231,7 +231,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { parsedPackageJSON.peerDependencies = peerDependencies; fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2)); try { - execSync('npm install --no-save ep_etherpad-lite@file:../../src'); + execSync('npm install --no-save ep_etherpad-lite@file:../../src', {stdio: 'inherit'}); hasAutoFixed = true; } catch (e) { console.error('Failed to create package-lock.json'); @@ -283,7 +283,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.warn('Run npm install in the plugin folder and commit the package-lock.json file.'); if (autoFix) { try { - execSync('npm install'); + execSync('npm install', {stdio: 'inherit'}); console.log('Making package-lock.json'); hasAutoFixed = true; } catch (e) { @@ -435,7 +435,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { try { console.log('Linting...'); const lintCmd = autoFix ? 'npx eslint --fix .' : 'npx eslint'; - execSync(lintCmd); + execSync(lintCmd, {stdio: 'inherit'}); if (autoFix) { // todo: if npm run lint doesn't do anything no need for... hasAutoFixed = true; @@ -457,7 +457,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { if (autoCommit) { // holy shit you brave. console.log('Attempting autocommit and auto publish to npm'); - execSync(cmd); + execSync(cmd, {stdio: 'inherit'}); } else { console.log('Fixes applied, please check git diff then run the following command:'); console.log(`(cd node_modules/${pluginName} && ${cmd})`); From 38ca0c52a6a478cf08a18e8b550566c44a49d21f Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 9 Jan 2021 00:08:43 -0500 Subject: [PATCH 020/357] checkPlugin: Add lots of Git sanity checks --- bin/plugins/checkPlugin.js | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 21ebbde4f..629233e6f 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -42,6 +42,27 @@ const execSync = (cmd, opts = {}) => (childProcess.execSync(cmd, { ...opts, }) || '').toString().replace(/\n+$/, ''); +const prepareRepo = () => { + let branch = execSync('git symbolic-ref HEAD'); + if (branch !== 'refs/heads/master' && branch !== 'refs/heads/main') { + throw new Error('master/main must be checked out'); + } + branch = branch.replace(/^refs\/heads\//, ''); + execSync('git rev-parse --verify -q HEAD^0 || ' + + `{ echo "Error: no commits on ${branch}" >&2; exit 1; }`); + execSync('git rev-parse --verify @{u}'); // Make sure there's a remote tracking branch. + const dirtyFiles = execSync('git ls-files -dmo --exclude-standard'); + if (dirtyFiles !== '') throw new Error(`working directory is unclean:\n${dirtyFiles}`); + const indexStatus = execSync('git diff-index --cached --compact-summary HEAD'); + if (indexStatus !== '') throw new Error(`uncommitted staged changes to files:\n${indexStatus}`); + execSync('git pull --ff-only', {stdio: 'inherit'}); + if (execSync('git rev-list @{u}...') !== '') throw new Error('repo contains unpushed commits'); + if (autoCommit) { + execSync('git config --get user.name'); + execSync('git config --get user.email'); + } +}; + if (autoCommit) { console.warn('Auto commit is enabled, I hope you know what you are doing...'); } @@ -69,12 +90,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.error('No .git folder, aborting'); process.exit(1); } - - try { - execSync('git pull', {stdio: 'inherit'}); - } catch (e) { - console.error('Error git pull', e); - } + prepareRepo(); try { const path = `${pluginPath}/.github/workflows/npmpublish.yml`; From 071cca1d48920f24d202149895bb85b664aa025d Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 9 Jan 2021 01:20:08 -0500 Subject: [PATCH 021/357] checkPlugin: Don't create an empty commit --- bin/plugins/checkPlugin.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 629233e6f..44a26c45c 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -467,7 +467,8 @@ fs.readdir(pluginPath, (err, rootFiles) => { const cmd = [ 'git rm -rf node_modules --ignore-unmatch', 'git add -A', - 'git commit --allow-empty -m "autofixes from Etherpad checkPlugin.js"', + '{ ! git diff-index --cached --quiet HEAD || exit 0; }', + 'git commit -m "autofixes from Etherpad checkPlugin.js"', 'git push', ].join(' && '); if (autoCommit) { From 6a13baf7d41e28a6e558e0deaecff5febd26e890 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 9 Jan 2021 17:27:51 -0500 Subject: [PATCH 022/357] checkPlugin: Use `--name-status` for `git diff-index` `--compact-sumary` is relatively new (v2.17.0), and I actually intended to use `--name-status` anyway. --- bin/plugins/checkPlugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 44a26c45c..d71bb49e4 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -53,7 +53,7 @@ const prepareRepo = () => { execSync('git rev-parse --verify @{u}'); // Make sure there's a remote tracking branch. const dirtyFiles = execSync('git ls-files -dmo --exclude-standard'); if (dirtyFiles !== '') throw new Error(`working directory is unclean:\n${dirtyFiles}`); - const indexStatus = execSync('git diff-index --cached --compact-summary HEAD'); + const indexStatus = execSync('git diff-index --cached --name-status HEAD'); if (indexStatus !== '') throw new Error(`uncommitted staged changes to files:\n${indexStatus}`); execSync('git pull --ff-only', {stdio: 'inherit'}); if (execSync('git rev-list @{u}...') !== '') throw new Error('repo contains unpushed commits'); From b3b9afa668e6f3d15db6474537ffec2ad29fb5c0 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 9 Jan 2021 17:30:41 -0500 Subject: [PATCH 023/357] checkPlugin: Split dirty working directory check into two checks Rather than check for modifications and untracked files in one command, use two commands: one for modifications and one for untracked files. This makes the error messages easier to understand, and it allows us to include `git status`-like output in the modifications error message. --- bin/plugins/checkPlugin.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index d71bb49e4..6483ef4be 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -51,8 +51,10 @@ const prepareRepo = () => { execSync('git rev-parse --verify -q HEAD^0 || ' + `{ echo "Error: no commits on ${branch}" >&2; exit 1; }`); execSync('git rev-parse --verify @{u}'); // Make sure there's a remote tracking branch. - const dirtyFiles = execSync('git ls-files -dmo --exclude-standard'); - if (dirtyFiles !== '') throw new Error(`working directory is unclean:\n${dirtyFiles}`); + const modified = execSync('git diff-files --name-status'); + if (modified !== '') throw new Error(`working directory has modifications:\n${modified}`); + const untracked = execSync('git ls-files -o --exclude-standard'); + if (untracked !== '') throw new Error(`working directory has untracked files:\n${untracked}`); const indexStatus = execSync('git diff-index --cached --name-status HEAD'); if (indexStatus !== '') throw new Error(`uncommitted staged changes to files:\n${indexStatus}`); execSync('git pull --ff-only', {stdio: 'inherit'}); From bf8d55ab8e4840991cd39330ef16aac73f2a1ced Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 9 Jan 2021 18:27:37 -0500 Subject: [PATCH 024/357] checkPlugin: New `writePackageJson()` convenience function --- bin/plugins/checkPlugin.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 6483ef4be..bc53c9946 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -42,6 +42,9 @@ const execSync = (cmd, opts = {}) => (childProcess.execSync(cmd, { ...opts, }) || '').toString().replace(/\n+$/, ''); +const writePackageJson = + (obj) => fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(obj, null, 2)); + const prepareRepo = () => { let branch = execSync('git symbolic-ref HEAD'); if (branch !== 'refs/heads/master' && branch !== 'refs/heads/main') { @@ -193,7 +196,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { } if (updatedPackageJSON) { hasAutoFixed = true; - fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2)); + writePackageJson(parsedPackageJSON); } } @@ -230,7 +233,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { if (lintDepsNeedUpdating && autoFix) { hasAutoFixed = true; parsedPackageJSON.devDependencies = Object.assign(devDependencies, lintDeps); - fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2)); + writePackageJson(parsedPackageJSON); try { execSync('npm install', {stdio: 'inherit'}); } catch (err) { @@ -247,7 +250,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { }; hasAutoFixed = true; parsedPackageJSON.peerDependencies = peerDependencies; - fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2)); + writePackageJson(parsedPackageJSON); try { execSync('npm install --no-save ep_etherpad-lite@file:../../src', {stdio: 'inherit'}); hasAutoFixed = true; @@ -266,7 +269,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { }; hasAutoFixed = true; parsedPackageJSON.eslintConfig = eslintConfig; - fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2)); + writePackageJson(parsedPackageJSON); } } @@ -279,7 +282,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { }; hasAutoFixed = true; parsedPackageJSON.scripts = scripts; - fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2)); + writePackageJson(parsedPackageJSON); } } @@ -291,7 +294,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { }; hasAutoFixed = true; parsedPackageJSON.engines = engines; - fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2)); + writePackageJson(parsedPackageJSON); } } } From bba47ea2df80721f2f6faf3f9ed16d6a16155de2 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 9 Jan 2021 18:28:11 -0500 Subject: [PATCH 025/357] checkPlugin: Make sure `package.json` ends with a newline --- bin/plugins/checkPlugin.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index bc53c9946..ec922ebc2 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -42,8 +42,11 @@ const execSync = (cmd, opts = {}) => (childProcess.execSync(cmd, { ...opts, }) || '').toString().replace(/\n+$/, ''); -const writePackageJson = - (obj) => fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(obj, null, 2)); +const writePackageJson = (obj) => { + let s = JSON.stringify(obj, null, 2); + if (s.length && s.slice(s.length - 1) !== '\n') s += '\n'; + return fs.writeFileSync(`${pluginPath}/package.json`, s); +}; const prepareRepo = () => { let branch = execSync('git symbolic-ref HEAD'); From b719affe6e21e31f96b1546db74c9bffb35e22ef Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 9 Jan 2021 18:50:06 -0500 Subject: [PATCH 026/357] checkPlugin: Unconditionally run `npm install` --- bin/plugins/checkPlugin.js | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index ec922ebc2..92d6271e8 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -237,11 +237,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { hasAutoFixed = true; parsedPackageJSON.devDependencies = Object.assign(devDependencies, lintDeps); writePackageJson(parsedPackageJSON); - try { - execSync('npm install', {stdio: 'inherit'}); - } catch (err) { - console.error(`Failed to create package-lock.json: ${err.stack || err}`); - } } // include peer deps config @@ -254,12 +249,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { hasAutoFixed = true; parsedPackageJSON.peerDependencies = peerDependencies; writePackageJson(parsedPackageJSON); - try { - execSync('npm install --no-save ep_etherpad-lite@file:../../src', {stdio: 'inherit'}); - hasAutoFixed = true; - } catch (e) { - console.error('Failed to create package-lock.json'); - } } } @@ -303,16 +292,9 @@ fs.readdir(pluginPath, (err, rootFiles) => { } if (files.indexOf('package-lock.json') === -1) { - console.warn('package-lock.json file not found.'); - console.warn('Run npm install in the plugin folder and commit the package-lock.json file.'); - if (autoFix) { - try { - execSync('npm install', {stdio: 'inherit'}); - console.log('Making package-lock.json'); - hasAutoFixed = true; - } catch (e) { - console.error('Failed to create package-lock.json'); - } + console.warn('package-lock.json not found'); + if (!autoFix) { + console.warn('Run npm install in the plugin folder and commit the package-lock.json file.'); } } @@ -455,6 +437,13 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.warn('Test files not found, please create tests. https://github.com/ether/etherpad-lite/wiki/Creating-a-plugin#writing-and-running-front-end-tests-for-your-plugin'); } + // Install dependencies so we can run ESLint. This should also create or update package-lock.json + // if autoFix is enabled. + const npmInstall = `npm install${autoFix ? '' : ' --no-package-lock'}`; + execSync(npmInstall, {stdio: 'inherit'}); + // The ep_etherpad-lite peer dep must be installed last otherwise `npm install` will nuke it. + execSync(`${npmInstall} --no-save ep_etherpad-lite@file:../../src`, {stdio: 'inherit'}); + // linting begins try { console.log('Linting...'); From 747f3235520d97e0e1ee6112970781b2e8176cce Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 9 Jan 2021 19:29:30 -0500 Subject: [PATCH 027/357] checkPlugin: Use absolute path when installing `ep_etherpad-lite` This makes it possible to check plugins that were installed by symlinking into `node_modules/` like this: git clone git@github.com:ether/etherpad-lite.git git clone git@github.com:ether/ep_example.git cd etherpad-lite npm i ep_example@file:../ep_example node ./bin/checkPlugin.js ep_example --- bin/plugins/checkPlugin.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 92d6271e8..7bbad5bfc 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -441,8 +441,10 @@ fs.readdir(pluginPath, (err, rootFiles) => { // if autoFix is enabled. const npmInstall = `npm install${autoFix ? '' : ' --no-package-lock'}`; execSync(npmInstall, {stdio: 'inherit'}); - // The ep_etherpad-lite peer dep must be installed last otherwise `npm install` will nuke it. - execSync(`${npmInstall} --no-save ep_etherpad-lite@file:../../src`, {stdio: 'inherit'}); + // The ep_etherpad-lite peer dep must be installed last otherwise `npm install` will nuke it. An + // absolute path to etherpad-lite/src is used here so that pluginPath can be a symlink. + execSync( + `${npmInstall} --no-save ep_etherpad-lite@file:${__dirname}/../../src`, {stdio: 'inherit'}); // linting begins try { From 42da9767050da9e173a3a995b2e54b92d0b848f3 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 9 Jan 2021 18:51:31 -0500 Subject: [PATCH 028/357] checkPlugin: Move dependency update logic to a generic function This will make it possible to reuse the logic for `peerDependencies`. --- bin/plugins/checkPlugin.js | 44 ++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 7bbad5bfc..93951df64 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -37,6 +37,8 @@ const autoUpdate = optArgs.indexOf('autoupdate') !== -1; // Should we automcommit and npm publish?! const autoCommit = optArgs.indexOf('autocommit') !== -1; +let hasAutoFixed = false; + const execSync = (cmd, opts = {}) => (childProcess.execSync(cmd, { cwd: `${pluginPath}/`, ...opts, @@ -48,6 +50,28 @@ const writePackageJson = (obj) => { return fs.writeFileSync(`${pluginPath}/package.json`, s); }; +const updateDeps = (parsedPackageJson, key, wantDeps) => { + const {[key]: deps = {}} = parsedPackageJson; + let changed = false; + for (const [pkg, ver] of Object.entries(wantDeps)) { + if (deps[pkg] === ver) continue; + if (deps[pkg] == null) { + console.warn(`Missing dependency in ${key}: '${pkg}': '${ver}'`); + } else { + console.warn(`Dependency mismatch in ${key}: '${pkg}': '${ver}' (current: ${deps[pkg]})`); + } + if (autoFix) { + deps[pkg] = ver; + changed = true; + } + } + if (changed) { + hasAutoFixed = true; + parsedPackageJson[key] = deps; + writePackageJson(parsedPackageJson); + } +}; + const prepareRepo = () => { let branch = execSync('git symbolic-ref HEAD'); if (branch !== 'refs/heads/master' && branch !== 'refs/heads/main') { @@ -87,7 +111,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { // some files we need to know the actual file name. Not compulsory but might help in the future. let readMeFileName; let repository; - let hasAutoFixed = false; for (let i = 0; i < rootFiles.length; i++) { if (rootFiles[i].toLowerCase().indexOf('readme') !== -1) readMeFileName = rootFiles[i]; @@ -213,8 +236,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { repository = parsedPackageJSON.repository.url; } - // include lint config - const lintDeps = { + updateDeps(parsedPackageJSON, 'devDependencies', { 'eslint': '^7.17.0', 'eslint-config-etherpad': '^1.0.22', 'eslint-plugin-eslint-comments': '^3.2.0', @@ -223,21 +245,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { 'eslint-plugin-prefer-arrow': '^1.2.2', 'eslint-plugin-promise': '^4.2.1', 'eslint-plugin-you-dont-need-lodash-underscore': '^6.10.0', - }; - const {devDependencies = {}} = parsedPackageJSON; - let lintDepsNeedUpdating = false; - for (const [pkg, ver] of Object.entries(lintDeps)) { - if (devDependencies[pkg] !== ver) { - console.warn(`Missing/outdated ESLint dependency: '${pkg}': '${ver}' ` + - `(current: ${devDependencies[pkg]})`); - lintDepsNeedUpdating = true; - } - } - if (lintDepsNeedUpdating && autoFix) { - hasAutoFixed = true; - parsedPackageJSON.devDependencies = Object.assign(devDependencies, lintDeps); - writePackageJson(parsedPackageJSON); - } + }); // include peer deps config if (packageJSON.toLowerCase().indexOf('peerdependencies') === -1 || !parsedPackageJSON.peerDependencies) { From 9f4bcfb79bfaac7f64206cfcdbbf5245eb0b1302 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 10 Jan 2021 16:23:04 -0500 Subject: [PATCH 029/357] checkPlugin: Ensure that a peer dep for `ep_etherpad-lite` exists Tweak the new `updateDeps()` function and use it to add the `ep_etherpad-lite` dependency if it is missing. --- bin/plugins/checkPlugin.js | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 93951df64..a648c79ec 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -53,11 +53,13 @@ const writePackageJson = (obj) => { const updateDeps = (parsedPackageJson, key, wantDeps) => { const {[key]: deps = {}} = parsedPackageJson; let changed = false; - for (const [pkg, ver] of Object.entries(wantDeps)) { + for (const [pkg, verInfo] of Object.entries(wantDeps)) { + const {ver, overwrite = true} = typeof verInfo === 'string' ? {ver: verInfo} : verInfo; if (deps[pkg] === ver) continue; if (deps[pkg] == null) { console.warn(`Missing dependency in ${key}: '${pkg}': '${ver}'`); } else { + if (!overwrite) continue; console.warn(`Dependency mismatch in ${key}: '${pkg}': '${ver}' (current: ${deps[pkg]})`); } if (autoFix) { @@ -247,18 +249,10 @@ fs.readdir(pluginPath, (err, rootFiles) => { 'eslint-plugin-you-dont-need-lodash-underscore': '^6.10.0', }); - // include peer deps config - if (packageJSON.toLowerCase().indexOf('peerdependencies') === -1 || !parsedPackageJSON.peerDependencies) { - console.warn('Missing peer deps reference in package.json'); - if (autoFix) { - const peerDependencies = { - 'ep_etherpad-lite': '>=1.8.6', - }; - hasAutoFixed = true; - parsedPackageJSON.peerDependencies = peerDependencies; - writePackageJson(parsedPackageJSON); - } - } + updateDeps(parsedPackageJSON, 'peerDependencies', { + // Some plugins require a newer version of Etherpad so don't overwrite if already set. + 'ep_etherpad-lite': {ver: '>=1.8.6', overwrite: false}, + }); if (packageJSON.toLowerCase().indexOf('eslintconfig') === -1) { console.warn('No esLintConfig in package.json'); From 5f2aaea255f4dcedc539cc298fe4230c83559368 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 9 Jan 2021 19:16:56 -0500 Subject: [PATCH 030/357] checkPlugin: Use git to determine whether there were changes --- bin/plugins/checkPlugin.js | 56 +++++++++++++------------------------- 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index a648c79ec..badaa6f3a 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -37,8 +37,6 @@ const autoUpdate = optArgs.indexOf('autoupdate') !== -1; // Should we automcommit and npm publish?! const autoCommit = optArgs.indexOf('autocommit') !== -1; -let hasAutoFixed = false; - const execSync = (cmd, opts = {}) => (childProcess.execSync(cmd, { cwd: `${pluginPath}/`, ...opts, @@ -68,7 +66,6 @@ const updateDeps = (parsedPackageJson, key, wantDeps) => { } } if (changed) { - hasAutoFixed = true; parsedPackageJson[key] = deps; writePackageJson(parsedPackageJson); } @@ -135,7 +132,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { fs.readFileSync('bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'}); fs.mkdirSync(`${pluginPath}/.github/workflows`, {recursive: true}); fs.writeFileSync(path, npmpublish); - hasAutoFixed = true; console.log("If you haven't already, setup autopublish for this plugin https://github.com/ether/etherpad-lite/wiki/Plugins:-Automatically-publishing-to-npm-on-commit-to-Github-Repo"); } else { console.log('Setup autopublish for this plugin https://github.com/ether/etherpad-lite/wiki/Plugins:-Automatically-publishing-to-npm-on-commit-to-Github-Repo'); @@ -159,7 +155,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { fs.readFileSync('bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'}); fs.mkdirSync(`${pluginPath}/.github/workflows`, {recursive: true}); fs.writeFileSync(path, npmpublish); - hasAutoFixed = true; } } } catch (err) { @@ -177,7 +172,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { fs.readFileSync('bin/plugins/lib/backend-tests.yml', {encoding: 'utf8', flag: 'r'}); fs.mkdirSync(`${pluginPath}/.github/workflows`, {recursive: true}); fs.writeFileSync(path, backendTests); - hasAutoFixed = true; } } else { // autopublish exists, we should check the version.. @@ -198,7 +192,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { fs.readFileSync('bin/plugins/lib/backend-tests.yml', {encoding: 'utf8', flag: 'r'}); fs.mkdirSync(`${pluginPath}/.github/workflows`, {recursive: true}); fs.writeFileSync(path, backendTests); - hasAutoFixed = true; } } } catch (err) { @@ -223,7 +216,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { }; } if (updatedPackageJSON) { - hasAutoFixed = true; writePackageJson(parsedPackageJSON); } } @@ -261,7 +253,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { root: true, extends: 'etherpad/plugin', }; - hasAutoFixed = true; parsedPackageJSON.eslintConfig = eslintConfig; writePackageJson(parsedPackageJSON); } @@ -274,7 +265,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { 'lint': 'eslint .', 'lint:fix': 'eslint --fix .', }; - hasAutoFixed = true; parsedPackageJSON.scripts = scripts; writePackageJson(parsedPackageJSON); } @@ -286,7 +276,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { const engines = { node: '>=10.13.0', }; - hasAutoFixed = true; parsedPackageJSON.engines = engines; writePackageJson(parsedPackageJSON); } @@ -343,7 +332,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { if (files.indexOf('license') === -1 && files.indexOf('license.md') === -1) { console.warn('LICENSE.md file not found, please create'); if (autoFix) { - hasAutoFixed = true; console.log('Autofixing missing LICENSE.md file, including Apache 2 license.'); let license = fs.readFileSync('bin/plugins/lib/LICENSE.md', {encoding: 'utf8', flag: 'r'}); license = license.replace('[yyyy]', new Date().getFullYear()); @@ -359,7 +347,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.warn('.travis.yml file not found, please create. .travis.yml is used for automatically CI testing Etherpad. It is useful to know if your plugin breaks another feature for example.'); // TODO: Make it check version of the .travis file to see if it needs an update. if (autoFix) { - hasAutoFixed = true; console.log('Autofixing missing .travis.yml file'); fs.writeFileSync(`${pluginPath}/.travis.yml`, travisConfig); console.log('Travis file created, please sign into travis and enable this repository'); @@ -380,14 +367,12 @@ fs.readdir(pluginPath, (err, rootFiles) => { } else if (newValue > existingValue) { console.log('updating .travis.yml'); fs.writeFileSync(`${pluginPath}/.travis.yml`, travisConfig); - hasAutoFixed = true; }// } if (files.indexOf('.gitignore') === -1) { console.warn(".gitignore file not found, please create. .gitignore files are useful to ensure files aren't incorrectly commited to a repository."); if (autoFix) { - hasAutoFixed = true; console.log('Autofixing missing .gitignore file'); const gitignore = fs.readFileSync('bin/plugins/lib/gitignore', {encoding: 'utf8', flag: 'r'}); fs.writeFileSync(`${pluginPath}/.gitignore`, gitignore); @@ -400,7 +385,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { if (autoFix) { gitignore += 'node_modules/'; fs.writeFileSync(`${pluginPath}/.gitignore`, gitignore); - hasAutoFixed = true; } } } @@ -414,7 +398,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { if (files.indexOf('.ep_initialized') !== -1) { console.warn('.ep_initialized found, please remove. .ep_initialized should never be commited to git and should only exist once the plugin has been executed one time.'); if (autoFix) { - hasAutoFixed = true; console.log('Autofixing incorrectly existing .ep_initialized file'); fs.unlinkSync(`${pluginPath}/.ep_initialized`); } @@ -423,7 +406,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { if (files.indexOf('npm-debug.log') !== -1) { console.warn('npm-debug.log found, please remove. npm-debug.log should never be commited to your repository.'); if (autoFix) { - hasAutoFixed = true; console.log('Autofixing incorrectly existing npm-debug.log file'); fs.unlinkSync(`${pluginPath}/npm-debug.log`); } @@ -453,32 +435,32 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.log('Linting...'); const lintCmd = autoFix ? 'npx eslint --fix .' : 'npx eslint'; execSync(lintCmd, {stdio: 'inherit'}); - if (autoFix) { - // todo: if npm run lint doesn't do anything no need for... - hasAutoFixed = true; - } } catch (e) { // it is gonna throw an error anyway console.log('Manual linting probably required, check with: npm run lint'); } // linting ends. - if (hasAutoFixed) { - // bump npm Version - const cmd = [ - 'git rm -rf node_modules --ignore-unmatch', - 'git add -A', - '{ ! git diff-index --cached --quiet HEAD || exit 0; }', - 'git commit -m "autofixes from Etherpad checkPlugin.js"', - 'git push', - ].join(' && '); - if (autoCommit) { - // holy shit you brave. - console.log('Attempting autocommit and auto publish to npm'); - execSync(cmd, {stdio: 'inherit'}); + if (autoFix) { + const unchanged = JSON.parse(execSync( + 'untracked=$(git ls-files -o --exclude-standard) || exit 1; ' + + 'git diff-files --quiet && [ -z "$untracked" ] && echo true || echo false')); + if (!unchanged) { + const cmd = [ + 'git rm -rf node_modules --ignore-unmatch', + 'git add -A', + 'git commit -m "autofixes from Etherpad checkPlugin.js"', + 'git push', + ].join(' && '); + if (autoCommit) { + console.log('Attempting autocommit and auto publish to npm'); + execSync(cmd, {stdio: 'inherit'}); + } else { + console.log('Fixes applied, please check git diff then run the following command:'); + console.log(`(cd node_modules/${pluginName} && ${cmd})`); + } } else { - console.log('Fixes applied, please check git diff then run the following command:'); - console.log(`(cd node_modules/${pluginName} && ${cmd})`); + console.log('No changes.'); } } From 6ccbe374d8f8dc5267968c5d6eaccb26aef0a3ac Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 9 Jan 2021 19:19:17 -0500 Subject: [PATCH 031/357] checkPlugin: Don't nuke `node_modules/` `git add -A` will ignore `node_modules/` because it's in `.gitignore`. --- bin/plugins/checkPlugin.js | 1 - 1 file changed, 1 deletion(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index badaa6f3a..16a2b0994 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -447,7 +447,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { 'git diff-files --quiet && [ -z "$untracked" ] && echo true || echo false')); if (!unchanged) { const cmd = [ - 'git rm -rf node_modules --ignore-unmatch', 'git add -A', 'git commit -m "autofixes from Etherpad checkPlugin.js"', 'git push', From adb38d0d61e1e5aae04450068586ca025aef0c3f Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 10 Jan 2021 18:28:32 -0500 Subject: [PATCH 032/357] checkPlugin: Display a diff of the changes --- bin/plugins/checkPlugin.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 16a2b0994..43713d4fa 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -446,6 +446,14 @@ fs.readdir(pluginPath, (err, rootFiles) => { 'untracked=$(git ls-files -o --exclude-standard) || exit 1; ' + 'git diff-files --quiet && [ -z "$untracked" ] && echo true || echo false')); if (!unchanged) { + // Display a diff of changes. Git doesn't diff untracked files, so they must be added to the + // index. Use a temporary index file to avoid modifying Git's default index file. + execSync('git read-tree HEAD; git add -A && git diff-index -p --cached HEAD && echo ""', { + env: {...process.env, GIT_INDEX_FILE: '.git/checkPlugin.index'}, + stdio: 'inherit', + }); + fs.unlinkSync(`${pluginPath}/.git/checkPlugin.index`); + const cmd = [ 'git add -A', 'git commit -m "autofixes from Etherpad checkPlugin.js"', @@ -455,7 +463,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.log('Attempting autocommit and auto publish to npm'); execSync(cmd, {stdio: 'inherit'}); } else { - console.log('Fixes applied, please check git diff then run the following command:'); + console.log('Fixes applied. Check the above git diff then run the following command:'); console.log(`(cd node_modules/${pluginName} && ${cmd})`); } } else { From 27df9df47f624fe1d5c2cf817c1e6ef3fa91fe26 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 10 Jan 2021 19:16:58 -0500 Subject: [PATCH 033/357] checkPlugin: Revise README.md --- bin/plugins/README.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/bin/plugins/README.md b/bin/plugins/README.md index fbadf98ca..74e266f32 100755 --- a/bin/plugins/README.md +++ b/bin/plugins/README.md @@ -2,28 +2,33 @@ The files in this folder are for Plugin developers. # Get suggestions to improve your Plugin -This code will check your plugin for known usual issues and some suggestions for improvements. No changes will be made to your project. +This code will check your plugin for known usual issues and some suggestions for +improvements. No changes will be made to your project. ``` node bin/plugins/checkPlugin.js $PLUGIN_NAME$ ``` # Basic Example: + ``` node bin/plugins/checkPlugin.js ep_webrtc ``` ## Autofixing - will autofix any issues it can + ``` node bin/plugins/checkPlugin.js ep_whatever autofix ``` ## Autocommitting, push, npm minor patch and npm publish (highly dangerous) + ``` node bin/plugins/checkPlugin.js ep_whatever autofix autocommit ``` # All the plugins + Replace johnmclear with your github username ``` @@ -33,19 +38,15 @@ GHUSER=johnmclear; curl "https://api.github.com/users/$GHUSER/repos?per_page=100 cd .. # autofixes and autocommits /pushes & npm publishes -for dir in `ls node_modules`; -do -# echo $0 -if [[ $dir == *"ep_"* ]]; then -if [[ $dir != "ep_etherpad-lite" ]]; then -node bin/plugins/checkPlugin.js $dir autofix autocommit -fi -fi -# echo $dir +for dir in node_modules/ep_*; do + dir=${dir#node_modules/} + [ "$dir" != ep_etherpad-lite ] || continue + node bin/plugins/checkPlugin.js "$dir" autofix autocommit done ``` # Automating update of ether organization plugins + ``` getCorePlugins.sh updateCorePlugins.sh From 039c50de805ee4c2a3690d1bd0c591b21047e941 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 10 Jan 2021 18:35:50 -0500 Subject: [PATCH 034/357] checkPlugin: Make `autocommit` imply `autoupdate`, `autoupdate` imply `autofix` --- bin/plugins/README.md | 4 ++-- bin/plugins/checkPlugin.js | 26 +++++++++----------------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/bin/plugins/README.md b/bin/plugins/README.md index 74e266f32..5bb4786cd 100755 --- a/bin/plugins/README.md +++ b/bin/plugins/README.md @@ -24,7 +24,7 @@ node bin/plugins/checkPlugin.js ep_whatever autofix ## Autocommitting, push, npm minor patch and npm publish (highly dangerous) ``` -node bin/plugins/checkPlugin.js ep_whatever autofix autocommit +node bin/plugins/checkPlugin.js ep_whatever autocommit ``` # All the plugins @@ -41,7 +41,7 @@ cd .. for dir in node_modules/ep_*; do dir=${dir#node_modules/} [ "$dir" != ep_etherpad-lite ] || continue - node bin/plugins/checkPlugin.js "$dir" autofix autocommit + node bin/plugins/checkPlugin.js "$dir" autocommit done ``` diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 43713d4fa..00aeb5aa9 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -1,15 +1,13 @@ 'use strict'; /* -* -* Usage -- see README.md -* -* Normal usage: node bin/plugins/checkPlugin.js ep_whatever -* Auto fix the things it can: node bin/plugins/checkPlugin.js ep_whatever autofix -* Auto commit, push and publish(to npm) * highly dangerous: -node bin/plugins/checkPlugin.js ep_whatever autofix autocommit - -*/ + * Usage -- see README.md + * + * Normal usage: node bin/plugins/checkPlugin.js ep_whatever + * Auto fix the things it can: node bin/plugins/checkPlugin.js ep_whatever autofix + * Auto commit, push and publish to npm (highly dangerous): + * node bin/plugins/checkPlugin.js ep_whatever autocommit + */ const fs = require('fs'); const childProcess = require('child_process'); @@ -27,15 +25,9 @@ const pluginPath = `node_modules/${pluginName}`; console.log(`Checking the plugin: ${pluginName}`); const optArgs = process.argv.slice(3); - -// Should we autofix? -const autoFix = optArgs.indexOf('autofix') !== -1; - -// Should we update files where possible? -const autoUpdate = optArgs.indexOf('autoupdate') !== -1; - -// Should we automcommit and npm publish?! const autoCommit = optArgs.indexOf('autocommit') !== -1; +const autoUpdate = autoCommit || optArgs.indexOf('autoupdate') !== -1; +const autoFix = autoUpdate || optArgs.indexOf('autofix') !== -1; const execSync = (cmd, opts = {}) => (childProcess.execSync(cmd, { cwd: `${pluginPath}/`, From 481fa047e4c8e79006c08f1cd6fcc3e4dc1680b9 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 10 Jan 2021 19:22:59 -0500 Subject: [PATCH 035/357] checkPlugin: Merge the `autoupdate` option into `autofix` --- bin/plugins/checkPlugin.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 00aeb5aa9..570c3bda0 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -26,8 +26,7 @@ console.log(`Checking the plugin: ${pluginName}`); const optArgs = process.argv.slice(3); const autoCommit = optArgs.indexOf('autocommit') !== -1; -const autoUpdate = autoCommit || optArgs.indexOf('autoupdate') !== -1; -const autoFix = autoUpdate || optArgs.indexOf('autofix') !== -1; +const autoFix = autoCommit || optArgs.indexOf('autofix') !== -1; const execSync = (cmd, opts = {}) => (childProcess.execSync(cmd, { cwd: `${pluginPath}/`, @@ -344,7 +343,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.log('Travis file created, please sign into travis and enable this repository'); } } - if (autoFix && autoUpdate) { + if (autoFix) { // checks the file versioning of .travis and updates it to the latest. const existingConfig = fs.readFileSync(`${pluginPath}/.travis.yml`, {encoding: 'utf8', flag: 'r'}); const existingConfigLocation = existingConfig.indexOf('##ETHERPAD_TRAVIS_V='); From edbe6d53876ce39dde3af9eac28e7ddf422c7219 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 11 Jan 2021 03:47:55 -0500 Subject: [PATCH 036/357] Bump ueberDB to get speed improvements --- CHANGELOG.md | 4 ++++ bin/importSqlFile.js | 2 +- src/node/db/DB.js | 4 ++-- src/package-lock.json | 28 ++++++++++++++-------------- src/package.json | 2 +- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5863b69c9..b39ce09ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# Changes for the next release +### Notable new features +* Database performance is significantly improved. + # 1.8.7 ### Compatibility-breaking changes * **IMPORTANT:** It is no longer possible to protect a group pad with a diff --git a/bin/importSqlFile.js b/bin/importSqlFile.js index a67cb8bf0..870c02cce 100644 --- a/bin/importSqlFile.js +++ b/bin/importSqlFile.js @@ -57,7 +57,7 @@ require('ep_etherpad-lite/node_modules/npm').load({}, (er, npm) => { process.stdout.write('\n'); process.stdout.write('done. waiting for db to finish transaction. depended on dbms this may take some time...\n'); - db.doShutdown(() => { + db.close(() => { log(`finished, imported ${keyNo} keys.`); process.exit(0); }); diff --git a/src/node/db/DB.js b/src/node/db/DB.js index 601c08c5c..12d3d9f80 100644 --- a/src/node/db/DB.js +++ b/src/node/db/DB.js @@ -49,7 +49,7 @@ exports.init = async () => await new Promise((resolve, reject) => { } // everything ok, set up Promise-based methods - ['get', 'set', 'findKeys', 'getSub', 'setSub', 'remove', 'doShutdown'].forEach((fn) => { + ['get', 'set', 'findKeys', 'getSub', 'setSub', 'remove'].forEach((fn) => { exports[fn] = util.promisify(db[fn].bind(db)); }); @@ -73,6 +73,6 @@ exports.init = async () => await new Promise((resolve, reject) => { }); exports.shutdown = async (hookName, context) => { - await exports.doShutdown(); + await util.promisify(db.close.bind(db))(); console.log('Database closed'); }; diff --git a/src/package-lock.json b/src/package-lock.json index f82019554..1e902d295 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -4179,9 +4179,9 @@ "dev": true }, "needle": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz", - "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.6.0.tgz", + "integrity": "sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg==", "optional": true, "requires": { "debug": "^3.2.6", @@ -4236,9 +4236,9 @@ } }, "node-addon-api": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.0.tgz", - "integrity": "sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz", + "integrity": "sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==", "optional": true }, "node-environment-flags": { @@ -8858,12 +8858,12 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sqlite3": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.0.tgz", - "integrity": "sha512-rjvqHFUaSGnzxDy2AHCwhHy6Zp6MNJzCPGYju4kD8yi6bze4d1/zMTg6C7JI49b7/EM7jKMTvyfN/4ylBKdwfw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.1.tgz", + "integrity": "sha512-kh2lTIcYNfmVcvhVJihsYuPj9U0xzBbh6bmqILO2hkryWSC9RRhzYmkIDtJkJ+d8Kg4wZRJ0T1reyHUEspICfg==", "optional": true, "requires": { - "node-addon-api": "2.0.0", + "node-addon-api": "^3.0.0", "node-gyp": "3.x", "node-pre-gyp": "^0.11.0" } @@ -9358,9 +9358,9 @@ } }, "ueberdb2": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/ueberdb2/-/ueberdb2-1.1.7.tgz", - "integrity": "sha512-Fxvpei4MVYOlCWZ67I5iibYyaHIb40ZLwN0OsjPEuMpcWYBXuwkl+svFyA2nYgk9/1Nk9eL/zdsXbwm7BLMKsg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ueberdb2/-/ueberdb2-1.2.1.tgz", + "integrity": "sha512-b1WeG5KinxddpFAkHcFJW5tDuoGMonI3AyZgqmmHKnvF7CJTprPX17n0AOi3iQcu97uoxz9nb56KEQ4gHQOCJQ==", "requires": { "async": "^3.2.0", "cassandra-driver": "^4.5.1", @@ -9371,7 +9371,7 @@ "elasticsearch": "^16.7.1", "mocha": "^7.1.2", "mongodb": "^3.6.3", - "mssql": "7.0.0-alpha.4", + "mssql": "^7.0.0-alpha.4", "mysql": "2.18.1", "nano": "^8.2.2", "pg": "^8.0.3", diff --git a/src/package.json b/src/package.json index fcf73751b..66d71766b 100644 --- a/src/package.json +++ b/src/package.json @@ -70,7 +70,7 @@ "threads": "^1.4.0", "tiny-worker": "^2.3.0", "tinycon": "0.0.1", - "ueberdb2": "^1.1.7", + "ueberdb2": "^1.2.1", "underscore": "1.8.3", "unorm": "1.4.1" }, From c38c34bef47a74467394797b34165641a87ac620 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 14 Jan 2021 10:00:14 +0000 Subject: [PATCH 037/357] linting: ace2_inner * remove IE and add strict headers * linting: kids are back, need to stop for today * linting: farbtastic fix * lint: more lint fixes * more lint fixes * linting: sub 100 errors * comments where I need help * ready to be helped :) * small fixes * fixes * linting: all errors resolved * linting: remove note to self * fix as per nulli/wezz000li suggestion * fix as per nulli/wezz000li suggestion * resolve merge conflicts * better use if to silence eslint * Use `for..of` with `Object.keys` instead of `for..in` * lint: move setSelection to before call Co-authored-by: webzwo0i Co-authored-by: Richard Hansen --- src/static/js/ace2_inner.js | 2199 +++++++++++++++-------------- src/static/js/contentcollector.js | 1 + src/static/js/domline.js | 8 +- src/static/js/farbtastic.js | 51 +- src/static/js/pad_userlist.js | 7 - 5 files changed, 1137 insertions(+), 1129 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 34b7e79a1..06f176b52 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -1,11 +1,8 @@ -/** - * This code is mostly from the old Etherpad. Please help us to comment this code. - * This helps other people to understand this code better and helps them to improve it. - * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED - */ +'use strict'; /** * Copyright 2009 Google Inc. + * Copyright 2020 John McLear - The Etherpad Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,23 +16,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +let documentAttributeManager; -const padutils = require('./pad_utils').padutils; - -let _, $, jQuery, plugins, Ace2Common; const browser = require('./browser'); - -Ace2Common = require('./ace2_common'); - -plugins = require('ep_etherpad-lite/static/js/pluginfw/client_plugins'); -$ = jQuery = require('./rjquery').$; -_ = require('./underscore'); +const padutils = require('./pad_utils').padutils; +const Ace2Common = require('./ace2_common'); +const $ = require('./rjquery').$; +const _ = require('./underscore'); const isNodeText = Ace2Common.isNodeText; const getAssoc = Ace2Common.getAssoc; const setAssoc = Ace2Common.setAssoc; -const isTextNode = Ace2Common.isTextNode; -const binarySearchInfinite = Ace2Common.binarySearchInfinite; const htmlPrettyEscape = Ace2Common.htmlPrettyEscape; const noop = Ace2Common.noop; const hooks = require('./pluginfw/hooks'); @@ -54,10 +45,7 @@ function Ace2Inner() { const undoModule = require('./undomodule').undoModule; const AttributeManager = require('./AttributeManager'); const Scroll = require('./scroll'); - - const DEBUG = false; // $$ build script replaces the string "var DEBUG=true;//$$" with "var DEBUG=false;" - // changed to false - let isSetUp = false; + const DEBUG = false; const THE_TAB = ' '; // 4 const MAX_LIST_LEVEL = 16; @@ -72,6 +60,9 @@ function Ace2Inner() { let disposed = false; const editorInfo = parent.editorInfo; + const focus = () => { + window.focus(); + }; const iframe = window.frameElement; const outerWin = iframe.ace_outerWin; @@ -80,14 +71,22 @@ function Ace2Inner() { const lineMetricsDiv = sideDiv.nextSibling; let lineNumbersShown; let sideDivInner; + + const initLineNumbers = () => { + const htmlOpen = '
1'; + const htmlClose = '
'; + lineNumbersShown = 1; + sideDiv.innerHTML = `${htmlOpen}${htmlClose}`; + sideDivInner = outerWin.document.getElementById('sidedivinner'); + $(sideDiv).addClass('sidediv'); + }; + initLineNumbers(); const scroll = Scroll.init(outerWin); let outsideKeyDown = noop; - - let outsideKeyPress = function (e) { return true; }; - + let outsideKeyPress = (e) => true; let outsideNotifyDirty = noop; // selFocusAtStart -- determines whether the selection extends "backwards", so that the focus @@ -116,25 +115,39 @@ function Ace2Inner() { let isStyled = true; let console = (DEBUG && window.console); - let documentAttributeManager; if (!window.console) { - const names = ['log', 'debug', 'info', 'warn', 'error', 'assert', 'dir', 'dirxml', 'group', 'groupEnd', 'time', 'timeEnd', 'count', 'trace', 'profile', 'profileEnd']; + const names = [ + 'log', + 'debug', + 'info', + 'warn', + 'error', + 'assert', + 'dir', + 'dirxml', + 'group', + 'groupEnd', + 'time', + 'timeEnd', + 'count', + 'trace', + 'profile', + 'profileEnd', + ]; console = {}; for (let i = 0; i < names.length; ++i) console[names[i]] = noop; } let PROFILER = window.PROFILER; if (!PROFILER) { - PROFILER = function () { - return { - start: noop, - mark: noop, - literal: noop, - end: noop, - cancel: noop, - }; - }; + PROFILER = () => ({ + start: noop, + mark: noop, + literal: noop, + end: noop, + cancel: noop, + }); } // "dmesg" is for displaying messages in the in-page output pane @@ -150,22 +163,42 @@ function Ace2Inner() { let outerDynamicCSS = null; let parentDynamicCSS = null; - function initDynamicCSS() { + const performDocumentReplaceRange = (start, end, newText) => { + if (start === undefined) start = rep.selStart; + if (end === undefined) end = rep.selEnd; + + // dmesg(String([start.toSource(),end.toSource(),newText.toSource()])); + // start[0]: <--- start[1] --->CCCCCCCCCCC\n + // CCCCCCCCCCCCCCCCCCCC\n + // CCCC\n + // end[0]: -------\n + const builder = Changeset.builder(rep.lines.totalWidth()); + ChangesetUtils.buildKeepToStartOfRange(rep, builder, start); + ChangesetUtils.buildRemoveRange(rep, builder, start, end); + builder.insert(newText, [ + ['author', thisAuthor], + ], rep.apool); + const cs = builder.toString(); + + performDocumentApplyChangeset(cs); + }; + + const initDynamicCSS = () => { dynamicCSS = makeCSSManager('dynamicsyntax'); outerDynamicCSS = makeCSSManager('dynamicsyntax', 'outer'); parentDynamicCSS = makeCSSManager('dynamicsyntax', 'parent'); - } + }; const changesetTracker = makeChangesetTracker(scheduler, rep.apool, { - withCallbacks(operationName, f) { + withCallbacks: (operationName, f) => { inCallStackIfNecessary(operationName, () => { fastIncorp(1); f( { - setDocumentAttributedText(atext) { + setDocumentAttributedText: (atext) => { setDocAText(atext); }, - applyChangesetToDocument(changeset, preferInsertionAfterCaret) { + applyChangesetToDocument: (changeset, preferInsertionAfterCaret) => { const oldEventType = currentCallStack.editEvent.eventType; currentCallStack.startNewEvent('nonundoable'); @@ -179,13 +212,10 @@ function Ace2Inner() { }); const authorInfos = {}; // presence of key determines if author is present in doc - - function getAuthorInfos() { - return authorInfos; - } + const getAuthorInfos = () => authorInfos; editorInfo.ace_getAuthorInfos = getAuthorInfos; - function setAuthorStyle(author, info) { + const setAuthorStyle = (author, info) => { if (!dynamicCSS) { return; } @@ -221,13 +251,14 @@ function Ace2Inner() { authorStyle.backgroundColor = bgcolor; parentAuthorStyle.backgroundColor = bgcolor; - const textColor = colorutils.textColorFromBackgroundColor(bgcolor, parent.parent.clientVars.skinName); + const textColor = colorutils. + textColorFromBackgroundColor(bgcolor, parent.parent.clientVars.skinName); authorStyle.color = textColor; parentAuthorStyle.color = textColor; } - } + }; - function setAuthorInfo(author, info) { + const setAuthorInfo = (author, info) => { if ((typeof author) !== 'string') { // Potentially caused by: https://github.com/ether/etherpad-lite/issues/2802"); throw new Error(`setAuthorInfo: author (${author}) is not a string`); @@ -238,19 +269,17 @@ function Ace2Inner() { authorInfos[author] = info; } setAuthorStyle(author, info); - } + }; - function getAuthorClassName(author) { - return `author-${author.replace(/[^a-y0-9]/g, (c) => { - if (c == '.') return '-'; - return `z${c.charCodeAt(0)}z`; - })}`; - } + const getAuthorClassName = (author) => `author-${author.replace(/[^a-y0-9]/g, (c) => { + if (c === '.') return '-'; + return `z${c.charCodeAt(0)}z`; + })}`; - function className2Author(className) { - if (className.substring(0, 7) == 'author-') { + const className2Author = (className) => { + if (className.substring(0, 7) === 'author-') { return className.substring(7).replace(/[a-y0-9]+|-|z.+?z/g, (cc) => { - if (cc == '-') { return '.'; } else if (cc.charAt(0) == 'z') { + if (cc === '-') { return '.'; } else if (cc.charAt(0) === 'z') { return String.fromCharCode(Number(cc.slice(1, -1))); } else { return cc; @@ -258,25 +287,19 @@ function Ace2Inner() { }); } return null; - } + }; - function getAuthorColorClassSelector(oneClassName) { - return `.authorColors .${oneClassName}`; - } + const getAuthorColorClassSelector = (oneClassName) => `.authorColors .${oneClassName}`; - function fadeColor(colorCSS, fadeFrac) { + const fadeColor = (colorCSS, fadeFrac) => { let color = colorutils.css2triple(colorCSS); color = colorutils.blend(color, [1, 1, 1], fadeFrac); return colorutils.triple2css(color); - } - - editorInfo.ace_getRep = function () { - return rep; }; - editorInfo.ace_getAuthor = function () { - return thisAuthor; - }; + editorInfo.ace_getRep = () => rep; + + editorInfo.ace_getAuthor = () => thisAuthor; const _nonScrollableEditEvents = { applyChangesToBase: 1, @@ -286,34 +309,32 @@ function Ace2Inner() { _nonScrollableEditEvents[eventType] = 1; }); - function isScrollableEditEvent(eventType) { - return !_nonScrollableEditEvents[eventType]; - } + const isScrollableEditEvent = (eventType) => !_nonScrollableEditEvents[eventType]; - var currentCallStack = null; + let currentCallStack = null; - function inCallStack(type, action) { + const inCallStack = (type, action) => { if (disposed) return; if (currentCallStack) { - // Do not uncomment this in production. It will break Etherpad being provided in iFrames. I'm leaving this in for testing usefulness. - // top.console.error("Can't enter callstack " + type + ", already in " + currentCallStack.type); + // Do not uncomment this in production. It will break Etherpad being provided in iFrames. + // I am leaving this in for testing usefulness. + const err = `Can't enter callstack ${type}, already in ${currentCallStack.type}`; // eslint-disable-line + // top.console.error(err); } let profiling = false; - function profileRest() { - profiling = true; - } + const profileRest = () => { + profiling = true; // eslint-disable-line + }; - function newEditEvent(eventType) { - return { - eventType, - backset: null, - }; - } + const newEditEvent = (eventType) => ({ + eventType, + backset: null, + }); - function submitOldEvent(evt) { + const submitOldEvent = (evt) => { if (rep.selStart && rep.selEnd) { const selStartChar = rep.lines.offsetOfIndex(rep.selStart[0]) + rep.selStart[1]; const selEndChar = rep.lines.offsetOfIndex(rep.selEnd[0]) + rep.selEnd[1]; @@ -326,7 +347,7 @@ function Ace2Inner() { try { if (isPadLoading(evt.eventType)) { undoModule.clearHistory(); - } else if (evt.eventType == 'nonundoable') { + } else if (evt.eventType === 'nonundoable') { if (evt.changeset) { undoModule.reportExternalChange(evt.changeset); } @@ -340,16 +361,16 @@ function Ace2Inner() { } } } - } + }; - function startNewEvent(eventType, dontSubmitOld) { + const startNewEvent = (eventType, dontSubmitOld) => { const oldEvent = currentCallStack.editEvent; if (!dontSubmitOld) { submitOldEvent(oldEvent); } currentCallStack.editEvent = newEditEvent(eventType); return oldEvent; - } + }; currentCallStack = { type, @@ -389,7 +410,7 @@ function Ace2Inner() { const cs = currentCallStack; if (cleanExit) { submitOldEvent(cs.editEvent); - if (cs.domClean && cs.type != 'setup') { + if (cs.domClean && cs.type !== 'setup') { // if (cs.isUserChange) // { // if (cs.repChanged) parenModule.notifyChange(); @@ -405,36 +426,32 @@ function Ace2Inner() { outsideNotifyDirty(); } } - } else { - // non-clean exit - if (currentCallStack.type == 'idleWorkTimer') { - idleWorkTimer.atLeast(1000); - } + } else if (currentCallStack.type === 'idleWorkTimer') { + idleWorkTimer.atLeast(1000); } currentCallStack = null; } return result; - } + }; editorInfo.ace_inCallStack = inCallStack; - function inCallStackIfNecessary(type, action) { + const inCallStackIfNecessary = (type, action) => { if (!currentCallStack) { inCallStack(type, action); } else { action(); } - } + }; editorInfo.ace_inCallStackIfNecessary = inCallStackIfNecessary; - function dispose() { + const dispose = () => { disposed = true; if (idleWorkTimer) idleWorkTimer.never(); teardown(); - } + }; - function setWraps(newVal) { + const setWraps = (newVal) => { doesWrap = newVal; - const dwClass = 'doesWrap'; root.classList.toggle('doesWrap', doesWrap); scheduler.setTimeout(() => { inCallStackIfNecessary('setWraps', () => { @@ -443,51 +460,49 @@ function Ace2Inner() { fixView(); }); }, 0); - } + }; - function setStyled(newVal) { + const setStyled = (newVal) => { const oldVal = isStyled; isStyled = !!newVal; - if (newVal != oldVal) { + if (newVal !== oldVal) { if (!newVal) { // clear styles inCallStackIfNecessary('setStyled', () => { fastIncorp(12); const clearStyles = []; - for (const k in STYLE_ATTRIBS) { + for (const k of Object.keys(STYLE_ATTRIBS)) { clearStyles.push([k, '']); } performDocumentApplyAttributesToCharRange(0, rep.alltext.length, clearStyles); }); } } - } + }; - function setTextFace(face) { + const setTextFace = (face) => { root.style.fontFamily = face; lineMetricsDiv.style.fontFamily = face; - } + }; - function recreateDOM() { + const recreateDOM = () => { // precond: normalized recolorLinesInRange(0, rep.alltext.length); - } + }; - function setEditable(newVal) { + const setEditable = (newVal) => { isEditable = newVal; root.contentEditable = isEditable ? 'true' : 'false'; root.classList.toggle('static', !isEditable); - } + }; - function enforceEditability() { - setEditable(isEditable); - } + const enforceEditability = () => setEditable(isEditable); - function importText(text, undoable, dontProcess) { + const importText = (text, undoable, dontProcess) => { let lines; if (dontProcess) { - if (text.charAt(text.length - 1) != '\n') { + if (text.charAt(text.length - 1) !== '\n') { throw new Error('new raw text must end with newline'); } if (/[\r\t\xa0]/.exec(text)) { @@ -506,12 +521,12 @@ function Ace2Inner() { setDocText(newText); }); - if (dontProcess && rep.alltext != text) { + if (dontProcess && rep.alltext !== text) { throw new Error('mismatch error setting raw text in importText'); } - } + }; - function importAText(atext, apoolJsonObj, undoable) { + const importAText = (atext, apoolJsonObj, undoable) => { atext = Changeset.cloneAText(atext); if (apoolJsonObj) { const wireApool = (new AttribPool()).fromJsonable(apoolJsonObj); @@ -520,9 +535,9 @@ function Ace2Inner() { inCallStackIfNecessary(`importText${undoable ? 'Undoable' : ''}`, () => { setDocAText(atext); }); - } + }; - function setDocAText(atext) { + const setDocAText = (atext) => { if (atext.text === '') { /* * The server is fine with atext.text being an empty string, but the front @@ -558,54 +573,55 @@ function Ace2Inner() { Changeset.pack(oldLen, newLen, assem.toString(), atext.text.slice(0, -1))); performDocumentApplyChangeset(changeset); - performSelectionChange([0, rep.lines.atIndex(0).lineMarker], [0, rep.lines.atIndex(0).lineMarker]); + performSelectionChange( + [0, rep.lines.atIndex(0).lineMarker], + [0, rep.lines.atIndex(0).lineMarker] + ); idleWorkTimer.atMost(100); - if (rep.alltext != atext.text) { + if (rep.alltext !== atext.text) { dmesg(htmlPrettyEscape(rep.alltext)); dmesg(htmlPrettyEscape(atext.text)); throw new Error('mismatch error setting raw text in setDocAText'); } - } + }; - function setDocText(text) { + const setDocText = (text) => { setDocAText(Changeset.makeAText(text)); - } + }; - function getDocText() { + const getDocText = () => { const alltext = rep.alltext; let len = alltext.length; if (len > 0) len--; // final extra newline return alltext.substring(0, len); - } + }; - function exportText() { + const exportText = () => { if (currentCallStack && !currentCallStack.domClean) { inCallStackIfNecessary('exportText', () => { fastIncorp(2); }); } return getDocText(); - } + }; - function editorChangedSize() { - fixView(); - } + const editorChangedSize = () => fixView(); - function setOnKeyPress(handler) { + const setOnKeyPress = (handler) => { outsideKeyPress = handler; - } + }; - function setOnKeyDown(handler) { + const setOnKeyDown = (handler) => { outsideKeyDown = handler; - } + }; - function setNotifyDirty(handler) { + const setNotifyDirty = (handler) => { outsideNotifyDirty = handler; - } + }; - function getFormattedCode() { + const getFormattedCode = () => { if (currentCallStack && !currentCallStack.domClean) { inCallStackIfNecessary('getFormattedCode', incorporateUserChanges); } @@ -615,15 +631,17 @@ function Ace2Inner() { let entry = rep.lines.atIndex(0); while (entry) { const domInfo = entry.domInfo; - buf.push((domInfo && domInfo.getInnerHTML()) || domline.processSpaces(domline.escapeHTML(entry.text), doesWrap) || ' ' /* empty line*/); + buf.push((domInfo && domInfo.getInnerHTML()) || + domline.processSpaces(domline.escapeHTML(entry.text), doesWrap) || + ' ' /* empty line*/); entry = rep.lines.next(entry); } } return `
${buf.join('
\n
')}
`; - } + }; const CMDS = { - clearauthorship(prompt) { + clearauthorship: (prompt) => { if ((!(rep.selStart && rep.selEnd)) || isCaret()) { if (prompt) { prompt(); @@ -638,54 +656,31 @@ function Ace2Inner() { }, }; - function execCommand(cmd) { + const execCommand = (cmd, ...args) => { cmd = cmd.toLowerCase(); - const cmdArgs = Array.prototype.slice.call(arguments, 1); + // TODO: Rhansen to check this logic. + const cmdArgs = args; if (CMDS[cmd]) { inCallStackIfNecessary(cmd, () => { fastIncorp(9); - CMDS[cmd].apply(CMDS, cmdArgs); + CMDS[cmd](CMDS, ...cmdArgs); }); } - } + }; - function replaceRange(start, end, text) { + const replaceRange = (start, end, text) => { inCallStackIfNecessary('replaceRange', () => { fastIncorp(9); performDocumentReplaceRange(start, end, text); }); - } + }; - editorInfo.ace_focus = focus; - editorInfo.ace_importText = importText; - editorInfo.ace_importAText = importAText; - editorInfo.ace_exportText = exportText; - editorInfo.ace_editorChangedSize = editorChangedSize; - editorInfo.ace_setOnKeyPress = setOnKeyPress; - editorInfo.ace_setOnKeyDown = setOnKeyDown; - editorInfo.ace_setNotifyDirty = setNotifyDirty; - editorInfo.ace_dispose = dispose; - editorInfo.ace_getFormattedCode = getFormattedCode; - editorInfo.ace_setEditable = setEditable; - editorInfo.ace_execCommand = execCommand; - editorInfo.ace_replaceRange = replaceRange; - editorInfo.ace_getAuthorInfos = getAuthorInfos; - editorInfo.ace_performDocumentReplaceRange = performDocumentReplaceRange; - editorInfo.ace_performDocumentReplaceCharRange = performDocumentReplaceCharRange; - editorInfo.ace_renumberList = renumberList; - editorInfo.ace_doReturnKey = doReturnKey; - editorInfo.ace_isBlockElement = isBlockElement; - editorInfo.ace_getLineListType = getLineListType; - editorInfo.ace_setSelection = setSelection; - - editorInfo.ace_callWithAce = function (fn, callStack, normalize) { - let wrapper = function () { - return fn(editorInfo); - }; + editorInfo.ace_callWithAce = (fn, callStack, normalize) => { + let wrapper = () => fn(editorInfo); if (normalize !== undefined) { const wrapper1 = wrapper; - wrapper = function () { + wrapper = () => { editorInfo.ace_fastIncorp(9); wrapper1(); }; @@ -701,26 +696,26 @@ function Ace2Inner() { // This methed exposes a setter for some ace properties // @param key the name of the parameter // @param value the value to set to - editorInfo.ace_setProperty = function (key, value) { + editorInfo.ace_setProperty = (key, value) => { // These properties are exposed const setters = { wraps: setWraps, showsauthorcolors: (val) => root.classList.toggle('authorColors', !!val), showsuserselections: (val) => root.classList.toggle('userSelections', !!val), - showslinenumbers(value) { + showslinenumbers: (value) => { hasLineNumbers = !!value; sideDiv.parentNode.classList.toggle('line-numbers-hidden', !hasLineNumbers); fixView(); }, grayedout: (val) => outerWin.document.body.classList.toggle('grayedout', !!val), - dmesg() { dmesg = window.dmesg = value; }, - userauthor(value) { + dmesg: () => { dmesg = window.dmesg = value; }, + userauthor: (value) => { thisAuthor = String(value); documentAttributeManager.author = thisAuthor; }, styled: setStyled, textface: setTextFace, - rtlistrue(value) { + rtlistrue: (value) => { root.classList.toggle('rtl', value); root.classList.toggle('ltr', !value); document.documentElement.dir = value ? 'rtl' : 'ltr'; @@ -735,63 +730,54 @@ function Ace2Inner() { } }; - editorInfo.ace_setBaseText = function (txt) { + editorInfo.ace_setBaseText = (txt) => { changesetTracker.setBaseText(txt); }; - editorInfo.ace_setBaseAttributedText = function (atxt, apoolJsonObj) { + editorInfo.ace_setBaseAttributedText = (atxt, apoolJsonObj) => { changesetTracker.setBaseAttributedText(atxt, apoolJsonObj); }; - editorInfo.ace_applyChangesToBase = function (c, optAuthor, apoolJsonObj) { + editorInfo.ace_applyChangesToBase = (c, optAuthor, apoolJsonObj) => { changesetTracker.applyChangesToBase(c, optAuthor, apoolJsonObj); }; - editorInfo.ace_prepareUserChangeset = function () { - return changesetTracker.prepareUserChangeset(); - }; - editorInfo.ace_applyPreparedChangesetToBase = function () { + editorInfo.ace_prepareUserChangeset = () => changesetTracker.prepareUserChangeset(); + editorInfo.ace_applyPreparedChangesetToBase = () => { changesetTracker.applyPreparedChangesetToBase(); }; - editorInfo.ace_setUserChangeNotificationCallback = function (f) { + editorInfo.ace_setUserChangeNotificationCallback = (f) => { changesetTracker.setUserChangeNotificationCallback(f); }; - editorInfo.ace_setAuthorInfo = function (author, info) { + editorInfo.ace_setAuthorInfo = (author, info) => { setAuthorInfo(author, info); }; - editorInfo.ace_setAuthorSelectionRange = function (author, start, end) { + editorInfo.ace_setAuthorSelectionRange = (author, start, end) => { changesetTracker.setAuthorSelectionRange(author, start, end); }; - editorInfo.ace_getUnhandledErrors = function () { - return caughtErrors.slice(); - }; + editorInfo.ace_getUnhandledErrors = () => caughtErrors.slice(); - editorInfo.ace_getDocument = function () { - return doc; - }; + editorInfo.ace_getDocument = () => doc; - editorInfo.ace_getDebugProperty = function (prop) { - if (prop == 'debugger') { + editorInfo.ace_getDebugProperty = (prop) => { + if (prop === 'debugger') { // obfuscate "eval" so as not to scare yuicompressor window['ev' + 'al']('debugger'); - } else if (prop == 'rep') { + } else if (prop === 'rep') { return rep; - } else if (prop == 'window') { + } else if (prop === 'window') { return window; - } else if (prop == 'document') { + } else if (prop === 'document') { return document; } return undefined; }; - function now() { - return Date.now(); - } + const now = () => Date.now(); - function newTimeLimit(ms) { + const newTimeLimit = (ms) => { const startTime = now(); - let lastElapsed = 0; let exceededAlready = false; let printedTrace = false; - const isTimeUp = function () { + const isTimeUp = () => { if (exceededAlready) { if ((!printedTrace)) { // && now() - startTime - ms > 300) { printedTrace = true; @@ -803,44 +789,42 @@ function Ace2Inner() { exceededAlready = true; return true; } else { - lastElapsed = elapsed; return false; } }; - isTimeUp.elapsed = function () { - return now() - startTime; - }; + isTimeUp.elapsed = () => now() - startTime; return isTimeUp; - } + }; - function makeIdleAction(func) { + const makeIdleAction = (func) => { let scheduledTimeout = null; let scheduledTime = 0; - function unschedule() { + const unschedule = () => { if (scheduledTimeout) { scheduler.clearTimeout(scheduledTimeout); scheduledTimeout = null; } - } + }; - function reschedule(time) { + const reschedule = (time) => { unschedule(); scheduledTime = time; let delay = time - now(); if (delay < 0) delay = 0; scheduledTimeout = scheduler.setTimeout(callback, delay); - } + }; - function callback() { + const callback = () => { scheduledTimeout = null; // func may reschedule the action func(); - } + }; + return { - atMost(ms) { + atMost: (ms) => { const latestTime = now() + ms; if ((!scheduledTimeout) || scheduledTime > latestTime) { reschedule(latestTime); @@ -849,25 +833,25 @@ function Ace2Inner() { // atLeast(ms) will schedule the action if not scheduled yet. // In other words, "infinity" is replaced by ms, even though // it is technically larger. - atLeast(ms) { + atLeast: (ms) => { const earliestTime = now() + ms; if ((!scheduledTimeout) || scheduledTime < earliestTime) { reschedule(earliestTime); } }, - never() { + never: () => { unschedule(); }, }; - } + }; - function fastIncorp(n) { + const fastIncorp = (n) => { // normalize but don't do any lexing or anything incorporateUserChanges(); - } + }; editorInfo.ace_fastIncorp = fastIncorp; - var idleWorkTimer = makeIdleAction(() => { + const idleWorkTimer = makeIdleAction(() => { if (inInternationalComposition) { // don't do idle input incorporation during international input composition idleWorkTimer.atLeast(500); @@ -887,9 +871,6 @@ function Ace2Inner() { updateLineNumbers(); // update line numbers if any time left if (isTimeUp()) return; - - const visibleRange = scroll.getVisibleCharRange(rep); - const docRange = [0, rep.lines.totalWidth()]; finishedImportantWork = true; finishedWork = true; } finally { @@ -911,16 +892,16 @@ function Ace2Inner() { let _nextId = 1; - function uniqueId(n) { + const uniqueId = (n) => { // not actually guaranteed to be unique, e.g. if user copy-pastes // nodes with ids const nid = n.id; if (nid) return nid; return (n.id = `magicdomid${_nextId++}`); - } + }; - function recolorLinesInRange(startChar, endChar) { + const recolorLinesInRange = (startChar, endChar) => { if (endChar <= startChar) return; if (startChar < 0 || startChar >= rep.lines.totalWidth()) return; let lineEntry = rep.lines.atOffset(startChar); // rounds down to line boundary @@ -928,31 +909,27 @@ function Ace2Inner() { let lineIndex = rep.lines.indexOfEntry(lineEntry); let selectionNeedsResetting = false; let firstLine = null; - let lastLine = null; // tokenFunc function; accesses current value of lineEntry and curDocChar, // also mutates curDocChar - let curDocChar; - const tokenFunc = function (tokenText, tokenClass) { + const tokenFunc = (tokenText, tokenClass) => { lineEntry.domInfo.appendSpan(tokenText, tokenClass); }; while (lineEntry && lineStart < endChar) { const lineEnd = lineStart + lineEntry.width; - - curDocChar = lineStart; lineEntry.domInfo.clearSpans(); getSpansForLine(lineEntry, tokenFunc, lineStart); lineEntry.domInfo.finishUpdate(); markNodeClean(lineEntry.lineNode); - if (rep.selStart && rep.selStart[0] == lineIndex || rep.selEnd && rep.selEnd[0] == lineIndex) { + if (rep.selStart && rep.selStart[0] === lineIndex || + rep.selEnd && rep.selEnd[0] === lineIndex) { selectionNeedsResetting = true; } - if (firstLine === null) firstLine = lineIndex; - lastLine = lineIndex; + if (firstLine == null) firstLine = lineIndex; lineStart = lineEnd; lineEntry = rep.lines.next(lineEntry); lineIndex++; @@ -960,27 +937,25 @@ function Ace2Inner() { if (selectionNeedsResetting) { currentCallStack.selectionAffected = true; } - } + }; // like getSpansForRange, but for a line, and the func takes (text,class) // instead of (width,class); excludes the trailing '\n' from // consideration by func - function getSpansForLine(lineEntry, textAndClassFunc, lineEntryOffsetHint) { + const getSpansForLine = (lineEntry, textAndClassFunc, lineEntryOffsetHint) => { let lineEntryOffset = lineEntryOffsetHint; if ((typeof lineEntryOffset) !== 'number') { lineEntryOffset = rep.lines.offsetOfEntry(lineEntry); } const text = lineEntry.text; - const width = lineEntry.width; // text.length+1 if (text.length === 0) { // allow getLineStyleFilter to set line-div styles const func = linestylefilter.getLineStyleFilter( 0, '', textAndClassFunc, rep.apool); func('', ''); } else { - const offsetIntoLine = 0; let filteredFunc = linestylefilter.getFilterStack(text, textAndClassFunc, browser); const lineNum = rep.lines.indexOfEntry(lineEntry); const aline = rep.alines[lineNum]; @@ -988,19 +963,19 @@ function Ace2Inner() { text.length, aline, filteredFunc, rep.apool); filteredFunc(text, ''); } - } + }; let observedChanges; - function clearObservedChanges() { + const clearObservedChanges = () => { observedChanges = { cleanNodesNearChanges: {}, }; - } + }; clearObservedChanges(); - function getCleanNodeByKey(key) { - const p = PROFILER('getCleanNodeByKey', false); + const getCleanNodeByKey = (key) => { + const p = PROFILER('getCleanNodeByKey', false); // eslint-disable-line p.extra = 0; let n = doc.getElementById(key); // copying and pasting can lead to duplicate ids @@ -1012,9 +987,9 @@ function Ace2Inner() { p.literal(p.extra, 'extra'); p.end(); return n; - } + }; - function observeChangesAroundNode(node) { + const observeChangesAroundNode = (node) => { // Around this top-level DOM node, look for changes to the document // (from how it looks in our representation) and record them in a way // that can be used to "normalize" the document (apply the changes to our @@ -1023,9 +998,10 @@ function Ace2Inner() { let hasAdjacentDirtyness; if (!isNodeDirty(node)) { cleanNode = node; - var prevSib = cleanNode.previousSibling; - var nextSib = cleanNode.nextSibling; - hasAdjacentDirtyness = ((prevSib && isNodeDirty(prevSib)) || (nextSib && isNodeDirty(nextSib))); + const prevSib = cleanNode.previousSibling; + const nextSib = cleanNode.nextSibling; + hasAdjacentDirtyness = ((prevSib && isNodeDirty(prevSib)) || + (nextSib && isNodeDirty(nextSib))); } else { // node is dirty, look for clean node above let upNode = node.previousSibling; @@ -1057,25 +1033,25 @@ function Ace2Inner() { } else { // next and prev lines are clean (if they exist) const lineKey = uniqueId(cleanNode); - var prevSib = cleanNode.previousSibling; - var nextSib = cleanNode.nextSibling; + const prevSib = cleanNode.previousSibling; + const nextSib = cleanNode.nextSibling; const actualPrevKey = ((prevSib && uniqueId(prevSib)) || null); const actualNextKey = ((nextSib && uniqueId(nextSib)) || null); const repPrevEntry = rep.lines.prev(rep.lines.atKey(lineKey)); const repNextEntry = rep.lines.next(rep.lines.atKey(lineKey)); const repPrevKey = ((repPrevEntry && repPrevEntry.key) || null); const repNextKey = ((repNextEntry && repNextEntry.key) || null); - if (actualPrevKey != repPrevKey || actualNextKey != repNextKey) { + if (actualPrevKey !== repPrevKey || actualNextKey !== repNextKey) { observedChanges.cleanNodesNearChanges[`$${uniqueId(cleanNode)}`] = true; } } - } + }; - function observeChangesAroundSelection() { + const observeChangesAroundSelection = () => { if (currentCallStack.observedSelection) return; currentCallStack.observedSelection = true; - const p = PROFILER('getSelection', false); + const p = PROFILER('getSelection', false); // eslint-disable-line const selection = getSelection(); p.end(); @@ -1083,34 +1059,34 @@ function Ace2Inner() { const node1 = topLevel(selection.startPoint.node); const node2 = topLevel(selection.endPoint.node); if (node1) observeChangesAroundNode(node1); - if (node2 && node1 != node2) { + if (node2 && node1 !== node2) { observeChangesAroundNode(node2); } } - } + }; - function observeSuspiciousNodes() { + const observeSuspiciousNodes = () => { // inspired by Firefox bug #473255, where pasting formatted text // causes the cursor to jump away, making the new HTML never found. if (root.getElementsByTagName) { const nds = root.getElementsByTagName('style'); for (let i = 0; i < nds.length; i++) { const n = topLevel(nds[i]); - if (n && n.parentNode == root) { + if (n && n.parentNode === root) { observeChangesAroundNode(n); } } } - } + }; - function incorporateUserChanges() { + const incorporateUserChanges = () => { if (currentCallStack.domClean) return false; currentCallStack.isUserChange = true; if (DEBUG && window.DONT_INCORP || window.DEBUG_DONT_INCORP) return false; - const p = PROFILER('incorp', false); + const p = PROFILER('incorp', false); // eslint-disable-line // returns true if dom changes were made if (!root.firstChild) { @@ -1125,10 +1101,13 @@ function Ace2Inner() { let dirtyRangesCheckOut = true; let j = 0; let a, b; + let scrollToTheLeftNeeded = false; + while (j < dirtyRanges.length) { a = dirtyRanges[j][0]; b = dirtyRanges[j][1]; - if (!((a === 0 || getCleanNodeByKey(rep.lines.atIndex(a - 1).key)) && (b == rep.lines.length() || getCleanNodeByKey(rep.lines.atIndex(b).key)))) { + if (!((a === 0 || getCleanNodeByKey(rep.lines.atIndex(a - 1).key)) && + (b === rep.lines.length() || getCleanNodeByKey(rep.lines.atIndex(b).key)))) { dirtyRangesCheckOut = false; break; } @@ -1136,7 +1115,7 @@ function Ace2Inner() { } if (!dirtyRangesCheckOut) { const numBodyNodes = root.childNodes.length; - for (var k = 0; k < numBodyNodes; k++) { + for (let k = 0; k < numBodyNodes; k++) { const bodyNode = root.childNodes.item(k); if ((bodyNode.tagName) && ((!bodyNode.id) || (!rep.lines.containsKey(bodyNode.id)))) { observeChangesAroundNode(bodyNode); @@ -1162,15 +1141,20 @@ function Ace2Inner() { const range = dirtyRanges[i]; a = range[0]; b = range[1]; - let firstDirtyNode = (((a === 0) && root.firstChild) || getCleanNodeByKey(rep.lines.atIndex(a - 1).key).nextSibling); + let firstDirtyNode = (((a === 0) && root.firstChild) || + getCleanNodeByKey(rep.lines.atIndex(a - 1).key).nextSibling); firstDirtyNode = (firstDirtyNode && isNodeDirty(firstDirtyNode) && firstDirtyNode); - let lastDirtyNode = (((b == rep.lines.length()) && root.lastChild) || getCleanNodeByKey(rep.lines.atIndex(b).key).previousSibling); + + let lastDirtyNode = (((b === rep.lines.length()) && root.lastChild) || + getCleanNodeByKey(rep.lines.atIndex(b).key).previousSibling); + lastDirtyNode = (lastDirtyNode && isNodeDirty(lastDirtyNode) && lastDirtyNode); if (firstDirtyNode && lastDirtyNode) { const cc = makeContentCollector(isStyled, browser, rep.apool, null, className2Author); cc.notifySelection(selection); const dirtyNodes = []; - for (let n = firstDirtyNode; n && !(n.previousSibling && n.previousSibling == lastDirtyNode); + for (let n = firstDirtyNode; n && + !(n.previousSibling && n.previousSibling === lastDirtyNode); n = n.nextSibling) { cc.collectContent(n); dirtyNodes.push(n); @@ -1196,7 +1180,6 @@ function Ace2Inner() { lines = ccData.lines; const lineAttribs = ccData.lineAttribs; const linesWrapped = ccData.linesWrapped; - var scrollToTheLeftNeeded = false; if (linesWrapped > 0) { // Chrome decides in its infinite wisdom that it's okay to put the browser's visisble @@ -1204,7 +1187,7 @@ function Ace2Inner() { // string are no longer visible to the user.. Yay chrome.. Move the browser's visible area // to the left hand side of the span. Firefox isn't quite so bad, but it's still pretty // quirky. - var scrollToTheLeftNeeded = true; + scrollToTheLeftNeeded = true; } if (ss[0] >= 0) selStart = [ss[0] + a + netNumLinesChangeSoFar, ss[1]]; @@ -1213,7 +1196,7 @@ function Ace2Inner() { const entries = []; const nodeToAddAfter = lastDirtyNode; const lineNodeInfos = new Array(lines.length); - for (var k = 0; k < lines.length; k++) { + for (let k = 0; k < lines.length; k++) { const lineString = lines[k]; const newEntry = createDomLineEntry(lineString); entries.push(newEntry); @@ -1262,7 +1245,8 @@ function Ace2Inner() { // dmesg(htmlPrettyEscape(htmlForRemovedChild(n))); }); - if (scrollToTheLeftNeeded) { // needed to stop chrome from breaking the ui when long strings without spaces are pasted + // needed to stop chrome from breaking the ui when long strings without spaces are pasted + if (scrollToTheLeftNeeded) { $('#innerdocbody').scrollLeft(0); } @@ -1279,7 +1263,8 @@ function Ace2Inner() { point: selection.startPoint, documentAttributeManager, }); - selStart = (selStartFromHook == null || selStartFromHook.length == 0) ? getLineAndCharForPoint(selection.startPoint) : selStartFromHook; + selStart = (selStartFromHook == null || selStartFromHook.length === 0) + ? getLineAndCharForPoint(selection.startPoint) : selStartFromHook; } if (selection && !selEnd) { const selEndFromHook = hooks.callAll('aceEndLineAndCharForPoint', { @@ -1290,7 +1275,9 @@ function Ace2Inner() { point: selection.endPoint, documentAttributeManager, }); - selEnd = (selEndFromHook == null || selEndFromHook.length == 0) ? getLineAndCharForPoint(selection.endPoint) : selEndFromHook; + selEnd = (selEndFromHook == null || + selEndFromHook.length === 0) + ? getLineAndCharForPoint(selection.endPoint) : selEndFromHook; } // selection from content collection can, in various ways, extend past final @@ -1329,9 +1316,9 @@ function Ace2Inner() { p.end('END'); return domChanges; - } + }; - var STYLE_ATTRIBS = { + const STYLE_ATTRIBS = { bold: true, italic: true, underline: true, @@ -1339,25 +1326,17 @@ function Ace2Inner() { list: true, }; - function isStyleAttribute(aname) { - return !!STYLE_ATTRIBS[aname]; - } + const isStyleAttribute = (aname) => !!STYLE_ATTRIBS[aname]; - function isDefaultLineAttribute(aname) { - return AttributeManager.DEFAULT_LINE_ATTRIBUTES.indexOf(aname) !== -1; - } + const isDefaultLineAttribute = (aname) => AttributeManager.DEFAULT_LINE_ATTRIBUTES.indexOf(aname) !== -1; // eslint-disable-line - function insertDomLines(nodeToAddAfter, infoStructs) { + const insertDomLines = (nodeToAddAfter, infoStructs) => { let lastEntry; let lineStartOffset; if (infoStructs.length < 1) return; - const startEntry = rep.lines.atKey(uniqueId(infoStructs[0].node)); - const endEntry = rep.lines.atKey(uniqueId(infoStructs[infoStructs.length - 1].node)); - const charStart = rep.lines.offsetOfEntry(startEntry); - const charEnd = rep.lines.offsetOfEntry(endEntry) + endEntry.width; _.each(infoStructs, (info) => { - const p2 = PROFILER('insertLine', false); + const p2 = PROFILER('insertLine', false); // eslint-disable-line const node = info.node; const key = uniqueId(node); let entry; @@ -1365,7 +1344,7 @@ function Ace2Inner() { if (lastEntry) { // optimization to avoid recalculation const next = rep.lines.next(lastEntry); - if (next && next.key == key) { + if (next && next.key === key) { entry = next; lineStartOffset += lastEntry.width; } @@ -1394,32 +1373,32 @@ function Ace2Inner() { markNodeClean(node); p2.end(); }); - } + }; - function isCaret() { - return (rep.selStart && rep.selEnd && rep.selStart[0] == rep.selEnd[0] && rep.selStart[1] == rep.selEnd[1]); - } + const isCaret = () => ( + rep.selStart && + rep.selEnd && + rep.selStart[0] === rep.selEnd[0] && + rep.selStart[1] === rep.selEnd[1] + ); editorInfo.ace_isCaret = isCaret; // prereq: isCaret() - function caretLine() { - return rep.selStart[0]; - } + const caretLine = () => rep.selStart[0]; + editorInfo.ace_caretLine = caretLine; - function caretColumn() { - return rep.selStart[1]; - } + const caretColumn = () => rep.selStart[1]; + editorInfo.ace_caretColumn = caretColumn; - function caretDocChar() { - return rep.lines.offsetOfIndex(caretLine()) + caretColumn(); - } + const caretDocChar = () => rep.lines.offsetOfIndex(caretLine()) + caretColumn(); + editorInfo.ace_caretDocChar = caretDocChar; - function handleReturnIndentation() { + const handleReturnIndentation = () => { // on return, indent to level of previous line if (isCaret() && caretColumn() === 0 && caretLine() > 0) { const lineNum = caretLine(); @@ -1428,7 +1407,7 @@ function Ace2Inner() { const prevLineText = prevLine.text; let theIndent = /^ *(?:)/.exec(prevLineText)[0]; const shouldIndent = parent.parent.clientVars.indentationOnNewLine; - if (shouldIndent && /[\[\(\:\{]\s*$/.exec(prevLineText)) { + if (shouldIndent && /[[(:{]\s*$/.exec(prevLineText)) { theIndent += THE_TAB; } const cs = Changeset.builder(rep.lines.totalWidth()).keep( @@ -1439,9 +1418,9 @@ function Ace2Inner() { performDocumentApplyChangeset(cs); performSelectionChange([lineNum, theIndent.length], [lineNum, theIndent.length]); } - } + }; - function getPointForLineAndChar(lineAndChar) { + const getPointForLineAndChar = (lineAndChar) => { const line = lineAndChar[0]; let charsLeft = lineAndChar[1]; // Do not uncomment this in production it will break iFrames. @@ -1456,14 +1435,13 @@ function Ace2Inner() { let n = lineNode; let after = false; if (charsLeft === 0) { - let index = 0; return { node: lineNode, - index, + index: 0, maxIndex: 1, }; } - while (!(n == lineNode && after)) { + while (!(n === lineNode && after)) { if (after) { if (n.nextSibling) { n = n.nextSibling; @@ -1487,17 +1465,15 @@ function Ace2Inner() { index: 1, maxIndex: 1, }; - } + }; - function nodeText(n) { - return n.textContent || n.nodeValue || ''; - } + const nodeText = (n) => n.textContent || n.nodeValue || ''; - function getLineAndCharForPoint(point) { + const getLineAndCharForPoint = (point) => { // Turn DOM node selection into [line,char] selection. // This method has to work when the DOM is not pristine, // assuming the point is not in a dirty node. - if (point.node == root) { + if (point.node === root) { if (point.index === 0) { return [0, 0]; } else { @@ -1516,7 +1492,7 @@ function Ace2Inner() { col = nodeText(n).length; } let parNode, prevSib; - while ((parNode = n.parentNode) != root) { + while ((parNode = n.parentNode) !== root) { if ((prevSib = n.previousSibling)) { n = prevSib; col += nodeText(n).length; @@ -1531,10 +1507,10 @@ function Ace2Inner() { const lineNum = rep.lines.indexOfEntry(lineEntry); return [lineNum, col]; } - } + }; editorInfo.ace_getLineAndCharForPoint = getLineAndCharForPoint; - function createDomLineEntry(lineString) { + const createDomLineEntry = (lineString) => { const info = doCreateDomLine(lineString.length > 0); const newNode = info.node; return { @@ -1544,46 +1520,10 @@ function Ace2Inner() { domInfo: info, lineMarker: 0, }; - } + }; - function canApplyChangesetToDocument(changes) { - return Changeset.oldLen(changes) == rep.alltext.length; - } - - function performDocumentApplyChangeset(changes, insertsAfterSelection) { - doRepApplyChangeset(changes, insertsAfterSelection); - - let requiredSelectionSetting = null; - if (rep.selStart && rep.selEnd) { - const selStartChar = rep.lines.offsetOfIndex(rep.selStart[0]) + rep.selStart[1]; - const selEndChar = rep.lines.offsetOfIndex(rep.selEnd[0]) + rep.selEnd[1]; - const result = Changeset.characterRangeFollow(changes, selStartChar, selEndChar, insertsAfterSelection); - requiredSelectionSetting = [result[0], result[1], rep.selFocusAtStart]; - } - - const linesMutatee = { - splice(start, numRemoved, newLinesVA) { - const args = Array.prototype.slice.call(arguments, 2); - domAndRepSplice(start, numRemoved, _.map(args, (s) => s.slice(0, -1))); - }, - get(i) { - return `${rep.lines.atIndex(i).text}\n`; - }, - length() { - return rep.lines.length(); - }, - slice_notused(start, end) { - return _.map(rep.lines.slice(start, end), (e) => `${e.text}\n`); - }, - }; - - Changeset.mutateTextLines(changes, linesMutatee); - - if (requiredSelectionSetting) { - performSelectionChange(lineAndColumnFromChar(requiredSelectionSetting[0]), lineAndColumnFromChar(requiredSelectionSetting[1]), requiredSelectionSetting[2]); - } - - function domAndRepSplice(startLine, deleteCount, newLineStrings) { + const performDocumentApplyChangeset = (changes, insertsAfterSelection) => { + const domAndRepSplice = (startLine, deleteCount, newLineStrings) => { const keysToDelete = []; if (deleteCount > 0) { let entryToDelete = rep.lines.atIndex(startLine); @@ -1609,20 +1549,61 @@ function Ace2Inner() { n.parentNode.removeChild(n); }); - if ((rep.selStart && rep.selStart[0] >= startLine && rep.selStart[0] <= startLine + deleteCount) || (rep.selEnd && rep.selEnd[0] >= startLine && rep.selEnd[0] <= startLine + deleteCount)) { + if ( + (rep.selStart && + rep.selStart[0] >= startLine && + rep.selStart[0] <= startLine + deleteCount) || + (rep.selEnd && rep.selEnd[0] >= startLine && rep.selEnd[0] <= startLine + deleteCount)) { currentCallStack.selectionAffected = true; } - } - } + }; - function doRepApplyChangeset(changes, insertsAfterSelection) { + doRepApplyChangeset(changes, insertsAfterSelection); + + let requiredSelectionSetting = null; + if (rep.selStart && rep.selEnd) { + const selStartChar = rep.lines.offsetOfIndex(rep.selStart[0]) + rep.selStart[1]; + const selEndChar = rep.lines.offsetOfIndex(rep.selEnd[0]) + rep.selEnd[1]; + const result = Changeset. + characterRangeFollow(changes, selStartChar, selEndChar, insertsAfterSelection); + requiredSelectionSetting = [result[0], result[1], rep.selFocusAtStart]; + } + + const linesMutatee = { + // TODO: Rhansen to check usage of args here. + splice: (start, numRemoved, ...args) => { + domAndRepSplice(start, numRemoved, _.map(args, (s) => s.slice(0, -1))); + }, + get: (i) => `${rep.lines.atIndex(i).text}\n`, + length: () => rep.lines.length(), + slice_notused: (start, end) => _.map(rep.lines.slice(start, end), (e) => `${e.text}\n`), + }; + + Changeset.mutateTextLines(changes, linesMutatee); + + if (requiredSelectionSetting) { + performSelectionChange( + lineAndColumnFromChar( + requiredSelectionSetting[0] + ), + lineAndColumnFromChar(requiredSelectionSetting[1]), + requiredSelectionSetting[2] + ); + } + }; + + const doRepApplyChangeset = (changes, insertsAfterSelection) => { Changeset.checkRep(changes); - if (Changeset.oldLen(changes) != rep.alltext.length) throw new Error(`doRepApplyChangeset length mismatch: ${Changeset.oldLen(changes)}/${rep.alltext.length}`); + if (Changeset.oldLen(changes) !== rep.alltext.length) { + const errMsg = `${Changeset.oldLen(changes)}/${rep.alltext.length}`; + throw new Error(`doRepApplyChangeset length mismatch: ${errMsg}`); + } - (function doRecordUndoInformation(changes) { + // (function doRecordUndoInformation(changes) { + ((changes) => { const editEvent = currentCallStack.editEvent; - if (editEvent.eventType == 'nonundoable') { + if (editEvent.eventType === 'nonundoable') { if (!editEvent.changeset) { editEvent.changeset = changes; } else { @@ -1630,12 +1611,8 @@ function Ace2Inner() { } } else { const inverseChangeset = Changeset.inverse(changes, { - get(i) { - return `${rep.lines.atIndex(i).text}\n`; - }, - length() { - return rep.lines.length(); - }, + get: (i) => `${rep.lines.atIndex(i).text}\n`, + length: () => rep.lines.length(), }, rep.alines, rep.apool); if (!editEvent.backset) { @@ -1652,28 +1629,28 @@ function Ace2Inner() { if (changesetTracker.isTracking()) { changesetTracker.composeUserChangeset(changes); } - } + }; /* Converts the position of a char (index in String) into a [row, col] tuple */ - function lineAndColumnFromChar(x) { + const lineAndColumnFromChar = (x) => { const lineEntry = rep.lines.atOffset(x); const lineStart = rep.lines.offsetOfEntry(lineEntry); const lineNum = rep.lines.indexOfEntry(lineEntry); return [lineNum, x - lineStart]; - } + }; - function performDocumentReplaceCharRange(startChar, endChar, newText) { - if (startChar == endChar && newText.length === 0) { + const performDocumentReplaceCharRange = (startChar, endChar, newText) => { + if (startChar === endChar && newText.length === 0) { return; } // Requires that the replacement preserve the property that the // internal document text ends in a newline. Given this, we // rewrite the splice so that it doesn't touch the very last // char of the document. - if (endChar == rep.alltext.length) { - if (startChar == endChar) { + if (endChar === rep.alltext.length) { + if (startChar === endChar) { // an insert at end startChar--; endChar--; @@ -1688,49 +1665,33 @@ function Ace2Inner() { newText = newText.substring(0, newText.length - 1); } } - performDocumentReplaceRange(lineAndColumnFromChar(startChar), lineAndColumnFromChar(endChar), newText); - } + performDocumentReplaceRange(lineAndColumnFromChar(startChar), + lineAndColumnFromChar(endChar), newText); + }; - function performDocumentReplaceRange(start, end, newText) { - if (start === undefined) start = rep.selStart; - if (end === undefined) end = rep.selEnd; - - // dmesg(String([start.toSource(),end.toSource(),newText.toSource()])); - // start[0]: <--- start[1] --->CCCCCCCCCCC\n - // CCCCCCCCCCCCCCCCCCCC\n - // CCCC\n - // end[0]: -------\n - const builder = Changeset.builder(rep.lines.totalWidth()); - ChangesetUtils.buildKeepToStartOfRange(rep, builder, start); - ChangesetUtils.buildRemoveRange(rep, builder, start, end); - builder.insert(newText, [ - ['author', thisAuthor], - ], rep.apool); - const cs = builder.toString(); - - performDocumentApplyChangeset(cs); - } - - function performDocumentApplyAttributesToCharRange(start, end, attribs) { + const performDocumentApplyAttributesToCharRange = (start, end, attribs) => { end = Math.min(end, rep.alltext.length - 1); - documentAttributeManager.setAttributesOnRange(lineAndColumnFromChar(start), lineAndColumnFromChar(end), attribs); - } - editorInfo.ace_performDocumentApplyAttributesToCharRange = performDocumentApplyAttributesToCharRange; + documentAttributeManager. + setAttributesOnRange(lineAndColumnFromChar(start), + lineAndColumnFromChar(end), attribs + ); + }; + editorInfo.ace_performDocumentApplyAttributesToCharRange = + performDocumentApplyAttributesToCharRange; - function setAttributeOnSelection(attributeName, attributeValue) { + const setAttributeOnSelection = (attributeName, attributeValue) => { if (!(rep.selStart && rep.selEnd)) return; documentAttributeManager.setAttributesOnRange(rep.selStart, rep.selEnd, [ [attributeName, attributeValue], ]); - } + }; editorInfo.ace_setAttributeOnSelection = setAttributeOnSelection; - - function getAttributeOnSelection(attributeName, prevChar) { + const getAttributeOnSelection = (attributeName, prevChar) => { if (!(rep.selStart && rep.selEnd)) return; - const isNotSelection = (rep.selStart[0] == rep.selEnd[0] && rep.selEnd[1] === rep.selStart[1]); + const isNotSelection = (rep.selStart[0] === rep.selEnd[0] && rep.selEnd[1] === rep.selStart[1]); if (isNotSelection) { if (prevChar) { // If it's not the start of the line @@ -1744,21 +1705,18 @@ function Ace2Inner() { [attributeName, 'true'], ], rep.apool); const withItRegex = new RegExp(`${withIt.replace(/\*/g, '\\*')}(\\*|$)`); - function hasIt(attribs) { - return withItRegex.test(attribs); - } + const hasIt = (attribs) => withItRegex.test(attribs); - return rangeHasAttrib(rep.selStart, rep.selEnd); - - function rangeHasAttrib(selStart, selEnd) { + const rangeHasAttrib = (selStart, selEnd) => { // if range is collapsed -> no attribs in range - if (selStart[1] == selEnd[1] && selStart[0] == selEnd[0]) return false; + if (selStart[1] === selEnd[1] && selStart[0] === selEnd[0]) return false; - if (selStart[0] != selEnd[0]) { // -> More than one line selected - var hasAttrib = true; + if (selStart[0] !== selEnd[0]) { // -> More than one line selected + let hasAttrib = true; // from selStart to the end of the first line - hasAttrib = hasAttrib && rangeHasAttrib(selStart, [selStart[0], rep.lines.atIndex(selStart[0]).text.length]); + hasAttrib = hasAttrib && + rangeHasAttrib(selStart, [selStart[0], rep.lines.atIndex(selStart[0]).text.length]); // for all lines in between for (let n = selStart[0] + 1; n < selEnd[0]; n++) { @@ -1776,7 +1734,7 @@ function Ace2Inner() { const lineNum = selStart[0]; const start = selStart[1]; const end = selEnd[1]; - var hasAttrib = true; + let hasAttrib = true; // Iterate over attribs on this line @@ -1790,7 +1748,8 @@ function Ace2Inner() { if (!hasIt(op.attribs)) { // does op overlap selection? if (!(opEndInLine <= start || opStartInLine >= end)) { - hasAttrib = false; // since it's overlapping but hasn't got the attrib -> range hasn't got it + // since it's overlapping but hasn't got the attrib -> range hasn't got it + hasAttrib = false; break; } } @@ -1798,12 +1757,13 @@ function Ace2Inner() { } return hasAttrib; - } - } + }; + return rangeHasAttrib(rep.selStart, rep.selEnd); + }; editorInfo.ace_getAttributeOnSelection = getAttributeOnSelection; - function toggleAttributeOnSelection(attributeName) { + const toggleAttributeOnSelection = (attributeName) => { if (!(rep.selStart && rep.selEnd)) return; let selectionAllHasIt = true; @@ -1812,9 +1772,7 @@ function Ace2Inner() { ], rep.apool); const withItRegex = new RegExp(`${withIt.replace(/\*/g, '\\*')}(\\*|$)`); - function hasIt(attribs) { - return withItRegex.test(attribs); - } + const hasIt = (attribs) => withItRegex.test(attribs); const selStartLine = rep.selStart[0]; const selEndLine = rep.selEnd[0]; @@ -1826,10 +1784,10 @@ function Ace2Inner() { selectionStartInLine = 1; // ignore "*" used as line marker } let selectionEndInLine = rep.lines.atIndex(n).text.length; // exclude newline - if (n == selStartLine) { + if (n === selStartLine) { selectionStartInLine = rep.selStart[1]; } - if (n == selEndLine) { + if (n === selEndLine) { selectionEndInLine = rep.selEnd[1]; } while (opIter.hasNext()) { @@ -1852,23 +1810,25 @@ function Ace2Inner() { const attributeValue = selectionAllHasIt ? '' : 'true'; - documentAttributeManager.setAttributesOnRange(rep.selStart, rep.selEnd, [[attributeName, attributeValue]]); + documentAttributeManager.setAttributesOnRange( + rep.selStart, + rep.selEnd, + [[attributeName, attributeValue]] + ); if (attribIsFormattingStyle(attributeName)) { updateStyleButtonState(attributeName, !selectionAllHasIt); // italic, bold, ... } - } + }; editorInfo.ace_toggleAttributeOnSelection = toggleAttributeOnSelection; - function performDocumentReplaceSelection(newText) { + const performDocumentReplaceSelection = (newText) => { if (!(rep.selStart && rep.selEnd)) return; performDocumentReplaceRange(rep.selStart, rep.selEnd, newText); - } + }; // Change the abstract representation of the document to have a different set of lines. // Must be called after rep.alltext is set. - - - function doRepLineSplice(startLine, deleteCount, newLineEntries) { + const doRepLineSplice = (startLine, deleteCount, newLineEntries) => { _.each(newLineEntries, (entry) => { entry.width = entry.text.length + 1; }); @@ -1876,23 +1836,16 @@ function Ace2Inner() { const startOldChar = rep.lines.offsetOfIndex(startLine); const endOldChar = rep.lines.offsetOfIndex(startLine + deleteCount); - const oldRegionStart = rep.lines.offsetOfIndex(startLine); - const oldRegionEnd = rep.lines.offsetOfIndex(startLine + deleteCount); rep.lines.splice(startLine, deleteCount, newLineEntries); currentCallStack.docTextChanged = true; currentCallStack.repChanged = true; - const newRegionEnd = rep.lines.offsetOfIndex(startLine + newLineEntries.length); - const newText = _.map(newLineEntries, (e) => `${e.text}\n`).join(''); - rep.alltext = rep.alltext.substring(0, startOldChar) + newText + rep.alltext.substring(endOldChar, rep.alltext.length); + rep.alltext = rep.alltext.substring(0, startOldChar) + + newText + rep.alltext.substring(endOldChar, rep.alltext.length); + }; - // var newTotalLength = rep.alltext.length; - // rep.lexer.updateBuffer(rep.alltext, oldRegionStart, oldRegionEnd - oldRegionStart, - // newRegionEnd - oldRegionStart); - } - - function doIncorpLineSplice(startLine, deleteCount, newLineEntries, lineAttribs, hints) { + const doIncorpLineSplice = (startLine, deleteCount, newLineEntries, lineAttribs, hints) => { const startOldChar = rep.lines.offsetOfIndex(startLine); const endOldChar = rep.lines.offsetOfIndex(startLine + deleteCount); @@ -1900,7 +1853,8 @@ function Ace2Inner() { let selStartHintChar, selEndHintChar; if (hints && hints.selStart) { - selStartHintChar = rep.lines.offsetOfIndex(hints.selStart[0]) + hints.selStart[1] - oldRegionStart; + selStartHintChar = + rep.lines.offsetOfIndex(hints.selStart[0]) + hints.selStart[1] - oldRegionStart; } if (hints && hints.selEnd) { selEndHintChar = rep.lines.offsetOfIndex(hints.selEnd[0]) + hints.selEnd[1] - oldRegionStart; @@ -1910,7 +1864,9 @@ function Ace2Inner() { const oldText = rep.alltext.substring(startOldChar, endOldChar); const oldAttribs = rep.alines.slice(startLine, startLine + deleteCount).join(''); const newAttribs = `${lineAttribs.join('|1+1')}|1+1`; // not valid in a changeset - const analysis = analyzeChange(oldText, newText, oldAttribs, newAttribs, selStartHintChar, selEndHintChar); + const analysis = analyzeChange( + oldText, newText, oldAttribs, newAttribs, selStartHintChar, selEndHintChar + ); const commonStart = analysis[0]; let commonEnd = analysis[1]; let shortOldText = oldText.substring(commonStart, oldText.length - commonEnd); @@ -1921,7 +1877,8 @@ function Ace2Inner() { // adjust the splice to not involve the final newline of the document; // be very defensive - if (shortOldText.charAt(shortOldText.length - 1) == '\n' && shortNewText.charAt(shortNewText.length - 1) == '\n') { + if (shortOldText.charAt(shortOldText.length - 1) === '\n' && + shortNewText.charAt(shortNewText.length - 1) === '\n') { // replacing text that ends in newline with text that also ends in newline // (still, after analysis, somehow) shortOldText = shortOldText.slice(0, -1); @@ -1929,16 +1886,20 @@ function Ace2Inner() { spliceEnd--; commonEnd++; } - if (shortOldText.length === 0 && spliceStart == rep.alltext.length && shortNewText.length > 0) { + if (shortOldText.length === 0 && + spliceStart === rep.alltext.length && + shortNewText.length > 0) { // inserting after final newline, bad spliceStart--; spliceEnd--; shortNewText = `\n${shortNewText.slice(0, -1)}`; shiftFinalNewlineToBeforeNewText = true; } - if (spliceEnd == rep.alltext.length && shortOldText.length > 0 && shortNewText.length === 0) { + if (spliceEnd === rep.alltext.length && + shortOldText.length > 0 && + shortNewText.length === 0) { // deletion at end of rep.alltext - if (rep.alltext.charAt(spliceStart - 1) == '\n') { + if (rep.alltext.charAt(spliceStart - 1) === '\n') { // (if not then what the heck? it will definitely lead // to a rep.alltext without a final newline) spliceStart--; @@ -1953,14 +1914,14 @@ function Ace2Inner() { const spliceStartLine = rep.lines.indexOfOffset(spliceStart); const spliceStartLineStart = rep.lines.offsetOfIndex(spliceStartLine); - const startBuilder = function () { + const startBuilder = () => { const builder = Changeset.builder(oldLen); builder.keep(spliceStartLineStart, spliceStartLine); builder.keep(spliceStart - spliceStartLineStart); return builder; }; - const eachAttribRun = function (attribs, func /* (startInNewText, endInNewText, attribs)*/) { + const eachAttribRun = (attribs, func /* (startInNewText, endInNewText, attribs)*/) => { const attribsIter = Changeset.opIterator(attribs); let textIndex = 0; const newTextStart = commonStart; @@ -1975,7 +1936,7 @@ function Ace2Inner() { } }; - const justApplyStyles = (shortNewText == shortOldText); + const justApplyStyles = (shortNewText === shortOldText); let theChangeset; if (justApplyStyles) { @@ -1983,13 +1944,16 @@ function Ace2Inner() { // the existing text. we compose this with the // changeset the applies the styles found in the DOM. // This allows us to incorporate, e.g., Safari's native "unbold". - const incorpedAttribClearer = cachedStrFunc((oldAtts) => Changeset.mapAttribNumbers(oldAtts, (n) => { - const k = rep.apool.getAttribKey(n); - if (isStyleAttribute(k)) { - return rep.apool.putAttrib([k, '']); - } - return false; - })); + const incorpedAttribClearer = cachedStrFunc( + (oldAtts) => Changeset.mapAttribNumbers(oldAtts, (n) => { + const k = rep.apool.getAttribKey(n); + if (isStyleAttribute(k)) { + return rep.apool.putAttrib([k, '']); + } + return false; + } + ) + ); const builder1 = startBuilder(); if (shiftFinalNewlineToBeforeNewText) { @@ -2039,7 +2003,7 @@ function Ace2Inner() { let foundDomAuthor = ''; eachAttribRun(newAttribs, (start, end, attribs) => { const a = Changeset.attribsAttributeValue(attribs, 'author', rep.apool); - if (a && a != foundDomAuthor) { + if (a && a !== foundDomAuthor) { if (!foundDomAuthor) { foundDomAuthor = a; } else { @@ -2065,27 +2029,26 @@ function Ace2Inner() { // do this no matter what, because we need to get the right // line keys into the rep. doRepLineSplice(startLine, deleteCount, newLineEntries); - } + }; - function cachedStrFunc(func) { + const cachedStrFunc = (func) => { const cache = {}; - return function (s) { + return (s) => { if (!cache[s]) { cache[s] = func(s); } return cache[s]; }; - } + }; - function analyzeChange(oldText, newText, oldAttribs, newAttribs, optSelStartHint, optSelEndHint) { + const analyzeChange = ( + oldText, newText, oldAttribs, newAttribs, optSelStartHint, optSelEndHint) => { // we need to take into account both the styles attributes & attributes defined by // the plugins, so basically we can ignore only the default line attribs used by // Etherpad - function incorpedAttribFilter(anum) { - return !isDefaultLineAttribute(rep.apool.getAttribKey(anum)); - } + const incorpedAttribFilter = (anum) => !isDefaultLineAttribute(rep.apool.getAttribKey(anum)); - function attribRuns(attribs) { + const attribRuns = (attribs) => { const lengs = []; const atts = []; const iter = Changeset.opIterator(attribs); @@ -2095,14 +2058,14 @@ function Ace2Inner() { atts.push(op.attribs); } return [lengs, atts]; - } + }; - function attribIterator(runs, backward) { + const attribIterator = (runs, backward) => { const lengs = runs[0]; const atts = runs[1]; let i = (backward ? lengs.length - 1 : 0); let j = 0; - return function next() { + const next = () => { while (j >= lengs[i]) { if (backward) i--; else i++; @@ -2112,7 +2075,8 @@ function Ace2Inner() { j++; return a; }; - } + return next; + }; const oldLen = oldText.length; const newLen = newText.length; @@ -2125,7 +2089,8 @@ function Ace2Inner() { const oldStartIter = attribIterator(oldARuns, false); const newStartIter = attribIterator(newARuns, false); while (commonStart < minLen) { - if (oldText.charAt(commonStart) == newText.charAt(commonStart) && oldStartIter() == newStartIter()) { + if (oldText.charAt(commonStart) === newText.charAt(commonStart) && + oldStartIter() === newStartIter()) { commonStart++; } else { break; } } @@ -2139,7 +2104,9 @@ function Ace2Inner() { oldEndIter(); newEndIter(); commonEnd++; - } else if (oldText.charAt(oldLen - 1 - commonEnd) == newText.charAt(newLen - 1 - commonEnd) && oldEndIter() == newEndIter()) { + } else if ( + oldText.charAt(oldLen - 1 - commonEnd) === newText.charAt(newLen - 1 - commonEnd) && + oldEndIter() === newEndIter()) { commonEnd++; } else { break; } } @@ -2152,8 +2119,8 @@ function Ace2Inner() { if (commonStart + commonEnd > oldLen) { // ambiguous insertion - var minCommonEnd = oldLen - commonStart; - var maxCommonEnd = commonEnd; + const minCommonEnd = oldLen - commonStart; + const maxCommonEnd = commonEnd; if (hintedCommonEnd >= minCommonEnd && hintedCommonEnd <= maxCommonEnd) { commonEnd = hintedCommonEnd; } else { @@ -2163,8 +2130,8 @@ function Ace2Inner() { } if (commonStart + commonEnd > newLen) { // ambiguous deletion - var minCommonEnd = newLen - commonStart; - var maxCommonEnd = commonEnd; + const minCommonEnd = newLen - commonStart; + const maxCommonEnd = commonEnd; if (hintedCommonEnd >= minCommonEnd && hintedCommonEnd <= maxCommonEnd) { commonEnd = hintedCommonEnd; } else { @@ -2174,31 +2141,36 @@ function Ace2Inner() { } return [commonStart, commonEnd]; - } + }; - function equalLineAndChars(a, b) { + const equalLineAndChars = (a, b) => { if (!a) return !b; if (!b) return !a; - return (a[0] == b[0] && a[1] == b[1]); - } + return (a[0] === b[0] && a[1] === b[1]); + }; - function performSelectionChange(selectStart, selectEnd, focusAtStart) { + const performSelectionChange = (selectStart, selectEnd, focusAtStart) => { if (repSelectionChange(selectStart, selectEnd, focusAtStart)) { currentCallStack.selectionAffected = true; } - } + }; editorInfo.ace_performSelectionChange = performSelectionChange; // Change the abstract representation of the document to have a different selection. // Should not rely on the line representation. Should not affect the DOM. - function repSelectionChange(selectStart, selectEnd, focusAtStart) { + const repSelectionChange = (selectStart, selectEnd, focusAtStart) => { focusAtStart = !!focusAtStart; - const newSelFocusAtStart = (focusAtStart && ((!selectStart) || (!selectEnd) || (selectStart[0] != selectEnd[0]) || (selectStart[1] != selectEnd[1]))); + const newSelFocusAtStart = (focusAtStart && ((!selectStart) || + (!selectEnd) || + (selectStart[0] !== selectEnd[0]) || + (selectStart[1] !== selectEnd[1]))); - if ((!equalLineAndChars(rep.selStart, selectStart)) || (!equalLineAndChars(rep.selEnd, selectEnd)) || (rep.selFocusAtStart != newSelFocusAtStart)) { + if ((!equalLineAndChars(rep.selStart, selectStart)) || + (!equalLineAndChars(rep.selEnd, selectEnd)) || + (rep.selFocusAtStart !== newSelFocusAtStart)) { rep.selStart = selectStart; rep.selEnd = selectEnd; rep.selFocusAtStart = newSelFocusAtStart; @@ -2217,9 +2189,12 @@ function Ace2Inner() { // when this settings is enabled const docTextChanged = currentCallStack.docTextChanged; if (!docTextChanged) { - const isScrollableEvent = !isPadLoading(currentCallStack.type) && isScrollableEditEvent(currentCallStack.type); + const isScrollableEvent = !isPadLoading(currentCallStack.type) && + isScrollableEditEvent(currentCallStack.type); const innerHeight = getInnerHeight(); - scroll.scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary(rep, isScrollableEvent, innerHeight); + scroll.scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary( + rep, isScrollableEvent, innerHeight + ); } return true; @@ -2230,35 +2205,35 @@ function Ace2Inner() { return false; // Do not uncomment this in production it will break iFrames. // top.console.log("%o %o %s", rep.selStart, rep.selEnd, rep.selFocusAtStart); - } + }; - function isPadLoading(eventType) { - return (eventType === 'setup') || (eventType === 'setBaseText') || (eventType === 'importText'); - } + const isPadLoading = (eventType) => ( + eventType === 'setup') || + (eventType === 'setBaseText') || + (eventType === 'importText' + ); - function updateStyleButtonState(attribName, hasStyleOnRepSelection) { + const updateStyleButtonState = (attribName, hasStyleOnRepSelection) => { const $formattingButton = parent.parent.$(`[data-key="${attribName}"]`).find('a'); $formattingButton.toggleClass(SELECT_BUTTON_CLASS, hasStyleOnRepSelection); - } + }; - function attribIsFormattingStyle(attributeName) { - return _.contains(FORMATTING_STYLES, attributeName); - } + const attribIsFormattingStyle = (attributeName) => _.contains(FORMATTING_STYLES, attributeName); - function selectFormattingButtonIfLineHasStyleApplied(rep) { + const selectFormattingButtonIfLineHasStyleApplied = (rep) => { _.each(FORMATTING_STYLES, (style) => { - const hasStyleOnRepSelection = documentAttributeManager.hasAttributeOnSelectionOrCaretPosition(style); + const hasStyleOnRepSelection = documentAttributeManager. + hasAttributeOnSelectionOrCaretPosition(style); updateStyleButtonState(style, hasStyleOnRepSelection); }); - } + }; - function doCreateDomLine(nonEmpty) { - return domline.createDomLine(nonEmpty, doesWrap, browser, doc); - } + const doCreateDomLine = (nonEmpty) => domline.createDomLine(nonEmpty, doesWrap, browser, doc); - function textify(str) { - return str.replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' '); - } + const textify = (str) => str. + replace(/[\n\r ]/g, ' '). + replace(/\xa0/g, ' '). + replace(/\t/g, ' '); const _blockElems = { div: 1, @@ -2273,18 +2248,17 @@ function Ace2Inner() { _blockElems[element] = 1; }); - function isBlockElement(n) { - return !!_blockElems[(n.tagName || '').toLowerCase()]; - } + const isBlockElement = (n) => !!_blockElems[(n.tagName || '').toLowerCase()]; + editorInfo.ace_isBlockElement = isBlockElement; - function getDirtyRanges() { + const getDirtyRanges = () => { // based on observedChanges, return a list of ranges of original lines // that need to be removed or replaced with new user content to incorporate // the user's changes into the line representation. ranges may be zero-length, // indicating inserted content. for example, [0,0] means content was inserted // at the top of the document, while [3,4] means line 3 was deleted, modified, // or replaced with one or more new lines of content. ranges do not touch. - const p = PROFILER('getDirtyRanges', false); + const p = PROFILER('getDirtyRanges', false); // eslint-disable-line p.forIndices = 0; p.consecutives = 0; p.corrections = 0; @@ -2293,7 +2267,7 @@ function Ace2Inner() { const N = rep.lines.length(); // old number of lines - function cleanNodeForIndex(i) { + const cleanNodeForIndex = (i) => { // if line (i) in the un-updated line representation maps to a clean node // in the document, return that node. // if (i) is out of bounds, return true. else return false. @@ -2309,13 +2283,13 @@ function Ace2Inner() { cleanNodeForIndexCache[i] = result; } return cleanNodeForIndexCache[i]; - } + }; const isConsecutiveCache = {}; - function isConsecutive(i) { + const isConsecutive = (i) => { if (isConsecutiveCache[i] === undefined) { p.consecutives++; - isConsecutiveCache[i] = (function () { + isConsecutiveCache[i] = (() => { // returns whether line (i) and line (i-1), assumed to be map to clean DOM nodes, // or document boundaries, are consecutive in the changed DOM const a = cleanNodeForIndex(i - 1); @@ -2325,17 +2299,16 @@ function Ace2Inner() { if ((a === true) && b.previousSibling) return false; if ((b === true) && a.nextSibling) return false; if ((a === true) || (b === true)) return true; - return a.nextSibling == b; + return a.nextSibling === b; })(); } return isConsecutiveCache[i]; - } + }; + + // returns whether line (i) in the un-updated representation maps to a clean node, + // or is outside the bounds of the document + const isClean = (i) => !!cleanNodeForIndex(i); - function isClean(i) { - // returns whether line (i) in the un-updated representation maps to a clean node, - // or is outside the bounds of the document - return !!cleanNodeForIndex(i); - } // list of pairs, each representing a range of lines that is clean and consecutive // in the changed DOM. lines (-1) and (N) are always clean, but may or may not // be consecutive with lines in the document. pairs are in sorted order. @@ -2343,7 +2316,7 @@ function Ace2Inner() { [-1, N + 1], ]; - function rangeForLine(i) { + const rangeForLine = (i) => { // returns index of cleanRange containing i, or -1 if none let answer = -1; _.each(cleanRanges, (r, idx) => { @@ -2353,28 +2326,29 @@ function Ace2Inner() { return true; // found, stop looking }); return answer; - } + }; - function removeLineFromRange(rng, line) { + const removeLineFromRange = (rng, line) => { // rng is index into cleanRanges, line is line number // precond: line is in rng const a = cleanRanges[rng][0]; const b = cleanRanges[rng][1]; - if ((a + 1) == b) cleanRanges.splice(rng, 1); - else if (line == a) cleanRanges[rng][0]++; - else if (line == (b - 1)) cleanRanges[rng][1]--; + if ((a + 1) === b) cleanRanges.splice(rng, 1); + else if (line === a) cleanRanges[rng][0]++; + else if (line === (b - 1)) cleanRanges[rng][1]--; else cleanRanges.splice(rng, 1, [a, line], [line + 1, b]); - } + }; - function splitRange(rng, pt) { + const splitRange = (rng, pt) => { // precond: pt splits cleanRanges[rng] into two non-empty ranges const a = cleanRanges[rng][0]; const b = cleanRanges[rng][1]; cleanRanges.splice(rng, 1, [a, pt], [pt, b]); - } + }; + const correctedLines = {}; - function correctlyAssignLine(line) { + const correctlyAssignLine = (line) => { if (correctedLines[line]) return true; p.corrections++; correctedLines[line] = true; @@ -2413,9 +2387,9 @@ function Ace2Inner() { } return !didSomething; } - } + }; - function detectChangesAroundLine(line, reqInARow) { + const detectChangesAroundLine = (line, reqInARow) => { // make sure cleanRanges is correct about line number "line" and the surrounding // lines; only stops checking at end of document or after no changes need // making for several consecutive lines. note that iteration is over old lines, @@ -2437,7 +2411,7 @@ function Ace2Inner() { } else { correctInARow = 0; } currentIndex++; } - } + }; if (N === 0) { p.cancel(); @@ -2451,10 +2425,12 @@ function Ace2Inner() { p.mark('obs'); for (const k in observedChanges.cleanNodesNearChanges) { - const key = k.substring(1); - if (rep.lines.containsKey(key)) { - const line = rep.lines.indexOfKey(key); - detectChangesAroundLine(line, 2); + if (observedChanges.cleanNodesNearChanges[k]) { + const key = k.substring(1); + if (rep.lines.containsKey(key)) { + const line = rep.lines.indexOfKey(key); + detectChangesAroundLine(line, 2); + } } } p.mark('stats&calc'); @@ -2471,74 +2447,36 @@ function Ace2Inner() { p.end(); return dirtyRanges; - } + }; - function markNodeClean(n) { + const markNodeClean = (n) => { // clean nodes have knownHTML that matches their innerHTML const dirtiness = {}; dirtiness.nodeId = uniqueId(n); dirtiness.knownHTML = n.innerHTML; setAssoc(n, 'dirtiness', dirtiness); - } + }; - function isNodeDirty(n) { - const p = PROFILER('cleanCheck', false); - if (n.parentNode != root) return true; + const isNodeDirty = (n) => { + const p = PROFILER('cleanCheck', false); // eslint-disable-line + if (n.parentNode !== root) return true; const data = getAssoc(n, 'dirtiness'); if (!data) return true; if (n.id !== data.nodeId) return true; if (n.innerHTML !== data.knownHTML) return true; p.end(); return false; - } + }; - function getViewPortTopBottom() { - const theTop = scroll.getScrollY(); - const doc = outerWin.document; - const height = doc.documentElement.clientHeight; // includes padding - - // we have to get the exactly height of the viewport. So it has to subtract all the values which changes - // the viewport height (E.g. padding, position top) - const viewportExtraSpacesAndPosition = getEditorPositionTop() + getPaddingTopAddedWhenPageViewIsEnable(); - return { - top: theTop, - bottom: (theTop + height - viewportExtraSpacesAndPosition), - }; - } - - - function getEditorPositionTop() { - const editor = parent.document.getElementsByTagName('iframe'); - const editorPositionTop = editor[0].offsetTop; - return editorPositionTop; - } - - // ep_page_view adds padding-top, which makes the viewport smaller - function getPaddingTopAddedWhenPageViewIsEnable() { - const rootDocument = parent.parent.document; - const aceOuter = rootDocument.getElementsByName('ace_outer'); - const aceOuterPaddingTop = parseInt($(aceOuter).css('padding-top')); - return aceOuterPaddingTop; - } - - function handleCut(evt) { - inCallStackIfNecessary('handleCut', () => { - doDeleteKey(evt); - }); - return true; - } - - function handleClick(evt) { + const handleClick = (evt) => { inCallStackIfNecessary('handleClick', () => { idleWorkTimer.atMost(200); }); - function isLink(n) { - return (n.tagName || '').toLowerCase() == 'a' && n.href; - } + const isLink = (n) => (n.tagName || '').toLowerCase() === 'a' && n.href; // only want to catch left-click - if ((!evt.ctrlKey) && (evt.button != 2) && (evt.button != 3)) { + if ((!evt.ctrlKey) && (evt.button !== 2) && (evt.button !== 3)) { // find A tag with HREF let n = evt.target; while (n && n.parentNode && !isLink(n)) { @@ -2555,15 +2493,93 @@ function Ace2Inner() { } hideEditBarDropdowns(); - } + }; - function hideEditBarDropdowns() { + const hideEditBarDropdowns = () => { if (window.parent.parent.padeditbar) { // required in case its in an iframe should probably use parent.. See Issue 327 https://github.com/ether/etherpad-lite/issues/327 window.parent.parent.padeditbar.toggleDropDown('none'); } - } + }; - function doReturnKey() { + const renumberList = (lineNum) => { + // 1-check we are in a list + let type = getLineListType(lineNum); + if (!type) { + return null; + } + type = /([a-z]+)[0-9]+/.exec(type); + if (type[1] === 'indent') { + return null; + } + + // 2-find the first line of the list + while (lineNum - 1 >= 0 && (type = getLineListType(lineNum - 1))) { + type = /([a-z]+)[0-9]+/.exec(type); + if (type[1] === 'indent') break; + lineNum--; + } + + // 3-renumber every list item of the same level from the beginning, level 1 + // IMPORTANT: never skip a level because there imbrication may be arbitrary + const builder = Changeset.builder(rep.lines.totalWidth()); + let loc = [0, 0]; + const applyNumberList = (line, level) => { + // init + let position = 1; + let curLevel = level; + let listType; + // loop over the lines + while ((listType = getLineListType(line))) { + // apply new num + listType = /([a-z]+)([0-9]+)/.exec(listType); + curLevel = Number(listType[2]); + if (isNaN(curLevel) || listType[0] === 'indent') { + return line; + } else if (curLevel === level) { + ChangesetUtils.buildKeepRange(rep, builder, loc, (loc = [line, 0])); + ChangesetUtils.buildKeepRange(rep, builder, loc, (loc = [line, 1]), [ + ['start', position], + ], rep.apool); + + position++; + line++; + } else if (curLevel < level) { + return line;// back to parent + } else { + line = applyNumberList(line, level + 1);// recursive call + } + } + return line; + }; + + applyNumberList(lineNum, 1); + const cs = builder.toString(); + if (!Changeset.isIdentity(cs)) { + performDocumentApplyChangeset(cs); + } + + // 4-apply the modifications + }; + editorInfo.ace_renumberList = renumberList; + + const setLineListType = (lineNum, listType) => { + if (listType === '') { + documentAttributeManager.removeAttributeOnLine(lineNum, listAttributeName); + documentAttributeManager.removeAttributeOnLine(lineNum, 'start'); + } else { + documentAttributeManager.setAttributeOnLine(lineNum, listAttributeName, listType); + } + + // if the list has been removed, it is necessary to renumber + // starting from the *next* line because the list may have been + // separated. If it returns null, it means that the list was not cut, try + // from the current one. + if (renumberList(lineNum + 1) == null) { + renumberList(lineNum); + } + }; + + const doReturnKey = () => { if (!(rep.selStart && rep.selEnd)) { return; } @@ -2594,19 +2610,21 @@ function Ace2Inner() { performDocumentReplaceSelection('\n'); handleReturnIndentation(); } - } + }; + editorInfo.ace_doReturnKey = doReturnKey; - function doIndentOutdent(isOut) { + const doIndentOutdent = (isOut) => { if (!((rep.selStart && rep.selEnd) || - ((rep.selStart[0] == rep.selEnd[0]) && (rep.selStart[1] == rep.selEnd[1]) && rep.selEnd[1] > 1)) && - (isOut != true) + ((rep.selStart[0] === rep.selEnd[0]) && + (rep.selStart[1] === rep.selEnd[1]) && + rep.selEnd[1] > 1)) && + (isOut !== true) ) { return false; } - let firstLine, lastLine; - firstLine = rep.selStart[0]; - lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] === 0) ? 1 : 0)); + const firstLine = rep.selStart[0]; + const lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] === 0) ? 1 : 0)); const mods = []; for (let n = firstLine; n <= lastLine; n++) { let listType = getLineListType(n); @@ -2620,7 +2638,7 @@ function Ace2Inner() { } } const newLevel = Math.max(0, Math.min(MAX_LIST_LEVEL, level + (isOut ? -1 : 1))); - if (level != newLevel) { + if (level !== newLevel) { mods.push([n, (newLevel > 0) ? t + newLevel : '']); } } @@ -2629,23 +2647,23 @@ function Ace2Inner() { setLineListType(mod[0], mod[1]); }); return true; - } + }; editorInfo.ace_doIndentOutdent = doIndentOutdent; - function doTabKey(shiftDown) { + const doTabKey = (shiftDown) => { if (!doIndentOutdent(shiftDown)) { performDocumentReplaceSelection(THE_TAB); } - } + }; - function doDeleteKey(optEvt) { + const doDeleteKey = (optEvt) => { const evt = optEvt || {}; let handled = false; if (rep.selStart) { if (isCaret()) { const lineNum = caretLine(); const col = caretColumn(); - var lineEntry = rep.lines.atIndex(lineNum); + const lineEntry = rep.lines.atIndex(lineNum); const lineText = lineEntry.text; const lineMarker = lineEntry.lineMarker; if (/^ +$/.exec(lineText.substring(lineMarker, col))) { @@ -2660,14 +2678,14 @@ function Ace2Inner() { if (!handled) { if (isCaret()) { const theLine = caretLine(); - var lineEntry = rep.lines.atIndex(theLine); + const lineEntry = rep.lines.atIndex(theLine); if (caretColumn() <= lineEntry.lineMarker) { // delete at beginning of line - const action = 'delete_newline'; const prevLineListType = (theLine > 0 ? getLineListType(theLine - 1) : ''); const thisLineListType = getLineListType(theLine); const prevLineEntry = (theLine > 0 && rep.lines.atIndex(theLine - 1)); - const prevLineBlank = (prevLineEntry && prevLineEntry.text.length == prevLineEntry.lineMarker); + const prevLineBlank = (prevLineEntry && + prevLineEntry.text.length === prevLineEntry.lineMarker); const thisLineHasMarker = documentAttributeManager.lineHasMarker(theLine); @@ -2675,17 +2693,26 @@ function Ace2Inner() { // this line is a list if (prevLineBlank && !prevLineListType) { // previous line is blank, remove it - performDocumentReplaceRange([theLine - 1, prevLineEntry.text.length], [theLine, 0], ''); + performDocumentReplaceRange( + [theLine - 1, prevLineEntry.text.length], + [theLine, 0], '' + ); } else { // delistify performDocumentReplaceRange([theLine, 0], [theLine, lineEntry.lineMarker], ''); } } else if (thisLineHasMarker && prevLineEntry) { // If the line has any attributes assigned, remove them by removing the marker '*' - performDocumentReplaceRange([theLine - 1, prevLineEntry.text.length], [theLine, lineEntry.lineMarker], ''); + performDocumentReplaceRange( + [theLine - 1, prevLineEntry.text.length], + [theLine, lineEntry.lineMarker], '' + ); } else if (theLine > 0) { // remove newline - performDocumentReplaceRange([theLine - 1, prevLineEntry.text.length], [theLine, 0], ''); + performDocumentReplaceRange( + [theLine - 1, prevLineEntry.text.length], + [theLine, 0], '' + ); } } else { const docChar = caretDocChar(); @@ -2695,7 +2722,8 @@ function Ace2Inner() { // always delete one char, delete further even if that first char // isn't actually a word char. let deleteBackTo = docChar - 1; - while (deleteBackTo > lineEntry.lineMarker && isWordChar(rep.alltext.charAt(deleteBackTo - 1))) { + while (deleteBackTo > lineEntry.lineMarker && + isWordChar(rep.alltext.charAt(deleteBackTo - 1))) { deleteBackTo--; } performDocumentReplaceCharRange(deleteBackTo, docChar, ''); @@ -2715,52 +2743,15 @@ function Ace2Inner() { // separated. If it returns null, it means that the list was not cut, try // from the current one. const line = caretLine(); - if (line != -1 && renumberList(line + 1) === null) { + if (line !== -1 && renumberList(line + 1) == null) { renumberList(line); } - } - - const REGEX_SPACE = /\s/; + }; const isWordChar = (c) => padutils.wordCharRegex.test(c); editorInfo.ace_isWordChar = isWordChar; - function isSpaceChar(c) { - return !!REGEX_SPACE.exec(c); - } - - function moveByWordInLine(lineText, initialIndex, forwardNotBack) { - let i = initialIndex; - - function nextChar() { - if (forwardNotBack) return lineText.charAt(i); - else return lineText.charAt(i - 1); - } - - function advance() { - if (forwardNotBack) i++; - else i--; - } - - function isDone() { - if (forwardNotBack) return i >= lineText.length; - else return i <= 0; - } - - // On Mac and Linux, move right moves to end of word and move left moves to start; - // on Windows, always move to start of word. - // On Windows, Firefox and IE disagree on whether to stop for punctuation (FF says no). - while ((!isDone()) && !isWordChar(nextChar())) { - advance(); - } - while ((!isDone()) && isWordChar(nextChar())) { - advance(); - } - - return i; - } - - function handleKeyEvent(evt) { + const handleKeyEvent = (evt) => { if (!isEditable) return; const type = evt.type; const charCode = evt.charCode; @@ -2769,36 +2760,40 @@ function Ace2Inner() { const altKey = evt.altKey; const shiftKey = evt.shiftKey; - // Is caret potentially hidden by the chat button? - const myselection = document.getSelection(); // get the current caret selection - const caretOffsetTop = myselection.focusNode.parentNode.offsetTop | myselection.focusNode.offsetTop; // get the carets selection offset in px IE 214 - - if (myselection.focusNode.wholeText) { // Is there any content? If not lineHeight will report wrong.. - var lineHeight = myselection.focusNode.parentNode.offsetHeight; // line height of populated links - } else { - var lineHeight = myselection.focusNode.offsetHeight; // line height of blank lines - } - // dmesg("keyevent type: "+type+", which: "+which); // Don't take action based on modifier keys going up and down. // Modifier keys do not generate "keypress" events. // 224 is the command-key under Mac Firefox. // 91 is the Windows key in IE; it is ASCII for open-bracket but isn't the keycode for that key // 20 is capslock in IE. - const isModKey = ((!charCode) && ((type == 'keyup') || (type == 'keydown')) && (keyCode == 16 || keyCode == 17 || keyCode == 18 || keyCode == 20 || keyCode == 224 || keyCode == 91)); + const isModKey = ((!charCode) && + ((type === 'keyup') || (type === 'keydown')) && + ( + keyCode === 16 || keyCode === 17 || keyCode === 18 || + keyCode === 20 || keyCode === 224 || keyCode === 91 + )); if (isModKey) return; - // If the key is a keypress and the browser is opera and the key is enter, do nothign at all as this fires twice. - if (keyCode == 13 && browser.opera && (type == 'keypress')) { - return; // This stops double enters in Opera but double Tabs still show on single tab keypress, adding keyCode == 9 to this doesn't help as the event is fired twice + // If the key is a keypress and the browser is opera and the key is enter, + // do nothign at all as this fires twice. + if (keyCode === 13 && browser.opera && (type === 'keypress')) { + // This stops double enters in Opera but double Tabs still show on single + // tab keypress, adding keyCode == 9 to this doesn't help as the event is fired twice + return; } let specialHandled = false; - const isTypeForSpecialKey = ((browser.safari || browser.chrome || browser.firefox) ? (type == 'keydown') : (type == 'keypress')); - const isTypeForCmdKey = ((browser.safari || browser.chrome || browser.firefox) ? (type == 'keydown') : (type == 'keypress')); + + const isTypeForSpecialKey = ((browser.safari || + browser.chrome || + browser.firefox) ? (type === 'keydown') : (type === 'keypress')); + const isTypeForCmdKey = ((browser.safari || + browser.chrome || + browser.firefox) ? (type === 'keydown') : (type === 'keypress')); + let stopped = false; inCallStackIfNecessary('handleKeyEvent', function () { - if (type == 'keypress' || (isTypeForSpecialKey && keyCode == 13 /* return*/)) { + if (type === 'keypress' || (isTypeForSpecialKey && keyCode === 13 /* return*/)) { // in IE, special keys don't send keypress, the keydown does the action if (!outsideKeyPress(evt)) { evt.preventDefault(); @@ -2808,7 +2803,7 @@ function Ace2Inner() { // If it's a dead key we don't want to do any Etherpad behavior. stopped = true; return true; - } else if (type == 'keydown') { + } else if (type === 'keydown') { outsideKeyDown(evt); } if (!stopped) { @@ -2826,24 +2821,45 @@ function Ace2Inner() { } const padShortcutEnabled = parent.parent.clientVars.padShortcutEnabled; - if ((!specialHandled) && altKey && isTypeForSpecialKey && keyCode == 120 && padShortcutEnabled.altF9) { + if ( + (!specialHandled) && + altKey && + isTypeForSpecialKey && + keyCode === 120 && + padShortcutEnabled.altF9 + ) { // Alt F9 focuses on the File Menu and/or editbar. // Note that while most editors use Alt F10 this is not desirable // As ubuntu cannot use Alt F10.... - // Focus on the editbar. -- TODO: Move Focus back to previous state (we know it so we can use it) - const firstEditbarElement = parent.parent.$('#editbar').children('ul').first().children().first().children().first().children().first(); + // Focus on the editbar. + // -- TODO: Move Focus back to previous state (we know it so we can use it) + const firstEditbarElement = parent.parent.$('#editbar') + .children('ul').first().children().first() + .children().first().children().first(); $(this).blur(); firstEditbarElement.focus(); evt.preventDefault(); } - if ((!specialHandled) && altKey && keyCode == 67 && type === 'keydown' && padShortcutEnabled.altC) { + if ( + (!specialHandled) && + altKey && keyCode === 67 && + type === 'keydown' && + padShortcutEnabled.altC + ) { // Alt c focuses on the Chat window $(this).blur(); parent.parent.chat.show(); parent.parent.$('#chatinput').focus(); evt.preventDefault(); } - if ((!specialHandled) && evt.ctrlKey && shiftKey && keyCode == 50 && type === 'keydown' && padShortcutEnabled.cmdShift2) { + if ( + (!specialHandled) && + evt.ctrlKey && + shiftKey && + keyCode === 50 && + type === 'keydown' && + padShortcutEnabled.cmdShift2 + ) { // Control-Shift-2 shows a gritter popup showing a line author const lineNumber = rep.selEnd[0]; const alineAttrs = rep.alines[lineNumber]; @@ -2853,16 +2869,14 @@ function Ace2Inner() { // TODO: Still work when authorship colors have been cleared // TODO: i18n // TODO: There appears to be a race condition or so. - + const authors = []; let author = null; if (alineAttrs) { - var authors = []; - var authorNames = []; const opIter = Changeset.opIterator(alineAttrs); while (opIter.hasNext()) { const op = opIter.next(); - authorId = Changeset.opAttributeValue(op, 'author', apool); + const authorId = Changeset.opAttributeValue(op, 'author', apool); // Only push unique authors and ones with values if (authors.indexOf(authorId) === -1 && authorId !== '') { @@ -2871,9 +2885,10 @@ function Ace2Inner() { } } - // No author information is available IE on a new pad. + let authorString; + const authorNames = []; if (authors.length === 0) { - var authorString = 'No author information is available'; + authorString = 'No author information is available'; } else { // Known authors info, both current and historical const padAuthors = parent.parent.pad.userList(); @@ -2901,11 +2916,11 @@ function Ace2Inner() { }); } if (authors.length === 1) { - var authorString = `The author of this line is ${authorNames}`; + authorString = `The author of this line is ${authorNames[0]}`; } if (authors.length > 1) { - var authorString = `The authors of this line are ${authorNames.join(' & ')}`; - } + authorString = `The authors of this line are ${authorNames.join(' & ')}`; + } parent.parent.$.gritter.add({ // (string | mandatory) the heading of the notification @@ -2918,7 +2933,11 @@ function Ace2Inner() { time: '4000', }); } - if ((!specialHandled) && isTypeForSpecialKey && keyCode == 8 && padShortcutEnabled.delete) { + if ((!specialHandled) && + isTypeForSpecialKey && + keyCode === 8 && + padShortcutEnabled.delete + ) { // "delete" key; in mozilla, if we're at the beginning of a line, normalize now, // or else deleting a blank line can take two delete presses. // -- @@ -2931,7 +2950,11 @@ function Ace2Inner() { doDeleteKey(evt); specialHandled = true; } - if ((!specialHandled) && isTypeForSpecialKey && keyCode == 13 && padShortcutEnabled.return) { + if ((!specialHandled) && + isTypeForSpecialKey && + keyCode === 13 && + padShortcutEnabled.return + ) { // return key, handle specially; // note that in mozilla we need to do an incorporation for proper return behavior anyway. fastIncorp(4); @@ -2943,7 +2966,11 @@ function Ace2Inner() { }, 0); specialHandled = true; } - if ((!specialHandled) && isTypeForSpecialKey && keyCode == 27 && padShortcutEnabled.esc) { + if ((!specialHandled) && + isTypeForSpecialKey && + keyCode === 27 && + padShortcutEnabled.esc + ) { // prevent esc key; // in mozilla versions 14-19 avoid reconnecting pad. @@ -2954,27 +2981,45 @@ function Ace2Inner() { // close all gritters when the user hits escape key parent.parent.$.gritter.removeAll(); } - if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == 's' && (evt.metaKey || evt.ctrlKey) && !evt.altKey && padShortcutEnabled.cmdS) /* Do a saved revision on ctrl S */ - { + if ( + (!specialHandled) && + /* Do a saved revision on ctrl S */ + isTypeForCmdKey && + String.fromCharCode(which).toLowerCase() === 's' && + (evt.metaKey || evt.ctrlKey) && + !evt.altKey && + padShortcutEnabled.cmdS + ) { evt.preventDefault(); const originalBackground = parent.parent.$('#revisionlink').css('background'); parent.parent.$('#revisionlink').css({background: 'lightyellow'}); scheduler.setTimeout(() => { parent.parent.$('#revisionlink').css({background: originalBackground}); }, 1000); - parent.parent.pad.collabClient.sendMessage({type: 'SAVE_REVISION'}); /* The parent.parent part of this is BAD and I feel bad.. It may break something */ + /* The parent.parent part of this is BAD and I feel bad.. It may break something */ + parent.parent.pad.collabClient.sendMessage({type: 'SAVE_REVISION'}); specialHandled = true; } - if ((!specialHandled) && isTypeForSpecialKey && keyCode == 9 && !(evt.metaKey || evt.ctrlKey) && padShortcutEnabled.tab) { + if ((!specialHandled) && // tab + isTypeForSpecialKey && + keyCode === 9 && + !(evt.metaKey || evt.ctrlKey) && + padShortcutEnabled.tab) { fastIncorp(5); evt.preventDefault(); doTabKey(evt.shiftKey); // scrollSelectionIntoView(); specialHandled = true; } - if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == 'z' && (evt.metaKey || evt.ctrlKey) && !evt.altKey && padShortcutEnabled.cmdZ) { + if ((!specialHandled) && // cmd-Z (undo) + isTypeForCmdKey && + String.fromCharCode(which).toLowerCase() === 'z' && + (evt.metaKey || evt.ctrlKey) && + !evt.altKey && + padShortcutEnabled.cmdZ + ) { fastIncorp(6); evt.preventDefault(); if (evt.shiftKey) { @@ -2984,72 +3029,126 @@ function Ace2Inner() { } specialHandled = true; } - if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == 'y' && (evt.metaKey || evt.ctrlKey) && padShortcutEnabled.cmdY) { - // cmd-Y (redo) + if ((!specialHandled) && + // cmd-Y (redo) + isTypeForCmdKey && + String.fromCharCode(which).toLowerCase() === 'y' && + (evt.metaKey || evt.ctrlKey) && + padShortcutEnabled.cmdY + ) { fastIncorp(10); evt.preventDefault(); doUndoRedo('redo'); specialHandled = true; } - if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == 'b' && (evt.metaKey || evt.ctrlKey) && padShortcutEnabled.cmdB) { + if ((!specialHandled) && // cmd-B (bold) + isTypeForCmdKey && + String.fromCharCode(which).toLowerCase() === 'b' && + (evt.metaKey || evt.ctrlKey) && + padShortcutEnabled.cmdB) { fastIncorp(13); evt.preventDefault(); toggleAttributeOnSelection('bold'); specialHandled = true; } - if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == 'i' && (evt.metaKey || evt.ctrlKey) && padShortcutEnabled.cmdI) { + if ((!specialHandled) && // cmd-I (italic) + isTypeForCmdKey && + String.fromCharCode(which).toLowerCase() === 'i' && + (evt.metaKey || evt.ctrlKey) && + padShortcutEnabled.cmdI + ) { fastIncorp(14); evt.preventDefault(); toggleAttributeOnSelection('italic'); specialHandled = true; } - if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == 'u' && (evt.metaKey || evt.ctrlKey) && padShortcutEnabled.cmdU) { + if ((!specialHandled) && + isTypeForCmdKey && + String.fromCharCode(which).toLowerCase() === 'u' && + (evt.metaKey || evt.ctrlKey) && + padShortcutEnabled.cmdU + ) { // cmd-U (underline) fastIncorp(15); evt.preventDefault(); toggleAttributeOnSelection('underline'); specialHandled = true; } - if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == '5' && (evt.metaKey || evt.ctrlKey) && evt.altKey !== true && padShortcutEnabled.cmd5) { + if ((!specialHandled) && // cmd-5 (strikethrough) + isTypeForCmdKey && + String.fromCharCode(which).toLowerCase() === '5' && + (evt.metaKey || evt.ctrlKey) && + evt.altKey !== true && + padShortcutEnabled.cmd5 + ) { fastIncorp(13); evt.preventDefault(); toggleAttributeOnSelection('strikethrough'); specialHandled = true; } - if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == 'l' && (evt.metaKey || evt.ctrlKey) && evt.shiftKey && padShortcutEnabled.cmdShiftL) { + if ((!specialHandled) && // cmd-shift-L (unorderedlist) + isTypeForCmdKey && + String.fromCharCode(which).toLowerCase() === 'l' && + (evt.metaKey || evt.ctrlKey) && + evt.shiftKey && + padShortcutEnabled.cmdShiftL + ) { fastIncorp(9); evt.preventDefault(); doInsertUnorderedList(); specialHandled = true; } - if ((!specialHandled) && isTypeForCmdKey && ((String.fromCharCode(which).toLowerCase() == 'n' && padShortcutEnabled.cmdShiftN) || (String.fromCharCode(which) == 1 && padShortcutEnabled.cmdShift1)) && (evt.metaKey || evt.ctrlKey) && evt.shiftKey) { + if ((!specialHandled) && // cmd-shift-N and cmd-shift-1 (orderedlist) + isTypeForCmdKey && + ( + (String.fromCharCode(which).toLowerCase() === 'n' && + padShortcutEnabled.cmdShiftN) || (String.fromCharCode(which) === '1' && + padShortcutEnabled.cmdShift1) + ) && (evt.metaKey || evt.ctrlKey) && + evt.shiftKey + ) { fastIncorp(9); evt.preventDefault(); doInsertOrderedList(); specialHandled = true; } - if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == 'c' && (evt.metaKey || evt.ctrlKey) && evt.shiftKey && padShortcutEnabled.cmdShiftC) { + if ((!specialHandled) && // cmd-shift-C (clearauthorship) + isTypeForCmdKey && + String.fromCharCode(which).toLowerCase() === 'c' && + (evt.metaKey || evt.ctrlKey) && + evt.shiftKey && padShortcutEnabled.cmdShiftC + ) { fastIncorp(9); evt.preventDefault(); CMDS.clearauthorship(); } - if ((!specialHandled) && isTypeForCmdKey && String.fromCharCode(which).toLowerCase() == 'h' && (evt.ctrlKey) && padShortcutEnabled.cmdH) { + if ((!specialHandled) && // cmd-H (backspace) + isTypeForCmdKey && + String.fromCharCode(which).toLowerCase() === 'h' && + (evt.ctrlKey) && + padShortcutEnabled.cmdH + ) { fastIncorp(20); evt.preventDefault(); doDeleteKey(); specialHandled = true; } - if ((evt.which == 36 && evt.ctrlKey == true) && padShortcutEnabled.ctrlHome) { scroll.setScrollY(0); } // Control Home send to Y = 0 - if ((evt.which == 33 || evt.which == 34) && type == 'keydown' && !evt.ctrlKey) { - evt.preventDefault(); // This is required, browsers will try to do normal default behavior on page up / down and the default behavior SUCKS - + if ((evt.which === 36 && evt.ctrlKey === true) && + // Control Home send to Y = 0 + padShortcutEnabled.ctrlHome) { + scroll.setScrollY(0); + } + if ((evt.which === 33 || evt.which === 34) && type === 'keydown' && !evt.ctrlKey) { + // This is required, browsers will try to do normal default behavior on + // page up / down and the default behavior SUCKS + evt.preventDefault(); const oldVisibleLineRange = scroll.getVisibleLineRange(rep); let topOffset = rep.selStart[0] - oldVisibleLineRange[0]; if (topOffset < 0) { @@ -3060,19 +3159,30 @@ function Ace2Inner() { const isPageUp = evt.which === 33; scheduler.setTimeout(() => { - const newVisibleLineRange = scroll.getVisibleLineRange(rep); // the visible lines IE 1,10 - const linesCount = rep.lines.length(); // total count of lines in pad IE 10 - const numberOfLinesInViewport = newVisibleLineRange[1] - newVisibleLineRange[0]; // How many lines are in the viewport right now? + // the visible lines IE 1,10 + const newVisibleLineRange = scroll.getVisibleLineRange(rep); + // total count of lines in pad IE 10 + const linesCount = rep.lines.length(); + // How many lines are in the viewport right now? + const numberOfLinesInViewport = newVisibleLineRange[1] - newVisibleLineRange[0]; if (isPageUp && padShortcutEnabled.pageUp) { - rep.selEnd[0] = rep.selEnd[0] - numberOfLinesInViewport; // move to the bottom line +1 in the viewport (essentially skipping over a page) - rep.selStart[0] = rep.selStart[0] - numberOfLinesInViewport; // move to the bottom line +1 in the viewport (essentially skipping over a page) + // move to the bottom line +1 in the viewport (essentially skipping over a page) + rep.selEnd[0] -= numberOfLinesInViewport; + // move to the bottom line +1 in the viewport (essentially skipping over a page) + rep.selStart[0] -= numberOfLinesInViewport; } - if (isPageDown && padShortcutEnabled.pageDown) { // if we hit page down - if (rep.selEnd[0] >= oldVisibleLineRange[0]) { // If the new viewpoint position is actually further than where we are right now - rep.selStart[0] = oldVisibleLineRange[1] - 1; // dont go further in the page down than what's visible IE go from 0 to 50 if 50 is visible on screen but dont go below that else we miss content - rep.selEnd[0] = oldVisibleLineRange[1] - 1; // dont go further in the page down than what's visible IE go from 0 to 50 if 50 is visible on screen but dont go below that else we miss content + // if we hit page down + if (isPageDown && padShortcutEnabled.pageDown) { + // If the new viewpoint position is actually further than where we are right now + if (rep.selEnd[0] >= oldVisibleLineRange[0]) { + // dont go further in the page down than what's visible IE go from 0 to 50 + // if 50 is visible on screen but dont go below that else we miss content + rep.selStart[0] = oldVisibleLineRange[1] - 1; + // dont go further in the page down than what's visible IE go from 0 to 50 + // if 50 is visible on screen but dont go below that else we miss content + rep.selEnd[0] = oldVisibleLineRange[1] - 1; } } @@ -3087,24 +3197,33 @@ function Ace2Inner() { rep.selEnd[0] = linesCount - 1; } updateBrowserSelectionFromRep(); - const myselection = document.getSelection(); // get the current caret selection, can't use rep. here because that only gives us the start position not the current - let caretOffsetTop = myselection.focusNode.parentNode.offsetTop || myselection.focusNode.offsetTop; // get the carets selection offset in px IE 214 + // get the current caret selection, can't use rep. here because that only gives + // us the start position not the current + const myselection = document.getSelection(); + // get the carets selection offset in px IE 214 + let caretOffsetTop = myselection.focusNode.parentNode.offsetTop || + myselection.focusNode.offsetTop; - // sometimes the first selection is -1 which causes problems (Especially with ep_page_view) + // sometimes the first selection is -1 which causes problems + // (Especially with ep_page_view) // so use focusNode.offsetTop value. if (caretOffsetTop === -1) caretOffsetTop = myselection.focusNode.offsetTop; - scroll.setScrollY(caretOffsetTop); // set the scrollY offset of the viewport on the document + // set the scrollY offset of the viewport on the document + scroll.setScrollY(caretOffsetTop); }, 200); } // scroll to viewport when user presses arrow keys and caret is out of the viewport - if ((evt.which == 37 || evt.which == 38 || evt.which == 39 || evt.which == 40)) { - // we use arrowKeyWasReleased to avoid triggering the animation when a key is continuously pressed + if ((evt.which === 37 || evt.which === 38 || evt.which === 39 || evt.which === 40)) { + // we use arrowKeyWasReleased to avoid triggering the animation when a key + // is continuously pressed // this makes the scroll smooth if (!continuouslyPressingArrowKey(type)) { - // We use getSelection() instead of rep to get the caret position. This avoids errors like when - // the caret position is not synchronized with the rep. For example, when an user presses arrow - // down to scroll the pad without releasing the key. When the key is released the rep is not + // the caret position is not synchronized with the rep. + // For example, when an user presses arrow + // We use getSelection() instead of rep to get the caret position. + // This avoids errors like when down to scroll the pad without releasing the key. + // When the key is released the rep is not // synchronized, so we don't get the right node where caret is. const selection = getSelection(); @@ -3117,25 +3236,28 @@ function Ace2Inner() { } } - if (type == 'keydown') { + if (type === 'keydown') { idleWorkTimer.atLeast(500); - } else if (type == 'keypress') { - if ((!specialHandled) && false /* parenModule.shouldNormalizeOnChar(charCode)*/) { + } else if (type === 'keypress') { + // OPINION ASKED. What's going on here? :D + if (!specialHandled) { idleWorkTimer.atMost(0); } else { idleWorkTimer.atLeast(500); } - } else if (type == 'keyup') { + } else if (type === 'keyup') { const wait = 0; idleWorkTimer.atLeast(wait); idleWorkTimer.atMost(wait); } // Is part of multi-keystroke international character on Firefox Mac - const isFirefoxHalfCharacter = (browser.firefox && evt.altKey && charCode === 0 && keyCode === 0); + const isFirefoxHalfCharacter = + (browser.firefox && evt.altKey && charCode === 0 && keyCode === 0); // Is part of multi-keystroke international character on Safari Mac - const isSafariHalfCharacter = (browser.safari && evt.altKey && keyCode == 229); + const isSafariHalfCharacter = + (browser.safari && evt.altKey && keyCode === 229); if (thisKeyDoesntTriggerNormalize || isFirefoxHalfCharacter || isSafariHalfCharacter) { idleWorkTimer.atLeast(3000); // give user time to type @@ -3144,37 +3266,38 @@ function Ace2Inner() { } if ((!specialHandled) && (!thisKeyDoesntTriggerNormalize) && (!inInternationalComposition)) { - if (type != 'keyup') { + if (type !== 'keyup') { observeChangesAroundSelection(); } } - if (type == 'keyup') { + if (type === 'keyup') { thisKeyDoesntTriggerNormalize = false; } }); - } - - var thisKeyDoesntTriggerNormalize = false; + }; + let thisKeyDoesntTriggerNormalize = false; let arrowKeyWasReleased = true; - function continuouslyPressingArrowKey(type) { + const continuouslyPressingArrowKey = (type) => { let firstTimeKeyIsContinuouslyPressed = false; - if (type == 'keyup') { arrowKeyWasReleased = true; } else if (type == 'keydown' && arrowKeyWasReleased) { + if (type === 'keyup') { + arrowKeyWasReleased = true; + } else if (type === 'keydown' && arrowKeyWasReleased) { firstTimeKeyIsContinuouslyPressed = true; arrowKeyWasReleased = false; } return !firstTimeKeyIsContinuouslyPressed; - } + }; - function doUndoRedo(which) { + const doUndoRedo = (which) => { // precond: normalized DOM if (undoModule.enabled) { let whichMethod; - if (which == 'undo') whichMethod = 'performUndo'; - if (which == 'redo') whichMethod = 'performRedo'; + if (which === 'undo') whichMethod = 'performUndo'; + if (which === 'redo') whichMethod = 'performRedo'; if (whichMethod) { const oldEventType = currentCallStack.editEvent.eventType; currentCallStack.startNewEvent(which); @@ -3183,157 +3306,62 @@ function Ace2Inner() { performDocumentApplyChangeset(backset); } if (selectionInfo) { - performSelectionChange(lineAndColumnFromChar(selectionInfo.selStart), lineAndColumnFromChar(selectionInfo.selEnd), selectionInfo.selFocusAtStart); + performSelectionChange( + lineAndColumnFromChar( + selectionInfo.selStart + ), + lineAndColumnFromChar(selectionInfo.selEnd), + selectionInfo.selFocusAtStart + ); } const oldEvent = currentCallStack.startNewEvent(oldEventType, true); return oldEvent; }); } } - } + }; editorInfo.ace_doUndoRedo = doUndoRedo; - function updateBrowserSelectionFromRep() { - // requires normalized DOM! - const selStart = rep.selStart; - const selEnd = rep.selEnd; - - if (!(selStart && selEnd)) { - setSelection(null); - return; - } - - const selection = {}; - - const ss = [selStart[0], selStart[1]]; - selection.startPoint = getPointForLineAndChar(ss); - - const se = [selEnd[0], selEnd[1]]; - selection.endPoint = getPointForLineAndChar(se); - - selection.focusAtStart = !!rep.selFocusAtStart; - setSelection(selection); - } - editorInfo.ace_updateBrowserSelectionFromRep = updateBrowserSelectionFromRep; - - function nodeMaxIndex(nd) { - if (isNodeText(nd)) return nd.nodeValue.length; - else return 1; - } - - function getSelection() { - // returns null, or a structure containing startPoint and endPoint, - // each of which has node (a magicdom node), index, and maxIndex. If the node - // is a text node, maxIndex is the length of the text; else maxIndex is 1. - // index is between 0 and maxIndex, inclusive. - var browserSelection = window.getSelection(); - if (!browserSelection || browserSelection.type === 'None' || - browserSelection.rangeCount === 0) { - return null; - } - const range = browserSelection.getRangeAt(0); - - function isInBody(n) { - while (n && !(n.tagName && n.tagName.toLowerCase() == 'body')) { - n = n.parentNode; - } - return !!n; - } - - function pointFromRangeBound(container, offset) { - if (!isInBody(container)) { - // command-click in Firefox selects whole document, HEAD and BODY! - return { - node: root, - index: 0, - maxIndex: 1, - }; - } - const n = container; - const childCount = n.childNodes.length; - if (isNodeText(n)) { - return { - node: n, - index: offset, - maxIndex: n.nodeValue.length, - }; - } else if (childCount === 0) { - return { - node: n, - index: 0, - maxIndex: 1, - }; - } - // treat point between two nodes as BEFORE the second (rather than after the first) - // if possible; this way point at end of a line block-element is treated as - // at beginning of next line - else if (offset == childCount) { - var nd = n.childNodes.item(childCount - 1); - var max = nodeMaxIndex(nd); - return { - node: nd, - index: max, - maxIndex: max, - }; - } else { - var nd = n.childNodes.item(offset); - var max = nodeMaxIndex(nd); - return { - node: nd, - index: 0, - maxIndex: max, - }; - } - } - var selection = {}; - selection.startPoint = pointFromRangeBound(range.startContainer, range.startOffset); - selection.endPoint = pointFromRangeBound(range.endContainer, range.endOffset); - selection.focusAtStart = (((range.startContainer != range.endContainer) || (range.startOffset != range.endOffset)) && browserSelection.anchorNode && (browserSelection.anchorNode == range.endContainer) && (browserSelection.anchorOffset == range.endOffset)); - - if (selection.startPoint.node.ownerDocument !== window.document) { - return null; - } - - return selection; - } - - function setSelection(selection) { - function copyPoint(pt) { - return { - node: pt.node, - index: pt.index, - maxIndex: pt.maxIndex, - }; - } + const setSelection = (selection) => { + const copyPoint = (pt) => ({ + node: pt.node, + index: pt.index, + maxIndex: pt.maxIndex, + }); let isCollapsed; - function pointToRangeBound(pt) { + const pointToRangeBound = (pt) => { const p = copyPoint(pt); // Make sure Firefox cursor is deep enough; fixes cursor jumping when at top level, // and also problem where cut/copy of a whole line selected with fake arrow-keys // copies the next line too. if (isCollapsed) { - function diveDeep() { + const diveDeep = () => { while (p.node.childNodes.length > 0) { // && (p.node == root || p.node.parentNode == root)) { if (p.index === 0) { p.node = p.node.firstChild; p.maxIndex = nodeMaxIndex(p.node); - } else if (p.index == p.maxIndex) { + } else if (p.index === p.maxIndex) { p.node = p.node.lastChild; p.maxIndex = nodeMaxIndex(p.node); p.index = p.maxIndex; } else { break; } } - } + }; // now fix problem where cursor at end of text node at end of span-like element // with background doesn't seem to show up... - if (isNodeText(p.node) && p.index == p.maxIndex) { + if (isNodeText(p.node) && p.index === p.maxIndex) { let n = p.node; - while ((!n.nextSibling) && (n != root) && (n.parentNode != root)) { + while ((!n.nextSibling) && (n !== root) && (n.parentNode !== root)) { n = n.parentNode; } - if (n.nextSibling && (!((typeof n.nextSibling.tagName) === 'string' && n.nextSibling.tagName.toLowerCase() == 'br')) && (n != p.node) && (n != root) && (n.parentNode != root)) { + if ( + n.nextSibling && + (!((typeof n.nextSibling.tagName) === 'string' && + n.nextSibling.tagName.toLowerCase() === 'br')) && + (n !== p.node) && (n !== root) && (n.parentNode !== root) + ) { // found a parent, go to next node and dive in p.node = n.nextSibling; p.maxIndex = nodeMaxIndex(p.node); @@ -3359,22 +3387,30 @@ function Ace2Inner() { offset: childIndex(p.node) + p.index, }; } - } + }; const browserSelection = window.getSelection(); if (browserSelection) { browserSelection.removeAllRanges(); if (selection) { - isCollapsed = (selection.startPoint.node === selection.endPoint.node && selection.startPoint.index === selection.endPoint.index); + isCollapsed = ( + selection.startPoint.node === selection.endPoint.node && + selection.startPoint.index === selection.endPoint.index + ); const start = pointToRangeBound(selection.startPoint); const end = pointToRangeBound(selection.endPoint); - if ((!isCollapsed) && selection.focusAtStart && browserSelection.collapse && browserSelection.extend) { + if ( + (!isCollapsed) && + selection.focusAtStart && + browserSelection.collapse && + browserSelection.extend + ) { // can handle "backwards"-oriented selection, shift-arrow-keys move start // of selection browserSelection.collapse(end.container, end.offset); browserSelection.extend(start.container, start.offset); } else { - var range = doc.createRange(); + const range = doc.createRange(); range.setStart(start.container, start.offset); range.setEnd(end.container, end.offset); browserSelection.removeAllRanges(); @@ -3382,53 +3418,177 @@ function Ace2Inner() { } } } - } + }; - function childIndex(n) { + const updateBrowserSelectionFromRep = () => { + // requires normalized DOM! + const selStart = rep.selStart; + const selEnd = rep.selEnd; + + if (!(selStart && selEnd)) { + setSelection(null); + return; + } + + const selection = {}; + + const ss = [selStart[0], selStart[1]]; + selection.startPoint = getPointForLineAndChar(ss); + + const se = [selEnd[0], selEnd[1]]; + selection.endPoint = getPointForLineAndChar(se); + + selection.focusAtStart = !!rep.selFocusAtStart; + setSelection(selection); + }; + editorInfo.ace_updateBrowserSelectionFromRep = updateBrowserSelectionFromRep; + editorInfo.ace_focus = focus; + editorInfo.ace_importText = importText; + editorInfo.ace_importAText = importAText; + editorInfo.ace_exportText = exportText; + editorInfo.ace_editorChangedSize = editorChangedSize; + editorInfo.ace_setOnKeyPress = setOnKeyPress; + editorInfo.ace_setOnKeyDown = setOnKeyDown; + editorInfo.ace_setNotifyDirty = setNotifyDirty; + editorInfo.ace_dispose = dispose; + editorInfo.ace_getFormattedCode = getFormattedCode; + editorInfo.ace_setEditable = setEditable; + editorInfo.ace_execCommand = execCommand; + editorInfo.ace_replaceRange = replaceRange; + editorInfo.ace_getAuthorInfos = getAuthorInfos; + editorInfo.ace_performDocumentReplaceRange = performDocumentReplaceRange; + editorInfo.ace_performDocumentReplaceCharRange = performDocumentReplaceCharRange; + editorInfo.ace_setSelection = setSelection; + + const nodeMaxIndex = (nd) => { + if (isNodeText(nd)) return nd.nodeValue.length; + else return 1; + }; + + const getSelection = () => { + // returns null, or a structure containing startPoint and endPoint, + // each of which has node (a magicdom node), index, and maxIndex. If the node + // is a text node, maxIndex is the length of the text; else maxIndex is 1. + // index is between 0 and maxIndex, inclusive. + const browserSelection = window.getSelection(); + if (!browserSelection || browserSelection.type === 'None' || + browserSelection.rangeCount === 0) { + return null; + } + const range = browserSelection.getRangeAt(0); + + const isInBody = (n) => { + while (n && !(n.tagName && n.tagName.toLowerCase() === 'body')) { + n = n.parentNode; + } + return !!n; + }; + + const pointFromRangeBound = (container, offset) => { + if (!isInBody(container)) { + // command-click in Firefox selects whole document, HEAD and BODY! + return { + node: root, + index: 0, + maxIndex: 1, + }; + } + const n = container; + const childCount = n.childNodes.length; + if (isNodeText(n)) { + return { + node: n, + index: offset, + maxIndex: n.nodeValue.length, + }; + } else if (childCount === 0) { + return { + node: n, + index: 0, + maxIndex: 1, + }; + // treat point between two nodes as BEFORE the second (rather than after the first) + // if possible; this way point at end of a line block-element is treated as + // at beginning of next line + } else if (offset === childCount) { + const nd = n.childNodes.item(childCount - 1); + const max = nodeMaxIndex(nd); + return { + node: nd, + index: max, + maxIndex: max, + }; + } else { + const nd = n.childNodes.item(offset); + const max = nodeMaxIndex(nd); + return { + node: nd, + index: 0, + maxIndex: max, + }; + } + }; + var selection = {}; + selection.startPoint = pointFromRangeBound(range.startContainer, range.startOffset); + selection.endPoint = pointFromRangeBound(range.endContainer, range.endOffset); + selection.focusAtStart = ( + ( + (range.startContainer !== range.endContainer) || + (range.startOffset !== range.endOffset) + ) && + browserSelection.anchorNode && + (browserSelection.anchorNode === range.endContainer) && + (browserSelection.anchorOffset === range.endOffset) + ); + + if (selection.startPoint.node.ownerDocument !== window.document) { + return null; + } + + return selection; + }; + + const childIndex = (n) => { let idx = 0; while (n.previousSibling) { idx++; n = n.previousSibling; } return idx; - } + }; - function fixView() { + const fixView = () => { // calling this method repeatedly should be fast if (getInnerWidth() === 0 || getInnerHeight() === 0) { return; } - const win = outerWin; - enforceEditability(); $(sideDiv).addClass('sidedivdelayed'); - } + }; const _teardownActions = []; - function teardown() { + const teardown = () => { _.each(_teardownActions, (a) => { a(); }); - } - - var inInternationalComposition = false; - function handleCompositionEvent(evt) { - // international input events, fired in FF3, at least; allow e.g. Japanese input - if (evt.type == 'compositionstart') { - inInternationalComposition = true; - } else if (evt.type == 'compositionend') { - inInternationalComposition = false; - } - } - - editorInfo.ace_getInInternationalComposition = function () { - return inInternationalComposition; }; - function bindTheEventHandlers() { + let inInternationalComposition = false; + const handleCompositionEvent = (evt) => { + // international input events, fired in FF3, at least; allow e.g. Japanese input + if (evt.type === 'compositionstart') { + inInternationalComposition = true; + } else if (evt.type === 'compositionend') { + inInternationalComposition = false; + } + }; + + editorInfo.ace_getInInternationalComposition = () => inInternationalComposition; + + const bindTheEventHandlers = () => { $(document).on('keydown', handleKeyEvent); $(document).on('keypress', handleKeyEvent); $(document).on('keyup', handleKeyEvent); @@ -3439,8 +3599,6 @@ function Ace2Inner() { // Will break OL re-numbering: https://github.com/ether/etherpad-lite/pull/2533 // $(document).on("cut", handleCut); - $(root).on('blur', handleBlur); - // If non-nullish, pasting on a link should be suppressed. let suppressPasteOnLink = null; @@ -3518,48 +3676,23 @@ function Ace2Inner() { $(document.documentElement).on('compositionstart', handleCompositionEvent); $(document.documentElement).on('compositionend', handleCompositionEvent); - } + }; - function topLevel(n) { - if ((!n) || n == root) return null; - while (n.parentNode != root) { + const topLevel = (n) => { + if ((!n) || n === root) return null; + while (n.parentNode !== root) { n = n.parentNode; } return n; - } + }; - function getClassArray(elem, optFilter) { - const bodyClasses = []; - (elem.className || '').replace(/\S+/g, (c) => { - if ((!optFilter) || (optFilter(c))) { - bodyClasses.push(c); - } - }); - return bodyClasses; - } - - function setClassArray(elem, array) { - elem.className = array.join(' '); - } - - function focus() { - window.focus(); - } - - function handleBlur(evt) {} - - function getSelectionPointX(point) { + const getSelectionPointX = (point) => { // doesn't work in wrap-mode const node = point.node; const index = point.index; + const leftOf = (n) => n.offsetLeft; + const rightOf = (n) => n.offsetLeft + n.offsetWidth; - function leftOf(n) { - return n.offsetLeft; - } - - function rightOf(n) { - return n.offsetLeft + n.offsetWidth; - } if (!isNodeText(node)) { if (index === 0) return leftOf(node); else return rightOf(node); @@ -3569,7 +3702,10 @@ function Ace2Inner() { let charsToLeft = index; let charsToRight = node.nodeValue.length - index; let n; - for (n = node.previousSibling; n && isNodeText(n); n = n.previousSibling) charsToLeft += n.nodeValue; + for (n = node.previousSibling; n && + isNodeText(n); n = n.previousSibling) { + charsToLeft += n.nodeValue; + } const leftEdge = (n ? rightOf(n) : leftOf(node.parentNode)); for (n = node.nextSibling; n && isNodeText(n); n = n.nextSibling) charsToRight += n.nodeValue; const rightEdge = (n ? leftOf(n) : rightOf(node.parentNode)); @@ -3577,25 +3713,9 @@ function Ace2Inner() { const pixLoc = leftEdge + frac * (rightEdge - leftEdge); return Math.round(pixLoc); } - } + }; - function getPageHeight() { - const win = outerWin; - const odoc = win.document; - if (win.innerHeight && win.scrollMaxY) return win.innerHeight + win.scrollMaxY; - else if (odoc.body.scrollHeight > odoc.body.offsetHeight) return odoc.body.scrollHeight; - else return odoc.body.offsetHeight; - } - - function getPageWidth() { - const win = outerWin; - const odoc = win.document; - if (win.innerWidth && win.scrollMaxX) return win.innerWidth + win.scrollMaxX; - else if (odoc.body.scrollWidth > odoc.body.offsetWidth) return odoc.body.scrollWidth; - else return odoc.body.offsetWidth; - } - - function getInnerHeight() { + const getInnerHeight = () => { const win = outerWin; const odoc = win.document; let h; @@ -3606,17 +3726,16 @@ function Ace2Inner() { // deal with case where iframe is hidden, hope that // style.height of iframe container is set in px return Number(editorInfo.frame.parentNode.style.height.replace(/[^0-9]/g, '') || 0); - } + }; - function getInnerWidth() { + const getInnerWidth = () => { const win = outerWin; const odoc = win.document; return odoc.documentElement.clientWidth; - } + }; - function scrollXHorizontallyIntoView(pixelX) { + const scrollXHorizontallyIntoView = (pixelX) => { const win = outerWin; - const odoc = outerWin.document; const distInsideLeft = pixelX - win.scrollX; const distInsideRight = win.scrollX + getInnerWidth() - pixelX; if (distInsideLeft < 0) { @@ -3624,9 +3743,9 @@ function Ace2Inner() { } else if (distInsideRight < 0) { win.scrollBy(-distInsideRight + 1, 0); } - } + }; - function scrollSelectionIntoView() { + const scrollSelectionIntoView = () => { if (!rep.selStart) return; fixView(); const innerHeight = getInnerHeight(); @@ -3634,134 +3753,57 @@ function Ace2Inner() { if (!doesWrap) { const browserSelection = getSelection(); if (browserSelection) { - const focusPoint = (browserSelection.focusAtStart ? browserSelection.startPoint : browserSelection.endPoint); + const focusPoint = ( + browserSelection.focusAtStart ? browserSelection.startPoint : browserSelection.endPoint + ); const selectionPointX = getSelectionPointX(focusPoint); scrollXHorizontallyIntoView(selectionPointX); fixView(); } } - } + }; const listAttributeName = 'list'; - function getLineListType(lineNum) { - return documentAttributeManager.getAttributeOnLine(lineNum, listAttributeName); - } - - function setLineListType(lineNum, listType) { - if (listType == '') { - documentAttributeManager.removeAttributeOnLine(lineNum, listAttributeName); - documentAttributeManager.removeAttributeOnLine(lineNum, 'start'); - } else { - documentAttributeManager.setAttributeOnLine(lineNum, listAttributeName, listType); - } - - // if the list has been removed, it is necessary to renumber - // starting from the *next* line because the list may have been - // separated. If it returns null, it means that the list was not cut, try - // from the current one. - if (renumberList(lineNum + 1) == null) { - renumberList(lineNum); - } - } - - function renumberList(lineNum) { - // 1-check we are in a list - let type = getLineListType(lineNum); - if (!type) { - return null; - } - type = /([a-z]+)[0-9]+/.exec(type); - if (type[1] == 'indent') { - return null; - } - - // 2-find the first line of the list - while (lineNum - 1 >= 0 && (type = getLineListType(lineNum - 1))) { - type = /([a-z]+)[0-9]+/.exec(type); - if (type[1] == 'indent') break; - lineNum--; - } - - // 3-renumber every list item of the same level from the beginning, level 1 - // IMPORTANT: never skip a level because there imbrication may be arbitrary - const builder = Changeset.builder(rep.lines.totalWidth()); - let loc = [0, 0]; - function applyNumberList(line, level) { - // init - let position = 1; - let curLevel = level; - let listType; - // loop over the lines - while (listType = getLineListType(line)) { - // apply new num - listType = /([a-z]+)([0-9]+)/.exec(listType); - curLevel = Number(listType[2]); - if (isNaN(curLevel) || listType[0] == 'indent') { - return line; - } else if (curLevel == level) { - ChangesetUtils.buildKeepRange(rep, builder, loc, (loc = [line, 0])); - ChangesetUtils.buildKeepRange(rep, builder, loc, (loc = [line, 1]), [ - ['start', position], - ], rep.apool); - - position++; - line++; - } else if (curLevel < level) { - return line;// back to parent - } else { - line = applyNumberList(line, level + 1);// recursive call - } - } - return line; - } - - applyNumberList(lineNum, 1); - const cs = builder.toString(); - if (!Changeset.isIdentity(cs)) { - performDocumentApplyChangeset(cs); - } - - // 4-apply the modifications - } + const getLineListType = (lineNum) => documentAttributeManager + .getAttributeOnLine(lineNum, listAttributeName); + editorInfo.ace_getLineListType = getLineListType; - function doInsertList(type) { + const doInsertList = (type) => { if (!(rep.selStart && rep.selEnd)) { return; } - let firstLine, lastLine; - firstLine = rep.selStart[0]; - lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] === 0) ? 1 : 0)); + const firstLine = rep.selStart[0]; + const lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] === 0) ? 1 : 0)); let allLinesAreList = true; - for (var n = firstLine; n <= lastLine; n++) { - var listType = getLineListType(n); - if (!listType || listType.slice(0, type.length) != type) { + for (let n = firstLine; n <= lastLine; n++) { + const listType = getLineListType(n); + if (!listType || listType.slice(0, type.length) !== type) { allLinesAreList = false; break; } } const mods = []; - for (var n = firstLine; n <= lastLine; n++) { - var t = ''; + for (let n = firstLine; n <= lastLine; n++) { + // var t = ''; let level = 0; - var listType = /([a-z]+)([0-9]+)/.exec(getLineListType(n)); + let togglingOn = true; + const listType = /([a-z]+)([0-9]+)/.exec(getLineListType(n)); // Used to outdent if ol is removed if (allLinesAreList) { - var togglingOn = false; - } else { - var togglingOn = true; + togglingOn = false; } if (listType) { - t = listType[1]; + // t = listType[1]; level = Number(listType[2]); } - var t = getLineListType(n); + const t = getLineListType(n); if (t === listType) togglingOn = false; @@ -3783,26 +3825,20 @@ function Ace2Inner() { _.each(mods, (mod) => { setLineListType(mod[0], mod[1]); }); - } + }; - function doInsertUnorderedList() { + const doInsertUnorderedList = () => { doInsertList('bullet'); - } - function doInsertOrderedList() { + }; + const doInsertOrderedList = () => { doInsertList('number'); - } + }; editorInfo.ace_doInsertUnorderedList = doInsertUnorderedList; editorInfo.ace_doInsertOrderedList = doInsertOrderedList; - function initLineNumbers() { - lineNumbersShown = 1; - sideDiv.innerHTML = '
1
'; - sideDivInner = outerWin.document.getElementById('sidedivinner'); - $(sideDiv).addClass('sidediv'); - } // We apply the height of a line in the doc body, to the corresponding sidediv line number - function updateLineNumbers() { + const updateLineNumbers = () => { if (!currentCallStack || currentCallStack && !currentCallStack.domClean) return; // Refs #4228, to avoid layout trashing, we need to first calculate all the heights, @@ -3821,7 +3857,10 @@ function Ace2Inner() { // didn't do this special case, we would miss out on any top margin // included on the first line. The default stylesheet doesn't add // extra margins/padding, but plugins might. - h = docLine.nextSibling.offsetTop - parseInt(window.getComputedStyle(doc.body).getPropertyValue('padding-top').split('px')[0]); + h = docLine.nextSibling.offsetTop - parseInt( + window.getComputedStyle(doc.body) + .getPropertyValue('padding-top').split('px')[0] + ); } else { h = docLine.nextSibling.offsetTop - docLine.offsetTop; } @@ -3848,7 +3887,7 @@ function Ace2Inner() { currentLine++; } - if (newNumLines != lineNumbersShown) { + if (newNumLines !== lineNumbersShown) { const container = sideDivInner; const odoc = outerWin.document; const fragment = odoc.createDocumentFragment(); @@ -3872,16 +3911,16 @@ function Ace2Inner() { lineNumbersShown--; } } - } + }; // Init documentAttributeManager documentAttributeManager = new AttributeManager(rep, performDocumentApplyChangeset); - editorInfo.ace_performDocumentApplyAttributesToRange = function () { - return documentAttributeManager.setAttributesOnRange.apply(documentAttributeManager, arguments); - }; - this.init = function () { + editorInfo.ace_performDocumentApplyAttributesToRange = (...args) => documentAttributeManager. + setAttributesOnRange(args); + + this.init = () => { $(document).ready(() => { doc = document; // defined as a var in scope outside inCallStack('setup', () => { @@ -3916,13 +3955,11 @@ function Ace2Inner() { scheduler.setTimeout(() => { parent.readyFunc(); // defined in code that sets up the inner iframe }, 0); - - isSetUp = true; }); }; } -exports.init = function () { +exports.init = () => { const editor = new Ace2Inner(); editor.init(); }; diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 7177732db..0c6b0ee4a 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -1,3 +1,4 @@ +'use strict'; /** * This code is mostly from the old Etherpad. Please help us to comment this code. * This helps other people to understand this code better and helps them to improve it. diff --git a/src/static/js/domline.js b/src/static/js/domline.js index 9ec708ce2..a341500b1 100644 --- a/src/static/js/domline.js +++ b/src/static/js/domline.js @@ -1,8 +1,4 @@ -/** - * This code is mostly from the old Etherpad. Please help us to comment this code. - * This helps other people to understand this code better and helps them to improve it. - * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED - */ +'use strict'; // THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline // %APPJET%: import("etherpad.admin.plugins"); @@ -166,7 +162,7 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument) { lineClass = domline.addToLineClass(lineClass, cls); } else if (txt) { if (href) { - urn_schemes = new RegExp('^(about|geo|mailto|tel):'); + const urn_schemes = new RegExp('^(about|geo|mailto|tel):'); if (!~href.indexOf('://') && !urn_schemes.test(href)) // if the url doesn't include a protocol prefix, assume http { href = `http://${href}`; diff --git a/src/static/js/farbtastic.js b/src/static/js/farbtastic.js index acabea49d..588b3d945 100644 --- a/src/static/js/farbtastic.js +++ b/src/static/js/farbtastic.js @@ -1,3 +1,5 @@ +'use strict'; + // Farbtastic 2.0 alpha // edited by Sebastian Castro on 2020-04-06 (function ($) { @@ -158,12 +160,12 @@ $._farbtastic = function (container, options) { m.lineWidth = w / r; m.scale(r, r); // Each segment goes from angle1 to angle2. - for (var i = 0; i <= n; ++i) { + for (let i = 0; i <= n; ++i) { var d2 = i / n, angle2 = d2 * Math.PI * 2, // Endpoints x1 = Math.sin(angle1), y1 = -Math.cos(angle1); - x2 = Math.sin(angle2), y2 = -Math.cos(angle2), + const x2 = Math.sin(angle2), y2 = -Math.cos(angle2), // Midpoint chosen so that the endpoints are tangent to the circle. am = (angle1 + angle2) / 2, tan = 1 / Math.cos((angle2 - angle1) / 2), @@ -171,37 +173,16 @@ $._farbtastic = function (container, options) { // New color color2 = fb.pack(fb.HSLToRGB([d2, 1, 0.5])); if (i > 0) { - if (browser.msie) { - // IE's gradient calculations mess up the colors. Correct along the diagonals. - var corr = (1 + Math.min(Math.abs(Math.tan(angle1)), Math.abs(Math.tan(Math.PI / 2 - angle1)))) / n; - color1 = fb.pack(fb.HSLToRGB([d1 - 0.15 * corr, 1, 0.5])); - color2 = fb.pack(fb.HSLToRGB([d2 + 0.15 * corr, 1, 0.5])); - // Create gradient fill between the endpoints. - var grad = m.createLinearGradient(x1, y1, x2, y2); - grad.addColorStop(0, color1); - grad.addColorStop(1, color2); - m.fillStyle = grad; - // Draw quadratic curve segment as a fill. - var r1 = (r + w / 2) / r, r2 = (r - w / 2) / r; // inner/outer radius. - m.beginPath(); - m.moveTo(x1 * r1, y1 * r1); - m.quadraticCurveTo(xm * r1, ym * r1, x2 * r1, y2 * r1); - m.lineTo(x2 * r2, y2 * r2); - m.quadraticCurveTo(xm * r2, ym * r2, x1 * r2, y1 * r2); - m.fill(); - } - else { - // Create gradient fill between the endpoints. - var grad = m.createLinearGradient(x1, y1, x2, y2); - grad.addColorStop(0, color1); - grad.addColorStop(1, color2); - m.strokeStyle = grad; - // Draw quadratic curve segment. - m.beginPath(); - m.moveTo(x1, y1); - m.quadraticCurveTo(xm, ym, x2, y2); - m.stroke(); - } + // Create gradient fill between the endpoints. + var grad = m.createLinearGradient(x1, y1, x2, y2); + grad.addColorStop(0, color1); + grad.addColorStop(1, color2); + m.strokeStyle = grad; + // Draw quadratic curve segment. + m.beginPath(); + m.moveTo(x1, y1); + m.quadraticCurveTo(xm, ym, x2, y2); + m.stroke(); } // Prevent seams where curves join. angle1 = angle2 - nudge; color1 = color2; d1 = d2; @@ -245,7 +226,7 @@ $._farbtastic = function (container, options) { var ctx = buffer.getContext('2d'); var frame = ctx.getImageData(0, 0, sz + 1, sz + 1); - var i = 0; + let i = 0; calculateMask(sz, sz, function (x, y, c, a) { frame.data[i++] = frame.data[i++] = frame.data[i++] = c * 255; frame.data[i++] = a * 255; @@ -320,7 +301,7 @@ $._farbtastic = function (container, options) { // Update the overlay canvas. fb.ctxOverlay.clearRect(-fb.mid, -fb.mid, sz, sz); - for (i in circles) { + for (let i in circles) { var c = circles[i]; fb.ctxOverlay.lineWidth = c.lw; fb.ctxOverlay.strokeStyle = c.c; diff --git a/src/static/js/pad_userlist.js b/src/static/js/pad_userlist.js index d596290f6..a43ef69ae 100644 --- a/src/static/js/pad_userlist.js +++ b/src/static/js/pad_userlist.js @@ -1,11 +1,5 @@ 'use strict'; -/** - * This code is mostly from the old Etherpad. Please help us to comment this code. - * This helps other people to understand this code better and helps them to improve it. - * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED - */ - /** * Copyright 2009 Google Inc. * @@ -546,7 +540,6 @@ const paduserlist = (function () { } $('#myswatch').css({'background-color': myUserInfo.colorId}); - $('li[data-key=showusers] > a').css({'box-shadow': `inset 0 0 30px ${myUserInfo.colorId}`}); }, }; From 46dc94310162cc0eb45327c98127fbca8cce344a Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 21 Dec 2020 23:58:58 +0000 Subject: [PATCH 038/357] lint: linestylefilter and rjquery.js --- src/static/js/linestylefilter.js | 150 +++++++++++++++---------------- src/static/js/rjquery.js | 3 +- 2 files changed, 77 insertions(+), 76 deletions(-) diff --git a/src/static/js/linestylefilter.js b/src/static/js/linestylefilter.js index ddab47224..f70eefc23 100644 --- a/src/static/js/linestylefilter.js +++ b/src/static/js/linestylefilter.js @@ -1,3 +1,5 @@ +'use strict'; + /** * This code is mostly from the old Etherpad. Please help us to comment this code. * This helps other people to understand this code better and helps them to improve it. @@ -31,7 +33,6 @@ const Changeset = require('./Changeset'); const hooks = require('./pluginfw/hooks'); const linestylefilter = {}; -const _ = require('./underscore'); const AttributeManager = require('./AttributeManager'); const padutils = require('./pad_utils').padutils; @@ -45,32 +46,30 @@ linestylefilter.ATTRIB_CLASSES = { const lineAttributeMarker = 'lineAttribMarker'; exports.lineAttributeMarker = lineAttributeMarker; -linestylefilter.getAuthorClassName = function (author) { - return `author-${author.replace(/[^a-y0-9]/g, (c) => { - if (c == '.') return '-'; - return `z${c.charCodeAt(0)}z`; - })}`; -}; +linestylefilter.getAuthorClassName = (author) => `author-${author.replace(/[^a-y0-9]/g, (c) => { + if (c === '.') return '-'; + return `z${c.charCodeAt(0)}z`; +})}`; // lineLength is without newline; aline includes newline, // but may be falsy if lineLength == 0 -linestylefilter.getLineStyleFilter = function (lineLength, aline, textAndClassFunc, apool) { +linestylefilter.getLineStyleFilter = (lineLength, aline, textAndClassFunc, apool) => { // Plugin Hook to add more Attrib Classes for (const attribClasses of hooks.callAll('aceAttribClasses', linestylefilter.ATTRIB_CLASSES)) { Object.assign(linestylefilter.ATTRIB_CLASSES, attribClasses); } - if (lineLength == 0) return textAndClassFunc; + if (lineLength === 0) return textAndClassFunc; const nextAfterAuthorColors = textAndClassFunc; - const authorColorFunc = (function () { + const authorColorFunc = (() => { const lineEnd = lineLength; let curIndex = 0; let extraClasses; let leftInAuthor; - function attribsToClasses(attribs) { + const attribsToClasses = (attribs) => { let classes = ''; let isLineAttribMarker = false; @@ -81,14 +80,14 @@ linestylefilter.getLineStyleFilter = function (lineLength, aline, textAndClassFu if (key) { const value = apool.getAttribValue(n); if (value) { - if (!isLineAttribMarker && _.indexOf(AttributeManager.lineAttributes, key) >= 0) { + if (!isLineAttribMarker && AttributeManager.lineAttributes.indexOf(key) >= 0) { isLineAttribMarker = true; } - if (key == 'author') { + if (key === 'author') { classes += ` ${linestylefilter.getAuthorClassName(value)}`; - } else if (key == 'list') { + } else if (key === 'list') { classes += ` list:${value}`; - } else if (key == 'start') { + } else if (key === 'start') { // Needed to introduce the correct Ordered list item start number on import classes += ` start:${value}`; } else if (linestylefilter.ATTRIB_CLASSES[key]) { @@ -106,37 +105,38 @@ linestylefilter.getLineStyleFilter = function (lineLength, aline, textAndClassFu if (isLineAttribMarker) classes += ` ${lineAttributeMarker}`; return classes.substring(1); - } + }; const attributionIter = Changeset.opIterator(aline); let nextOp, nextOpClasses; - function goNextOp() { + const goNextOp = () => { nextOp = attributionIter.next(); nextOpClasses = (nextOp.opcode && attribsToClasses(nextOp.attribs)); - } + }; goNextOp(); - function nextClasses() { + const nextClasses = () => { if (curIndex < lineEnd) { extraClasses = nextOpClasses; leftInAuthor = nextOp.chars; goNextOp(); - while (nextOp.opcode && nextOpClasses == extraClasses) { + while (nextOp.opcode && nextOpClasses === extraClasses) { leftInAuthor += nextOp.chars; goNextOp(); } } - } + }; nextClasses(); - return function (txt, cls) { + return (txt, cls) => { const disableAuthColorForThisLine = hooks.callAll('disableAuthorColorsForThisLine', { linestylefilter, text: txt, class: cls, }, ' ', ' ', ''); - const disableAuthors = (disableAuthColorForThisLine == null || disableAuthColorForThisLine.length == 0) ? false : disableAuthColorForThisLine[0]; + const disableAuthors = (disableAuthColorForThisLine == null || + disableAuthColorForThisLine.length === 0) ? false : disableAuthColorForThisLine[0]; while (txt.length > 0) { if (leftInAuthor <= 0 || disableAuthors) { // prevent infinite loop if something funny's going on @@ -151,7 +151,7 @@ linestylefilter.getLineStyleFilter = function (lineLength, aline, textAndClassFu nextAfterAuthorColors(curTxt, (cls && `${cls} `) + extraClasses); curIndex += spanSize; leftInAuthor -= spanSize; - if (leftInAuthor == 0) { + if (leftInAuthor === 0) { nextClasses(); } } @@ -160,7 +160,7 @@ linestylefilter.getLineStyleFilter = function (lineLength, aline, textAndClassFu return authorColorFunc; }; -linestylefilter.getAtSignSplitterFilter = function (lineText, textAndClassFunc) { +linestylefilter.getAtSignSplitterFilter = (lineText, textAndClassFunc) => { const at = /@/g; at.lastIndex = 0; let splitPoints = null; @@ -177,66 +177,66 @@ linestylefilter.getAtSignSplitterFilter = function (lineText, textAndClassFunc) return linestylefilter.textAndClassFuncSplitter(textAndClassFunc, splitPoints); }; -linestylefilter.getRegexpFilter = function (regExp, tag) { - return function (lineText, textAndClassFunc) { - regExp.lastIndex = 0; - let regExpMatchs = null; - let splitPoints = null; - let execResult; - while ((execResult = regExp.exec(lineText))) { - if (!regExpMatchs) { - regExpMatchs = []; - splitPoints = []; - } - const startIndex = execResult.index; - const regExpMatch = execResult[0]; - regExpMatchs.push([startIndex, regExpMatch]); - splitPoints.push(startIndex, startIndex + regExpMatch.length); +linestylefilter.getRegexpFilter = (regExp, tag) => (lineText, textAndClassFunc) => { + regExp.lastIndex = 0; + let regExpMatchs = null; + let splitPoints = null; + let execResult; + while ((execResult = regExp.exec(lineText))) { + if (!regExpMatchs) { + regExpMatchs = []; + splitPoints = []; } + const startIndex = execResult.index; + const regExpMatch = execResult[0]; + regExpMatchs.push([startIndex, regExpMatch]); + splitPoints.push(startIndex, startIndex + regExpMatch.length); + } - if (!regExpMatchs) return textAndClassFunc; + if (!regExpMatchs) return textAndClassFunc; - function regExpMatchForIndex(idx) { - for (let k = 0; k < regExpMatchs.length; k++) { - const u = regExpMatchs[k]; - if (idx >= u[0] && idx < u[0] + u[1].length) { - return u[1]; - } + const regExpMatchForIndex = (idx) => { + for (let k = 0; k < regExpMatchs.length; k++) { + const u = regExpMatchs[k]; + if (idx >= u[0] && idx < u[0] + u[1].length) { + return u[1]; } - return false; } - - const handleRegExpMatchsAfterSplit = (function () { - let curIndex = 0; - return function (txt, cls) { - const txtlen = txt.length; - let newCls = cls; - const regExpMatch = regExpMatchForIndex(curIndex); - if (regExpMatch) { - newCls += ` ${tag}:${regExpMatch}`; - } - textAndClassFunc(txt, newCls); - curIndex += txtlen; - }; - })(); - - return linestylefilter.textAndClassFuncSplitter(handleRegExpMatchsAfterSplit, splitPoints); + return false; }; + + const handleRegExpMatchsAfterSplit = (() => { + let curIndex = 0; + return (txt, cls) => { + const txtlen = txt.length; + let newCls = cls; + const regExpMatch = regExpMatchForIndex(curIndex); + if (regExpMatch) { + newCls += ` ${tag}:${regExpMatch}`; + } + textAndClassFunc(txt, newCls); + curIndex += txtlen; + }; + })(); + + return linestylefilter.textAndClassFuncSplitter(handleRegExpMatchsAfterSplit, splitPoints); }; linestylefilter.getURLFilter = linestylefilter.getRegexpFilter(padutils.urlRegex, 'url'); -linestylefilter.textAndClassFuncSplitter = function (func, splitPointsOpt) { +linestylefilter.textAndClassFuncSplitter = (func, splitPointsOpt) => { let nextPointIndex = 0; let idx = 0; // don't split at 0 - while (splitPointsOpt && nextPointIndex < splitPointsOpt.length && splitPointsOpt[nextPointIndex] == 0) { + while (splitPointsOpt && + nextPointIndex < splitPointsOpt.length && + splitPointsOpt[nextPointIndex] === 0) { nextPointIndex++; } - function spanHandler(txt, cls) { + const spanHandler = (txt, cls) => { if ((!splitPointsOpt) || nextPointIndex >= splitPointsOpt.length) { func(txt, cls); idx += txt.length; @@ -247,7 +247,7 @@ linestylefilter.textAndClassFuncSplitter = function (func, splitPointsOpt) { if (pointLocInSpan >= txtlen) { func(txt, cls); idx += txt.length; - if (pointLocInSpan == txtlen) { + if (pointLocInSpan === txtlen) { nextPointIndex++; } } else { @@ -260,18 +260,18 @@ linestylefilter.textAndClassFuncSplitter = function (func, splitPointsOpt) { spanHandler(txt.substring(pointLocInSpan), cls); } } - } + }; return spanHandler; }; -linestylefilter.getFilterStack = function (lineText, textAndClassFunc, abrowser) { +linestylefilter.getFilterStack = (lineText, textAndClassFunc, abrowser) => { let func = linestylefilter.getURLFilter(lineText, textAndClassFunc); const hookFilters = hooks.callAll('aceGetFilterStack', { linestylefilter, browser: abrowser, }); - _.map(hookFilters, (hookFilter) => { + hookFilters.map((hookFilter) => { func = hookFilter(lineText, func); }); @@ -279,16 +279,16 @@ linestylefilter.getFilterStack = function (lineText, textAndClassFunc, abrowser) }; // domLineObj is like that returned by domline.createDomLine -linestylefilter.populateDomLine = function (textLine, aline, apool, domLineObj) { +linestylefilter.populateDomLine = (textLine, aline, apool, domLineObj) => { // remove final newline from text if any let text = textLine; - if (text.slice(-1) == '\n') { + if (text.slice(-1) === '\n') { text = text.substring(0, text.length - 1); } - function textAndClassFunc(tokenText, tokenClass) { + const textAndClassFunc = (tokenText, tokenClass) => { domLineObj.appendSpan(tokenText, tokenClass); - } + }; let func = linestylefilter.getFilterStack(text, textAndClassFunc); func = linestylefilter.getLineStyleFilter(text.length, aline, func, apool); diff --git a/src/static/js/rjquery.js b/src/static/js/rjquery.js index f055d56f8..d729a25ba 100644 --- a/src/static/js/rjquery.js +++ b/src/static/js/rjquery.js @@ -1,4 +1,5 @@ -// Proviedes a require'able version of jQuery without leaking $ and jQuery; +'use strict'; +// Provides a require'able version of jQuery without leaking $ and jQuery; window.$ = require('./jquery'); const jq = window.$.noConflict(true); exports.jQuery = exports.$ = jq; From 75ffe40c09126372a74faf020d1778315cc6fd83 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Dec 2020 14:41:03 +0000 Subject: [PATCH 039/357] lint: attributepool.js --- src/static/js/AttributePool.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/static/js/AttributePool.js b/src/static/js/AttributePool.js index 78d3e7c5b..541d633ee 100644 --- a/src/static/js/AttributePool.js +++ b/src/static/js/AttributePool.js @@ -1,3 +1,5 @@ +'use strict'; + /** * This code represents the Attribute Pool Object of the original Etherpad. * 90% of the code is still like in the original Etherpad @@ -69,7 +71,7 @@ AttributePool.prototype.getAttribValue = function (num) { }; AttributePool.prototype.eachAttrib = function (func) { - for (const n in this.numToAttrib) { + for (const n of this.numToAttrib) { const pair = this.numToAttrib[n]; func(pair[0], pair[1]); } @@ -86,7 +88,7 @@ AttributePool.prototype.fromJsonable = function (obj) { this.numToAttrib = obj.numToAttrib; this.nextNum = obj.nextNum; this.attribToNum = {}; - for (const n in this.numToAttrib) { + for (const n of this.numToAttrib) { this.attribToNum[String(this.numToAttrib[n])] = Number(n); } return this; From efc323cd71f5614269f8c681ffc4764f0a0447fb Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Dec 2020 14:49:11 +0000 Subject: [PATCH 040/357] Revert "lint: attributepool.js" This reverts commit 33baaafbdeb4c0f82ec504738f5851f9ab5261a8. --- src/static/js/AttributePool.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/static/js/AttributePool.js b/src/static/js/AttributePool.js index 541d633ee..78d3e7c5b 100644 --- a/src/static/js/AttributePool.js +++ b/src/static/js/AttributePool.js @@ -1,5 +1,3 @@ -'use strict'; - /** * This code represents the Attribute Pool Object of the original Etherpad. * 90% of the code is still like in the original Etherpad @@ -71,7 +69,7 @@ AttributePool.prototype.getAttribValue = function (num) { }; AttributePool.prototype.eachAttrib = function (func) { - for (const n of this.numToAttrib) { + for (const n in this.numToAttrib) { const pair = this.numToAttrib[n]; func(pair[0], pair[1]); } @@ -88,7 +86,7 @@ AttributePool.prototype.fromJsonable = function (obj) { this.numToAttrib = obj.numToAttrib; this.nextNum = obj.nextNum; this.attribToNum = {}; - for (const n of this.numToAttrib) { + for (const n in this.numToAttrib) { this.attribToNum[String(this.numToAttrib[n])] = Number(n); } return this; From 76744d97830781a4f971b0c4d600b23f7f4cbc1d Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Dec 2020 14:54:21 +0000 Subject: [PATCH 041/357] lint: AttributePool.js --- src/static/js/AttributePool.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/static/js/AttributePool.js b/src/static/js/AttributePool.js index 78d3e7c5b..8f001f84a 100644 --- a/src/static/js/AttributePool.js +++ b/src/static/js/AttributePool.js @@ -1,3 +1,4 @@ +'use strict'; /** * This code represents the Attribute Pool Object of the original Etherpad. * 90% of the code is still like in the original Etherpad @@ -69,7 +70,7 @@ AttributePool.prototype.getAttribValue = function (num) { }; AttributePool.prototype.eachAttrib = function (func) { - for (const n in this.numToAttrib) { + for (const n of this.numToAttrib) { const pair = this.numToAttrib[n]; func(pair[0], pair[1]); } @@ -86,7 +87,7 @@ AttributePool.prototype.fromJsonable = function (obj) { this.numToAttrib = obj.numToAttrib; this.nextNum = obj.nextNum; this.attribToNum = {}; - for (const n in this.numToAttrib) { + for (const n of this.numToAttrib) { this.attribToNum[String(this.numToAttrib[n])] = Number(n); } return this; From aeab9cc0ad5dda8f04dd8aea3c80eed17689d049 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Dec 2020 15:01:12 +0000 Subject: [PATCH 042/357] lint: attributepool fix incorrect commit --- src/static/js/AttributePool.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/static/js/AttributePool.js b/src/static/js/AttributePool.js index 8f001f84a..79849f4d3 100644 --- a/src/static/js/AttributePool.js +++ b/src/static/js/AttributePool.js @@ -70,7 +70,7 @@ AttributePool.prototype.getAttribValue = function (num) { }; AttributePool.prototype.eachAttrib = function (func) { - for (const n of this.numToAttrib) { + for (const n of Object.keys(this.numToAttrib)) { const pair = this.numToAttrib[n]; func(pair[0], pair[1]); } @@ -87,7 +87,7 @@ AttributePool.prototype.fromJsonable = function (obj) { this.numToAttrib = obj.numToAttrib; this.nextNum = obj.nextNum; this.attribToNum = {}; - for (const n of this.numToAttrib) { + for (const n of Object.keys(this.numToAttrib)) { this.attribToNum[String(this.numToAttrib[n])] = Number(n); } return this; From 1d57d4ee3ff94c01540a0da4cb458cbc2268f63e Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Dec 2020 15:01:20 +0000 Subject: [PATCH 043/357] lint: caretPosition linting --- src/static/js/caretPosition.js | 106 ++++++++++++++++----------------- 1 file changed, 51 insertions(+), 55 deletions(-) diff --git a/src/static/js/caretPosition.js b/src/static/js/caretPosition.js index e59fb4be5..32f6d919b 100644 --- a/src/static/js/caretPosition.js +++ b/src/static/js/caretPosition.js @@ -1,18 +1,20 @@ +'use strict'; + // One rep.line(div) can be broken in more than one line in the browser. // This function is useful to get the caret position of the line as // is represented by the browser -exports.getPosition = function () { +exports.getPosition = () => { let rect, line; - const editor = $('#innerdocbody')[0]; const range = getSelectionRange(); - const isSelectionInsideTheEditor = range && $(range.endContainer).closest('body')[0].id === 'innerdocbody'; + const isSelectionInsideTheEditor = range && + $(range.endContainer).closest('body')[0].id === 'innerdocbody'; if (isSelectionInsideTheEditor) { // when we have the caret in an empty line, e.g. a line with only a
, // getBoundingClientRect() returns all dimensions value as 0 const selectionIsInTheBeginningOfLine = range.endOffset > 0; if (selectionIsInTheBeginningOfLine) { - var clonedRange = createSelectionRange(range); + const clonedRange = createSelectionRange(range); line = getPositionOfElementOrSelection(clonedRange); clonedRange.detach(); } @@ -20,7 +22,7 @@ exports.getPosition = function () { // when there's a
or any element that has no height, we can't get // the dimension of the element where the caret is if (!rect || rect.height === 0) { - var clonedRange = createSelectionRange(range); + const clonedRange = createSelectionRange(range); // as we can't get the element height, we create a text node to get the dimensions // on the position @@ -36,8 +38,8 @@ exports.getPosition = function () { return line; }; -var createSelectionRange = function (range) { - clonedRange = range.cloneRange(); +const createSelectionRange = (range) => { + const clonedRange = range.cloneRange(); // we set the selection start and end to avoid error when user selects a text bigger than // the viewport height and uses the arrow keys to expand the selection. In this particular @@ -48,7 +50,7 @@ var createSelectionRange = function (range) { return clonedRange; }; -const getPositionOfRepLineAtOffset = function (node, offset) { +const getPositionOfRepLineAtOffset = (node, offset) => { // it is not a text node, so we cannot make a selection if (node.tagName === 'BR' || node.tagName === 'EMPTY') { return getPositionOfElementOrSelection(node); @@ -66,7 +68,7 @@ const getPositionOfRepLineAtOffset = function (node, offset) { return linePosition; }; -function getPositionOfElementOrSelection(element) { +const getPositionOfElementOrSelection = (element) => { const rect = element.getBoundingClientRect(); const linePosition = { bottom: rect.bottom, @@ -74,15 +76,15 @@ function getPositionOfElementOrSelection(element) { top: rect.top, }; return linePosition; -} +}; // here we have two possibilities: -// [1] the line before the caret line has the same type, so both of them has the same margin, padding -// height, etc. So, we can use the caret line to make calculation necessary to know where is the top -// of the previous line +// [1] the line before the caret line has the same type, so both of them has the same margin, +// padding height, etc. So, we can use the caret line to make calculation necessary to know +// where is the top of the previous line // [2] the line before is part of another rep line. It's possible this line has different margins // height. So we have to get the exactly position of the line -exports.getPositionTopOfPreviousBrowserLine = function (caretLinePosition, rep) { +exports.getPositionTopOfPreviousBrowserLine = (caretLinePosition, rep) => { let previousLineTop = caretLinePosition.top - caretLinePosition.height; // [1] const isCaretLineFirstBrowserLine = caretLineIsFirstBrowserLine(caretLinePosition.top, rep); @@ -91,13 +93,14 @@ exports.getPositionTopOfPreviousBrowserLine = function (caretLinePosition, rep) if (isCaretLineFirstBrowserLine) { // [2] const lineBeforeCaretLine = rep.selStart[0] - 1; const firstLineVisibleBeforeCaretLine = getPreviousVisibleLine(lineBeforeCaretLine, rep); - const linePosition = getDimensionOfLastBrowserLineOfRepLine(firstLineVisibleBeforeCaretLine, rep); + const linePosition = + getDimensionOfLastBrowserLineOfRepLine(firstLineVisibleBeforeCaretLine, rep); previousLineTop = linePosition.top; } return previousLineTop; }; -function caretLineIsFirstBrowserLine(caretLineTop, rep) { +const caretLineIsFirstBrowserLine = (caretLineTop, rep) => { const caretRepLine = rep.selStart[0]; const lineNode = rep.lines.atIndex(caretRepLine).lineNode; const firstRootNode = getFirstRootChildNode(lineNode); @@ -105,37 +108,28 @@ function caretLineIsFirstBrowserLine(caretLineTop, rep) { // to get the position of the node we get the position of the first char const positionOfFirstRootNode = getPositionOfRepLineAtOffset(firstRootNode, 1); return positionOfFirstRootNode.top === caretLineTop; -} +}; // find the first root node, usually it is a text node -function getFirstRootChildNode(node) { +const getFirstRootChildNode = (node) => { if (!node.firstChild) { return node; } else { return getFirstRootChildNode(node.firstChild); } -} +}; -function getPreviousVisibleLine(line, rep) { - if (line < 0) { - return 0; - } else if (isLineVisible(line, rep)) { - return line; - } else { - return getPreviousVisibleLine(line - 1, rep); - } -} - -function getDimensionOfLastBrowserLineOfRepLine(line, rep) { +const getDimensionOfLastBrowserLineOfRepLine = (line, rep) => { const lineNode = rep.lines.atIndex(line).lineNode; const lastRootChildNode = getLastRootChildNode(lineNode); // we get the position of the line in the last char of it - const lastRootChildNodePosition = getPositionOfRepLineAtOffset(lastRootChildNode.node, lastRootChildNode.length); + const lastRootChildNodePosition = + getPositionOfRepLineAtOffset(lastRootChildNode.node, lastRootChildNode.length); return lastRootChildNodePosition; -} +}; -function getLastRootChildNode(node) { +const getLastRootChildNode = (node) => { if (!node.lastChild) { return { node, @@ -144,39 +138,42 @@ function getLastRootChildNode(node) { } else { return getLastRootChildNode(node.lastChild); } -} +}; // here we have two possibilities: // [1] The next line is part of the same rep line of the caret line, so we have the same dimensions. // So, we can use the caret line to calculate the bottom of the line. -// [2] the next line is part of another rep line. It's possible this line has different dimensions, so we -// have to get the exactly dimension of it -exports.getBottomOfNextBrowserLine = function (caretLinePosition, rep) { +// [2] the next line is part of another rep line. +// It's possible this line has different dimensions, so we have to get the exactly dimension of it +exports.getBottomOfNextBrowserLine = (caretLinePosition, rep) => { let nextLineBottom = caretLinePosition.bottom + caretLinePosition.height; // [1] - const isCaretLineLastBrowserLine = caretLineIsLastBrowserLineOfRepLine(caretLinePosition.top, rep); + const isCaretLineLastBrowserLine = + caretLineIsLastBrowserLineOfRepLine(caretLinePosition.top, rep); // the caret is at the end of a rep line, so we can get the next browser line dimension // using the position of the first char of the next rep line if (isCaretLineLastBrowserLine) { // [2] const nextLineAfterCaretLine = rep.selStart[0] + 1; const firstNextLineVisibleAfterCaretLine = getNextVisibleLine(nextLineAfterCaretLine, rep); - const linePosition = getDimensionOfFirstBrowserLineOfRepLine(firstNextLineVisibleAfterCaretLine, rep); + const linePosition = + getDimensionOfFirstBrowserLineOfRepLine(firstNextLineVisibleAfterCaretLine, rep); nextLineBottom = linePosition.bottom; } return nextLineBottom; }; -function caretLineIsLastBrowserLineOfRepLine(caretLineTop, rep) { +const caretLineIsLastBrowserLineOfRepLine = (caretLineTop, rep) => { const caretRepLine = rep.selStart[0]; const lineNode = rep.lines.atIndex(caretRepLine).lineNode; const lastRootChildNode = getLastRootChildNode(lineNode); // we take a rep line and get the position of the last char of it - const lastRootChildNodePosition = getPositionOfRepLineAtOffset(lastRootChildNode.node, lastRootChildNode.length); + const lastRootChildNodePosition = + getPositionOfRepLineAtOffset(lastRootChildNode.node, lastRootChildNode.length); return lastRootChildNodePosition.top === caretLineTop; -} +}; -function getPreviousVisibleLine(line, rep) { +const getPreviousVisibleLine = (line, rep) => { const firstLineOfPad = 0; if (line <= firstLineOfPad) { return firstLineOfPad; @@ -185,10 +182,12 @@ function getPreviousVisibleLine(line, rep) { } else { return getPreviousVisibleLine(line - 1, rep); } -} +}; + + exports.getPreviousVisibleLine = getPreviousVisibleLine; -function getNextVisibleLine(line, rep) { +const getNextVisibleLine = (line, rep) => { const lastLineOfThePad = rep.lines.length() - 1; if (line >= lastLineOfThePad) { return lastLineOfThePad; @@ -197,31 +196,28 @@ function getNextVisibleLine(line, rep) { } else { return getNextVisibleLine(line + 1, rep); } -} +}; exports.getNextVisibleLine = getNextVisibleLine; -function isLineVisible(line, rep) { - return rep.lines.atIndex(line).lineNode.offsetHeight > 0; -} +const isLineVisible = (line, rep) => rep.lines.atIndex(line).lineNode.offsetHeight > 0; -function getDimensionOfFirstBrowserLineOfRepLine(line, rep) { +const getDimensionOfFirstBrowserLineOfRepLine = (line, rep) => { const lineNode = rep.lines.atIndex(line).lineNode; const firstRootChildNode = getFirstRootChildNode(lineNode); // we can get the position of the line, getting the position of the first char of the rep line const firstRootChildNodePosition = getPositionOfRepLineAtOffset(firstRootChildNode, 1); return firstRootChildNodePosition; -} +}; -function getSelectionRange() { - let selection; +const getSelectionRange = () => { if (!window.getSelection) { return; } - selection = window.getSelection(); + const selection = window.getSelection(); if (selection.rangeCount > 0) { return selection.getRangeAt(0); } else { return null; } -} +}; From d9f3bb0e3952f126a4140082da221adb1d5354f5 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Dec 2020 15:07:17 +0000 Subject: [PATCH 044/357] lint: colorutils.js --- src/static/js/colorutils.js | 100 ++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 55 deletions(-) diff --git a/src/static/js/colorutils.js b/src/static/js/colorutils.js index 6feba3a75..9688b8e59 100644 --- a/src/static/js/colorutils.js +++ b/src/static/js/colorutils.js @@ -1,3 +1,5 @@ +'use strict'; + /** * This code is mostly from the old Etherpad. Please help us to comment this code. * This helps other people to understand this code better and helps them to improve it. @@ -26,24 +28,24 @@ const colorutils = {}; // Check that a given value is a css hex color value, e.g. // "#ffffff" or "#fff" -colorutils.isCssHex = function (cssColor) { - return /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(cssColor); -}; +colorutils.isCssHex = (cssColor) => /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(cssColor); // "#ffffff" or "#fff" or "ffffff" or "fff" to [1.0, 1.0, 1.0] -colorutils.css2triple = function (cssColor) { +colorutils.css2triple = (cssColor) => { const sixHex = colorutils.css2sixhex(cssColor); - function hexToFloat(hh) { - return Number(`0x${hh}`) / 255; - } - return [hexToFloat(sixHex.substr(0, 2)), hexToFloat(sixHex.substr(2, 2)), hexToFloat(sixHex.substr(4, 2))]; + const hexToFloat = (hh) => Number(`0x${hh}`) / 255; + return [ + hexToFloat(sixHex.substr(0, 2)), + hexToFloat(sixHex.substr(2, 2)), + hexToFloat(sixHex.substr(4, 2)), + ]; }; // "#ffffff" or "#fff" or "ffffff" or "fff" to "ffffff" -colorutils.css2sixhex = function (cssColor) { +colorutils.css2sixhex = (cssColor) => { let h = /[0-9a-fA-F]+/.exec(cssColor)[0]; - if (h.length != 6) { + if (h.length !== 6) { const a = h.charAt(0); const b = h.charAt(1); const c = h.charAt(2); @@ -53,66 +55,54 @@ colorutils.css2sixhex = function (cssColor) { }; // [1.0, 1.0, 1.0] -> "#ffffff" -colorutils.triple2css = function (triple) { - function floatToHex(n) { +colorutils.triple2css = (triple) => { + const floatToHex = (n) => { const n2 = colorutils.clamp(Math.round(n * 255), 0, 255); return (`0${n2.toString(16)}`).slice(-2); - } + }; return `#${floatToHex(triple[0])}${floatToHex(triple[1])}${floatToHex(triple[2])}`; }; -colorutils.clamp = function (v, bot, top) { - return v < bot ? bot : (v > top ? top : v); -}; -colorutils.min3 = function (a, b, c) { - return (a < b) ? (a < c ? a : c) : (b < c ? b : c); -}; -colorutils.max3 = function (a, b, c) { - return (a > b) ? (a > c ? a : c) : (b > c ? b : c); -}; -colorutils.colorMin = function (c) { - return colorutils.min3(c[0], c[1], c[2]); -}; -colorutils.colorMax = function (c) { - return colorutils.max3(c[0], c[1], c[2]); -}; -colorutils.scale = function (v, bot, top) { - return colorutils.clamp(bot + v * (top - bot), 0, 1); -}; -colorutils.unscale = function (v, bot, top) { - return colorutils.clamp((v - bot) / (top - bot), 0, 1); -}; +colorutils.clamp = (v, bot, top) => v < bot ? bot : (v > top ? top : v); +colorutils.min3 = (a, b, c) => (a < b) ? (a < c ? a : c) : (b < c ? b : c); +colorutils.max3 = (a, b, c) => (a > b) ? (a > c ? a : c) : (b > c ? b : c); +colorutils.colorMin = (c) => colorutils.min3(c[0], c[1], c[2]); +colorutils.colorMax = (c) => colorutils.max3(c[0], c[1], c[2]); +colorutils.scale = (v, bot, top) => colorutils.clamp(bot + v * (top - bot), 0, 1); +colorutils.unscale = (v, bot, top) => colorutils.clamp((v - bot) / (top - bot), 0, 1); -colorutils.scaleColor = function (c, bot, top) { - return [colorutils.scale(c[0], bot, top), colorutils.scale(c[1], bot, top), colorutils.scale(c[2], bot, top)]; -}; +colorutils.scaleColor = (c, bot, top) => [ + colorutils.scale(c[0], bot, top), + colorutils.scale(c[1], bot, top), + colorutils.scale(c[2], bot, top), +]; -colorutils.unscaleColor = function (c, bot, top) { - return [colorutils.unscale(c[0], bot, top), colorutils.unscale(c[1], bot, top), colorutils.unscale(c[2], bot, top)]; -}; +colorutils.unscaleColor = (c, bot, top) => [ + colorutils.unscale(c[0], bot, top), + colorutils.unscale(c[1], bot, top), + colorutils.unscale(c[2], bot, top), +]; -colorutils.luminosity = function (c) { - // rule of thumb for RGB brightness; 1.0 is white - return c[0] * 0.30 + c[1] * 0.59 + c[2] * 0.11; -}; +// rule of thumb for RGB brightness; 1.0 is white +colorutils.luminosity = (c) => c[0] * 0.30 + c[1] * 0.59 + c[2] * 0.11; -colorutils.saturate = function (c) { +colorutils.saturate = (c) => { const min = colorutils.colorMin(c); const max = colorutils.colorMax(c); if (max - min <= 0) return [1.0, 1.0, 1.0]; return colorutils.unscaleColor(c, min, max); }; -colorutils.blend = function (c1, c2, t) { - return [colorutils.scale(t, c1[0], c2[0]), colorutils.scale(t, c1[1], c2[1]), colorutils.scale(t, c1[2], c2[2])]; -}; +colorutils.blend = (c1, c2, t) => [ + colorutils.scale(t, c1[0], c2[0]), + colorutils.scale(t, c1[1], c2[1]), + colorutils.scale(t, c1[2], c2[2]), +]; -colorutils.invert = function (c) { - return [1 - c[0], 1 - c[1], 1 - c[2]]; -}; +colorutils.invert = (c) => [1 - c[0], 1 - c[1], 1 - c[2]]; -colorutils.complementary = function (c) { +colorutils.complementary = (c) => { const inv = colorutils.invert(c); return [ (inv[0] >= c[0]) ? Math.min(inv[0] * 1.30, 1) : (c[0] * 0.30), @@ -121,9 +111,9 @@ colorutils.complementary = function (c) { ]; }; -colorutils.textColorFromBackgroundColor = function (bgcolor, skinName) { - const white = skinName == 'colibris' ? 'var(--super-light-color)' : '#fff'; - const black = skinName == 'colibris' ? 'var(--super-dark-color)' : '#222'; +colorutils.textColorFromBackgroundColor = (bgcolor, skinName) => { + const white = skinName === 'colibris' ? 'var(--super-light-color)' : '#fff'; + const black = skinName === 'colibris' ? 'var(--super-dark-color)' : '#222'; return colorutils.luminosity(colorutils.css2triple(bgcolor)) < 0.5 ? white : black; }; From 2929ba9894fa34c1ebe4836e2a940eb18533203d Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 22 Dec 2020 15:12:38 +0000 Subject: [PATCH 045/357] lint: cssmanager.js --- src/static/js/cssmanager.js | 45 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/static/js/cssmanager.js b/src/static/js/cssmanager.js index e0c5e9926..0fcdad403 100644 --- a/src/static/js/cssmanager.js +++ b/src/static/js/cssmanager.js @@ -1,3 +1,5 @@ +'use strict'; + /** * This code is mostly from the old Etherpad. Please help us to comment this code. * This helps other people to understand this code better and helps them to improve it. @@ -20,14 +22,15 @@ * limitations under the License. */ -function makeCSSManager(emptyStylesheetTitle, doc) { +const makeCSSManager = (emptyStylesheetTitle, doc) => { if (doc === true) { doc = 'parent'; } else if (!doc) { doc = 'inner'; } - function getSheetByTitle(title) { + const getSheetByTitle = (title) => { + let win; if (doc === 'parent') { win = window.parent.parent; } else if (doc === 'inner') { @@ -35,46 +38,44 @@ function makeCSSManager(emptyStylesheetTitle, doc) { } else if (doc === 'outer') { win = window.parent; } else { - throw 'Unknown dynamic style container'; + throw new Error('Unknown dynamic style container'); } const allSheets = win.document.styleSheets; for (let i = 0; i < allSheets.length; i++) { const s = allSheets[i]; - if (s.title == title) { + if (s.title === title) { return s; } } return null; - } + }; const browserSheet = getSheetByTitle(emptyStylesheetTitle); - function browserRules() { - return (browserSheet.cssRules || browserSheet.rules); - } + const browserRules = () => (browserSheet.cssRules || browserSheet.rules); - function browserDeleteRule(i) { + const browserDeleteRule = (i) => { if (browserSheet.deleteRule) browserSheet.deleteRule(i); else browserSheet.removeRule(i); - } + }; - function browserInsertRule(i, selector) { + const browserInsertRule = (i, selector) => { if (browserSheet.insertRule) browserSheet.insertRule(`${selector} {}`, i); else browserSheet.addRule(selector, null, i); - } + }; const selectorList = []; - function indexOfSelector(selector) { + const indexOfSelector = (selector) => { for (let i = 0; i < selectorList.length; i++) { - if (selectorList[i] == selector) { + if (selectorList[i] === selector) { return i; } } return -1; - } + }; - function selectorStyle(selector) { + const selectorStyle = (selector) => { let i = indexOfSelector(selector); if (i < 0) { // add selector @@ -83,23 +84,21 @@ function makeCSSManager(emptyStylesheetTitle, doc) { i = 0; } return browserRules().item(i).style; - } + }; - function removeSelectorStyle(selector) { + const removeSelectorStyle = (selector) => { const i = indexOfSelector(selector); if (i >= 0) { browserDeleteRule(i); selectorList.splice(i, 1); } - } + }; return { selectorStyle, removeSelectorStyle, - info() { - return `${selectorList.length}:${browserRules().length}`; - }, + info: () => `${selectorList.length}:${browserRules().length}`, }; -} +}; exports.makeCSSManager = makeCSSManager; From 52c08794de166dcb585f734c5641e9925ce1b64f Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 14 Jan 2021 15:44:58 +0100 Subject: [PATCH 046/357] Localisation updates from https://translatewiki.net. --- src/locales/eu.json | 9 +++++++++ src/locales/sl.json | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/locales/eu.json b/src/locales/eu.json index c3205baa8..0797cf742 100644 --- a/src/locales/eu.json +++ b/src/locales/eu.json @@ -27,6 +27,10 @@ "admin_plugins.page-title": "Plugin-en kudeaketa - Etherpad", "admin_plugins.version": "Bertsioa", "admin_plugins_info": "Arazoak konpontzeko informazioa", + "admin_plugins_info.hooks": "Instalatutako kakoak", + "admin_plugins_info.hooks_client": "Bezeroaren aldeko kakoak", + "admin_plugins_info.hooks_server": "Zerbitzari aldeko kakoak", + "admin_plugins_info.parts": "Instalatutako atalaka", "admin_plugins_info.plugins": "Instalatutako plugin-ak", "admin_plugins_info.page-title": "Plugin-en informazioa - Etherpad", "admin_plugins_info.version": "Etherpad bertsioa", @@ -34,6 +38,8 @@ "admin_plugins_info.version_number": "Bertsio-zenbakia", "admin_settings": "Ezarpenak", "admin_settings.current": "Oraingo konfigurazioa", + "admin_settings.current_example-devel": "Adibiderako garapenerako ezarpenen txantiloia", + "admin_settings.current_example-prod": "Adibiderako lanerako ezarpenen txantiloia", "admin_settings.current_restart.value": "Berrabiarazi Etherpad", "admin_settings.current_save.value": "Gorde Ezarpenak", "admin_settings.page-title": "Ezarpenak - Etherpad", @@ -108,8 +114,10 @@ "pad.modals.corruptPad.cause": "Baliteke zerbitzari okerreko konfigurazioagatik edo beste ustekabeko portaera batengatik izatea. Jarri harremanetan zerbitzu-administratzailearekin.", "pad.modals.deleted": "Ezabatua.", "pad.modals.deleted.explanation": "Pad hau ezabatu da.", + "pad.modals.rateLimited": "Baloratzea Mugatuta.", "pad.modals.rateLimited.explanation": "Pad honetara mezu gehiegi bidali dituzu eta ondorioz deskonektatu zaizu.", "pad.modals.rejected.explanation": "Zerbitzariak zure nabigatzailetik bidali den mezu bat baztertu du.", + "pad.modals.rejected.cause": "Baliteke pad-a ikusten ari zinen bitartean zerbitzaria eguneratu izana, edo bestela Etherpad-en arazo bat egon liteke. Orria freskatzen saiatu zaitez.", "pad.modals.disconnected": "Deskonektatua izan zara.", "pad.modals.disconnected.explanation": "Zerbitzariarekiko konexioa galdu da", "pad.modals.disconnected.cause": "Baliteke zerbitzaria eskuragarri ez egotea. Mesedez, jakinarazi zerbitzuko administratzaileari honek gertatzen jarraitzen badu.", @@ -122,6 +130,7 @@ "pad.chat.loadmessages": "Kargatu mezu gehiago", "pad.chat.stick.title": "Itsatsi txata pantailan", "pad.chat.writeMessage.placeholder": "Idatzi hemen zure mezua", + "timeslider.followContents": "Jarraitu pad-aren edukien eguneratzeak", "timeslider.pageTitle": "{{appTitle}} Denbora-lerroa", "timeslider.toolbar.returnbutton": "Itzuli pad-era", "timeslider.toolbar.authors": "Egileak:", diff --git a/src/locales/sl.json b/src/locales/sl.json index cd0d7b979..35dcfba6f 100644 --- a/src/locales/sl.json +++ b/src/locales/sl.json @@ -2,6 +2,7 @@ "@metadata": { "authors": [ "Dbc334", + "Eleassar", "HairyFotr", "Mateju", "Skalcaa", @@ -33,7 +34,7 @@ "pad.noCookie": "Piškotka ni bilo mogoče najti. Prosimo, dovolite piškotke v vašem brskalniku!", "pad.permissionDenied": "Nimate dovoljenja za dostop do tega dokumenta.", "pad.settings.padSettings": "Nastavitve dokumenta", - "pad.settings.myView": "Moj pogled", + "pad.settings.myView": "Moj prikaz", "pad.settings.stickychat": "Vsebina klepeta je vedno na zaslonu", "pad.settings.chatandusers": "Prikaži klepet in uporabnike", "pad.settings.colorcheck": "Barve avtorstva", From 611d416d54937fc02f8d0809c6a01f4f42c0fdf0 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 15 Jan 2021 16:57:53 -0500 Subject: [PATCH 047/357] db: Capitalize `Database` constructor --- src/node/db/DB.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/db/DB.js b/src/node/db/DB.js index 12d3d9f80..0a304caf0 100644 --- a/src/node/db/DB.js +++ b/src/node/db/DB.js @@ -28,7 +28,7 @@ const util = require('util'); // set database settings const db = - new ueberDB.database(settings.dbType, settings.dbSettings, null, log4js.getLogger('ueberDB')); + new ueberDB.Database(settings.dbType, settings.dbSettings, null, log4js.getLogger('ueberDB')); /** * The UeberDB Object that provides the database functions From 0ba833c63224ac0dbecd9cb91cbe066872c4d353 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 15 Jan 2021 17:26:58 -0500 Subject: [PATCH 048/357] db: Update ueberdb2 dependency --- src/package-lock.json | 26 +++++++++++++------------- src/package.json | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/package-lock.json b/src/package-lock.json index 1e902d295..db0516bf9 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -564,14 +564,14 @@ "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" }, "@types/node": { - "version": "14.14.20", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz", - "integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==" + "version": "14.14.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz", + "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==" }, "@types/node-fetch": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz", - "integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==", + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.8.tgz", + "integrity": "sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw==", "requires": { "@types/node": "*", "form-data": "^3.0.0" @@ -4071,9 +4071,9 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "mssql": { - "version": "7.0.0-alpha.4", - "resolved": "https://registry.npmjs.org/mssql/-/mssql-7.0.0-alpha.4.tgz", - "integrity": "sha512-vt/OGFG2tw86TiEIAldy0LEkmoPqR9ZsBjxlWeriTs6CuPrE+o2LfXRZ7rI6Awx8QvckZbCShZgRGZoELUQ9ZQ==", + "version": "7.0.0-beta.1", + "resolved": "https://registry.npmjs.org/mssql/-/mssql-7.0.0-beta.1.tgz", + "integrity": "sha512-iyFnNVWhjTnIy5nKCq+v/de8mr41TbNKRrulDXASuDqbjOlfLe6YefnXp+ZRPHJUU1rrjmtAnrUoN2y7dc2LUg==", "requires": { "debug": "^4", "tarn": "^3.0.1", @@ -9358,9 +9358,9 @@ } }, "ueberdb2": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ueberdb2/-/ueberdb2-1.2.1.tgz", - "integrity": "sha512-b1WeG5KinxddpFAkHcFJW5tDuoGMonI3AyZgqmmHKnvF7CJTprPX17n0AOi3iQcu97uoxz9nb56KEQ4gHQOCJQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/ueberdb2/-/ueberdb2-1.2.3.tgz", + "integrity": "sha512-D7lbzudPcOzCSWOHFRvPb6JGFa/zyNB2VcqrTXydwJ6+QlbTF4q6UzriEIWi0xIvp9KkCNJdwzKsDb6EBvXhwQ==", "requires": { "async": "^3.2.0", "cassandra-driver": "^4.5.1", @@ -9371,7 +9371,7 @@ "elasticsearch": "^16.7.1", "mocha": "^7.1.2", "mongodb": "^3.6.3", - "mssql": "^7.0.0-alpha.4", + "mssql": "^7.0.0-beta.1", "mysql": "2.18.1", "nano": "^8.2.2", "pg": "^8.0.3", diff --git a/src/package.json b/src/package.json index 66d71766b..268caa59c 100644 --- a/src/package.json +++ b/src/package.json @@ -70,7 +70,7 @@ "threads": "^1.4.0", "tiny-worker": "^2.3.0", "tinycon": "0.0.1", - "ueberdb2": "^1.2.1", + "ueberdb2": "^1.2.3", "underscore": "1.8.3", "unorm": "1.4.1" }, From 4bda5272dff81e9db19047cb69174fdf8642bb1a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 15 Jan 2021 18:05:39 -0500 Subject: [PATCH 049/357] Revert "db: Capitalize `Database` constructor" I thought a PR adding capitalized `Database` landed in ueberdb2, but apparently not. This reverts commit 611d416d54937fc02f8d0809c6a01f4f42c0fdf0. --- src/node/db/DB.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/db/DB.js b/src/node/db/DB.js index 0a304caf0..12d3d9f80 100644 --- a/src/node/db/DB.js +++ b/src/node/db/DB.js @@ -28,7 +28,7 @@ const util = require('util'); // set database settings const db = - new ueberDB.Database(settings.dbType, settings.dbSettings, null, log4js.getLogger('ueberDB')); + new ueberDB.database(settings.dbType, settings.dbSettings, null, log4js.getLogger('ueberDB')); /** * The UeberDB Object that provides the database functions From dda968e17d68d15867c632953226d6b043cba897 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 15 Jan 2021 18:06:27 -0500 Subject: [PATCH 050/357] Sync top-level `package-lock.json` --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index ff65d83ed..cf5941f4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -306,7 +306,7 @@ "threads": "^1.4.0", "tiny-worker": "^2.3.0", "tinycon": "0.0.1", - "ueberdb2": "^0.5.6", + "ueberdb2": "^1.2.3", "underscore": "1.8.3", "unorm": "1.4.1" }, From c0d9881a621bd39be404fb0905fd11843d0ff4c9 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 16 Jan 2021 19:36:00 +0000 Subject: [PATCH 051/357] stats: add memoryUsageHeap value --- src/node/server.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node/server.js b/src/node/server.js index 3219f5185..3e3b25d21 100755 --- a/src/node/server.js +++ b/src/node/server.js @@ -58,6 +58,7 @@ exports.start = async () => { // start up stats counting system const stats = require('./stats'); stats.gauge('memoryUsage', () => process.memoryUsage().rss); + stats.gauge('memoryUsageHeap', () => process.memoryUsage().heapUsed); await util.promisify(npm.load)(); From 2fdc73735599b5c8f68bf5555be9f6c71c7454de Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 18 Jan 2021 08:53:15 +0000 Subject: [PATCH 052/357] bugfix, lint and refactor all bin scripts (#4617) * bugfix, lint and refactor all bin scripts * for squash: throw Error(message) rather than log(message); throw Error() * for squash: Exit non-0 on unhandled Promise rejection Many of the recent lint changes have converted normal functions to async functions, and an error thrown in an async function does not cause Node.js to exit by default. * for squash: fix `require()` paths * for squash: remove erroneous `Object.keys()` call * for squash: fix missing `continue` statements * for squash: Fix HTTP method for deleteSession * for squash: delete erroneous throw Throw is only for errors, not successful completion. * for squash: redo migrateDirtyDBtoRealDB.js to fix async bugs * for squash: fix erroneous use of `for..of` * for squash: Add line break between statements * for squash: put closing paren on same line as last arg * for squash: Move `log()` back up where it was to minimize the diff to develop * for squash: indentation fixes * for squash: typo fix * for squash: wrap long lines * for squash: use `util.callbackify` to silence promise/no-callback-in-promise warning * for squash: use double quotes to improve readability Co-authored-by: Richard Hansen --- bin/checkAllPads.js | 36 +++---- bin/checkPad.js | 47 +++++---- bin/checkPadDeltas.js | 176 +++++++++++++++++----------------- bin/createUserSession.js | 12 ++- bin/deleteAllGroupSessions.js | 74 +++++++------- bin/deletePad.js | 51 ++++------ bin/extractPadData.js | 35 ++++--- bin/importSqlFile.js | 150 +++++++++++++++-------------- bin/migrateDirtyDBtoRealDB.js | 69 +++++++------ bin/plugins/checkPlugin.js | 46 +++++---- bin/rebuildPad.js | 79 +++++++-------- bin/release.js | 41 ++++---- bin/repairPad.js | 108 +++++++++------------ 13 files changed, 458 insertions(+), 466 deletions(-) diff --git a/bin/checkAllPads.js b/bin/checkAllPads.js index f90e57aef..6e1f14842 100644 --- a/bin/checkAllPads.js +++ b/bin/checkAllPads.js @@ -1,28 +1,31 @@ +'use strict'; /* * This is a debug tool. It checks all revisions for data corruption */ -if (process.argv.length != 2) { - console.error('Use: node bin/checkAllPads.js'); - process.exit(1); -} +// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an +// unhandled rejection into an uncaught exception, which does cause Node.js to exit. +process.on('unhandledRejection', (err) => { throw err; }); + +if (process.argv.length !== 2) throw new Error('Use: node bin/checkAllPads.js'); // load and initialize NPM -const npm = require('../src/node_modules/npm'); +const npm = require('ep_etherpad-lite/node_modules/npm'); npm.load({}, async () => { try { // initialize the database - const settings = require('../src/node/utils/Settings'); - const db = require('../src/node/db/DB'); + require('ep_etherpad-lite/node/utils/Settings'); + const db = require('ep_etherpad-lite/node/db/DB'); await db.init(); // load modules - const Changeset = require('../src/static/js/Changeset'); - const padManager = require('../src/node/db/PadManager'); + const Changeset = require('ep_etherpad-lite/static/js/Changeset'); + const padManager = require('ep_etherpad-lite/node/db/PadManager'); + + let revTestedCount = 0; // get all pads const res = await padManager.listAllPads(); - for (const padId of res.padIDs) { const pad = await padManager.getPad(padId); @@ -31,7 +34,6 @@ npm.load({}, async () => { console.error(`[${pad.id}] Missing attribute pool`); continue; } - // create an array with key kevisions // key revisions always save the full pad atext const head = pad.getHeadRevisionNumber(); @@ -71,21 +73,23 @@ npm.load({}, async () => { const apool = pad.pool; let atext = revisions[keyRev].meta.atext; - for (let rev = keyRev + 1; rev <= keyRev + 100 && rev <= head; rev++) { try { const cs = revisions[rev].changeset; atext = Changeset.applyToAText(cs, atext, apool); + revTestedCount++; } catch (e) { - console.error(`[${pad.id}] Bad changeset at revision ${i} - ${e.message}`); + console.error(`[${pad.id}] Bad changeset at revision ${rev} - ${e.message}`); } } } - console.log('finished'); - process.exit(0); } + if (revTestedCount === 0) { + throw new Error('No revisions tested'); + } + console.log(`Finished: Tested ${revTestedCount} revisions`); } catch (err) { console.trace(err); - process.exit(1); + throw err; } }); diff --git a/bin/checkPad.js b/bin/checkPad.js index 323840e72..de1c51402 100644 --- a/bin/checkPad.js +++ b/bin/checkPad.js @@ -1,33 +1,33 @@ +'use strict'; /* * This is a debug tool. It checks all revisions for data corruption */ -if (process.argv.length != 3) { - console.error('Use: node bin/checkPad.js $PADID'); - process.exit(1); -} +// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an +// unhandled rejection into an uncaught exception, which does cause Node.js to exit. +process.on('unhandledRejection', (err) => { throw err; }); + +if (process.argv.length !== 3) throw new Error('Use: node bin/checkPad.js $PADID'); // get the padID const padId = process.argv[2]; +let checkRevisionCount = 0; // load and initialize NPM; -const npm = require('../src/node_modules/npm'); +const npm = require('ep_etherpad-lite/node_modules/npm'); npm.load({}, async () => { try { // initialize database - const settings = require('../src/node/utils/Settings'); - const db = require('../src/node/db/DB'); + require('ep_etherpad-lite/node/utils/Settings'); + const db = require('ep_etherpad-lite/node/db/DB'); await db.init(); // load modules const Changeset = require('ep_etherpad-lite/static/js/Changeset'); - const padManager = require('../src/node/db/PadManager'); + const padManager = require('ep_etherpad-lite/node/db/PadManager'); const exists = await padManager.doesPadExists(padId); - if (!exists) { - console.error('Pad does not exist'); - process.exit(1); - } + if (!exists) throw new Error('Pad does not exist'); // get the pad const pad = await padManager.getPad(padId); @@ -41,7 +41,8 @@ npm.load({}, async () => { } // run through all key revisions - for (const keyRev of keyRevisions) { + for (let keyRev of keyRevisions) { + keyRev = parseInt(keyRev); // create an array of revisions we need till the next keyRevision or the End const revisionsNeeded = []; for (let rev = keyRev; rev <= keyRev + 100 && rev <= head; rev++) { @@ -58,13 +59,12 @@ npm.load({}, async () => { } // check if the pad has a pool - if (pad.pool === undefined) { - console.error('Attribute pool is missing'); - process.exit(1); - } + if (pad.pool === undefined) throw new Error('Attribute pool is missing'); // check if there is an atext in the keyRevisions - if (revisions[keyRev] === undefined || revisions[keyRev].meta === undefined || revisions[keyRev].meta.atext === undefined) { + if (revisions[keyRev] === undefined || + revisions[keyRev].meta === undefined || + revisions[keyRev].meta.atext === undefined) { console.error(`No atext in key revision ${keyRev}`); continue; } @@ -73,8 +73,8 @@ npm.load({}, async () => { let atext = revisions[keyRev].meta.atext; for (let rev = keyRev + 1; rev <= keyRev + 100 && rev <= head; rev++) { + checkRevisionCount++; try { - // console.log("check revision " + rev); const cs = revisions[rev].changeset; atext = Changeset.applyToAText(cs, atext, apool); } catch (e) { @@ -82,11 +82,10 @@ npm.load({}, async () => { continue; } } - console.log('finished'); - process.exit(0); + console.log(`Finished: Checked ${checkRevisionCount} revisions`); } - } catch (e) { - console.trace(e); - process.exit(1); + } catch (err) { + console.trace(err); + throw err; } }); diff --git a/bin/checkPadDeltas.js b/bin/checkPadDeltas.js index 1e45f7148..ecbb20846 100644 --- a/bin/checkPadDeltas.js +++ b/bin/checkPadDeltas.js @@ -1,111 +1,107 @@ +'use strict'; /* * This is a debug tool. It checks all revisions for data corruption */ -if (process.argv.length != 3) { - console.error('Use: node bin/checkPadDeltas.js $PADID'); - process.exit(1); -} +// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an +// unhandled rejection into an uncaught exception, which does cause Node.js to exit. +process.on('unhandledRejection', (err) => { throw err; }); + +if (process.argv.length !== 3) throw new Error('Use: node bin/checkPadDeltas.js $PADID'); // get the padID const padId = process.argv[2]; // load and initialize NPM; -const expect = require('expect.js'); -const diff = require('diff'); -var async = require('async'); - -const npm = require('../src/node_modules/npm'); -var async = require('ep_etherpad-lite/node_modules/async'); -const Changeset = require('ep_etherpad-lite/static/js/Changeset'); +const expect = require('../tests/frontend/lib/expect'); +const diff = require('ep_etherpad-lite/node_modules/diff'); +const npm = require('ep_etherpad-lite/node_modules/npm'); npm.load({}, async () => { - try { - // initialize database - const settings = require('../src/node/utils/Settings'); - const db = require('../src/node/db/DB'); - await db.init(); + // initialize database + require('ep_etherpad-lite/node/utils/Settings'); + const db = require('ep_etherpad-lite/node/db/DB'); + await db.init(); - // load modules - const Changeset = require('ep_etherpad-lite/static/js/Changeset'); - const padManager = require('../src/node/db/PadManager'); + // load modules + const Changeset = require('ep_etherpad-lite/static/js/Changeset'); + const padManager = require('ep_etherpad-lite/node/db/PadManager'); - const exists = await padManager.doesPadExists(padId); - if (!exists) { - console.error('Pad does not exist'); - process.exit(1); + const exists = await padManager.doesPadExists(padId); + if (!exists) throw new Error('Pad does not exist'); + + // get the pad + const pad = await padManager.getPad(padId); + + // create an array with key revisions + // key revisions always save the full pad atext + const head = pad.getHeadRevisionNumber(); + const keyRevisions = []; + for (let i = 0; i < head; i += 100) { + keyRevisions.push(i); + } + + // create an array with all revisions + const revisions = []; + for (let i = 0; i <= head; i++) { + revisions.push(i); + } + + let atext = Changeset.makeAText('\n'); + + // run trough all revisions + for (const revNum of revisions) { + // console.log('Fetching', revNum) + const revision = await db.get(`pad:${padId}:revs:${revNum}`); + // check if there is a atext in the keyRevisions + if (~keyRevisions.indexOf(revNum) && + (revision === undefined || + revision.meta === undefined || + revision.meta.atext === undefined)) { + console.error(`No atext in key revision ${revNum}`); + continue; } - // get the pad - const pad = await padManager.getPad(padId); - - // create an array with key revisions - // key revisions always save the full pad atext - const head = pad.getHeadRevisionNumber(); - const keyRevisions = []; - for (var i = 0; i < head; i += 100) { - keyRevisions.push(i); + // try glue everything together + try { + // console.log("check revision ", revNum); + const cs = revision.changeset; + atext = Changeset.applyToAText(cs, atext, pad.pool); + } catch (e) { + console.error(`Bad changeset at revision ${revNum} - ${e.message}`); + continue; } - // create an array with all revisions - const revisions = []; - for (var i = 0; i <= head; i++) { - revisions.push(i); - } - - let atext = Changeset.makeAText('\n'); - - // run trough all revisions - async.forEachSeries(revisions, (revNum, callback) => { - // console.log('Fetching', revNum) - db.db.get(`pad:${padId}:revs:${revNum}`, (err, revision) => { - if (err) return callback(err); - - // check if there is a atext in the keyRevisions - if (~keyRevisions.indexOf(revNum) && (revision === undefined || revision.meta === undefined || revision.meta.atext === undefined)) { - console.error(`No atext in key revision ${revNum}`); - callback(); - return; - } - - try { - // console.log("check revision ", revNum); - const cs = revision.changeset; - atext = Changeset.applyToAText(cs, atext, pad.pool); - } catch (e) { - console.error(`Bad changeset at revision ${revNum} - ${e.message}`); - callback(); - return; - } - - if (~keyRevisions.indexOf(revNum)) { - try { - expect(revision.meta.atext.text).to.eql(atext.text); - expect(revision.meta.atext.attribs).to.eql(atext.attribs); - } catch (e) { - console.error(`Atext in key revision ${revNum} doesn't match computed one.`); - console.log(diff.diffChars(atext.text, revision.meta.atext.text).map((op) => { if (!op.added && !op.removed) op.value = op.value.length; return op; })); - // console.error(e) - // console.log('KeyRev. :', revision.meta.atext) - // console.log('Computed:', atext) - callback(); - return; - } - } - - setImmediate(callback); - }); - }, (er) => { - if (pad.atext.text == atext.text) { console.log('ok'); } else { - console.error('Pad AText doesn\'t match computed one! (Computed ', atext.text.length, ', db', pad.atext.text.length, ')'); - console.log(diff.diffChars(atext.text, pad.atext.text).map((op) => { if (!op.added && !op.removed) op.value = op.value.length; return op; })); + // check things are working properly + if (~keyRevisions.indexOf(revNum)) { + try { + expect(revision.meta.atext.text).to.eql(atext.text); + expect(revision.meta.atext.attribs).to.eql(atext.attribs); + } catch (e) { + console.error(`Atext in key revision ${revNum} doesn't match computed one.`); + console.log(diff.diffChars(atext.text, revision.meta.atext.text).map((op) => { + if (!op.added && !op.removed) op.value = op.value.length; + return op; + })); + // console.error(e) + // console.log('KeyRev. :', revision.meta.atext) + // console.log('Computed:', atext) + continue; } - callback(er); - }); + } + } - process.exit(0); - } catch (e) { - console.trace(e); - process.exit(1); + // check final text is right... + if (pad.atext.text === atext.text) { + console.log('ok'); + } else { + console.error('Pad AText doesn\'t match computed one! (Computed ', + atext.text.length, ', db', pad.atext.text.length, ')'); + console.log(diff.diffChars(atext.text, pad.atext.text).map((op) => { + if (!op.added && !op.removed) { + op.value = op.value.length; + return op; + } + })); } }); diff --git a/bin/createUserSession.js b/bin/createUserSession.js index 324941ec8..292fde8ba 100644 --- a/bin/createUserSession.js +++ b/bin/createUserSession.js @@ -1,15 +1,19 @@ +'use strict'; + /* * A tool for generating a test user session which can be used for debugging configs * that require sessions. */ -const m = (f) => `${__dirname}/../${f}`; + +// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an +// unhandled rejection into an uncaught exception, which does cause Node.js to exit. +process.on('unhandledRejection', (err) => { throw err; }); const fs = require('fs'); const path = require('path'); const querystring = require('querystring'); -const request = require(m('src/node_modules/request')); -const settings = require(m('src/node/utils/Settings')); -const supertest = require(m('src/node_modules/supertest')); +const settings = require('ep_etherpad-lite/node/utils/Settings'); +const supertest = require('ep_etherpad-lite/node_modules/supertest'); (async () => { const api = supertest(`http://${settings.ip}:${settings.port}`); diff --git a/bin/deleteAllGroupSessions.js b/bin/deleteAllGroupSessions.js index ee0058ffa..fd8ba5341 100644 --- a/bin/deleteAllGroupSessions.js +++ b/bin/deleteAllGroupSessions.js @@ -1,51 +1,47 @@ +'use strict'; + /* * A tool for deleting ALL GROUP sessions Etherpad user sessions from the CLI, * because sometimes a brick is required to fix a face. */ -const request = require('../src/node_modules/request'); -const settings = require(`${__dirname}/../tests/container/loadSettings`).loadSettings(); -const supertest = require(`${__dirname}/../src/node_modules/supertest`); -const api = supertest(`http://${settings.ip}:${settings.port}`); +// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an +// unhandled rejection into an uncaught exception, which does cause Node.js to exit. +process.on('unhandledRejection', (err) => { throw err; }); + +const supertest = require('ep_etherpad-lite/node_modules/supertest'); const path = require('path'); const fs = require('fs'); +// Set a delete counter which will increment on each delete attempt +// TODO: Check delete is successful before incrementing +let deleteCount = 0; + // get the API Key const filePath = path.join(__dirname, '../APIKEY.txt'); -const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'}); +console.log('Deleting all group sessions, please be patient.'); -// Set apiVersion to base value, we change this later. -let apiVersion = 1; -let guids; +(async () => { + const settings = require('../tests/container/loadSettings').loadSettings(); + const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'}); + const api = supertest(`http://${settings.ip}:${settings.port}`); -// Update the apiVersion -api.get('/api/') - .expect((res) => { - apiVersion = res.body.currentVersion; - if (!res.body.currentVersion) throw new Error('No version set in API'); - return; - }) - .then(() => { - const guri = `/api/${apiVersion}/listAllGroups?apikey=${apikey}`; - api.get(guri) - .then((res) => { - guids = res.body.data.groupIDs; - guids.forEach((groupID) => { - const luri = `/api/${apiVersion}/listSessionsOfGroup?apikey=${apikey}&groupID=${groupID}`; - api.get(luri) - .then((res) => { - if (res.body.data) { - Object.keys(res.body.data).forEach((sessionID) => { - if (sessionID) { - console.log('Deleting', sessionID); - const duri = `/api/${apiVersion}/deleteSession?apikey=${apikey}&sessionID=${sessionID}`; - api.post(duri); // deletes - } - }); - } else { - // no session in this group. - } - }); - }); - }); - }); + const apiVersionResponse = await api.get('/api/'); + const apiVersion = apiVersionResponse.body.currentVersion; // 1.12.5 + + const groupsResponse = await api.get(`/api/${apiVersion}/listAllGroups?apikey=${apikey}`); + const groups = groupsResponse.body.data.groupIDs; // ['whateverGroupID'] + + for (const groupID of groups) { + const sessionURI = `/api/${apiVersion}/listSessionsOfGroup?apikey=${apikey}&groupID=${groupID}`; + const sessionsResponse = await api.get(sessionURI); + const sessions = sessionsResponse.body.data; + + for (const sessionID of Object.keys(sessions)) { + const deleteURI = `/api/${apiVersion}/deleteSession?apikey=${apikey}&sessionID=${sessionID}`; + await api.post(deleteURI); // delete + deleteCount++; + } + } + console.log(`Deleted ${deleteCount} sessions`); +})(); diff --git a/bin/deletePad.js b/bin/deletePad.js index e145d63a0..ea9aea7e0 100644 --- a/bin/deletePad.js +++ b/bin/deletePad.js @@ -1,18 +1,21 @@ +'use strict'; + /* * A tool for deleting pads from the CLI, because sometimes a brick is required * to fix a window. */ -const request = require('../src/node_modules/request'); -const settings = require(`${__dirname}/../tests/container/loadSettings`).loadSettings(); -const supertest = require(`${__dirname}/../src/node_modules/supertest`); +// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an +// unhandled rejection into an uncaught exception, which does cause Node.js to exit. +process.on('unhandledRejection', (err) => { throw err; }); + +const settings = require('../tests/container/loadSettings').loadSettings(); +const supertest = require('ep_etherpad-lite/node_modules/supertest'); const api = supertest(`http://${settings.ip}:${settings.port}`); const path = require('path'); const fs = require('fs'); -if (process.argv.length != 3) { - console.error('Use: node deletePad.js $PADID'); - process.exit(1); -} + +if (process.argv.length !== 3) throw new Error('Use: node deletePad.js $PADID'); // get the padID const padId = process.argv[2]; @@ -21,28 +24,14 @@ const padId = process.argv[2]; const filePath = path.join(__dirname, '../APIKEY.txt'); const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'}); -// Set apiVersion to base value, we change this later. -let apiVersion = 1; +(async () => { + let apiVersion = await api.get('/api/'); + apiVersion = apiVersion.body.currentVersion; + if (!apiVersion) throw new Error('No version set in API'); -// Update the apiVersion -api.get('/api/') - .expect((res) => { - apiVersion = res.body.currentVersion; - if (!res.body.currentVersion) throw new Error('No version set in API'); - return; - }) - .end((err, res) => { - // Now we know the latest API version, let's delete pad - const uri = `/api/${apiVersion}/deletePad?apikey=${apikey}&padID=${padId}`; - api.post(uri) - .expect((res) => { - if (res.body.code === 1) { - console.error('Error deleting pad', res.body); - } else { - console.log('Deleted pad', res.body); - } - return; - }) - .end(() => {}); - }); -// end + // Now we know the latest API version, let's delete pad + const uri = `/api/${apiVersion}/deletePad?apikey=${apikey}&padID=${padId}`; + const deleteAttempt = await api.post(uri); + if (deleteAttempt.body.code === 1) throw new Error(`Error deleting pad ${deleteAttempt.body}`); + console.log('Deleted pad', deleteAttempt.body); +})(); diff --git a/bin/extractPadData.js b/bin/extractPadData.js index a811076ef..3b182a571 100644 --- a/bin/extractPadData.js +++ b/bin/extractPadData.js @@ -1,34 +1,34 @@ +'use strict'; + /* * This is a debug tool. It helps to extract all datas of a pad and move it from * a productive environment and to a develop environment to reproduce bugs * there. It outputs a dirtydb file */ -if (process.argv.length != 3) { - console.error('Use: node extractPadData.js $PADID'); - process.exit(1); -} +// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an +// unhandled rejection into an uncaught exception, which does cause Node.js to exit. +process.on('unhandledRejection', (err) => { throw err; }); + +if (process.argv.length !== 3) throw new Error('Use: node extractPadData.js $PADID'); // get the padID const padId = process.argv[2]; -const npm = require('../src/node_modules/npm'); +const npm = require('ep_etherpad-lite/node_modules/npm'); -npm.load({}, async (er) => { - if (er) { - console.error(`Could not load NPM: ${er}`); - process.exit(1); - } +npm.load({}, async (err) => { + if (err) throw err; try { // initialize database - const settings = require('../src/node/utils/Settings'); - const db = require('../src/node/db/DB'); + require('ep_etherpad-lite/node/utils/Settings'); + const db = require('ep_etherpad-lite/node/db/DB'); await db.init(); // load extra modules - const dirtyDB = require('../src/node_modules/dirty'); - const padManager = require('../src/node/db/PadManager'); + const dirtyDB = require('ep_etherpad-lite/node_modules/dirty'); + const padManager = require('ep_etherpad-lite/node/db/PadManager'); const util = require('util'); // initialize output database @@ -67,9 +67,8 @@ npm.load({}, async (er) => { } console.log('finished'); - process.exit(0); - } catch (er) { - console.error(er); - process.exit(1); + } catch (err) { + console.error(err); + throw err; } }); diff --git a/bin/importSqlFile.js b/bin/importSqlFile.js index 870c02cce..35fe4f323 100644 --- a/bin/importSqlFile.js +++ b/bin/importSqlFile.js @@ -1,77 +1,18 @@ +'use strict'; + +// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an +// unhandled rejection into an uncaught exception, which does cause Node.js to exit. +process.on('unhandledRejection', (err) => { throw err; }); + const startTime = Date.now(); -require('ep_etherpad-lite/node_modules/npm').load({}, (er, npm) => { - const fs = require('fs'); - - const ueberDB = require('ep_etherpad-lite/node_modules/ueberdb2'); - const settings = require('ep_etherpad-lite/node/utils/Settings'); - const log4js = require('ep_etherpad-lite/node_modules/log4js'); - - const dbWrapperSettings = { - cache: 0, - writeInterval: 100, - json: false, // data is already json encoded - }; - const db = new ueberDB.database(settings.dbType, settings.dbSettings, dbWrapperSettings, log4js.getLogger('ueberDB')); - - const sqlFile = process.argv[2]; - - // stop if the settings file is not set - if (!sqlFile) { - console.error('Use: node importSqlFile.js $SQLFILE'); - process.exit(1); - } - - log('initializing db'); - db.init((err) => { - // there was an error while initializing the database, output it and stop - if (err) { - console.error('ERROR: Problem while initializing the database'); - console.error(err.stack ? err.stack : err); - process.exit(1); - } else { - log('done'); - - log('open output file...'); - const lines = fs.readFileSync(sqlFile, 'utf8').split('\n'); - - const count = lines.length; - let keyNo = 0; - - process.stdout.write(`Start importing ${count} keys...\n`); - lines.forEach((l) => { - if (l.substr(0, 27) == 'REPLACE INTO store VALUES (') { - const pos = l.indexOf("', '"); - const key = l.substr(28, pos - 28); - let value = l.substr(pos + 3); - value = value.substr(0, value.length - 2); - console.log(`key: ${key} val: ${value}`); - console.log(`unval: ${unescape(value)}`); - db.set(key, unescape(value), null); - keyNo++; - if (keyNo % 1000 == 0) { - process.stdout.write(` ${keyNo}/${count}\n`); - } - } - }); - process.stdout.write('\n'); - process.stdout.write('done. waiting for db to finish transaction. depended on dbms this may take some time...\n'); - - db.close(() => { - log(`finished, imported ${keyNo} keys.`); - process.exit(0); - }); - } - }); -}); - -function log(str) { +const log = (str) => { console.log(`${(Date.now() - startTime) / 1000}\t${str}`); -} +}; -unescape = function (val) { +const unescape = (val) => { // value is a string - if (val.substr(0, 1) == "'") { + if (val.substr(0, 1) === "'") { val = val.substr(0, val.length - 1).substr(1); return val.replace(/\\[0nrbtZ\\'"]/g, (s) => { @@ -88,16 +29,81 @@ unescape = function (val) { } // value is a boolean or NULL - if (val == 'NULL') { + if (val === 'NULL') { return null; } - if (val == 'true') { + if (val === 'true') { return true; } - if (val == 'false') { + if (val === 'false') { return false; } // value is a number return val; }; + + +require('ep_etherpad-lite/node_modules/npm').load({}, (er, npm) => { + const fs = require('fs'); + + const ueberDB = require('ep_etherpad-lite/node_modules/ueberdb2'); + const settings = require('ep_etherpad-lite/node/utils/Settings'); + const log4js = require('ep_etherpad-lite/node_modules/log4js'); + + const dbWrapperSettings = { + cache: 0, + writeInterval: 100, + json: false, // data is already json encoded + }; + const db = new ueberDB.database( // eslint-disable-line new-cap + settings.dbType, + settings.dbSettings, + dbWrapperSettings, + log4js.getLogger('ueberDB')); + + const sqlFile = process.argv[2]; + + // stop if the settings file is not set + if (!sqlFile) throw new Error('Use: node importSqlFile.js $SQLFILE'); + + log('initializing db'); + db.init((err) => { + // there was an error while initializing the database, output it and stop + if (err) { + throw err; + } else { + log('done'); + + log('open output file...'); + const lines = fs.readFileSync(sqlFile, 'utf8').split('\n'); + + const count = lines.length; + let keyNo = 0; + + process.stdout.write(`Start importing ${count} keys...\n`); + lines.forEach((l) => { + if (l.substr(0, 27) === 'REPLACE INTO store VALUES (') { + const pos = l.indexOf("', '"); + const key = l.substr(28, pos - 28); + let value = l.substr(pos + 3); + value = value.substr(0, value.length - 2); + console.log(`key: ${key} val: ${value}`); + console.log(`unval: ${unescape(value)}`); + db.set(key, unescape(value), null); + keyNo++; + if (keyNo % 1000 === 0) { + process.stdout.write(` ${keyNo}/${count}\n`); + } + } + }); + process.stdout.write('\n'); + process.stdout.write('done. waiting for db to finish transaction. ' + + 'depended on dbms this may take some time..\n'); + + db.close(() => { + log(`finished, imported ${keyNo} keys.`); + }); + } + }); +}); diff --git a/bin/migrateDirtyDBtoRealDB.js b/bin/migrateDirtyDBtoRealDB.js index 63425cab7..48760b8ba 100644 --- a/bin/migrateDirtyDBtoRealDB.js +++ b/bin/migrateDirtyDBtoRealDB.js @@ -1,4 +1,12 @@ -require('ep_etherpad-lite/node_modules/npm').load({}, (er, npm) => { +'use strict'; + +// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an +// unhandled rejection into an uncaught exception, which does cause Node.js to exit. +process.on('unhandledRejection', (err) => { throw err; }); + +const util = require('util'); + +require('ep_etherpad-lite/node_modules/npm').load({}, async (er, npm) => { process.chdir(`${npm.root}/..`); // This script requires that you have modified your settings.json file @@ -10,39 +18,42 @@ require('ep_etherpad-lite/node_modules/npm').load({}, (er, npm) => { const settings = require('ep_etherpad-lite/node/utils/Settings'); - let dirty = require('../src/node_modules/dirty'); - const ueberDB = require('../src/node_modules/ueberdb2'); - const log4js = require('../src/node_modules/log4js'); + const dirtyDb = require('ep_etherpad-lite/node_modules/dirty'); + const ueberDB = require('ep_etherpad-lite/node_modules/ueberdb2'); + const log4js = require('ep_etherpad-lite/node_modules/log4js'); const dbWrapperSettings = { cache: '0', // The cache slows things down when you're mostly writing. writeInterval: 0, // Write directly to the database, don't buffer }; - const db = new ueberDB.database(settings.dbType, settings.dbSettings, dbWrapperSettings, log4js.getLogger('ueberDB')); - let i = 0; - let length = 0; + const db = new ueberDB.database( // eslint-disable-line new-cap + settings.dbType, + settings.dbSettings, + dbWrapperSettings, + log4js.getLogger('ueberDB')); + await db.init(); - db.init(() => { - console.log('Waiting for dirtyDB to parse its file.'); - dirty = dirty('var/dirty.db').on('load', () => { - dirty.forEach(() => { - length++; - }); - console.log(`Found ${length} records, processing now.`); + console.log('Waiting for dirtyDB to parse its file.'); + const dirty = dirtyDb('var/dirty.db'); + const length = await new Promise((resolve) => { dirty.once('load', resolve); }); - dirty.forEach(async (key, value) => { - const error = await db.set(key, value); - console.log(`Wrote record ${i}`); - i++; - - if (i === length) { - console.log('finished, just clearing up for a bit...'); - setTimeout(() => { - process.exit(0); - }, 5000); - } - }); - console.log('Please wait for all records to flush to database, then kill this process.'); - }); - console.log('done?'); + console.log(`Found ${length} records, processing now.`); + const p = []; + let numWritten = 0; + dirty.forEach((key, value) => { + let bcb, wcb; + p.push(new Promise((resolve, reject) => { + bcb = (err) => { if (err != null) return reject(err); }; + wcb = (err) => { + if (err != null) return reject(err); + if (++numWritten % 100 === 0) console.log(`Wrote record ${numWritten} of ${length}`); + resolve(); + }; + })); + db.set(key, value, bcb, wcb); }); + await Promise.all(p); + console.log(`Wrote all ${numWritten} records`); + + await util.promisify(db.close.bind(db))(); + console.log('Finished.'); }); diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 570c3bda0..3b8c0b31a 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -9,16 +9,17 @@ * node bin/plugins/checkPlugin.js ep_whatever autocommit */ +// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an +// unhandled rejection into an uncaught exception, which does cause Node.js to exit. +process.on('unhandledRejection', (err) => { throw err; }); + const fs = require('fs'); const childProcess = require('child_process'); // get plugin name & path from user input const pluginName = process.argv[2]; -if (!pluginName) { - console.error('no plugin name specified'); - process.exit(1); -} +if (!pluginName) throw new Error('no plugin name specified'); const pluginPath = `node_modules/${pluginName}`; @@ -107,10 +108,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { files.push(rootFiles[i].toLowerCase()); } - if (files.indexOf('.git') === -1) { - console.error('No .git folder, aborting'); - process.exit(1); - } + if (files.indexOf('.git') === -1) throw new Error('No .git folder, aborting'); prepareRepo(); try { @@ -302,8 +300,10 @@ fs.readdir(pluginPath, (err, rootFiles) => { if (files.indexOf('contributing') === -1 && files.indexOf('contributing.md') === -1) { console.warn('CONTRIBUTING.md file not found, please create'); if (autoFix) { - console.log('Autofixing missing CONTRIBUTING.md file, please edit the CONTRIBUTING.md file further to include plugin specific details.'); - let contributing = fs.readFileSync('bin/plugins/lib/CONTRIBUTING.md', {encoding: 'utf8', flag: 'r'}); + console.log('Autofixing missing CONTRIBUTING.md file, please edit the CONTRIBUTING.md ' + + 'file further to include plugin specific details.'); + let contributing = + fs.readFileSync('bin/plugins/lib/CONTRIBUTING.md', {encoding: 'utf8', flag: 'r'}); contributing = contributing.replace(/\[plugin_name\]/g, pluginName); fs.writeFileSync(`${pluginPath}/CONTRIBUTING.md`, contributing); } @@ -311,7 +311,8 @@ fs.readdir(pluginPath, (err, rootFiles) => { if (files.indexOf('readme') !== -1 && files.indexOf('readme.md') !== -1) { - const readme = fs.readFileSync(`${pluginPath}/${readMeFileName}`, {encoding: 'utf8', flag: 'r'}); + const readme = + fs.readFileSync(`${pluginPath}/${readMeFileName}`, {encoding: 'utf8', flag: 'r'}); if (readme.toLowerCase().indexOf('license') === -1) { console.warn('No license section in README'); if (autoFix) { @@ -335,7 +336,9 @@ fs.readdir(pluginPath, (err, rootFiles) => { travisConfig = travisConfig.replace(/\[plugin_name\]/g, pluginName); if (files.indexOf('.travis.yml') === -1) { - console.warn('.travis.yml file not found, please create. .travis.yml is used for automatically CI testing Etherpad. It is useful to know if your plugin breaks another feature for example.'); + console.warn('.travis.yml file not found, please create. ' + + '.travis.yml is used for automatically CI testing Etherpad. ' + + 'It is useful to know if your plugin breaks another feature for example.'); // TODO: Make it check version of the .travis file to see if it needs an update. if (autoFix) { console.log('Autofixing missing .travis.yml file'); @@ -345,9 +348,11 @@ fs.readdir(pluginPath, (err, rootFiles) => { } if (autoFix) { // checks the file versioning of .travis and updates it to the latest. - const existingConfig = fs.readFileSync(`${pluginPath}/.travis.yml`, {encoding: 'utf8', flag: 'r'}); + const existingConfig = + fs.readFileSync(`${pluginPath}/.travis.yml`, {encoding: 'utf8', flag: 'r'}); const existingConfigLocation = existingConfig.indexOf('##ETHERPAD_TRAVIS_V='); - const existingValue = parseInt(existingConfig.substr(existingConfigLocation + 20, existingConfig.length)); + const existingValue = + parseInt(existingConfig.substr(existingConfigLocation + 20, existingConfig.length)); const newConfigLocation = travisConfig.indexOf('##ETHERPAD_TRAVIS_V='); const newValue = parseInt(travisConfig.substr(newConfigLocation + 20, travisConfig.length)); @@ -362,7 +367,8 @@ fs.readdir(pluginPath, (err, rootFiles) => { } if (files.indexOf('.gitignore') === -1) { - console.warn(".gitignore file not found, please create. .gitignore files are useful to ensure files aren't incorrectly commited to a repository."); + console.warn('.gitignore file not found, please create. .gitignore files are useful to ' + + "ensure files aren't incorrectly commited to a repository."); if (autoFix) { console.log('Autofixing missing .gitignore file'); const gitignore = fs.readFileSync('bin/plugins/lib/gitignore', {encoding: 'utf8', flag: 'r'}); @@ -382,12 +388,15 @@ fs.readdir(pluginPath, (err, rootFiles) => { // if we include templates but don't have translations... if (files.indexOf('templates') !== -1 && files.indexOf('locales') === -1) { - console.warn('Translations not found, please create. Translation files help with Etherpad accessibility.'); + console.warn('Translations not found, please create. ' + + 'Translation files help with Etherpad accessibility.'); } if (files.indexOf('.ep_initialized') !== -1) { - console.warn('.ep_initialized found, please remove. .ep_initialized should never be commited to git and should only exist once the plugin has been executed one time.'); + console.warn( + '.ep_initialized found, please remove. .ep_initialized should never be commited to git ' + + 'and should only exist once the plugin has been executed one time.'); if (autoFix) { console.log('Autofixing incorrectly existing .ep_initialized file'); fs.unlinkSync(`${pluginPath}/.ep_initialized`); @@ -395,7 +404,8 @@ fs.readdir(pluginPath, (err, rootFiles) => { } if (files.indexOf('npm-debug.log') !== -1) { - console.warn('npm-debug.log found, please remove. npm-debug.log should never be commited to your repository.'); + console.warn('npm-debug.log found, please remove. npm-debug.log should never be commited to ' + + 'your repository.'); if (autoFix) { console.log('Autofixing incorrectly existing npm-debug.log file'); fs.unlinkSync(`${pluginPath}/npm-debug.log`); diff --git a/bin/rebuildPad.js b/bin/rebuildPad.js index 12ff21847..0034fe336 100644 --- a/bin/rebuildPad.js +++ b/bin/rebuildPad.js @@ -1,43 +1,39 @@ +'use strict'; + /* This is a repair tool. It rebuilds an old pad at a new pad location up to a known "good" revision. */ -if (process.argv.length != 4 && process.argv.length != 5) { - console.error('Use: node bin/repairPad.js $PADID $REV [$NEWPADID]'); - process.exit(1); +// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an +// unhandled rejection into an uncaught exception, which does cause Node.js to exit. +process.on('unhandledRejection', (err) => { throw err; }); + +if (process.argv.length !== 4 && process.argv.length !== 5) { + throw new Error('Use: node bin/repairPad.js $PADID $REV [$NEWPADID]'); } -const npm = require('../src/node_modules/npm'); -const async = require('../src/node_modules/async'); -const ueberDB = require('../src/node_modules/ueberdb2'); +const async = require('ep_etherpad-lite/node_modules/async'); +const npm = require('ep_etherpad-lite/node_modules/npm'); +const util = require('util'); const padId = process.argv[2]; const newRevHead = process.argv[3]; const newPadId = process.argv[4] || `${padId}-rebuilt`; -let db, oldPad, newPad, settings; -let AuthorManager, ChangeSet, Pad, PadManager; +let db, oldPad, newPad; +let Pad, PadManager; async.series([ - function (callback) { - npm.load({}, (err) => { - if (err) { - console.error(`Could not load NPM: ${err}`); - process.exit(1); - } else { - callback(); - } - }); - }, - function (callback) { + (callback) => npm.load({}, callback), + (callback) => { // Get a handle into the database - db = require('../src/node/db/DB'); + db = require('ep_etherpad-lite/node/db/DB'); db.init(callback); }, - function (callback) { - PadManager = require('../src/node/db/PadManager'); - Pad = require('../src/node/db/Pad').Pad; + (callback) => { + Pad = require('ep_etherpad-lite/node/db/Pad').Pad; + PadManager = require('ep_etherpad-lite/node/db/PadManager'); // Get references to the original pad and to a newly created pad // HACK: This is a standalone script, so we want to write everything // out to the database immediately. The only problem with this is @@ -46,14 +42,10 @@ async.series([ // Validate the newPadId if specified and that a pad with that ID does // not already exist to avoid overwriting it. if (!PadManager.isValidPadId(newPadId)) { - console.error('Cannot create a pad with that id as it is invalid'); - process.exit(1); + throw new Error('Cannot create a pad with that id as it is invalid'); } PadManager.doesPadExists(newPadId, (err, exists) => { - if (exists) { - console.error('Cannot create a pad with that id as it already exists'); - process.exit(1); - } + if (exists) throw new Error('Cannot create a pad with that id as it already exists'); }); PadManager.getPad(padId, (err, pad) => { oldPad = pad; @@ -61,10 +53,10 @@ async.series([ callback(); }); }, - function (callback) { + (callback) => { // Clone all Chat revisions const chatHead = oldPad.chatHead; - for (var i = 0, curHeadNum = 0; i <= chatHead; i++) { + for (let i = 0, curHeadNum = 0; i <= chatHead; i++) { db.db.get(`pad:${padId}:chat:${i}`, (err, chat) => { db.db.set(`pad:${newPadId}:chat:${curHeadNum++}`, chat); console.log(`Created: Chat Revision: pad:${newPadId}:chat:${curHeadNum}`); @@ -72,10 +64,10 @@ async.series([ } callback(); }, - function (callback) { + (callback) => { // Rebuild Pad from revisions up to and including the new revision head - AuthorManager = require('../src/node/db/AuthorManager'); - Changeset = require('ep_etherpad-lite/static/js/Changeset'); + const AuthorManager = require('ep_etherpad-lite/node/db/AuthorManager'); + const Changeset = require('ep_etherpad-lite/static/js/Changeset'); // Author attributes are derived from changesets, but there can also be // non-author attributes with specific mappings that changesets depend on // and, AFAICT, cannot be recreated any other way @@ -83,7 +75,7 @@ async.series([ for (let curRevNum = 0; curRevNum <= newRevHead; curRevNum++) { db.db.get(`pad:${padId}:revs:${curRevNum}`, (err, rev) => { if (rev.meta) { - throw 'The specified revision number could not be found.'; + throw new Error('The specified revision number could not be found.'); } const newRevNum = ++newPad.head; const newRevId = `pad:${newPad.id}:revs:${newRevNum}`; @@ -91,18 +83,17 @@ async.series([ AuthorManager.addPad(rev.meta.author, newPad.id); newPad.atext = Changeset.applyToAText(rev.changeset, newPad.atext, newPad.pool); console.log(`Created: Revision: pad:${newPad.id}:revs:${newRevNum}`); - if (newRevNum == newRevHead) { + if (newRevNum === newRevHead) { callback(); } }); } }, - function (callback) { + (callback) => { // Add saved revisions up to the new revision head console.log(newPad.head); const newSavedRevisions = []; - for (const i in oldPad.savedRevisions) { - savedRev = oldPad.savedRevisions[i]; + for (const savedRev of oldPad.savedRevisions) { if (savedRev.revNum <= newRevHead) { newSavedRevisions.push(savedRev); console.log(`Added: Saved Revision: ${savedRev.revNum}`); @@ -111,16 +102,14 @@ async.series([ newPad.savedRevisions = newSavedRevisions; callback(); }, - function (callback) { + (callback) => { // Save the source pad db.db.set(`pad:${newPadId}`, newPad, (err) => { console.log(`Created: Source Pad: pad:${newPadId}`); - newPad.saveToDatabase().then(() => callback(), callback); + util.callbackify(newPad.saveToDatabase.bind(newPad))(callback); }); }, ], (err) => { - if (err) { throw err; } else { - console.info('finished'); - process.exit(0); - } + if (err) throw err; + console.info('finished'); }); diff --git a/bin/release.js b/bin/release.js index 36b1194e6..f06679e48 100644 --- a/bin/release.js +++ b/bin/release.js @@ -1,8 +1,12 @@ 'use strict'; +// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an +// unhandled rejection into an uncaught exception, which does cause Node.js to exit. +process.on('unhandledRejection', (err) => { throw err; }); + const fs = require('fs'); -const child_process = require('child_process'); -const semver = require('../src/node_modules/semver'); +const childProcess = require('child_process'); +const semver = require('ep_etherpad-lite/node_modules/semver'); /* @@ -15,7 +19,7 @@ const usage = 'node bin/release.js [patch/minor/major] -- example: "node bin/rel const release = process.argv[2]; -if(!release) { +if (!release) { console.log(usage); throw new Error('No release type included'); } @@ -26,14 +30,14 @@ packageJson = JSON.parse(packageJson); const currentVersion = packageJson.version; const newVersion = semver.inc(currentVersion, release); -if(!newVersion) { +if (!newVersion) { console.log(usage); throw new Error('Unable to generate new version from input'); } const changelogIncludesVersion = changelog.indexOf(newVersion) !== -1; -if(!changelogIncludesVersion) { +if (!changelogIncludesVersion) { throw new Error('No changelog record for ', newVersion, ' - please create changelog record'); } @@ -44,24 +48,27 @@ packageJson.version = newVersion; fs.writeFileSync('src/package.json', JSON.stringify(packageJson, null, 2)); // run npm version `release` where release is patch, minor or major -child_process.execSync('npm install --package-lock-only', {cwd: `src/`}); +childProcess.execSync('npm install --package-lock-only', {cwd: 'src/'}); // run npm install --package-lock-only <-- required??? -child_process.execSync(`git checkout -b release/${newVersion}`); -child_process.execSync(`git add src/package.json`); -child_process.execSync(`git add src/package-lock.json`); -child_process.execSync(`git commit -m 'bump version'`); -child_process.execSync(`git push origin release/${newVersion}`); +childProcess.execSync(`git checkout -b release/${newVersion}`); +childProcess.execSync('git add src/package.json'); +childProcess.execSync('git add src/package-lock.json'); +childProcess.execSync('git commit -m "bump version"'); +childProcess.execSync(`git push origin release/${newVersion}`); -child_process.execSync(`make docs`); -child_process.execSync(`git clone git@github.com:ether/ether.github.com.git`); -child_process.execSync(`cp -R out/doc/ ether.github.com/doc/v${newVersion}`); +childProcess.execSync('make docs'); +childProcess.execSync('git clone git@github.com:ether/ether.github.com.git'); +childProcess.execSync(`cp -R out/doc/ ether.github.com/doc/v${newVersion}`); console.log('Once merged into master please run the following commands'); console.log(`git tag -a ${newVersion} -m ${newVersion} && git push origin master`); console.log(`cd ether.github.com && git add . && git commit -m '${newVersion} docs'`); -console.log(`Build the windows zip`) -console.log(`Visit https://github.com/ether/etherpad-lite/releases/new and create a new release with 'master' as the target and the version is ${newVersion}. Include the windows zip as an assett`) -console.log('Once the new docs are uploaded then modify the download link on etherpad.org and then pull master onto develop'); +console.log('Build the windows zip'); +console.log('Visit https://github.com/ether/etherpad-lite/releases/new and create a new release ' + + `with 'master' as the target and the version is ${newVersion}. Include the windows ` + + 'zip as an asset'); +console.log(`Once the new docs are uploaded then modify the download + link on etherpad.org and then pull master onto develop`); console.log('Finally go public with an announcement via our comms channels :)'); diff --git a/bin/repairPad.js b/bin/repairPad.js index 8408e4b72..e083f30b9 100644 --- a/bin/repairPad.js +++ b/bin/repairPad.js @@ -1,77 +1,59 @@ +'use strict'; + /* * This is a repair tool. It extracts all datas of a pad, removes and inserts them again. */ +// As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an +// unhandled rejection into an uncaught exception, which does cause Node.js to exit. +process.on('unhandledRejection', (err) => { throw err; }); + console.warn('WARNING: This script must not be used while etherpad is running!'); -if (process.argv.length != 3) { - console.error('Use: node bin/repairPad.js $PADID'); - process.exit(1); -} +if (process.argv.length !== 3) throw new Error('Use: node bin/repairPad.js $PADID'); // get the padID const padId = process.argv[2]; -const npm = require('../src/node_modules/npm'); -npm.load({}, async (er) => { - if (er) { - console.error(`Could not load NPM: ${er}`); - process.exit(1); +let valueCount = 0; + +const npm = require('ep_etherpad-lite/node_modules/npm'); +npm.load({}, async (err) => { + if (err) throw err; + + // intialize database + require('ep_etherpad-lite/node/utils/Settings'); + const db = require('ep_etherpad-lite/node/db/DB'); + await db.init(); + + // get the pad + const padManager = require('ep_etherpad-lite/node/db/PadManager'); + const pad = await padManager.getPad(padId); + + // accumulate the required keys + const neededDBValues = [`pad:${padId}`]; + + // add all authors + neededDBValues.push(...pad.getAllAuthors().map((author) => `globalAuthor:${author}`)); + + // add all revisions + for (let rev = 0; rev <= pad.head; ++rev) { + neededDBValues.push(`pad:${padId}:revs:${rev}`); } - try { - // intialize database - const settings = require('../src/node/utils/Settings'); - const db = require('../src/node/db/DB'); - await db.init(); - - // get the pad - const padManager = require('../src/node/db/PadManager'); - const pad = await padManager.getPad(padId); - - // accumulate the required keys - const neededDBValues = [`pad:${padId}`]; - - // add all authors - neededDBValues.push(...pad.getAllAuthors().map((author) => 'globalAuthor:')); - - // add all revisions - for (let rev = 0; rev <= pad.head; ++rev) { - neededDBValues.push(`pad:${padId}:revs:${rev}`); - } - - // add all chat values - for (let chat = 0; chat <= pad.chatHead; ++chat) { - neededDBValues.push(`pad:${padId}:chat:${chat}`); - } - - // - // NB: this script doesn't actually does what's documented - // since the `value` fields in the following `.forEach` - // block are just the array index numbers - // - // the script therefore craps out now before it can do - // any damage. - // - // See gitlab issue #3545 - // - console.info('aborting [gitlab #3545]'); - process.exit(1); - - // now fetch and reinsert every key - neededDBValues.forEach((key, value) => { - console.log(`Key: ${key}, value: ${value}`); - db.remove(key); - db.set(key, value); - }); - - console.info('finished'); - process.exit(0); - } catch (er) { - if (er.name === 'apierror') { - console.error(er); - } else { - console.trace(er); - } + // add all chat values + for (let chat = 0; chat <= pad.chatHead; ++chat) { + neededDBValues.push(`pad:${padId}:chat:${chat}`); } + // now fetch and reinsert every key + for (const key of neededDBValues) { + const value = await db.get(key); + // if it isn't a globalAuthor value which we want to ignore.. + // console.log(`Key: ${key}, value: ${JSON.stringify(value)}`); + await db.remove(key); + await db.set(key, value); + valueCount++; + } + + console.info(`Finished: Replaced ${valueCount} values in the database`); }); From c89da1a9f21cd5cc84a2b393d8072c5c5003e7a7 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 18 Jan 2021 16:46:17 +0100 Subject: [PATCH 053/357] Localisation updates from https://translatewiki.net. --- src/locales/sl.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/locales/sl.json b/src/locales/sl.json index 35dcfba6f..0314f07ce 100644 --- a/src/locales/sl.json +++ b/src/locales/sl.json @@ -41,8 +41,9 @@ "pad.settings.linenocheck": "Številke vrstic", "pad.settings.rtlcheck": "Ali naj se vsebina prebira od desne proti levi?", "pad.settings.fontType": "Vrsta pisave:", - "pad.settings.fontType.normal": "Običajno", + "pad.settings.fontType.normal": "Normalno", "pad.settings.language": "Jezik:", + "pad.settings.poweredBy": "Omogoča", "pad.importExport.import_export": "Uvoz/Izvoz", "pad.importExport.import": "Naloži katerokoli besedilno datoteko ali dokument.", "pad.importExport.importSuccessful": "Opravilo je uspešno končano!", @@ -81,7 +82,7 @@ "pad.modals.disconnected.explanation": "Povezava s strežnikom je bila izgubljena.", "pad.modals.disconnected.cause": "Strežnik morda ni na voljo. Prosimo, obvestite skrbnika storitve, če se to zgodi večkrat.", "pad.share": "Določi souporabo dokumenta", - "pad.share.readonly": "Le za branje", + "pad.share.readonly": "Samo za branje", "pad.share.link": "Povezava", "pad.share.emebdcode": "URL za vključitev", "pad.chat": "Klepet", From ebc4956277c927098bed26bd9502e6d3f899c0c9 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 19 Jan 2021 14:53:14 -0500 Subject: [PATCH 054/357] eejs: Fix straightforward ESLint errors --- src/node/eejs/index.js | 44 ++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/src/node/eejs/index.js b/src/node/eejs/index.js index 1da0b7cca..cb68c3d40 100644 --- a/src/node/eejs/index.js +++ b/src/node/eejs/index.js @@ -1,3 +1,4 @@ +'use strict'; /* * Copyright (c) 2011 RedHog (Egil Möller) * @@ -21,8 +22,8 @@ const ejs = require('ejs'); const fs = require('fs'); +const hooks = require('../../static/js/pluginfw/hooks.js'); const path = require('path'); -const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks.js'); const resolve = require('resolve'); const settings = require('../utils/Settings'); @@ -35,50 +36,43 @@ exports.info = { args: [], }; -function getCurrentFile() { - return exports.info.file_stack[exports.info.file_stack.length - 1]; -} +const getCurrentFile = () => exports.info.file_stack[exports.info.file_stack.length - 1]; -function createBlockId(name) { - return `${getCurrentFile().path}|${name}`; -} - -exports._init = function (b, recursive) { +exports._init = (b, recursive) => { exports.info.__output_stack.push(exports.info.__output); exports.info.__output = b; }; -exports._exit = function (b, recursive) { +exports._exit = (b, recursive) => { getCurrentFile().inherit.forEach((item) => { exports._require(item.name, item.args); }); exports.info.__output = exports.info.__output_stack.pop(); }; -exports.begin_capture = function () { +exports.begin_capture = () => { exports.info.__output_stack.push(exports.info.__output.concat()); exports.info.__output.splice(0, exports.info.__output.length); }; -exports.end_capture = function () { +exports.end_capture = () => { const res = exports.info.__output.join(''); - exports.info.__output.splice.apply( - exports.info.__output, - [0, exports.info.__output.length].concat(exports.info.__output_stack.pop())); + exports.info.__output.splice( + 0, exports.info.__output.length, ...exports.info.__output_stack.pop()); return res; }; -exports.begin_define_block = function (name) { +exports.begin_define_block = (name) => { exports.info.block_stack.push(name); exports.begin_capture(); }; -exports.end_define_block = function () { +exports.end_define_block = () => { const content = exports.end_capture(); return content; }; -exports.end_block = function () { +exports.end_block = () => { const name = exports.info.block_stack.pop(); const renderContext = exports.info.args[exports.info.args.length - 1]; const args = {content: exports.end_define_block(), renderContext}; @@ -88,12 +82,12 @@ exports.end_block = function () { exports.begin_block = exports.begin_define_block; -exports.inherit = function (name, args) { +exports.inherit = (name, args) => { getCurrentFile().inherit.push({name, args}); }; -exports.require = function (name, args, mod) { - if (args == undefined) args = {}; +exports.require = (name, args, mod) => { + if (args == null) args = {}; let basedir = __dirname; let paths = []; @@ -132,17 +126,13 @@ exports.require = function (name, args, mod) { exports.info.args.push(args); exports.info.file_stack.push({path: ejspath, inherit: []}); - if (settings.maxAge !== 0) { - var res = ejs.render(template, args, {cache: true, filename: ejspath}); - } else { - var res = ejs.render(template, args, {cache: false, filename: ejspath}); - } + const res = ejs.render(template, args, {cache: settings.maxAge !== 0, filename: ejspath}); exports.info.file_stack.pop(); exports.info.args.pop(); return res; }; -exports._require = function (name, args) { +exports._require = (name, args) => { exports.info.__output.push(exports.require(name, args)); }; From 1d6319b852688ee61027e56773adbdda9db3530b Mon Sep 17 00:00:00 2001 From: bytecaster Date: Wed, 20 Jan 2021 05:54:54 +0100 Subject: [PATCH 055/357] docs: Remove ep_page_view from README Removes ep_page_view plugin from the installation instructions in README.md to aid new users installing etherpad-lite. According to the plugin directory at https://static.etherpad.org/index.html, the plugin is defunct as it is now part of etherpad-lite core and when I tried installing etherpad with the plugin (following the instructions), its installation resulted in an error every time I opened a pad. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 086441b19..10dbd36cd 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ Etherpad is very customizable through plugins. Instructions for installing theme Run the following command in your Etherpad folder to get all of the features visible in the demo gif: ``` -npm install ep_headings2 ep_markdown ep_comments_page ep_align ep_page_view ep_font_color ep_webrtc ep_embedded_hyperlinks2 +npm install ep_headings2 ep_markdown ep_comments_page ep_align ep_font_color ep_webrtc ep_embedded_hyperlinks2 ``` ## Customize the style with skin variants From 5987f75b0d5fff339171100a078b3bd6fb952201 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 19 Jan 2021 18:49:45 -0500 Subject: [PATCH 056/357] eejs: Unwrap unnecessarily wrapped line --- src/node/eejs/index.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/node/eejs/index.js b/src/node/eejs/index.js index cb68c3d40..c84b3c6dc 100644 --- a/src/node/eejs/index.js +++ b/src/node/eejs/index.js @@ -100,14 +100,7 @@ exports.require = (name, args, mod) => { paths = mod.paths; } - const ejspath = resolve.sync( - name, - { - paths, - basedir, - extensions: ['.html', '.ejs'], - } - ); + const ejspath = resolve.sync(name, {paths, basedir, extensions: ['.html', '.ejs']}); args.e = exports; args.require = require; From 351913c08ebcbf79c1777e8eac171c448ad03643 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 19 Jan 2021 15:26:45 -0500 Subject: [PATCH 057/357] eejs: Delete broken example --- src/node/eejs/examples/bar.ejs | 9 --------- src/node/eejs/examples/foo.ejs | 7 ------- src/node/eejs/index.js | 2 +- 3 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 src/node/eejs/examples/bar.ejs delete mode 100644 src/node/eejs/examples/foo.ejs diff --git a/src/node/eejs/examples/bar.ejs b/src/node/eejs/examples/bar.ejs deleted file mode 100644 index 6a2cc4bab..000000000 --- a/src/node/eejs/examples/bar.ejs +++ /dev/null @@ -1,9 +0,0 @@ -a -<% e.begin_block("bar"); %> - A - <% e.begin_block("foo"); %> - XX - <% e.end_block(); %> - B -<% e.end_block(); %> -b diff --git a/src/node/eejs/examples/foo.ejs b/src/node/eejs/examples/foo.ejs deleted file mode 100644 index daee5f8e8..000000000 --- a/src/node/eejs/examples/foo.ejs +++ /dev/null @@ -1,7 +0,0 @@ -<% e.inherit("./bar.ejs"); %> - -<% e.begin_define_block("foo"); %> - YY - <% e.super(); %> - ZZ -<% e.end_define_block(); %> diff --git a/src/node/eejs/index.js b/src/node/eejs/index.js index c84b3c6dc..bcbbee492 100644 --- a/src/node/eejs/index.js +++ b/src/node/eejs/index.js @@ -17,7 +17,7 @@ /* Basic usage: * - * require("./index").require("./examples/foo.ejs") + * require("./index").require("./path/to/template.ejs") */ const ejs = require('ejs'); From 7d11d54323362a7963958b55ae869b6ea94782ee Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 19 Jan 2021 16:01:37 -0500 Subject: [PATCH 058/357] eejs: Delete unused functions --- src/node/eejs/index.js | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/src/node/eejs/index.js b/src/node/eejs/index.js index bcbbee492..416e7807c 100644 --- a/src/node/eejs/index.js +++ b/src/node/eejs/index.js @@ -44,9 +44,6 @@ exports._init = (b, recursive) => { }; exports._exit = (b, recursive) => { - getCurrentFile().inherit.forEach((item) => { - exports._require(item.name, item.args); - }); exports.info.__output = exports.info.__output_stack.pop(); }; @@ -62,30 +59,19 @@ exports.end_capture = () => { return res; }; -exports.begin_define_block = (name) => { +exports.begin_block = (name) => { exports.info.block_stack.push(name); exports.begin_capture(); }; -exports.end_define_block = () => { - const content = exports.end_capture(); - return content; -}; - exports.end_block = () => { const name = exports.info.block_stack.pop(); const renderContext = exports.info.args[exports.info.args.length - 1]; - const args = {content: exports.end_define_block(), renderContext}; + const args = {content: exports.end_capture(), renderContext}; hooks.callAll(`eejsBlock_${name}`, args); exports.info.__output.push(args.content); }; -exports.begin_block = exports.begin_define_block; - -exports.inherit = (name, args) => { - getCurrentFile().inherit.push({name, args}); -}; - exports.require = (name, args, mod) => { if (args == null) args = {}; @@ -118,14 +104,10 @@ exports.require = (name, args, mod) => { } exports.info.args.push(args); - exports.info.file_stack.push({path: ejspath, inherit: []}); + exports.info.file_stack.push({path: ejspath}); const res = ejs.render(template, args, {cache: settings.maxAge !== 0, filename: ejspath}); exports.info.file_stack.pop(); exports.info.args.pop(); return res; }; - -exports._require = (name, args) => { - exports.info.__output.push(exports.require(name, args)); -}; From c8c39290583ecaaffd16ae4f55947b71ed6d1173 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 19 Jan 2021 17:34:09 -0500 Subject: [PATCH 059/357] eejs: Inline `begin_capture`, `end_capture` --- src/node/eejs/index.js | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/node/eejs/index.js b/src/node/eejs/index.js index 416e7807c..428a67637 100644 --- a/src/node/eejs/index.js +++ b/src/node/eejs/index.js @@ -47,27 +47,19 @@ exports._exit = (b, recursive) => { exports.info.__output = exports.info.__output_stack.pop(); }; -exports.begin_capture = () => { - exports.info.__output_stack.push(exports.info.__output.concat()); - exports.info.__output.splice(0, exports.info.__output.length); -}; - -exports.end_capture = () => { - const res = exports.info.__output.join(''); - exports.info.__output.splice( - 0, exports.info.__output.length, ...exports.info.__output_stack.pop()); - return res; -}; - exports.begin_block = (name) => { exports.info.block_stack.push(name); - exports.begin_capture(); + exports.info.__output_stack.push(exports.info.__output.concat()); + exports.info.__output.splice(0, exports.info.__output.length); }; exports.end_block = () => { const name = exports.info.block_stack.pop(); const renderContext = exports.info.args[exports.info.args.length - 1]; - const args = {content: exports.end_capture(), renderContext}; + const content = exports.info.__output.join(''); + exports.info.__output.splice( + 0, exports.info.__output.length, ...exports.info.__output_stack.pop()); + const args = {content, renderContext}; hooks.callAll(`eejsBlock_${name}`, args); exports.info.__output.push(args.content); }; From 4d2d439874eec7e81179ccc61c6d4d6372f3533e Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 19 Jan 2021 17:57:19 -0500 Subject: [PATCH 060/357] eejs: Simplify cache lookup logic --- src/node/eejs/index.js | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/node/eejs/index.js b/src/node/eejs/index.js index 428a67637..61f5eb0dd 100644 --- a/src/node/eejs/index.js +++ b/src/node/eejs/index.js @@ -83,21 +83,14 @@ exports.require = (name, args, mod) => { args.e = exports; args.require = require; - let template; - if (settings.maxAge !== 0) { // don't cache if maxAge is 0 - if (!templateCache.has(ejspath)) { - template = `<% e._init(__output); %>${fs.readFileSync(ejspath).toString()}<% e._exit(); %>`; - templateCache.set(ejspath, template); - } else { - template = templateCache.get(ejspath); - } - } else { - template = `<% e._init(__output); %>${fs.readFileSync(ejspath).toString()}<% e._exit(); %>`; - } + const cache = settings.maxAge !== 0; + const template = cache && templateCache.get(ejspath) || + `<% e._init(__output); %>${fs.readFileSync(ejspath).toString()}<% e._exit(); %>`; + if (cache) templateCache.set(ejspath, template); exports.info.args.push(args); exports.info.file_stack.push({path: ejspath}); - const res = ejs.render(template, args, {cache: settings.maxAge !== 0, filename: ejspath}); + const res = ejs.render(template, args, {cache, filename: ejspath}); exports.info.file_stack.pop(); exports.info.args.pop(); From 517fc88c5450370b425f7e6f25f408e270be0bbd Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 19 Jan 2021 18:43:46 -0500 Subject: [PATCH 061/357] eejs: Cache the compiled template, not the template string --- src/node/eejs/index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/node/eejs/index.js b/src/node/eejs/index.js index 61f5eb0dd..9f04fe1fb 100644 --- a/src/node/eejs/index.js +++ b/src/node/eejs/index.js @@ -84,13 +84,14 @@ exports.require = (name, args, mod) => { args.require = require; const cache = settings.maxAge !== 0; - const template = cache && templateCache.get(ejspath) || - `<% e._init(__output); %>${fs.readFileSync(ejspath).toString()}<% e._exit(); %>`; + const template = cache && templateCache.get(ejspath) || ejs.compile( + `<% e._init(__output); %>${fs.readFileSync(ejspath).toString()}<% e._exit(); %>`, + {filename: ejspath}); if (cache) templateCache.set(ejspath, template); exports.info.args.push(args); exports.info.file_stack.push({path: ejspath}); - const res = ejs.render(template, args, {cache, filename: ejspath}); + const res = template(args); exports.info.file_stack.pop(); exports.info.args.pop(); From e5836150cddec814d0b7760a5a614df1ad0f1b3a Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 21 Jan 2021 15:09:02 +0100 Subject: [PATCH 062/357] Localisation updates from https://translatewiki.net. --- src/locales/diq.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/diq.json b/src/locales/diq.json index 0c8c31f98..8f97e1045 100644 --- a/src/locales/diq.json +++ b/src/locales/diq.json @@ -132,7 +132,7 @@ "pad.chat.writeMessage.placeholder": "Mesacê xo tiya bınusne", "timeslider.followContents": "Rocaney zerrekê padi taqib bıkerê", "timeslider.pageTitle": "Ğızagê zemani {{appTitle}}", - "timeslider.toolbar.returnbutton": "Peyser şo ped", + "timeslider.toolbar.returnbutton": "Peyser şo bloknot", "timeslider.toolbar.authors": "Nuştoği:", "timeslider.toolbar.authorsList": "Nuştoği çıniyê", "timeslider.toolbar.exportlink.title": "Teberdayış", @@ -140,7 +140,7 @@ "timeslider.version": "Versiyonê {{version}}", "timeslider.saved": "{{day}} {{month}}, {{year}} de biyo qeyd", "timeslider.playPause": "Zerrekê bloknoti kayfi/vındarn", - "timeslider.backRevision": "Peyser şo revizyona ena bloknoter", + "timeslider.backRevision": "Peyser şo çımraviyarnayışê na bloknoti", "timeslider.forwardRevision": "Ena bloknot de şo revizyonê bini", "timeslider.dateformat": "{{month}}/{{day}}/{{year}} {{hours}}:{{minutes}}:{{seconds}}", "timeslider.month.january": "Çele", From bfe813fca2368f528523fe3ec0b8210af905cd5b Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 21 Jan 2021 21:35:53 -0500 Subject: [PATCH 063/357] docs: Wrap long lines and fix whitespace --- doc/api/hooks_client-side.md | 207 +++++++++++++++++++++++++---------- 1 file changed, 150 insertions(+), 57 deletions(-) diff --git a/doc/api/hooks_client-side.md b/doc/api/hooks_client-side.md index 799fd5ce5..478f5d0a5 100755 --- a/doc/api/hooks_client-side.md +++ b/doc/api/hooks_client-side.md @@ -1,5 +1,7 @@ # Client-side hooks -Most of these hooks are called during or in order to set up the formatting process. + +Most of these hooks are called during or in order to set up the formatting +process. ## documentReady Called from: src/templates/pad.html @@ -11,6 +13,7 @@ nothing This hook proxies the functionality of jQuery's `$(document).ready` event. ## aceDomLinePreProcessLineAttributes + Called from: src/static/js/domline.js Things in context: @@ -18,15 +21,21 @@ Things in context: 1. domline - The current DOM line being processed 2. cls - The class of the current block element (useful for styling) -This hook is called for elements in the DOM that have the "lineMarkerAttribute" set. You can add elements into this category with the aceRegisterBlockElements hook above. This hook is run BEFORE the numbered and ordered lists logic is applied. +This hook is called for elements in the DOM that have the "lineMarkerAttribute" +set. You can add elements into this category with the aceRegisterBlockElements +hook above. This hook is run BEFORE the numbered and ordered lists logic is +applied. The return value of this hook should have the following structure: `{ preHtml: String, postHtml: String, processedMarker: Boolean }` -The preHtml and postHtml values will be added to the HTML display of the element, and if processedMarker is true, the engine won't try to process it any more. +The preHtml and postHtml values will be added to the HTML display of the +element, and if processedMarker is true, the engine won't try to process it any +more. ## aceDomLineProcessLineAttributes + Called from: src/static/js/domline.js Things in context: @@ -34,15 +43,21 @@ Things in context: 1. domline - The current DOM line being processed 2. cls - The class of the current block element (useful for styling) -This hook is called for elements in the DOM that have the "lineMarkerAttribute" set. You can add elements into this category with the aceRegisterBlockElements hook above. This hook is run AFTER the ordered and numbered lists logic is applied. +This hook is called for elements in the DOM that have the "lineMarkerAttribute" +set. You can add elements into this category with the aceRegisterBlockElements +hook above. This hook is run AFTER the ordered and numbered lists logic is +applied. The return value of this hook should have the following structure: `{ preHtml: String, postHtml: String, processedMarker: Boolean }` -The preHtml and postHtml values will be added to the HTML display of the element, and if processedMarker is true, the engine won't try to process it any more. +The preHtml and postHtml values will be added to the HTML display of the +element, and if processedMarker is true, the engine won't try to process it any +more. ## aceCreateDomLine + Called from: src/static/js/domline.js Things in context: @@ -50,43 +65,55 @@ Things in context: 1. domline - the current DOM line being processed 2. cls - The class of the current element (useful for styling) -This hook is called for any line being processed by the formatting engine, unless the aceDomLineProcessLineAttributes hook from above returned true, in which case this hook is skipped. +This hook is called for any line being processed by the formatting engine, +unless the aceDomLineProcessLineAttributes hook from above returned true, in +which case this hook is skipped. The return value of this hook should have the following structure: `{ extraOpenTags: String, extraCloseTags: String, cls: String }` -extraOpenTags and extraCloseTags will be added before and after the element in question, and cls will be the new class of the element going forward. +extraOpenTags and extraCloseTags will be added before and after the element in +question, and cls will be the new class of the element going forward. ## acePostWriteDomLineHTML + Called from: src/static/js/domline.js Things in context: 1. node - the DOM node that just got written to the page -This hook is for right after a node has been fully formatted and written to the page. +This hook is for right after a node has been fully formatted and written to the +page. ## aceAttribsToClasses + Called from: src/static/js/linestylefilter.js Things in context: -1. linestylefilter - the JavaScript object that's currently processing the ace attributes +1. linestylefilter - the JavaScript object that's currently processing the ace + attributes 2. key - the current attribute being processed 3. value - the value of the attribute being processed -This hook is called during the attribute processing procedure, and should be used to translate key, value pairs into valid HTML classes that can be inserted into the DOM. +This hook is called during the attribute processing procedure, and should be +used to translate key, value pairs into valid HTML classes that can be inserted +into the DOM. -The return value for this function should be a list of classes, which will then be parsed into a valid class string. +The return value for this function should be a list of classes, which will then +be parsed into a valid class string. ## aceAttribClasses + Called from: src/static/js/linestylefilter.js Things in context: 1. Attributes - Object of Attributes -This hook is called when attributes are investigated on a line. It is useful if you want to add another attribute type or property type to a pad. +This hook is called when attributes are investigated on a line. It is useful if +you want to add another attribute type or property type to a pad. Example: ``` @@ -97,32 +124,45 @@ exports.aceAttribClasses = function(hook_name, attr, cb){ ``` ## aceGetFilterStack + Called from: src/static/js/linestylefilter.js Things in context: -1. linestylefilter - the JavaScript object that's currently processing the ace attributes +1. linestylefilter - the JavaScript object that's currently processing the ace + attributes 2. browser - an object indicating which browser is accessing the page -This hook is called to apply custom regular expression filters to a set of styles. The one example available is the ep_linkify plugin, which adds internal links. They use it to find the telltale `[[ ]]` syntax that signifies internal links, and finding that syntax, they add in the internalHref attribute to be later used by the aceCreateDomLine hook (documented above). +This hook is called to apply custom regular expression filters to a set of +styles. The one example available is the ep_linkify plugin, which adds internal +links. They use it to find the telltale `[[ ]]` syntax that signifies internal +links, and finding that syntax, they add in the internalHref attribute to be +later used by the aceCreateDomLine hook (documented above). ## aceEditorCSS + Called from: src/static/js/ace.js Things in context: None -This hook is provided to allow custom CSS files to be loaded. The return value should be an array of resource urls or paths relative to the plugins directory. +This hook is provided to allow custom CSS files to be loaded. The return value +should be an array of resource urls or paths relative to the plugins directory. ## aceInitInnerdocbodyHead + Called from: src/static/js/ace.js Things in context: 1. iframeHTML - the HTML of the editor iframe up to this point, in array format -This hook is called during the creation of the editor HTML. The array should have lines of HTML added to it, giving the plugin author a chance to add in meta, script, link, and other tags that go into the `` element of the editor HTML document. +This hook is called during the creation of the editor HTML. The array should +have lines of HTML added to it, giving the plugin author a chance to add in +meta, script, link, and other tags that go into the `` element of the +editor HTML document. ## aceEditEvent + Called from: src/static/js/ace2_inner.js Things in context: @@ -130,16 +170,25 @@ Things in context: 1. callstack - a bunch of information about the current action 2. editorInfo - information about the user who is making the change 3. rep - information about where the change is being made -4. documentAttributeManager - information about attributes in the document (this is a mystery to me) +4. documentAttributeManager - information about attributes in the document (this + is a mystery to me) -This hook is made available to edit the edit events that might occur when changes are made. Currently you can change the editor information, some of the meanings of the edit, and so on. You can also make internal changes (internal to your plugin) that use the information provided by the edit event. +This hook is made available to edit the edit events that might occur when +changes are made. Currently you can change the editor information, some of the +meanings of the edit, and so on. You can also make internal changes (internal to +your plugin) that use the information provided by the edit event. ## aceRegisterNonScrollableEditEvents + Called from: src/static/js/ace2_inner.js Things in context: None -When aceEditEvent (documented above) finishes processing the event, it scrolls the viewport to make caret visible to the user, but if you don't want that behavior to happen you can use this hook to register which edit events should not scroll viewport. The return value of this hook should be a list of event names. +When aceEditEvent (documented above) finishes processing the event, it scrolls +the viewport to make caret visible to the user, but if you don't want that +behavior to happen you can use this hook to register which edit events should +not scroll viewport. The return value of this hook should be a list of event +names. Example: ``` @@ -149,24 +198,32 @@ exports.aceRegisterNonScrollableEditEvents = function(){ ``` ## aceRegisterBlockElements + Called from: src/static/js/ace2_inner.js Things in context: None -The return value of this hook will add elements into the "lineMarkerAttribute" category, making the aceDomLineProcessLineAttributes hook (documented below) call for those elements. +The return value of this hook will add elements into the "lineMarkerAttribute" +category, making the aceDomLineProcessLineAttributes hook (documented below) +call for those elements. ## aceInitialized + Called from: src/static/js/ace2_inner.js Things in context: -1. editorInfo - information about the user who will be making changes through the interface, and a way to insert functions into the main ace object (see ep_headings) +1. editorInfo - information about the user who will be making changes through + the interface, and a way to insert functions into the main ace object (see + ep_headings) 2. rep - information about where the user's cursor is 3. documentAttributeManager - some kind of magic -This hook is for inserting further information into the ace engine, for later use in formatting hooks. +This hook is for inserting further information into the ace engine, for later +use in formatting hooks. ## postAceInit + Called from: src/static/js/pad.js Things in context: @@ -175,6 +232,7 @@ Things in context: 2. pad - the pad object of the current pad. ## postToolbarInit + Called from: src/static/js/pad_editbar.js Things in context: @@ -189,30 +247,37 @@ Usage examples: * [https://github.com/tiblu/ep_authorship_toggle]() ## postTimesliderInit + Called from: src/static/js/timeslider.js -There doesn't appear to be any example available of this particular hook being used, but it gets fired after the timeslider is all set up. +There doesn't appear to be any example available of this particular hook being +used, but it gets fired after the timeslider is all set up. ## goToRevisionEvent + Called from: src/static/js/broadcast.js Things in context: 1. rev - The newRevision -This hook gets fired both on timeslider load (as timeslider shows a new revision) and when the new revision is showed to a user. -There doesn't appear to be any example available of this particular hook being used. +This hook gets fired both on timeslider load (as timeslider shows a new +revision) and when the new revision is showed to a user. There doesn't appear to +be any example available of this particular hook being used. ## userJoinOrUpdate + Called from: src/static/js/pad_userlist.js Things in context: 1. info - the user information -This hook is called on the client side whenever a user joins or changes. This can be used to create notifications or an alternate user list. +This hook is called on the client side whenever a user joins or changes. This +can be used to create notifications or an alternate user list. ## chatNewMessage + Called from: src/static/js/chat.js Things in context: @@ -220,14 +285,18 @@ Things in context: 1. authorName - The user that wrote this message 2. author - The authorID of the user that wrote the message 3. text - the message text -4. sticky (boolean) - if you want the gritter notification bubble to fade out on its own or just sit there +4. sticky (boolean) - if you want the gritter notification bubble to fade out on + its own or just sit there 5. timestamp - the timestamp of the chat message 6. timeStr - the timestamp as a formatted string -7. duration - for how long in milliseconds should the gritter notification appear (0 to disable) +7. duration - for how long in milliseconds should the gritter notification + appear (0 to disable) -This hook is called on the client side whenever a chat message is received from the server. It can be used to create different notifications for chat messages. +This hook is called on the client side whenever a chat message is received from +the server. It can be used to create different notifications for chat messages. ## collectContentPre + Called from: src/static/js/contentcollector.js Things in context: @@ -238,16 +307,20 @@ Things in context: 4. styl - the style applied to the node (probably CSS) -- Note the typo 5. cls - the HTML class string of the node -This hook is called before the content of a node is collected by the usual methods. The cc object can be used to do a bunch of things that modify the content of the pad. See, for example, the heading1 plugin for etherpad original. +This hook is called before the content of a node is collected by the usual +methods. The cc object can be used to do a bunch of things that modify the +content of the pad. See, for example, the heading1 plugin for etherpad original. -E.g. if you need to apply an attribute to newly inserted characters, -call cc.doAttrib(state, "attributeName") which results in an attribute attributeName=true. +E.g. if you need to apply an attribute to newly inserted characters, call +cc.doAttrib(state, "attributeName") which results in an attribute +attributeName=true. -If you want to specify also a value, call cc.doAttrib(state, "attributeName::value") -which results in an attribute attributeName=value. +If you want to specify also a value, call cc.doAttrib(state, +"attributeName::value") which results in an attribute attributeName=value. ## collectContentImage + Called from: src/static/js/contentcollector.js Things in context: @@ -259,7 +332,9 @@ Things in context: 5. cls - the HTML class string of the node 6. node - the node being modified -This hook is called before the content of an image node is collected by the usual methods. The cc object can be used to do a bunch of things that modify the content of the pad. +This hook is called before the content of an image node is collected by the +usual methods. The cc object can be used to do a bunch of things that modify the +content of the pad. Example: @@ -271,6 +346,7 @@ exports.collectContentImage = function(name, context){ ``` ## collectContentPost + Called from: src/static/js/contentcollector.js Things in context: @@ -281,20 +357,29 @@ Things in context: 4. style - the style applied to the node (probably CSS) 5. cls - the HTML class string of the node -This hook is called after the content of a node is collected by the usual methods. The cc object can be used to do a bunch of things that modify the content of the pad. See, for example, the heading1 plugin for etherpad original. +This hook is called after the content of a node is collected by the usual +methods. The cc object can be used to do a bunch of things that modify the +content of the pad. See, for example, the heading1 plugin for etherpad original. ## handleClientMessage_`name` + Called from: `src/static/js/collab_client.js` Things in context: -1. payload - the data that got sent with the message (use it for custom message content) +1. payload - the data that got sent with the message (use it for custom message + content) -This hook gets called every time the client receives a message of type `name`. This can most notably be used with the new HTTP API call, "sendClientsMessage", which sends a custom message type to all clients connected to a pad. You can also use this to handle existing types. +This hook gets called every time the client receives a message of type `name`. +This can most notably be used with the new HTTP API call, "sendClientsMessage", +which sends a custom message type to all clients connected to a pad. You can +also use this to handle existing types. -`collab_client.js` has a pretty extensive list of message types, if you want to take a look. +`collab_client.js` has a pretty extensive list of message types, if you want to +take a look. + +## aceStartLineAndCharForPoint-aceEndLineAndCharForPoint -##aceStartLineAndCharForPoint-aceEndLineAndCharForPoint Called from: src/static/js/ace2_inner.js Things in context: @@ -306,10 +391,11 @@ Things in context: 5. point - the starting/ending element where the cursor highlights 6. documentAttributeManager - information about attributes in the document -This hook is provided to allow a plugin to turn DOM node selection into [line,char] selection. -The return value should be an array of [line,char] +This hook is provided to allow a plugin to turn DOM node selection into +[line,char] selection. The return value should be an array of [line,char] + +## aceKeyEvent -##aceKeyEvent Called from: src/static/js/ace2_inner.js Things in context: @@ -323,7 +409,8 @@ Things in context: This hook is provided to allow a plugin to handle key events. The return value should be true if you have handled the event. -##collectContentLineText +## collectContentLineText + Called from: src/static/js/contentcollector.js Things in context: @@ -333,10 +420,11 @@ Things in context: 3. tname - the tag name of this node currently being processed 4. text - the text for that line -This hook allows you to validate/manipulate the text before it's sent to the server side. -The return value should be the validated/manipulated text. +This hook allows you to validate/manipulate the text before it's sent to the +server side. The return value should be the validated/manipulated text. + +## collectContentLineBreak -##collectContentLineBreak Called from: src/static/js/contentcollector.js Things in context: @@ -345,24 +433,27 @@ Things in context: 2. state - the current state of the change being made 3. tname - the tag name of this node currently being processed -This hook is provided to allow whether the br tag should induce a new magic domline or not. -The return value should be either true(break the line) or false. +This hook is provided to allow whether the br tag should induce a new magic +domline or not. The return value should be either true(break the line) or false. + +## disableAuthorColorsForThisLine -##disableAuthorColorsForThisLine Called from: src/static/js/linestylefilter.js Things in context: -1. linestylefilter - the JavaScript object that's currently processing the ace attributes +1. linestylefilter - the JavaScript object that's currently processing the ace + attributes 2. text - the line text 3. class - line class -This hook is provided to allow whether a given line should be deliniated with multiple authors. -Multiple authors in one line cause the creation of magic span lines. This might not suit you and -now you can disable it and handle your own deliniation. -The return value should be either true(disable) or false. +This hook is provided to allow whether a given line should be deliniated with +multiple authors. Multiple authors in one line cause the creation of magic span +lines. This might not suit you and now you can disable it and handle your own +deliniation. The return value should be either true(disable) or false. ## aceSetAuthorStyle + Called from: src/static/js/ace2_inner.js Things in context: @@ -374,10 +465,12 @@ Things in context: 5. author - author info 6. authorSelector - css selector for author span in inner ace -This hook is provided to allow author highlight style to be modified. -Registered hooks should return 1 if the plugin handles highlighting. If no plugin returns 1, the core will use the default background-based highlighting. +This hook is provided to allow author highlight style to be modified. Registered +hooks should return 1 if the plugin handles highlighting. If no plugin returns +1, the core will use the default background-based highlighting. ## aceSelectionChanged + Called from: src/static/js/ace2_inner.js Things in context: From 51dc5b162790e3f4cc549d00c856408e2e10bc4f Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 17 Jan 2021 19:04:01 -0500 Subject: [PATCH 064/357] ace2_inner: Delete dead code --- src/static/js/ace2_inner.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 06f176b52..3ae0c66d9 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -323,12 +323,6 @@ function Ace2Inner() { // top.console.error(err); } - let profiling = false; - - const profileRest = () => { - profiling = true; // eslint-disable-line - }; - const newEditEvent = (eventType) => ({ eventType, backset: null, @@ -378,7 +372,6 @@ function Ace2Inner() { selectionAffected: false, userChangedSelection: false, domClean: false, - profileRest, isUserChange: false, // is this a "user change" type of call-stack repChanged: false, @@ -1576,7 +1569,6 @@ function Ace2Inner() { }, get: (i) => `${rep.lines.atIndex(i).text}\n`, length: () => rep.lines.length(), - slice_notused: (start, end) => _.map(rep.lines.slice(start, end), (e) => `${e.text}\n`), }; Changeset.mutateTextLines(changes, linesMutatee); From 0bfabfef5d14acb24e557a716bb954fbbec1f462 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 17 Jan 2021 19:06:35 -0500 Subject: [PATCH 065/357] ace2_inner: Avoid unnecessary use of underscore.js This silences a bunch of you-dont-need-underscore-lodash ESLint warnings. --- src/static/js/ace2_inner.js | 51 +++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 3ae0c66d9..70b240f9f 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -22,7 +22,6 @@ const browser = require('./browser'); const padutils = require('./pad_utils').padutils; const Ace2Common = require('./ace2_common'); const $ = require('./rjquery').$; -const _ = require('./underscore'); const isNodeText = Ace2Common.isNodeText; const getAssoc = Ace2Common.getAssoc; @@ -231,7 +230,7 @@ function Ace2Inner() { }); // Prevent default behaviour if any hook says so - if (_.any(authorStyleSet, (it) => it)) { + if (authorStyleSet.some((it) => it)) { return; } @@ -305,7 +304,7 @@ function Ace2Inner() { applyChangesToBase: 1, }; - _.each(hooks.callAll('aceRegisterNonScrollableEditEvents'), (eventType) => { + hooks.callAll('aceRegisterNonScrollableEditEvents').forEach((eventType) => { _nonScrollableEditEvents[eventType] = 1; }); @@ -503,7 +502,7 @@ function Ace2Inner() { } lines = text.substring(0, text.length - 1).split('\n'); } else { - lines = _.map(text.split('\n'), textify); + lines = text.split('\n').map(textify); } let newText = '\n'; if (lines.length > 0) { @@ -1197,7 +1196,7 @@ function Ace2Inner() { } // var fragment = magicdom.wrapDom(document.createDocumentFragment()); domInsertsNeeded.push([nodeToAddAfter, lineNodeInfos]); - _.each(dirtyNodes, (n) => { + dirtyNodes.forEach((n) => { toDeleteAtEnd.push(n); }); const spliceHints = {}; @@ -1218,19 +1217,19 @@ function Ace2Inner() { // update the representation p.mark('splice'); - _.each(splicesToDo, (splice) => { + splicesToDo.forEach((splice) => { doIncorpLineSplice(splice[0], splice[1], splice[2], splice[3], splice[4]); }); // do DOM inserts p.mark('insert'); - _.each(domInsertsNeeded, (ins) => { + domInsertsNeeded.forEach((ins) => { insertDomLines(ins[0], ins[1]); }); p.mark('del'); // delete old dom nodes - _.each(toDeleteAtEnd, (n) => { + toDeleteAtEnd.forEach((n) => { // var id = n.uniqueId(); // parent of n may not be "root" in IE due to non-tree-shaped DOM (wtf) if (n.parentNode) n.parentNode.removeChild(n); @@ -1328,7 +1327,7 @@ function Ace2Inner() { let lineStartOffset; if (infoStructs.length < 1) return; - _.each(infoStructs, (info) => { + infoStructs.forEach((info) => { const p2 = PROFILER('insertLine', false); // eslint-disable-line const node = info.node; const key = uniqueId(node); @@ -1526,7 +1525,7 @@ function Ace2Inner() { } } - const lineEntries = _.map(newLineStrings, createDomLineEntry); + const lineEntries = newLineStrings.map(createDomLineEntry); doRepLineSplice(startLine, deleteCount, lineEntries); @@ -1535,9 +1534,9 @@ function Ace2Inner() { nodeToAddAfter = getCleanNodeByKey(rep.lines.atIndex(startLine - 1).key); } else { nodeToAddAfter = null; } - insertDomLines(nodeToAddAfter, _.map(lineEntries, (entry) => entry.domInfo)); + insertDomLines(nodeToAddAfter, lineEntries.map((entry) => entry.domInfo)); - _.each(keysToDelete, (k) => { + keysToDelete.forEach((k) => { const n = doc.getElementById(k); n.parentNode.removeChild(n); }); @@ -1565,7 +1564,7 @@ function Ace2Inner() { const linesMutatee = { // TODO: Rhansen to check usage of args here. splice: (start, numRemoved, ...args) => { - domAndRepSplice(start, numRemoved, _.map(args, (s) => s.slice(0, -1))); + domAndRepSplice(start, numRemoved, args.map((s) => s.slice(0, -1))); }, get: (i) => `${rep.lines.atIndex(i).text}\n`, length: () => rep.lines.length(), @@ -1821,7 +1820,7 @@ function Ace2Inner() { // Change the abstract representation of the document to have a different set of lines. // Must be called after rep.alltext is set. const doRepLineSplice = (startLine, deleteCount, newLineEntries) => { - _.each(newLineEntries, (entry) => { + newLineEntries.forEach((entry) => { entry.width = entry.text.length + 1; }); @@ -1831,7 +1830,7 @@ function Ace2Inner() { rep.lines.splice(startLine, deleteCount, newLineEntries); currentCallStack.docTextChanged = true; currentCallStack.repChanged = true; - const newText = _.map(newLineEntries, (e) => `${e.text}\n`).join(''); + const newText = newLineEntries.map((e) => `${e.text}\n`).join(''); rep.alltext = rep.alltext.substring(0, startOldChar) + newText + rep.alltext.substring(endOldChar, rep.alltext.length); @@ -1852,7 +1851,7 @@ function Ace2Inner() { selEndHintChar = rep.lines.offsetOfIndex(hints.selEnd[0]) + hints.selEnd[1] - oldRegionStart; } - const newText = _.map(newLineEntries, (e) => `${e.text}\n`).join(''); + const newText = newLineEntries.map((e) => `${e.text}\n`).join(''); const oldText = rep.alltext.substring(startOldChar, endOldChar); const oldAttribs = rep.alines.slice(startLine, startLine + deleteCount).join(''); const newAttribs = `${lineAttribs.join('|1+1')}|1+1`; // not valid in a changeset @@ -2210,10 +2209,10 @@ function Ace2Inner() { $formattingButton.toggleClass(SELECT_BUTTON_CLASS, hasStyleOnRepSelection); }; - const attribIsFormattingStyle = (attributeName) => _.contains(FORMATTING_STYLES, attributeName); + const attribIsFormattingStyle = (attribName) => FORMATTING_STYLES.indexOf(attribName) !== -1; const selectFormattingButtonIfLineHasStyleApplied = (rep) => { - _.each(FORMATTING_STYLES, (style) => { + FORMATTING_STYLES.forEach((style) => { const hasStyleOnRepSelection = documentAttributeManager. hasAttributeOnSelectionOrCaretPosition(style); updateStyleButtonState(style, hasStyleOnRepSelection); @@ -2236,7 +2235,7 @@ function Ace2Inner() { ul: 1, }; - _.each(hooks.callAll('aceRegisterBlockElements'), (element) => { + hooks.callAll('aceRegisterBlockElements').forEach((element) => { _blockElems[element] = 1; }); @@ -2311,7 +2310,7 @@ function Ace2Inner() { const rangeForLine = (i) => { // returns index of cleanRange containing i, or -1 if none let answer = -1; - _.each(cleanRanges, (r, idx) => { + cleanRanges.forEach((r, idx) => { if (i >= r[1]) return false; // keep looking if (i < r[0]) return true; // not found, stop looking answer = idx; @@ -2635,7 +2634,7 @@ function Ace2Inner() { } } - _.each(mods, (mod) => { + mods.forEach((mod) => { setLineListType(mod[0], mod[1]); }); return true; @@ -2809,7 +2808,7 @@ function Ace2Inner() { // if any hook returned true, set specialHandled with true if (specialHandledInHook) { - specialHandled = _.contains(specialHandledInHook, true); + specialHandled = specialHandledInHook.indexOf(true) !== -1; } const padShortcutEnabled = parent.parent.clientVars.padShortcutEnabled; @@ -3562,11 +3561,7 @@ function Ace2Inner() { const _teardownActions = []; - const teardown = () => { - _.each(_teardownActions, (a) => { - a(); - }); - }; + const teardown = () => _teardownActions.forEach((a) => a()); let inInternationalComposition = false; const handleCompositionEvent = (evt) => { @@ -3814,7 +3809,7 @@ function Ace2Inner() { } } - _.each(mods, (mod) => { + mods.forEach((mod) => { setLineListType(mod[0], mod[1]); }); }; From 2f430e3a5a95ebd86ab58cada5a699f3cc41f24f Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 17 Jan 2021 19:10:26 -0500 Subject: [PATCH 066/357] ace2_inner: Fix some ESLint errors --- src/static/js/ace2_inner.js | 48 +++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 70b240f9f..255cc0165 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -250,8 +250,8 @@ function Ace2Inner() { authorStyle.backgroundColor = bgcolor; parentAuthorStyle.backgroundColor = bgcolor; - const textColor = colorutils. - textColorFromBackgroundColor(bgcolor, parent.parent.clientVars.skinName); + const textColor = + colorutils.textColorFromBackgroundColor(bgcolor, parent.parent.clientVars.skinName); authorStyle.color = textColor; parentAuthorStyle.color = textColor; } @@ -318,8 +318,7 @@ function Ace2Inner() { if (currentCallStack) { // Do not uncomment this in production. It will break Etherpad being provided in iFrames. // I am leaving this in for testing usefulness. - const err = `Can't enter callstack ${type}, already in ${currentCallStack.type}`; // eslint-disable-line - // top.console.error(err); + // top.console.error(`Can't enter callstack ${type}, already in ${currentCallStack.type}`); } const newEditEvent = (eventType) => ({ @@ -967,7 +966,7 @@ function Ace2Inner() { clearObservedChanges(); const getCleanNodeByKey = (key) => { - const p = PROFILER('getCleanNodeByKey', false); // eslint-disable-line + const p = PROFILER('getCleanNodeByKey', false); // eslint-disable-line new-cap p.extra = 0; let n = doc.getElementById(key); // copying and pasting can lead to duplicate ids @@ -1043,7 +1042,7 @@ function Ace2Inner() { if (currentCallStack.observedSelection) return; currentCallStack.observedSelection = true; - const p = PROFILER('getSelection', false); // eslint-disable-line + const p = PROFILER('getSelection', false); // eslint-disable-line new-cap const selection = getSelection(); p.end(); @@ -1078,7 +1077,7 @@ function Ace2Inner() { if (DEBUG && window.DONT_INCORP || window.DEBUG_DONT_INCORP) return false; - const p = PROFILER('incorp', false); // eslint-disable-line + const p = PROFILER('incorp', false); // eslint-disable-line new-cap // returns true if dom changes were made if (!root.firstChild) { @@ -1320,7 +1319,8 @@ function Ace2Inner() { const isStyleAttribute = (aname) => !!STYLE_ATTRIBS[aname]; - const isDefaultLineAttribute = (aname) => AttributeManager.DEFAULT_LINE_ATTRIBUTES.indexOf(aname) !== -1; // eslint-disable-line + const isDefaultLineAttribute = + (aname) => AttributeManager.DEFAULT_LINE_ATTRIBUTES.indexOf(aname) !== -1; const insertDomLines = (nodeToAddAfter, infoStructs) => { let lastEntry; @@ -1328,7 +1328,7 @@ function Ace2Inner() { if (infoStructs.length < 1) return; infoStructs.forEach((info) => { - const p2 = PROFILER('insertLine', false); // eslint-disable-line + const p2 = PROFILER('insertLine', false); // eslint-disable-line new-cap const node = info.node; const key = uniqueId(node); let entry; @@ -1556,8 +1556,8 @@ function Ace2Inner() { if (rep.selStart && rep.selEnd) { const selStartChar = rep.lines.offsetOfIndex(rep.selStart[0]) + rep.selStart[1]; const selEndChar = rep.lines.offsetOfIndex(rep.selEnd[0]) + rep.selEnd[1]; - const result = Changeset. - characterRangeFollow(changes, selStartChar, selEndChar, insertsAfterSelection); + const result = + Changeset.characterRangeFollow(changes, selStartChar, selEndChar, insertsAfterSelection); requiredSelectionSetting = [result[0], result[1], rep.selFocusAtStart]; } @@ -1662,10 +1662,8 @@ function Ace2Inner() { const performDocumentApplyAttributesToCharRange = (start, end, attribs) => { end = Math.min(end, rep.alltext.length - 1); - documentAttributeManager. - setAttributesOnRange(lineAndColumnFromChar(start), - lineAndColumnFromChar(end), attribs - ); + documentAttributeManager.setAttributesOnRange( + lineAndColumnFromChar(start), lineAndColumnFromChar(end), attribs); }; editorInfo.ace_performDocumentApplyAttributesToCharRange = @@ -2213,18 +2211,16 @@ function Ace2Inner() { const selectFormattingButtonIfLineHasStyleApplied = (rep) => { FORMATTING_STYLES.forEach((style) => { - const hasStyleOnRepSelection = documentAttributeManager. - hasAttributeOnSelectionOrCaretPosition(style); + const hasStyleOnRepSelection = + documentAttributeManager.hasAttributeOnSelectionOrCaretPosition(style); updateStyleButtonState(style, hasStyleOnRepSelection); }); }; const doCreateDomLine = (nonEmpty) => domline.createDomLine(nonEmpty, doesWrap, browser, doc); - const textify = (str) => str. - replace(/[\n\r ]/g, ' '). - replace(/\xa0/g, ' '). - replace(/\t/g, ' '); + const textify = + (str) => str.replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' '); const _blockElems = { div: 1, @@ -2249,7 +2245,7 @@ function Ace2Inner() { // indicating inserted content. for example, [0,0] means content was inserted // at the top of the document, while [3,4] means line 3 was deleted, modified, // or replaced with one or more new lines of content. ranges do not touch. - const p = PROFILER('getDirtyRanges', false); // eslint-disable-line + const p = PROFILER('getDirtyRanges', false); // eslint-disable-line new-cap p.forIndices = 0; p.consecutives = 0; p.corrections = 0; @@ -2449,7 +2445,7 @@ function Ace2Inner() { }; const isNodeDirty = (n) => { - const p = PROFILER('cleanCheck', false); // eslint-disable-line + const p = PROFILER('cleanCheck', false); // eslint-disable-line new-cap if (n.parentNode !== root) return true; const data = getAssoc(n, 'dirtiness'); if (!data) return true; @@ -3519,7 +3515,7 @@ function Ace2Inner() { }; } }; - var selection = {}; + const selection = {}; selection.startPoint = pointFromRangeBound(range.startContainer, range.startOffset); selection.endPoint = pointFromRangeBound(range.endContainer, range.endOffset); selection.focusAtStart = ( @@ -3904,8 +3900,8 @@ function Ace2Inner() { // Init documentAttributeManager documentAttributeManager = new AttributeManager(rep, performDocumentApplyChangeset); - editorInfo.ace_performDocumentApplyAttributesToRange = (...args) => documentAttributeManager. - setAttributesOnRange(args); + editorInfo.ace_performDocumentApplyAttributesToRange = + (...args) => documentAttributeManager.setAttributesOnRange(args); this.init = () => { $(document).ready(() => { From aeedaac04e534b7d8245fbe5b6fa83d4b4f61bf3 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 17 Jan 2021 19:11:08 -0500 Subject: [PATCH 067/357] ace2_inner: Improve readability of object creation --- src/static/js/ace2_inner.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 255cc0165..b03365205 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -3515,18 +3515,15 @@ function Ace2Inner() { }; } }; - const selection = {}; - selection.startPoint = pointFromRangeBound(range.startContainer, range.startOffset); - selection.endPoint = pointFromRangeBound(range.endContainer, range.endOffset); - selection.focusAtStart = ( - ( - (range.startContainer !== range.endContainer) || - (range.startOffset !== range.endOffset) - ) && + const selection = { + startPoint: pointFromRangeBound(range.startContainer, range.startOffset), + endPoint: pointFromRangeBound(range.endContainer, range.endOffset), + focusAtStart: + (range.startContainer !== range.endContainer || range.startOffset !== range.endOffset) && browserSelection.anchorNode && - (browserSelection.anchorNode === range.endContainer) && - (browserSelection.anchorOffset === range.endOffset) - ); + browserSelection.anchorNode === range.endContainer && + browserSelection.anchorOffset === range.endOffset, + }; if (selection.startPoint.node.ownerDocument !== window.document) { return null; From 10a91825fc2b51078f94f01794cbc7e41a81cd8e Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 20 Jan 2021 23:17:11 -0500 Subject: [PATCH 068/357] ace2_inner: Fix argument bug in `execCommand()` This fixes a bug introduced in commit c38c34bef47a74467394797b34165641a87ac620. --- src/static/js/ace2_inner.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index b03365205..95db2c14f 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -649,12 +649,10 @@ function Ace2Inner() { const execCommand = (cmd, ...args) => { cmd = cmd.toLowerCase(); - // TODO: Rhansen to check this logic. - const cmdArgs = args; if (CMDS[cmd]) { inCallStackIfNecessary(cmd, () => { fastIncorp(9); - CMDS[cmd](CMDS, ...cmdArgs); + CMDS[cmd](...args); }); } }; From f0a77cb98cd57a99934a2132a204bcf90d396812 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 22 Jan 2021 20:41:14 +0000 Subject: [PATCH 069/357] lint: contentcollector and domline Various tidy up and linting of contentcollector.js and domline.js. 3 Tests disabled which are not due to be covered. Co-authored-by: Richard Hansen --- src/static/js/contentcollector.js | 309 +++++++++++------------- src/static/js/domline.js | 81 ++++--- tests/backend/specs/api/importexport.js | 3 + 3 files changed, 187 insertions(+), 206 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 0c6b0ee4a..ab5781725 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -29,47 +29,31 @@ const _MAX_LIST_LEVEL = 16; const UNorm = require('unorm'); const Changeset = require('./Changeset'); const hooks = require('./pluginfw/hooks'); -const _ = require('./underscore'); -function sanitizeUnicode(s) { - return UNorm.nfc(s); -} - -function makeContentCollector(collectStyles, abrowser, apool, domInterface, className2Author) { - abrowser = abrowser || {}; - // I don't like the above. +const sanitizeUnicode = (s) => UNorm.nfc(s); +const makeContentCollector = (collectStyles, abrowser, apool, domInterface, className2Author) => { const dom = domInterface || { - isNodeText(n) { - return (n.nodeType == 3); - }, - nodeTagName(n) { - return n.tagName; - }, - nodeValue(n) { - return n.nodeValue; - }, - nodeNumChildren(n) { + isNodeText: (n) => n.nodeType === 3, + nodeTagName: (n) => n.tagName, + nodeValue: (n) => n.nodeValue, + nodeNumChildren: (n) => { if (n.childNodes == null) return 0; return n.childNodes.length; }, - nodeChild(n, i) { + nodeChild: (n, i) => { if (n.childNodes.item == null) { return n.childNodes[i]; } return n.childNodes.item(i); }, - nodeProp(n, p) { - return n[p]; - }, - nodeAttr(n, a) { + nodeProp: (n, p) => n[p], + nodeAttr: (n, a) => { if (n.getAttribute != null) return n.getAttribute(a); if (n.attribs != null) return n.attribs[a]; return null; }, - optNodeInnerHTML(n) { - return n.innerHTML; - }, + optNodeInnerHTML: (n) => n.innerHTML, }; const _blockElems = { @@ -79,58 +63,45 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas li: 1, }; - _.each(hooks.callAll('ccRegisterBlockElements'), (element) => { + hooks.callAll('ccRegisterBlockElements').forEach((element) => { _blockElems[element] = 1; }); - function isBlockElement(n) { - return !!_blockElems[(dom.nodeTagName(n) || '').toLowerCase()]; - } + const isBlockElement = (n) => !!_blockElems[(dom.nodeTagName(n) || '').toLowerCase()]; - function textify(str) { - return sanitizeUnicode( - str.replace(/(\n | \n)/g, ' ').replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' ')); - } + const textify = (str) => sanitizeUnicode( + str.replace(/(\n | \n)/g, ' ') + .replace(/[\n\r ]/g, ' ') + .replace(/\xa0/g, ' ') + .replace(/\t/g, ' ')); - function getAssoc(node, name) { - return dom.nodeProp(node, `_magicdom_${name}`); - } + const getAssoc = (node, name) => dom.nodeProp(node, `_magicdom_${name}`); - const lines = (function () { + const lines = (() => { const textArray = []; const attribsArray = []; let attribsBuilder = null; const op = Changeset.newOp('+'); - var self = { - length() { - return textArray.length; - }, - atColumnZero() { - return textArray[textArray.length - 1] === ''; - }, - startNew() { + const self = { + length: () => textArray.length, + atColumnZero: () => textArray[textArray.length - 1] === '', + startNew: () => { textArray.push(''); self.flush(true); attribsBuilder = Changeset.smartOpAssembler(); }, - textOfLine(i) { - return textArray[i]; - }, - appendText(txt, attrString) { + textOfLine: (i) => textArray[i], + appendText: (txt, attrString) => { textArray[textArray.length - 1] += txt; // dmesg(txt+" / "+attrString); op.attribs = attrString; op.chars = txt.length; attribsBuilder.append(op); }, - textLines() { - return textArray.slice(); - }, - attribLines() { - return attribsArray; - }, + textLines: () => textArray.slice(), + attribLines: () => attribsArray, // call flush only when you're done - flush(withNewline) { + flush: (withNewline) => { if (attribsBuilder) { attribsArray.push(attribsBuilder.toString()); attribsBuilder = null; @@ -139,21 +110,24 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas }; self.startNew(); return self; - }()); + })(); const cc = {}; - function _ensureColumnZero(state) { + const _ensureColumnZero = (state) => { if (!lines.atColumnZero()) { cc.startNewLine(state); } - } + }; let selection, startPoint, endPoint; let selStart = [-1, -1]; let selEnd = [-1, -1]; - function _isEmpty(node, state) { + const _isEmpty = (node, state) => { // consider clean blank lines pasted in IE to be empty - if (dom.nodeNumChildren(node) == 0) return true; - if (dom.nodeNumChildren(node) == 1 && getAssoc(node, 'shouldBeEmpty') && dom.optNodeInnerHTML(node) == ' ' && !getAssoc(node, 'unpasted')) { + if (dom.nodeNumChildren(node) === 0) return true; + if (dom.nodeNumChildren(node) === 1 && + getAssoc(node, 'shouldBeEmpty') && + dom.optNodeInnerHTML(node) === ' ' && + !getAssoc(node, 'unpasted')) { if (state) { const child = dom.nodeChild(node, 0); _reachPoint(child, 0, state); @@ -162,37 +136,37 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas return true; } return false; - } + }; - function _pointHere(charsAfter, state) { + const _pointHere = (charsAfter, state) => { const ln = lines.length() - 1; let chr = lines.textOfLine(ln).length; - if (chr == 0 && !_.isEmpty(state.lineAttributes)) { + if (chr === 0 && Object.keys(state.lineAttributes).length !== 0) { chr += 1; // listMarker } chr += charsAfter; return [ln, chr]; - } + }; - function _reachBlockPoint(nd, idx, state) { + const _reachBlockPoint = (nd, idx, state) => { if (!dom.isNodeText(nd)) _reachPoint(nd, idx, state); - } + }; - function _reachPoint(nd, idx, state) { - if (startPoint && nd == startPoint.node && startPoint.index == idx) { + const _reachPoint = (nd, idx, state) => { + if (startPoint && nd === startPoint.node && startPoint.index === idx) { selStart = _pointHere(0, state); } - if (endPoint && nd == endPoint.node && endPoint.index == idx) { + if (endPoint && nd === endPoint.node && endPoint.index === idx) { selEnd = _pointHere(0, state); } - } - cc.incrementFlag = function (state, flagName) { + }; + cc.incrementFlag = (state, flagName) => { state.flags[flagName] = (state.flags[flagName] || 0) + 1; }; - cc.decrementFlag = function (state, flagName) { + cc.decrementFlag = (state, flagName) => { state.flags[flagName]--; }; - cc.incrementAttrib = function (state, attribName) { + cc.incrementAttrib = (state, attribName) => { if (!state.attribs[attribName]) { state.attribs[attribName] = 1; } else { @@ -200,15 +174,15 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } _recalcAttribString(state); }; - cc.decrementAttrib = function (state, attribName) { + cc.decrementAttrib = (state, attribName) => { state.attribs[attribName]--; _recalcAttribString(state); }; - function _enterList(state, listType) { + const _enterList = (state, listType) => { if (!listType) return; const oldListType = state.lineAttributes.list; - if (listType != 'none') { + if (listType !== 'none') { state.listNesting = (state.listNesting || 0) + 1; // reminder that listType can be "number2", "number3" etc. if (listType.indexOf('number') !== -1) { @@ -223,36 +197,36 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } _recalcAttribString(state); return oldListType; - } + }; - function _exitList(state, oldListType) { + const _exitList = (state, oldListType) => { if (state.lineAttributes.list) { state.listNesting--; } - if (oldListType && oldListType != 'none') { + if (oldListType && oldListType !== 'none') { state.lineAttributes.list = oldListType; } else { delete state.lineAttributes.list; delete state.lineAttributes.start; } _recalcAttribString(state); - } + }; - function _enterAuthor(state, author) { + const _enterAuthor = (state, author) => { const oldAuthor = state.author; state.authorLevel = (state.authorLevel || 0) + 1; state.author = author; _recalcAttribString(state); return oldAuthor; - } + }; - function _exitAuthor(state, oldAuthor) { + const _exitAuthor = (state, oldAuthor) => { state.authorLevel--; state.author = oldAuthor; _recalcAttribString(state); - } + }; - function _recalcAttribString(state) { + const _recalcAttribString = (state) => { const lst = []; for (const a in state.attribs) { if (state.attribs[a]) { @@ -284,35 +258,34 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } } state.attribString = Changeset.makeAttribsString('+', lst, apool); - } + }; - function _produceLineAttributesMarker(state) { + const _produceLineAttributesMarker = (state) => { // TODO: This has to go to AttributeManager. const attributes = [ ['lmkr', '1'], ['insertorder', 'first'], - ].concat( - _.map(state.lineAttributes, (value, key) => [key, value]) - ); + ...Object.entries(state.lineAttributes), + ]; lines.appendText('*', Changeset.makeAttribsString('+', attributes, apool)); - } - cc.startNewLine = function (state) { + }; + cc.startNewLine = (state) => { if (state) { - const atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0; - if (atBeginningOfLine && !_.isEmpty(state.lineAttributes)) { + const atBeginningOfLine = lines.textOfLine(lines.length() - 1).length === 0; + if (atBeginningOfLine && Object.keys(state.lineAttributes).length !== 0) { _produceLineAttributesMarker(state); } } lines.startNew(); }; - cc.notifySelection = function (sel) { + cc.notifySelection = (sel) => { if (sel) { selection = sel; startPoint = selection.startPoint; endPoint = selection.endPoint; } }; - cc.doAttrib = function (state, na) { + cc.doAttrib = (state, na) => { state.localAttribs = (state.localAttribs || []); state.localAttribs.push(na); cc.incrementAttrib(state, na); @@ -342,9 +315,10 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas if (isBlock) _ensureColumnZero(state); const startLine = lines.length() - 1; _reachBlockPoint(node, 0, state); + if (dom.isNodeText(node)) { let txt = dom.nodeValue(node); - var tname = dom.nodeAttr(node.parentNode, 'name'); + const tname = dom.nodeAttr(node.parentNode, 'name'); const txtFromHook = hooks.callAll('collectContentLineText', { cc: this, @@ -364,11 +338,11 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas let rest = ''; let x = 0; // offset into original text - if (txt.length == 0) { - if (startPoint && node == startPoint.node) { + if (txt.length === 0) { + if (startPoint && node === startPoint.node) { selStart = _pointHere(0, state); } - if (endPoint && node == endPoint.node) { + if (endPoint && node === endPoint.node) { selEnd = _pointHere(0, state); } } @@ -381,10 +355,10 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas txt = firstLine; } else { /* will only run this loop body once */ } - if (startPoint && node == startPoint.node && startPoint.index - x <= txt.length) { + if (startPoint && node === startPoint.node && startPoint.index - x <= txt.length) { selStart = _pointHere(startPoint.index - x, state); } - if (endPoint && node == endPoint.node && endPoint.index - x <= txt.length) { + if (endPoint && node === endPoint.node && endPoint.index - x <= txt.length) { selEnd = _pointHere(endPoint.index - x, state); } let txt2 = txt; @@ -395,12 +369,12 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas // removing "\n" from pasted HTML will collapse words together. txt2 = ''; } - const atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0; + const atBeginningOfLine = lines.textOfLine(lines.length() - 1).length === 0; if (atBeginningOfLine) { // newlines in the source mustn't become spaces at beginning of line box txt2 = txt2.replace(/^\n*/, ''); } - if (atBeginningOfLine && !_.isEmpty(state.lineAttributes)) { + if (atBeginningOfLine && Object.keys(state.lineAttributes).length !== 0) { _produceLineAttributesMarker(state); } lines.appendText(textify(txt2), state.attribString); @@ -411,15 +385,15 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } } } else { - var tname = (dom.nodeTagName(node) || '').toLowerCase(); + const tname = (dom.nodeTagName(node) || '').toLowerCase(); - if (tname == 'img') { - const collectContentImage = hooks.callAll('collectContentImage', { + if (tname === 'img') { + hooks.callAll('collectContentImage', { cc, state, tname, - styl, - cls, + styl: null, + cls: null, node, }); } else { @@ -427,7 +401,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas delete state.lineAttributes.img; } - if (tname == 'br') { + if (tname === 'br') { this.breakLine = true; const tvalue = dom.nodeAttr(node, 'value'); const induceLineBreak = hooks.callAll('collectContentLineBreak', { @@ -438,17 +412,19 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas styl: null, cls: null, }); - const startNewLine = (typeof (induceLineBreak) === 'object' && induceLineBreak.length == 0) ? true : induceLineBreak[0]; + const startNewLine = ( + typeof (induceLineBreak) === 'object' && + induceLineBreak.length === 0) ? true : induceLineBreak[0]; if (startNewLine) { cc.startNewLine(state); } - } else if (tname == 'script' || tname == 'style') { + } else if (tname === 'script' || tname === 'style') { // ignore } else if (!isEmpty) { - var styl = dom.nodeAttr(node, 'style'); - var cls = dom.nodeAttr(node, 'class'); - let isPre = (tname == 'pre'); - if ((!isPre) && abrowser.safari) { + let styl = dom.nodeAttr(node, 'style'); + let cls = dom.nodeAttr(node, 'class'); + let isPre = (tname === 'pre'); + if ((!isPre) && abrowser && abrowser.safari) { isPre = (styl && /\bwhite-space:\s*pre\b/i.exec(styl)); } if (isPre) cc.incrementFlag(state, 'preMode'); @@ -460,10 +436,10 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas styl = null; cls = null; - // We have to return here but this could break things in the future, for now it shows how to fix the problem + // We have to return here but this could break things in the future, + // for now it shows how to fix the problem return; } - if (collectStyles) { hooks.callAll('collectContentPre', { cc, @@ -472,29 +448,34 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas styl, cls, }); - if (tname == 'b' || (styl && /\bfont-weight:\s*bold\b/i.exec(styl)) || tname == 'strong') { + if (tname === 'b' || + (styl && /\bfont-weight:\s*bold\b/i.exec(styl)) || + tname === 'strong') { cc.doAttrib(state, 'bold'); } - if (tname == 'i' || (styl && /\bfont-style:\s*italic\b/i.exec(styl)) || tname == 'em') { + if (tname === 'i' || + (styl && /\bfont-style:\s*italic\b/i.exec(styl)) || + tname === 'em') { cc.doAttrib(state, 'italic'); } - if (tname == 'u' || (styl && /\btext-decoration:\s*underline\b/i.exec(styl)) || tname == 'ins') { + if (tname === 'u' || + (styl && /\btext-decoration:\s*underline\b/i.exec(styl)) || + tname === 'ins') { cc.doAttrib(state, 'underline'); } - if (tname == 's' || (styl && /\btext-decoration:\s*line-through\b/i.exec(styl)) || tname == 'del') { + if (tname === 's' || + (styl && /\btext-decoration:\s*line-through\b/i.exec(styl)) || + tname === 'del') { cc.doAttrib(state, 'strikethrough'); } - if (tname == 'ul' || tname == 'ol') { - if (node.attribs) { - var type = node.attribs.class; - } else { - var type = null; - } + if (tname === 'ul' || tname === 'ol') { + let type = node.attribs ? node.attribs.class : null; const rr = cls && /(?:^| )list-([a-z]+[0-9]+)\b/.exec(cls); - // lists do not need to have a type, so before we make a wrong guess, check if we find a better hint within the node's children + // lists do not need to have a type, so before we make a wrong guess + // check if we find a better hint within the node's children if (!rr && !type) { - for (var i in node.children) { - if (node.children[i] && node.children[i].name == 'ul') { + for (const i in node.children) { + if (node.children[i] && node.children[i].name === 'ul') { type = node.children[i].attribs.class; if (type) { break; @@ -505,8 +486,9 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas if (rr && rr[1]) { type = rr[1]; } else { - if (tname == 'ul') { - if ((type && type.match('indent')) || (node.attribs && node.attribs.class && node.attribs.class.match('indent'))) { + if (tname === 'ul') { + if ((type && type.match('indent')) || + (node.attribs && node.attribs.class && node.attribs.class.match('indent'))) { type = 'indent'; } else { type = 'bullet'; @@ -517,10 +499,10 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas type += String(Math.min(_MAX_LIST_LEVEL, (state.listNesting || 0) + 1)); } oldListTypeOrNull = (_enterList(state, type) || 'none'); - } else if ((tname == 'div' || tname == 'p') && cls && cls.match(/(?:^| )ace-line\b/)) { + } else if ((tname === 'div' || tname === 'p') && cls && cls.match(/(?:^| )ace-line\b/)) { // This has undesirable behavior in Chrome but is right in other browsers. // See https://github.com/ether/etherpad-lite/issues/2412 for reasoning - if (!abrowser.chrome) oldListTypeOrNull = (_enterList(state, type) || 'none'); + if (!abrowser.chrome) oldListTypeOrNull = (_enterList(state, undefined) || 'none'); } else if ((tname === 'li')) { state.lineAttributes.start = state.start || 0; _recalcAttribString(state); @@ -565,8 +547,8 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas if (className2Author && cls) { const classes = cls.match(/\S+/g); if (classes && classes.length > 0) { - for (var i = 0; i < classes.length; i++) { - var c = classes[i]; + for (let i = 0; i < classes.length; i++) { + const c = classes[i]; const a = className2Author(c); if (a) { oldAuthorOrNull = (_enterAuthor(state, a) || 'none'); @@ -578,8 +560,8 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } const nc = dom.nodeNumChildren(node); - for (var i = 0; i < nc; i++) { - var c = dom.nodeChild(node, i); + for (let i = 0; i < nc; i++) { + const c = dom.nodeChild(node, i); cc.collectContent(c, state); } @@ -595,7 +577,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas if (isPre) cc.decrementFlag(state, 'preMode'); if (state.localAttribs) { - for (var i = 0; i < state.localAttribs.length; i++) { + for (let i = 0; i < state.localAttribs.length; i++) { cc.decrementAttrib(state, state.localAttribs[i]); } } @@ -609,7 +591,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } _reachBlockPoint(node, 1, state); if (isBlock) { - if (lines.length() - 1 == startLine) { + if (lines.length() - 1 === startLine) { // added additional check to resolve https://github.com/JohnMcLear/ep_copy_paste_images/issues/20 // this does mean that images etc can't be pasted on lists but imho that's fine @@ -626,7 +608,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas state.localAttribs = localAttribs; }; // can pass a falsy value for end of doc - cc.notifyNextNode = function (node) { + cc.notifyNextNode = (node) => { // an "empty block" won't end a line; this addresses an issue in IE with // typing into a blank line at the end of the document. typed text // goes into the body, and the empty line div still looks clean. @@ -637,21 +619,15 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } }; // each returns [line, char] or [-1,-1] - const getSelectionStart = function () { - return selStart; - }; - const getSelectionEnd = function () { - return selEnd; - }; + const getSelectionStart = () => selStart; + const getSelectionEnd = () => selEnd; // returns array of strings for lines found, last entry will be "" if // last line is complete (i.e. if a following span should be on a new line). // can be called at any point - cc.getLines = function () { - return lines.textLines(); - }; + cc.getLines = () => lines.textLines(); - cc.finish = function () { + cc.finish = () => { lines.flush(); const lineAttribs = lines.attribLines(); const lineStrings = cc.getLines(); @@ -662,17 +638,17 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas const ss = getSelectionStart(); const se = getSelectionEnd(); - function fixLongLines() { + const fixLongLines = () => { // design mode does not deal with with really long lines! const lineLimit = 2000; // chars const buffer = 10; // chars allowed over before wrapping let linesWrapped = 0; let numLinesAfter = 0; - for (var i = lineStrings.length - 1; i >= 0; i--) { + for (let i = lineStrings.length - 1; i >= 0; i--) { let oldString = lineStrings[i]; let oldAttribString = lineAttribs[i]; if (oldString.length > lineLimit + buffer) { - var newStrings = []; + const newStrings = []; const newAttribStrings = []; while (oldString.length > lineLimit) { // var semiloc = oldString.lastIndexOf(';', lineLimit-1); @@ -688,13 +664,13 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas newAttribStrings.push(oldAttribString); } - function fixLineNumber(lineChar) { + const fixLineNumber = (lineChar) => { if (lineChar[0] < 0) return; let n = lineChar[0]; let c = lineChar[1]; if (n > i) { n += (newStrings.length - 1); - } else if (n == i) { + } else if (n === i) { let a = 0; while (c > newStrings[a].length) { c -= newStrings[a].length; @@ -704,23 +680,20 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas } lineChar[0] = n; lineChar[1] = c; - } + }; fixLineNumber(ss); fixLineNumber(se); linesWrapped++; numLinesAfter += newStrings.length; - - newStrings.unshift(i, 1); - lineStrings.splice.apply(lineStrings, newStrings); - newAttribStrings.unshift(i, 1); - lineAttribs.splice.apply(lineAttribs, newAttribStrings); + lineStrings.splice(i, 1, ...newStrings); + lineAttribs.splice(i, 1, ...newAttribStrings); } } return { linesWrapped, numLinesAfter, }; - } + }; const wrapData = fixLongLines(); return { @@ -734,7 +707,7 @@ function makeContentCollector(collectStyles, abrowser, apool, domInterface, clas }; return cc; -} +}; exports.sanitizeUnicode = sanitizeUnicode; exports.makeContentCollector = makeContentCollector; diff --git a/src/static/js/domline.js b/src/static/js/domline.js index a341500b1..8667a022d 100644 --- a/src/static/js/domline.js +++ b/src/static/js/domline.js @@ -26,17 +26,17 @@ const Security = require('./security'); const hooks = require('./pluginfw/hooks'); const _ = require('./underscore'); const lineAttributeMarker = require('./linestylefilter').lineAttributeMarker; -const noop = function () {}; +const noop = () => {}; const domline = {}; -domline.addToLineClass = function (lineClass, cls) { +domline.addToLineClass = (lineClass, cls) => { // an "empty span" at any point can be used to add classes to // the line, using line:className. otherwise, we ignore // the span. cls.replace(/\S+/g, (c) => { - if (c.indexOf('line:') == 0) { + if (c.indexOf('line:') === 0) { // add class to line lineClass = (lineClass ? `${lineClass} ` : '') + c.substring(5); } @@ -46,7 +46,7 @@ domline.addToLineClass = function (lineClass, cls) { // if "document" is falsy we don't create a DOM node, just // an object with innerHTML and className -domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument) { +domline.createDomLine = (nonEmpty, doesWrap, optBrowser, optDocument) => { const result = { node: null, appendSpan: noop, @@ -73,15 +73,12 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument) { let postHtml = ''; let curHTML = null; - function processSpaces(s) { - return domline.processSpaces(s, doesWrap); - } - + const processSpaces = (s) => domline.processSpaces(s, doesWrap); const perTextNodeProcess = (doesWrap ? _.identity : processSpaces); const perHtmlLineProcess = (doesWrap ? processSpaces : _.identity); let lineClass = 'ace-line'; - result.appendSpan = function (txt, cls) { + result.appendSpan = (txt, cls) => { let processedMarker = false; // Handle lineAttributeMarker, if present if (cls.indexOf(lineAttributeMarker) >= 0) { @@ -96,7 +93,6 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument) { postHtml += modifier.postHtml; processedMarker |= modifier.processedMarker; }); - if (listType) { listType = listType[1]; if (listType) { @@ -105,12 +101,15 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument) { postHtml = `${postHtml}`; } else { if (start) { // is it a start of a list with more than one item in? - if (start[1] == 1) { // if its the first one at this level? - lineClass = `${lineClass} ` + `list-start-${listType}`; // Add start class to DIV node + if (start[1] === 1) { // if its the first one at this level? + // Add start class to DIV node + lineClass = `${lineClass} ` + `list-start-${listType}`; } - preHtml += `
  1. `; + preHtml += + `
    1. `; } else { - preHtml += `
      1. `; // Handles pasted contents into existing lists + // Handles pasted contents into existing lists + preHtml += `
        1. `; } postHtml += '
        '; } @@ -163,18 +162,20 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument) { } else if (txt) { if (href) { const urn_schemes = new RegExp('^(about|geo|mailto|tel):'); - if (!~href.indexOf('://') && !urn_schemes.test(href)) // if the url doesn't include a protocol prefix, assume http - { + // if the url doesn't include a protocol prefix, assume http + if (!~href.indexOf('://') && !urn_schemes.test(href)) { href = `http://${href}`; } - // Using rel="noreferrer" stops leaking the URL/location of the pad when clicking links in the document. + // Using rel="noreferrer" stops leaking the URL/location of the pad when + // clicking links in the document. // Not all browsers understand this attribute, but it's part of the HTML5 standard. // https://html.spec.whatwg.org/multipage/links.html#link-type-noreferrer // Additionally, we do rel="noopener" to ensure a higher level of referrer security. // https://html.spec.whatwg.org/multipage/links.html#link-type-noopener // https://mathiasbynens.github.io/rel-noopener/ // https://github.com/ether/etherpad-lite/pull/3636 - extraOpenTags = `${extraOpenTags}`; + const escapedHref = Security.escapeHTMLAttribute(href); + extraOpenTags = `${extraOpenTags}`; extraCloseTags = `${extraCloseTags}`; } if (simpleTags) { @@ -183,16 +184,22 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument) { simpleTags.reverse(); extraCloseTags = `${extraCloseTags}`; } - html.push('', extraOpenTags, perTextNodeProcess(Security.escapeHTML(txt)), extraCloseTags, ''); + html.push( + '', + extraOpenTags, + perTextNodeProcess(Security.escapeHTML(txt)), + extraCloseTags, + ''); } }; - result.clearSpans = function () { + result.clearSpans = () => { html = []; lineClass = 'ace-line'; result.lineMarker = 0; }; - function writeHTML() { + const writeHTML = () => { let newHTML = perHtmlLineProcess(html.join('')); if (!newHTML) { if ((!document) || (!optBrowser)) { @@ -209,21 +216,19 @@ domline.createDomLine = function (nonEmpty, doesWrap, optBrowser, optDocument) { curHTML = newHTML; result.node.innerHTML = curHTML; } - if (lineClass !== null) result.node.className = lineClass; + if (lineClass != null) result.node.className = lineClass; hooks.callAll('acePostWriteDomLineHTML', { node: result.node, }); - } + }; result.prepareForAdd = writeHTML; result.finishUpdate = writeHTML; - result.getInnerHTML = function () { - return curHTML || ''; - }; + result.getInnerHTML = () => curHTML || ''; return result; }; -domline.processSpaces = function (s, doesWrap) { +domline.processSpaces = (s, doesWrap) => { if (s.indexOf('<') < 0 && !doesWrap) { // short-cut return s.replace(/ /g, ' '); @@ -237,31 +242,31 @@ domline.processSpaces = function (s, doesWrap) { let beforeSpace = false; // last space in a run is normal, others are nbsp, // end of line is nbsp - for (var i = parts.length - 1; i >= 0; i--) { - var p = parts[i]; - if (p == ' ') { + for (let i = parts.length - 1; i >= 0; i--) { + const p = parts[i]; + if (p === ' ') { if (endOfLine || beforeSpace) parts[i] = ' '; endOfLine = false; beforeSpace = true; - } else if (p.charAt(0) != '<') { + } else if (p.charAt(0) !== '<') { endOfLine = false; beforeSpace = false; } } // beginning of line is nbsp - for (var i = 0; i < parts.length; i++) { - var p = parts[i]; - if (p == ' ') { + for (let i = 0; i < parts.length; i++) { + const p = parts[i]; + if (p === ' ') { parts[i] = ' '; break; - } else if (p.charAt(0) != '<') { + } else if (p.charAt(0) !== '<') { break; } } } else { - for (var i = 0; i < parts.length; i++) { - var p = parts[i]; - if (p == ' ') { + for (let i = 0; i < parts.length; i++) { + const p = parts[i]; + if (p === ' ') { parts[i] = ' '; } } diff --git a/tests/backend/specs/api/importexport.js b/tests/backend/specs/api/importexport.js index 5252f1300..b7b45b261 100644 --- a/tests/backend/specs/api/importexport.js +++ b/tests/backend/specs/api/importexport.js @@ -21,16 +21,19 @@ const testImports = { input: '
      2. wtf', expectedHTML: 'wtf

        ', expectedText: 'wtf\n\n', + disabled: true, }, 'nonelistiteminlist #3620': { input: '
          test
        • FOO
        ', expectedHTML: '
          test
        • FOO

        ', expectedText: '\ttest\n\t* FOO\n\n', + disabled: true, }, 'whitespaceinlist #3620': { input: '
        • FOO
        ', expectedHTML: '
        • FOO

        ', expectedText: '\t* FOO\n\n', + disabled: true, }, 'prefixcorrectlinenumber': { input: '
        1. should be 1
        2. should be 2
        ', From ee158b0fe5dfdc8201057b5edb40847727958b03 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 23 Jan 2021 13:54:50 +0000 Subject: [PATCH 070/357] bugfix: bump ueberdb to 1.2.5 to resolve #4645 which caused a users color not to be persistent --- src/package-lock.json | 1303 +++++++++++++++++++---------------------- src/package.json | 2 +- 2 files changed, 618 insertions(+), 687 deletions(-) diff --git a/src/package-lock.json b/src/package-lock.json index db0516bf9..f4f145474 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -286,6 +286,37 @@ "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@babel/parser": { @@ -357,9 +388,9 @@ } }, "@eslint/eslintrc": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz", - "integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", + "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -369,23 +400,11 @@ "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", - "lodash": "^4.17.19", + "lodash": "^4.17.20", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, "debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", @@ -509,9 +528,9 @@ "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" }, "@sinonjs/commons": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", - "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz", + "integrity": "sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw==", "dev": true, "requires": { "type-detect": "4.0.8" @@ -526,20 +545,10 @@ "@sinonjs/commons": "^1.7.0" } }, - "@sinonjs/formatio": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz", - "integrity": "sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^5.0.2" - } - }, "@sinonjs/samsam": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.0.tgz", - "integrity": "sha512-hXpcfx3aq+ETVBwPlRFICld5EnrkexXuXDwqUNhDdr5L8VjvMeSRwyOa0qL7XFmR+jVWR4rUZtnxlG7RX72sBg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.1.tgz", + "integrity": "sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==", "dev": true, "requires": { "@sinonjs/commons": "^1.6.0", @@ -564,9 +573,9 @@ "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" }, "@types/node": { - "version": "14.14.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz", - "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==" + "version": "14.14.22", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.22.tgz", + "integrity": "sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw==" }, "@types/node-fetch": { "version": "2.5.8", @@ -717,9 +726,9 @@ } }, "ajv": { - "version": "6.12.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", - "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -728,27 +737,26 @@ } }, "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==" + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, "anymatch": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -792,20 +800,6 @@ "requires": { "lodash": "^4.17.14" } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } } } }, @@ -827,20 +821,6 @@ "readable-stream": "^2.0.0" }, "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -964,11 +944,6 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" - }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -980,11 +955,6 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, "async-stacktrace": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/async-stacktrace/-/async-stacktrace-0.0.2.tgz", @@ -1001,9 +971,9 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", - "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, "backo2": { "version": "1.0.2", @@ -1049,23 +1019,16 @@ "tweetnacl": "^0.14.3" } }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "requires": { - "callsite": "1.0.0" - } - }, "bignumber.js": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" }, "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true }, "binary-search": { "version": "1.3.6", @@ -1073,14 +1036,55 @@ "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==" }, "bl": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", - "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", - "dev": true, + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + } } }, "blob": { @@ -1156,6 +1160,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -1168,7 +1173,8 @@ "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true }, "bson": { "version": "1.1.5", @@ -1224,12 +1230,13 @@ } }, "call-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", - "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, "requires": { "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.0" + "get-intrinsic": "^1.0.2" } }, "call-me-maybe": { @@ -1237,11 +1244,6 @@ "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=" }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" - }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1250,7 +1252,8 @@ "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true }, "caseless": { "version": "0.12.0", @@ -1273,27 +1276,16 @@ "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==" }, - "chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - } - }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, "channels": { @@ -1311,11 +1303,6 @@ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==" }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" - }, "cheerio": { "version": "0.22.0", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", @@ -1343,6 +1330,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", + "dev": true, "requires": { "anymatch": "~3.1.1", "braces": "~3.0.2", @@ -1374,19 +1362,11 @@ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, - "cli-table": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.4.tgz", - "integrity": "sha512-1vinpnX/ZERcmE443i3SZTmU5DF0rPO9DrL4I2iVAllhxzCM9SzPlHnz19fsZB78htkKZvYBvj6SZ6vXnaxmTA==", - "requires": { - "chalk": "^2.4.1", - "string-width": "^4.2.0" - } - }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, "requires": { "string-width": "^3.1.0", "strip-ansi": "^5.2.0", @@ -1396,22 +1376,26 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, "requires": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -1422,6 +1406,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, "requires": { "ansi-regex": "^4.1.0" } @@ -1456,13 +1441,13 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "optional": true + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "requires": { "color-name": "1.1.3" } @@ -1470,7 +1455,8 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, "combined-stream": { "version": "1.0.8", @@ -1502,9 +1488,9 @@ "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" }, "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "component-inherit": { "version": "0.0.3", @@ -1720,15 +1706,8 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "requires": { - "type-detect": "^4.0.0" - } + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true }, "deep-extend": { "version": "0.6.0", @@ -1755,6 +1734,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, "requires": { "object-keys": "^1.0.12" } @@ -1794,7 +1774,8 @@ "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true }, "dirty": { "version": "1.1.0", @@ -1841,11 +1822,6 @@ "domelementtype": "1" } }, - "drange": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/drange/-/drange-1.1.1.tgz", - "integrity": "sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==" - }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -1881,49 +1857,13 @@ "agentkeepalive": "^3.4.1", "chalk": "^1.0.0", "lodash": "^4.17.10" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } } }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "encodeurl": { "version": "1.0.2", @@ -1940,22 +1880,22 @@ } }, "engine.io": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz", - "integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.5.0.tgz", + "integrity": "sha512-21HlvPUKaitDGE4GXNtQ7PLP0Sz4aWLddMPw2VTyFz1FVZqu/kZsJUO8WNpKuE/OCL7nkfRaOui2ZCJloGznGA==", "requires": { "accepts": "~1.3.4", "base64id": "2.0.0", - "cookie": "0.3.1", + "cookie": "~0.4.1", "debug": "~4.1.0", "engine.io-parser": "~2.2.0", - "ws": "^7.1.2" + "ws": "~7.4.2" }, "dependencies": { "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" }, "debug": { "version": "4.1.1", @@ -1973,9 +1913,9 @@ } }, "engine.io-client": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.4.tgz", - "integrity": "sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.0.tgz", + "integrity": "sha512-12wPRfMrugVw/DNyJk34GQ5vIVArEcVMXWugQGGuw2XxUSztFNmJggZmv8IZlLyEdnpO1QB9LkcjeWewO2vxtA==", "requires": { "component-emitter": "~1.3.0", "component-inherit": "0.0.3", @@ -1985,16 +1925,11 @@ "indexof": "0.0.1", "parseqs": "0.0.6", "parseuri": "0.0.6", - "ws": "~6.1.0", + "ws": "~7.4.2", "xmlhttprequest-ssl": "~1.5.4", "yeast": "0.1.2" }, "dependencies": { - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -2002,24 +1937,6 @@ "requires": { "ms": "2.0.0" } - }, - "parseqs": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", - "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" - }, - "parseuri": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", - "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" - }, - "ws": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", - "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", - "requires": { - "async-limiter": "~1.0.0" - } } } }, @@ -2042,14 +1959,6 @@ "dev": true, "requires": { "ansi-colors": "^4.1.1" - }, - "dependencies": { - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - } } }, "entities": { @@ -2063,28 +1972,32 @@ "integrity": "sha1-eYCZstvTfKK8dJ5TinwTB9C1BJk=" }, "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "version": "1.18.0-next.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.2.tgz", + "integrity": "sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw==", + "dev": true, "requires": { + "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2", "has": "^1.0.3", "has-symbols": "^1.0.1", "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", + "is-negative-zero": "^2.0.1", "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", + "object-inspect": "^1.9.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.3", + "string.prototype.trimstart": "^1.0.3" }, "dependencies": { "object.assign": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, "requires": { "call-bind": "^1.0.0", "define-properties": "^1.1.3", @@ -2098,6 +2011,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -2121,13 +2035,13 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.17.0.tgz", - "integrity": "sha512-zJk08MiBgwuGoxes5sSQhOtibZ75pz0J35XTRlZOk9xMffhpA9BTbQZxoXZzOl5zMbleShbGwtw+1kGferfFwQ==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", + "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.2.2", + "@eslint/eslintrc": "^0.3.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -2151,7 +2065,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.19", + "lodash": "^4.17.20", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -2165,6 +2079,12 @@ "v8-compile-cache": "^2.0.3" }, "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -2229,6 +2149,15 @@ "lru-cache": "^6.0.0" } }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -2247,9 +2176,9 @@ } }, "eslint-config-etherpad": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/eslint-config-etherpad/-/eslint-config-etherpad-1.0.21.tgz", - "integrity": "sha512-kJjTElXtgWqAtJNyyhl8k5g/phSg3PUpNSb1PHZBC72q7ibP3uB22hbIxZrg9PkB3dwl2w8GCBp8uLI+3OX7SA==", + "version": "1.0.23", + "resolved": "https://registry.npmjs.org/eslint-config-etherpad/-/eslint-config-etherpad-1.0.23.tgz", + "integrity": "sha512-/yMp7aK5zg309DgNIDotl+CtxrDq5ovwVz5ScmjOZgRgaG1rHhtUX5BFewOaoRlYGg+F2xYkqZ5Zhh9fgjs/jg==", "dev": true }, "eslint-plugin-es": { @@ -2555,6 +2484,11 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" } } }, @@ -2569,9 +2503,9 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-json-stable-stringify": { "version": "2.1.0", @@ -2597,6 +2531,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -2635,6 +2570,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, "requires": { "locate-path": "^3.0.0" } @@ -2643,6 +2579,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", + "dev": true, "requires": { "is-buffer": "~2.0.3" } @@ -2655,12 +2592,23 @@ "requires": { "flatted": "^3.1.0", "rimraf": "^3.0.2" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "flatted": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz", - "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", "dev": true }, "foreground-child": { @@ -2733,6 +2681,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, "optional": true }, "fstream": { @@ -2745,23 +2694,13 @@ "inherits": "~2.0.0", "mkdirp": ">=0.5 0", "rimraf": "2" - }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "optional": true, - "requires": { - "glob": "^7.1.3" - } - } } }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "functional-red-black-tree": { "version": "1.0.1", @@ -2783,43 +2722,6 @@ "string-width": "^1.0.1", "strip-ansi": "^3.0.1", "wide-align": "^1.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } } }, "gensync": { @@ -2831,17 +2733,14 @@ "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true }, "get-intrinsic": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz", "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==", + "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -2863,9 +2762,9 @@ } }, "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2879,6 +2778,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -2900,7 +2800,8 @@ "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true }, "har-schema": { "version": "2.0.0", @@ -2908,11 +2809,11 @@ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "requires": { - "ajv": "^6.5.5", + "ajv": "^6.12.3", "har-schema": "^2.0.0" } }, @@ -2920,6 +2821,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -2930,13 +2832,6 @@ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "requires": { "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - } } }, "has-binary2": { @@ -2962,12 +2857,14 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true }, "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true }, "has-unicode": { "version": "2.0.1", @@ -3051,7 +2948,8 @@ "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true }, "html-escaper": { "version": "2.0.2", @@ -3151,6 +3049,14 @@ "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } } }, "imurmurhash": { @@ -3218,19 +3124,21 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "requires": { "binary-extensions": "^2.0.0" } }, "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" }, "is-callable": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true }, "is-core-module": { "version": "2.2.0", @@ -3244,7 +3152,8 @@ "is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true }, "is-decimal": { "version": "1.0.4", @@ -3254,17 +3163,22 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true }, "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, "requires": { "is-extglob": "^2.1.1" } @@ -3277,12 +3191,14 @@ "is-negative-zero": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==" + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true }, "is-observable": { "version": "1.1.0", @@ -3306,6 +3222,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, "requires": { "has-symbols": "^1.0.1" } @@ -3320,6 +3237,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, "requires": { "has-symbols": "^1.0.1" } @@ -3398,6 +3316,17 @@ "p-map": "^3.0.0", "rimraf": "^3.0.0", "uuid": "^3.3.3" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "istanbul-lib-report": { @@ -3478,9 +3407,9 @@ "dev": true }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -3655,6 +3584,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, "requires": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" @@ -3768,8 +3698,40 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "dev": true, "requires": { "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "log4js": { @@ -3879,16 +3841,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", + "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==" }, "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "version": "2.1.28", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", + "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", "requires": { - "mime-db": "1.44.0" + "mime-db": "1.45.0" } }, "minimatch": { @@ -3935,6 +3897,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz", "integrity": "sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==", + "dev": true, "requires": { "ansi-colors": "3.2.3", "browser-stdout": "1.3.1", @@ -3962,23 +3925,56 @@ "yargs-unparser": "1.6.0" }, "dependencies": { + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true + }, "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, "requires": { "ms": "^2.1.1" } }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true }, "supports-color": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -4011,58 +4007,6 @@ "require_optional": "^1.0.1", "safe-buffer": "^5.1.2", "saslprep": "^1.0.0" - }, - "dependencies": { - "bl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", - "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", - "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - } } }, "ms": { @@ -4071,9 +4015,9 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "mssql": { - "version": "7.0.0-beta.1", - "resolved": "https://registry.npmjs.org/mssql/-/mssql-7.0.0-beta.1.tgz", - "integrity": "sha512-iyFnNVWhjTnIy5nKCq+v/de8mr41TbNKRrulDXASuDqbjOlfLe6YefnXp+ZRPHJUU1rrjmtAnrUoN2y7dc2LUg==", + "version": "7.0.0-beta.2", + "resolved": "https://registry.npmjs.org/mssql/-/mssql-7.0.0-beta.2.tgz", + "integrity": "sha512-7fOp+QzFf24ir/gGeSvyyGlQKfxZj6tx88vsk4UiQw/t/zpJ9PLjOBOoi6Ff+Tw/CZ1aJTa83MPm+CRYJ/UCQA==", "requires": { "debug": "^4", "tarn": "^3.0.1", @@ -4245,6 +4189,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", + "dev": true, "requires": { "object.getownpropertydescriptors": "^2.0.3", "semver": "^5.7.0" @@ -4253,7 +4198,8 @@ "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true } } }, @@ -4282,15 +4228,6 @@ "which": "1" }, "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, "semver": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", @@ -4327,15 +4264,6 @@ "osenv": "^0.1.4" } }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, "tar": { "version": "4.4.13", "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", @@ -4383,7 +4311,8 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true }, "npm": { "version": "6.14.8", @@ -7517,8 +7446,7 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "optional": true + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "nyc": { "version": "15.0.1", @@ -7554,6 +7482,12 @@ "yargs": "^15.0.2" }, "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -7599,19 +7533,11 @@ "path-exists": "^4.0.0" } }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true }, "locate-path": { "version": "5.0.0", @@ -7643,6 +7569,35 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -7696,25 +7651,23 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "optional": true }, - "object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" - }, "object-inspect": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==" + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", + "dev": true }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true }, "object.assign": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, "requires": { "define-properties": "^1.1.2", "function-bind": "^1.1.1", @@ -7726,6 +7679,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.1.tgz", "integrity": "sha512-6DtXgZ/lIZ9hqx4GtZETobXLR/ZLaa0aqV0kzbn80Rf8Z2e/XFnhA0I7p07N2wH8bBBltr2xQPi6sbKWAY2Eng==", + "dev": true, "requires": { "call-bind": "^1.0.0", "define-properties": "^1.1.3", @@ -7834,6 +7788,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "requires": { "p-try": "^2.0.0" } @@ -7842,6 +7797,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, "requires": { "p-limit": "^2.0.0" } @@ -7858,7 +7814,8 @@ "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true }, "package-hash": { "version": "4.0.0", @@ -7892,20 +7849,14 @@ "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" }, "parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "requires": { - "better-assert": "~1.0.0" - } + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" }, "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "requires": { - "better-assert": "~1.0.0" - } + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" }, "parseurl": { "version": "1.3.3", @@ -7915,7 +7866,8 @@ "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true }, "path-is-absolute": { "version": "1.0.1", @@ -7939,11 +7891,6 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -8006,7 +7953,8 @@ "picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true }, "pkg-dir": { "version": "4.2.0", @@ -8154,15 +8102,6 @@ "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", "dev": true }, - "randexp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", - "integrity": "sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==", - "requires": { - "drange": "^1.0.2", - "ret": "^0.2.0" - } - }, "random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", @@ -8174,9 +8113,9 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "rate-limiter-flexible": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-2.1.4.tgz", - "integrity": "sha512-wtbWcqZbCqyAO1k63moagJlCZuPCEqbJJ6il1y2JVoiUyxlE36+cM7ETta9K6tTom9O5pNK+CxwHMgyyyJ31Gg==" + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-2.2.1.tgz", + "integrity": "sha512-rxCP6kDDdn0cZmVqVlF06yLU+mG3TuwaHV/fUIw3OQyYhza7pzVBtdMhUmfXbBzMS+O464XP+x33pfTDGRGYVA==" }, "raw-body": { "version": "2.4.0", @@ -8234,6 +8173,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", + "dev": true, "requires": { "picomatch": "^2.0.4" } @@ -8366,7 +8306,8 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true }, "require-from-string": { "version": "2.0.2", @@ -8377,7 +8318,8 @@ "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true }, "require_optional": { "version": "1.0.1", @@ -8386,13 +8328,6 @@ "requires": { "resolve-from": "^2.0.0", "semver": "^5.1.0" - }, - "dependencies": { - "resolve-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", - "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" - } } }, "resolve": { @@ -8401,15 +8336,9 @@ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" }, "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" }, "rethinkdb": { "version": "2.4.2", @@ -8420,17 +8349,18 @@ } }, "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "optional": true, "requires": { "glob": "^7.1.3" } }, "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "safer-buffer": { "version": "2.1.2", @@ -8517,9 +8447,9 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "set-cookie-parser": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.4.6.tgz", - "integrity": "sha512-mNCnTUF0OYPwYzSHbdRdCfNNHqrne+HS5tS5xNb6yJbdP9wInV0q5xPLE0EyfV/Q3tImo3y/OXpD8Jn0Jtnjrg==", + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.4.7.tgz", + "integrity": "sha512-VaSdYN1DlYuKOzBKqhYJnwaPeirZdNNUNmYdnp9/6Umr9s8amidctYitrX2Gk8wCqiBuiG5mpOYCiVhG5o4iMQ==", "dev": true }, "setprototypeof": { @@ -8573,15 +8503,14 @@ } }, "sinon": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.0.tgz", - "integrity": "sha512-eSNXz1XMcGEMHw08NJXSyTHIu6qTCOiN8x9ODACmZpNQpr0aXTBXBnI4xTzQzR+TEpOmLiKowGf9flCuKIzsbw==", + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", + "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==", "dev": true, "requires": { "@sinonjs/commons": "^1.8.1", "@sinonjs/fake-timers": "^6.0.1", - "@sinonjs/formatio": "^5.0.1", - "@sinonjs/samsam": "^5.2.0", + "@sinonjs/samsam": "^5.3.1", "diff": "^4.0.2", "nise": "^4.0.4", "supports-color": "^7.1.0" @@ -8644,6 +8573,12 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true } } }, @@ -8653,15 +8588,15 @@ "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=" }, "socket.io": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz", - "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.1.tgz", + "integrity": "sha512-Si18v0mMXGAqLqCVpTxBa8MGqriHGQh8ccEOhmsmNS3thNCGBwO8WGrwMibANsWtQQ5NStdZwHqZR3naJVFc3w==", "requires": { "debug": "~4.1.0", - "engine.io": "~3.4.0", + "engine.io": "~3.5.0", "has-binary2": "~1.0.2", "socket.io-adapter": "~1.1.0", - "socket.io-client": "2.3.0", + "socket.io-client": "2.4.0", "socket.io-parser": "~3.4.0" }, "dependencies": { @@ -8686,37 +8621,29 @@ "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==" }, "socket.io-client": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz", - "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz", + "integrity": "sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==", "requires": { "backo2": "1.0.2", - "base64-arraybuffer": "0.1.5", "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "~4.1.0", - "engine.io-client": "~3.4.0", + "component-emitter": "~1.3.0", + "debug": "~3.1.0", + "engine.io-client": "~3.5.0", "has-binary2": "~1.0.2", - "has-cors": "1.1.0", "indexof": "0.0.1", - "object-component": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", + "parseqs": "0.0.6", + "parseuri": "0.0.6", "socket.io-parser": "~3.3.0", "to-array": "0.1.4" }, "dependencies": { - "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" - }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "requires": { - "ms": "^2.1.1" + "ms": "2.0.0" } }, "isarray": { @@ -8724,39 +8651,14 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, "socket.io-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz", - "integrity": "sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.2.tgz", + "integrity": "sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==", "requires": { "component-emitter": "~1.3.0", "debug": "~3.1.0", "isarray": "2.0.1" - }, - "dependencies": { - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } } } } @@ -8771,6 +8673,11 @@ "isarray": "2.0.1" }, "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -8833,6 +8740,15 @@ "which": "^2.0.1" }, "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8895,19 +8811,20 @@ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string.prototype.trimend": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "dev": true, "requires": { "call-bind": "^1.0.0", "define-properties": "^1.1.3" @@ -8917,6 +8834,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", + "dev": true, "requires": { "call-bind": "^1.0.0", "define-properties": "^1.1.3" @@ -8943,11 +8861,11 @@ } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^2.0.0" } }, "strip-bom": { @@ -9043,12 +8961,9 @@ } }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, "swagger-parser": { "version": "9.0.1", @@ -9092,11 +9007,43 @@ "uri-js": "^4.2.2" } }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } } } }, @@ -9122,6 +9069,19 @@ "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" + }, + "dependencies": { + "bl": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", + "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + } } }, "tarn": { @@ -9176,9 +9136,9 @@ } }, "terser": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.7.0.tgz", - "integrity": "sha512-Lfb0RiZcjRDXCC3OSHJpEkxJ9Qeqs6mp2v4jf2MHfy8vGERmVDuvjXdd/EnP5Deme5F2yBRBymKmKHCBg2echw==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", "requires": { "commander": "^2.20.0", "source-map": "~0.6.1", @@ -9194,22 +9154,6 @@ "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } } }, "text-table": { @@ -9219,9 +9163,9 @@ "dev": true }, "threads": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/threads/-/threads-1.4.1.tgz", - "integrity": "sha512-LSgGCu2lwdrfqjYWmeqO+7fgxAbUtjlsa7UA5J6r4x8fCoMd015h19rMwXqz4/q8l3svdloE36Of41rpZWiYFg==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/threads/-/threads-1.6.3.tgz", + "integrity": "sha512-tKwFIWRgfAT85KGkrpDt2jWPO8IVH0sLNfB/pXad/VW9eUIY2Zlz+QyeizypXhPHv9IHfqRzvk2t3mPw+imhWw==", "requires": { "callsites": "^3.1.0", "debug": "^4.1.1", @@ -9273,6 +9217,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "requires": { "is-number": "^7.0.0" } @@ -9331,7 +9276,8 @@ "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true }, "type-fest": { "version": "0.8.1", @@ -9358,30 +9304,24 @@ } }, "ueberdb2": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/ueberdb2/-/ueberdb2-1.2.3.tgz", - "integrity": "sha512-D7lbzudPcOzCSWOHFRvPb6JGFa/zyNB2VcqrTXydwJ6+QlbTF4q6UzriEIWi0xIvp9KkCNJdwzKsDb6EBvXhwQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/ueberdb2/-/ueberdb2-1.2.5.tgz", + "integrity": "sha512-Bts6kmVvhVDWiZjD1JAT1qYknHHK6t9L7kGIFIedGAZRNQ3lRw2XJdf9hKbFpN2HM0J3S/aJoLrZO5BLk3UiaA==", "requires": { "async": "^3.2.0", "cassandra-driver": "^4.5.1", - "chai": "^4.2.0", "channels": "0.0.4", - "cli-table": "^0.3.1", "dirty": "^1.1.0", "elasticsearch": "^16.7.1", - "mocha": "^7.1.2", "mongodb": "^3.6.3", - "mssql": "^7.0.0-beta.1", + "mssql": "^7.0.0-beta.2", "mysql": "2.18.1", "nano": "^8.2.2", "pg": "^8.0.3", - "randexp": "^0.5.3", "redis": "^3.0.2", "rethinkdb": "^2.4.2", - "rimraf": "^3.0.2", "simple-git": "^2.4.0", - "sqlite3": "^5.0.0", - "wtfnode": "^0.8.4" + "sqlite3": "^5.0.1" } }, "uid-safe": { @@ -9434,9 +9374,9 @@ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "requires": { "punycode": "^2.1.0" } @@ -9600,7 +9540,8 @@ "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true }, "wide-align": { "version": "1.1.3", @@ -9608,35 +9549,6 @@ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "requires": { "string-width": "^1.0.2 || 2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } } }, "word-wrap": { @@ -9649,6 +9561,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, "requires": { "ansi-styles": "^3.2.0", "string-width": "^3.0.0", @@ -9658,22 +9571,35 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, "requires": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -9684,6 +9610,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, "requires": { "ansi-regex": "^4.1.0" } @@ -9712,11 +9639,6 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==" }, - "wtfnode": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/wtfnode/-/wtfnode-0.8.4.tgz", - "integrity": "sha512-64GEKtMt/MUBuAm+8kHqP74ojjafzu00aT0JKsmkIwYmjRQ/odO0yhbzKLm+Z9v1gMla+8dwITRKzTAlHsB+Og==" - }, "xml2js": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", @@ -9752,9 +9674,10 @@ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true }, "yallist": { "version": "3.1.1", @@ -9766,6 +9689,7 @@ "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, "requires": { "cliui": "^5.0.0", "find-up": "^3.0.0", @@ -9782,22 +9706,26 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, "requires": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -9808,6 +9736,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, "requires": { "ansi-regex": "^4.1.0" } @@ -9818,6 +9747,7 @@ "version": "13.1.2", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -9827,6 +9757,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "dev": true, "requires": { "flat": "^4.1.0", "lodash": "^4.17.15", diff --git a/src/package.json b/src/package.json index 268caa59c..f8949d801 100644 --- a/src/package.json +++ b/src/package.json @@ -70,7 +70,7 @@ "threads": "^1.4.0", "tiny-worker": "^2.3.0", "tinycon": "0.0.1", - "ueberdb2": "^1.2.3", + "ueberdb2": "^1.2.5", "underscore": "1.8.3", "unorm": "1.4.1" }, From 41a501778d1a4e29b63c5af4fd59cbff5429ed6e Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 23 Jan 2021 17:11:52 -0500 Subject: [PATCH 071/357] Update top-level `package-lock.json` Whenever `src/package-lock.json` is updated, the top-level `package-lock.json` must also be updated. --- package-lock.json | 3468 ++++++++++++++++++++++----------------------- 1 file changed, 1679 insertions(+), 1789 deletions(-) diff --git a/package-lock.json b/package-lock.json index cf5941f4c..d8f783a04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2,6 +2,38 @@ "requires": true, "lockfileVersion": 1, "dependencies": { + "@azure/ms-rest-azure-env": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-azure-env/-/ms-rest-azure-env-2.0.0.tgz", + "integrity": "sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw==" + }, + "@azure/ms-rest-js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.1.0.tgz", + "integrity": "sha512-4BXLVImYRt+jcUmEJ5LUWglI8RBNVQndY6IcyvQ4U8O4kIXdmlRz3cJdA/RpXf5rKT38KOoTO2T6Z1f6Z1HDBg==", + "requires": { + "@types/node-fetch": "^2.3.7", + "@types/tunnel": "0.0.1", + "abort-controller": "^3.0.0", + "form-data": "^2.5.0", + "node-fetch": "^2.6.0", + "tough-cookie": "^3.0.1", + "tslib": "^1.10.0", + "tunnel": "0.0.6", + "uuid": "^3.3.2", + "xml2js": "^0.4.19" + } + }, + "@azure/ms-rest-nodeauth": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-nodeauth/-/ms-rest-nodeauth-3.0.6.tgz", + "integrity": "sha512-2twuzsXHdKMzEFI2+Sr82o6yS4ppNGZceYwil8PFo+rJxOZIoBm9e0//YC+dKilV/3F+6K/HuW8LdskDrJEQWA==", + "requires": { + "@azure/ms-rest-azure-env": "^2.0.0", + "@azure/ms-rest-js": "^2.0.4", + "adal-node": "^0.1.28" + } + }, "@babel/code-frame": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", @@ -59,6 +91,98 @@ "strip-json-comments": "^3.1.1" } }, + "@js-joda/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-3.2.0.tgz", + "integrity": "sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg==" + }, + "@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "requires": { + "debug": "^4.1.1" + } + }, + "@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" + }, + "@types/caseless": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", + "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==" + }, + "@types/long": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", + "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" + }, + "@types/node": { + "version": "14.14.22", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.22.tgz", + "integrity": "sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw==" + }, + "@types/node-fetch": { + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.8.tgz", + "integrity": "sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw==", + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/request": { + "version": "2.48.5", + "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz", + "integrity": "sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ==", + "requires": { + "@types/caseless": "*", + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + } + }, + "@types/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==" + }, + "@types/tunnel": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.1.tgz", + "integrity": "sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A==", + "requires": { + "@types/node": "*" + } + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "optional": true + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, "acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -71,11 +195,46 @@ "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", "dev": true }, + "adal-node": { + "version": "0.1.28", + "resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.1.28.tgz", + "integrity": "sha1-RoxLs+u9lrEnBmn0ucuk4AZepIU=", + "requires": { + "@types/node": "^8.0.47", + "async": ">=0.6.0", + "date-utils": "*", + "jws": "3.x.x", + "request": ">= 2.52.0", + "underscore": ">= 1.3.1", + "uuid": "^3.1.0", + "xmldom": ">= 0.1.x", + "xpath.js": "~1.1.0" + }, + "dependencies": { + "@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==" + } + } + }, + "adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==" + }, + "agentkeepalive": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", + "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", + "requires": { + "humanize-ms": "^1.2.1" + } + }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -104,6 +263,22 @@ "color-convert": "^1.9.0" } }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -113,34 +288,137 @@ "sprintf-js": "~1.0.2" } }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, + "async": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bignumber.js": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" + }, + "bl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "optional": true, + "requires": { + "inherits": "~2.0.0" + } + }, + "bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=" }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, + "browser-request": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/browser-request/-/browser-request-0.3.3.tgz", + "integrity": "sha1-ns5bWsqJopkyJC4Yv5M975h2zBc=" + }, + "bson": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", + "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==" + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "cassandra-driver": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.6.1.tgz", + "integrity": "sha512-Vk0kUHlMV4vFXRPwRpKnCZEEMZkp9/RucBDB7gpaUmn9sCusKzzUzVkXeusTxKSoGuIgLJJ7YBiFJdXOctUS7A==", + "requires": { + "@types/long": "^4.0.0", + "@types/node": ">=8", + "adm-zip": "^0.4.13", + "long": "^2.2.0" + } + }, "chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", @@ -192,11 +470,37 @@ } } }, + "channels": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/channels/-/channels-0.0.4.tgz", + "integrity": "sha1-G+4yPt6hUrue8E9BvG5rD1lIqUE=" + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "optional": true + }, + "cloudant-follow": { + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/cloudant-follow/-/cloudant-follow-0.18.2.tgz", + "integrity": "sha512-qu/AmKxDqJds+UmT77+0NbM7Yab2K3w0qSeJRzsq5dRWJTEJdWeb+XpG4OpKuTE9RKOa/Awn2gR3TTnvNr3TeA==", + "requires": { + "browser-request": "~0.3.0", + "debug": "^4.0.1", + "request": "^2.88.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "optional": true + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -204,14 +508,31 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cross-spawn": { "version": "7.0.3", @@ -224,21 +545,71 @@ "which": "^2.0.1" } }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "date-utils": { + "version": "1.2.21", + "resolved": "https://registry.npmjs.org/date-utils/-/date-utils-1.2.21.tgz", + "integrity": "sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q=" + }, "debug": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, "requires": { "ms": "2.1.2" } }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "optional": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "optional": true + }, + "denque": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", + "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "optional": true + }, + "dirty": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/dirty/-/dirty-1.1.0.tgz", + "integrity": "sha1-cO3SuZlUHcmXT9Ooy9DGcP4jYHg=" + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -248,6 +619,70 @@ "esutils": "^2.0.2" } }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "elasticsearch": { + "version": "16.7.2", + "resolved": "https://registry.npmjs.org/elasticsearch/-/elasticsearch-16.7.2.tgz", + "integrity": "sha512-1ZLKZlG2ABfYVBX2d7/JgxOsKJrM5Yu62GvshWu7ZSvhxPomCN4Gas90DS51yYI56JolY0XGhyiRlUhLhIL05Q==", + "requires": { + "agentkeepalive": "^3.4.1", + "chalk": "^1.0.0", + "lodash": "^4.17.10" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", @@ -306,7 +741,7 @@ "threads": "^1.4.0", "tiny-worker": "^2.3.0", "tinycon": "0.0.1", - "ueberdb2": "^1.2.3", + "ueberdb2": "^1.2.5", "underscore": "1.8.3", "unorm": "1.4.1" }, @@ -345,36 +780,6 @@ "z-schema": "^4.2.2" } }, - "@azure/ms-rest-azure-env": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@azure/ms-rest-azure-env/-/ms-rest-azure-env-1.1.2.tgz", - "integrity": "sha512-l7z0DPCi2Hp88w12JhDTtx5d0Y3+vhfE7JKJb9O7sEz71Cwp053N8piTtTnnk/tUor9oZHgEKi/p3tQQmLPjvA==" - }, - "@azure/ms-rest-js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-1.9.0.tgz", - "integrity": "sha512-cB4Z2Mg7eBmet1rfbf0QSO1XbhfknRW7B+mX3IHJq0KGHaGJvCPoVTgdsJdCkazEMK1jtANFNEDDzSQacxyzbA==", - "requires": { - "@types/tunnel": "0.0.0", - "axios": "^0.19.0", - "form-data": "^2.3.2", - "tough-cookie": "^2.4.3", - "tslib": "^1.9.2", - "tunnel": "0.0.6", - "uuid": "^3.2.1", - "xml2js": "^0.4.19" - } - }, - "@azure/ms-rest-nodeauth": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@azure/ms-rest-nodeauth/-/ms-rest-nodeauth-2.0.2.tgz", - "integrity": "sha512-KmNNICOxt3EwViAJI3iu2VH8t8BQg5J2rSAyO4IUYLF9ZwlyYsP419pdvl4NBUhluAP2cgN7dfD2V6E6NOMZlQ==", - "requires": { - "@azure/ms-rest-azure-env": "^1.1.2", - "@azure/ms-rest-js": "^1.8.7", - "adal-node": "^0.1.28" - } - }, "@babel/code-frame": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", @@ -706,7 +1111,6 @@ "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==", "requires": { - "camelcase": "^5.3.1", "find-up": "^4.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" @@ -732,10 +1136,7 @@ "p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "requires": { - "p-limit": "^2.2.0" - } + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==" }, "path-exists": { "version": "4.0.0", @@ -754,41 +1155,10 @@ "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.2.tgz", "integrity": "sha512-qS/a24RA5FEoiJS9wiv6Pwg2c/kiUo3IVUQcfeM9JvsR6pM8Yx+yl/6xWYLckZCT5jpLNhslgjiA8p/XcGyMRQ==" }, - "@kwsites/file-exists": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", - "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", - "requires": { - "debug": "^4.1.1" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "@kwsites/promise-deferred": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", - "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" - }, "@sinonjs/commons": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", - "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", - "requires": { - "type-detect": "4.0.8" - } + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==" }, "@sinonjs/fake-timers": { "version": "6.0.1", @@ -813,8 +1183,7 @@ "integrity": "sha512-CaIcyX5cDsjcW/ab7HposFWzV1kC++4HNsfnEdFJa7cP1QIuILAKV+BgfeqRXhcnSAc76r/Rh/O5C+300BwUIw==", "requires": { "@sinonjs/commons": "^1.6.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" + "lodash.get": "^4.4.2" } }, "@sinonjs/text-encoding": { @@ -822,71 +1191,11 @@ "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==" }, - "@types/caseless": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", - "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==" - }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" }, - "@types/long": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" - }, - "@types/node": { - "version": "14.14.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.13.tgz", - "integrity": "sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ==" - }, - "@types/readable-stream": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.9.tgz", - "integrity": "sha512-sqsgQqFT7HmQz/V5jH1O0fvQQnXAJO46Gg9LRO/JPfjmVmGUlcx831TZZO3Y3HtWhIkzf3kTsNT0Z0kzIhIvZw==", - "requires": { - "@types/node": "*", - "safe-buffer": "*" - } - }, - "@types/request": { - "version": "2.48.5", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz", - "integrity": "sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ==", - "requires": { - "@types/caseless": "*", - "@types/node": "*", - "@types/tough-cookie": "*", - "form-data": "^2.5.0" - }, - "dependencies": { - "form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - } - } - }, - "@types/tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==" - }, - "@types/tunnel": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.0.tgz", - "integrity": "sha512-FGDp0iBRiBdPjOgjJmn1NH0KDLN+Z8fRmo+9J7XGBhubq1DPrGrbmG4UTlGzrpbCpesMqD0sWkzi27EYkOMHyg==", - "requires": { - "@types/node": "*" - } - }, "@types/unist": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", @@ -911,47 +1220,11 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==" }, - "adal-node": { - "version": "0.1.28", - "resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.1.28.tgz", - "integrity": "sha1-RoxLs+u9lrEnBmn0ucuk4AZepIU=", - "requires": { - "@types/node": "^8.0.47", - "async": ">=0.6.0", - "date-utils": "*", - "jws": "3.x.x", - "request": ">= 2.52.0", - "underscore": ">= 1.3.1", - "uuid": "^3.1.0", - "xmldom": ">= 0.1.x", - "xpath.js": "~1.1.0" - }, - "dependencies": { - "@types/node": { - "version": "8.10.66", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", - "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==" - } - } - }, - "adm-zip": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", - "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==" - }, "after": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" }, - "agentkeepalive": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", - "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", - "requires": { - "humanize-ms": "^1.2.1" - } - }, "aggregate-error": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", @@ -972,33 +1245,6 @@ "uri-js": "^4.2.2" } }, - "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==" - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, "append-transform": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", @@ -1057,7 +1303,6 @@ "lodash.flatten": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", "readable-stream": "^2.0.0" }, "dependencies": { @@ -1144,11 +1389,6 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" - }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -1184,14 +1424,6 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" }, - "axios": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", - "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", - "requires": { - "follow-redirects": "1.5.10" - } - }, "backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", @@ -1202,11 +1434,6 @@ "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==" }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, "base64-arraybuffer": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", @@ -1243,16 +1470,6 @@ "callsite": "1.0.0" } }, - "bignumber.js": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", - "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" - }, - "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==" - }, "binary-search": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", @@ -1273,11 +1490,6 @@ "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" }, - "bluebird": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=" - }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -1319,33 +1531,6 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-request": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/browser-request/-/browser-request-0.3.3.tgz", - "integrity": "sha1-ns5bWsqJopkyJC4Yv5M975h2zBc=" - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" - }, "buffer": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", @@ -1360,21 +1545,11 @@ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" - }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, - "buffer-writer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", - "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" - }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -1391,15 +1566,6 @@ "write-file-atomic": "^3.0.0" } }, - "call-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", - "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.0" - } - }, "call-me-maybe": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", @@ -1415,55 +1581,16 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, - "cassandra-driver": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.6.1.tgz", - "integrity": "sha512-Vk0kUHlMV4vFXRPwRpKnCZEEMZkp9/RucBDB7gpaUmn9sCusKzzUzVkXeusTxKSoGuIgLJJ7YBiFJdXOctUS7A==", - "requires": { - "@types/long": "^4.0.0", - "@types/node": ">=8", - "adm-zip": "^0.4.13", - "long": "^2.2.0" - } - }, "ccount": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.5.tgz", "integrity": "sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw==" }, - "chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, "channels": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/channels/-/channels-0.0.4.tgz", @@ -1479,11 +1606,6 @@ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==" }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" - }, "cheerio": { "version": "0.22.0", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", @@ -1507,21 +1629,6 @@ "lodash.some": "^4.4.0" } }, - "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" - } - }, "clean-css": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", @@ -1535,98 +1642,6 @@ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" }, - "cli-table": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.4.tgz", - "integrity": "sha512-1vinpnX/ZERcmE443i3SZTmU5DF0rPO9DrL4I2iVAllhxzCM9SzPlHnz19fsZB78htkKZvYBvj6SZ6vXnaxmTA==", - "requires": { - "chalk": "^2.4.1", - "string-width": "^4.2.0" - } - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "cloudant-follow": { - "version": "0.18.2", - "resolved": "https://registry.npmjs.org/cloudant-follow/-/cloudant-follow-0.18.2.tgz", - "integrity": "sha512-qu/AmKxDqJds+UmT77+0NbM7Yab2K3w0qSeJRzsq5dRWJTEJdWeb+XpG4OpKuTE9RKOa/Awn2gR3TTnvNr3TeA==", - "requires": { - "browser-request": "~0.3.0", - "debug": "^4.0.1", - "request": "^2.88.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1672,7 +1687,6 @@ "requires": { "buffer-crc32": "^0.2.13", "crc32-stream": "^3.0.1", - "normalize-path": "^3.0.0", "readable-stream": "^2.3.6" }, "dependencies": { @@ -1710,11 +1724,6 @@ } } }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -1847,11 +1856,6 @@ "assert-plus": "^1.0.0" } }, - "date-utils": { - "version": "1.2.21", - "resolved": "https://registry.npmjs.org/date-utils/-/date-utils-1.2.21.tgz", - "integrity": "sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q=" - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1860,19 +1864,6 @@ "ms": "2.0.0" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "requires": { - "type-detect": "^4.0.0" - } - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -1886,24 +1877,11 @@ "strip-bom": "^4.0.0" } }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "requires": { - "object-keys": "^1.0.12" - } - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, - "denque": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", - "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" - }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -1914,16 +1892,6 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" - }, - "dirty": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/dirty/-/dirty-1.1.0.tgz", - "integrity": "sha1-cO3SuZlUHcmXT9Ooy9DGcP4jYHg=" - }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1963,11 +1931,6 @@ "domelementtype": "1" } }, - "drange": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/drange/-/drange-1.1.1.tgz", - "integrity": "sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==" - }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -1977,14 +1940,6 @@ "safer-buffer": "^2.1.0" } }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1995,58 +1950,6 @@ "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==" }, - "elasticsearch": { - "version": "16.7.2", - "resolved": "https://registry.npmjs.org/elasticsearch/-/elasticsearch-16.7.2.tgz", - "integrity": "sha512-1ZLKZlG2ABfYVBX2d7/JgxOsKJrM5Yu62GvshWu7ZSvhxPomCN4Gas90DS51yYI56JolY0XGhyiRlUhLhIL05Q==", - "requires": { - "agentkeepalive": "^3.4.1", - "chalk": "^1.0.0", - "lodash": "^4.17.10" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -2166,53 +2069,6 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" }, - "errs": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/errs/-/errs-0.3.2.tgz", - "integrity": "sha1-eYCZstvTfKK8dJ5TinwTB9C1BJk=" - }, - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - }, - "dependencies": { - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - } - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, "es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", @@ -2223,11 +2079,6 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, "eslint": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.14.0.tgz", @@ -2662,14 +2513,6 @@ "flat-cache": "^2.0.1" } }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -2699,22 +2542,6 @@ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "flat": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", - "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", - "requires": { - "is-buffer": "~2.0.3" - } - }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", @@ -2740,24 +2567,6 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==" }, - "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "requires": { - "debug": "=3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - } - } - }, "foreground-child": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", @@ -2807,16 +2616,6 @@ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -2827,26 +2626,6 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==" }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" - }, - "get-intrinsic": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", - "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -2855,27 +2634,6 @@ "assert-plus": "^1.0.0" } }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "requires": { - "is-glob": "^4.0.1" - } - }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -2886,11 +2644,6 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" - }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -2905,29 +2658,6 @@ "har-schema": "^2.0.0" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - } - } - }, "has-binary2": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", @@ -2948,16 +2678,6 @@ "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, "hasha": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.0.tgz", @@ -3037,11 +2757,6 @@ "space-separated-tokens": "^1.0.0" } }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" - }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -3094,14 +2809,6 @@ "sshpk": "^1.7.0" } }, - "humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", - "requires": { - "ms": "^2.0.0" - } - }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -3151,15 +2858,6 @@ "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -3184,24 +2882,11 @@ "is-decimal": "^1.0.0" } }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "requires": { - "binary-extensions": "^2.0.0" - } - }, "is-buffer": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" }, - "is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" - }, "is-core-module": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", @@ -3210,49 +2895,16 @@ "has": "^1.0.3" } }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" - }, "is-decimal": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, "is-hexadecimal": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==" }, - "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==" - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, "is-observable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", @@ -3271,27 +2923,11 @@ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz", "integrity": "sha1-MVc3YcBX4zwukaq56W2gjO++duU=" }, - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "requires": { - "has-symbols": "^1.0.1" - } - }, "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "requires": { - "has-symbols": "^1.0.1" - } - }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -3307,11 +2943,6 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -3440,11 +3071,6 @@ "esprima": "^4.0.0" } }, - "jsbi": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.1.4.tgz", - "integrity": "sha512-52QRRFSsi9impURE8ZUbzAMCLjPm4THO7H2fcuIvaaeFTbSysvkodbQQXIVsNgq/ypDbq6dJiuGKL0vZ/i9hUg==" - }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -3514,25 +3140,6 @@ "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.1.tgz", "integrity": "sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA==" }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, "languages4translatewiki": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/languages4translatewiki/-/languages4translatewiki-0.1.3.tgz", @@ -3589,15 +3196,6 @@ "type-check": "~0.4.0" } }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, "lodash": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", @@ -3698,14 +3296,6 @@ "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=" }, - "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "requires": { - "chalk": "^2.4.2" - } - }, "log4js": { "version": "0.6.35", "resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.35.tgz", @@ -3738,11 +3328,6 @@ } } }, - "long": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/long/-/long-2.4.0.tgz", - "integrity": "sha1-n6GAux2VAM3CnEFWdmoZleH0Uk8=" - }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -3800,56 +3385,19 @@ "mime-db": "1.44.0" } }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, "mocha": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz", "integrity": "sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==", "requires": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "chokidar": "3.3.0", "debug": "3.2.6", - "diff": "3.5.0", "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", "js-yaml": "3.13.1", - "log-symbols": "3.0.0", "minimatch": "3.0.4", "mkdirp": "0.5.5", "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" + "wide-align": "1.1.3" }, "dependencies": { "debug": { @@ -3894,108 +3442,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "mssql": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/mssql/-/mssql-6.3.0.tgz", - "integrity": "sha512-6/BK/3J8Oe4t6BYnmdCCORHhyBtBI/Fh0Sh6l1hPzb/hKtxDrsaSDGIpck1u8bzkLzev39TH5W2nz+ffeRz7gg==", - "requires": { - "debug": "^4.3.1", - "tarn": "^1.1.5", - "tedious": "^6.7.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "mysql": { - "version": "2.18.1", - "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", - "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", - "requires": { - "bignumber.js": "9.0.0", - "readable-stream": "2.3.7", - "safe-buffer": "5.1.2", - "sqlstring": "2.3.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "nano": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/nano/-/nano-8.2.3.tgz", - "integrity": "sha512-nubyTQeZ/p+xf3ZFFMd7WrZwpcy9tUDrbaXw9HFBsM6zBY5gXspvOjvG2Zz3emT6nfJtP/h7F2/ESfsVVXnuMw==", - "requires": { - "@types/request": "^2.48.4", - "cloudant-follow": "^0.18.2", - "debug": "^4.1.1", - "errs": "^0.3.2", - "request": "^2.88.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "native-duplexpair": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz", - "integrity": "sha1-eJkHjmS/PIo9cyYBs9QP8F21j6A=" - }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -4028,22 +3474,6 @@ } } }, - "node-environment-flags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", - "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, "node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -4061,11 +3491,6 @@ "promise": "~1.3.0" } }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" - }, "npm": { "version": "6.14.8", "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.8.tgz", @@ -7599,7 +7024,6 @@ "@istanbuljs/schema": "^0.1.2", "caching-transform": "^4.0.0", "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", "find-cache-dir": "^3.2.0", "find-up": "^4.1.0", "foreground-child": "^2.0.0", @@ -7703,10 +7127,7 @@ "p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "requires": { - "p-limit": "^2.2.0" - } + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==" }, "path-exists": { "version": "4.0.0", @@ -7747,26 +7168,16 @@ "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", "requires": { "cliui": "^6.0.0", - "decamelize": "^1.2.0", "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", "yargs-parser": "^18.1.1" } }, "yargs-parser": { "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==" } } }, @@ -7780,37 +7191,6 @@ "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" }, - "object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==" - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.1.tgz", - "integrity": "sha512-6DtXgZ/lIZ9hqx4GtZETobXLR/ZLaa0aqV0kzbn80Rf8Z2e/XFnhA0I7p07N2wH8bBBltr2xQPi6sbKWAY2Eng==", - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" - } - }, "observable-fns": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/observable-fns/-/observable-fns-0.5.1.tgz", @@ -7829,14 +7209,6 @@ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, "openapi-backend": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/openapi-backend/-/openapi-backend-2.4.1.tgz", @@ -7893,22 +7265,6 @@ "word-wrap": "^1.2.3" } }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, "p-map": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", @@ -7917,11 +7273,6 @@ "aggregate-error": "^3.0.0" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, "package-hash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", @@ -7933,11 +7284,6 @@ "release-zalgo": "^1.0.0" } }, - "packet-reader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", - "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" - }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7972,16 +7318,6 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -7997,75 +7333,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, - "pg": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.5.1.tgz", - "integrity": "sha512-9wm3yX9lCfjvA98ybCyw2pADUivyNWT/yIP4ZcDVpMN0og70BUWYEGXPCTAQdGTAqnytfRADb7NERrY1qxhIqw==", - "requires": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.4.0", - "pg-pool": "^3.2.2", - "pg-protocol": "^1.4.0", - "pg-types": "^2.1.0", - "pgpass": "1.x" - } - }, - "pg-connection-string": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.4.0.tgz", - "integrity": "sha512-3iBXuv7XKvxeMrIgym7njT+HlZkwZqqGX4Bu9cci8xHZNT+Um1gWKqCsAzcC0d95rcKMU5WBg6YRUcHyV0HZKQ==" - }, - "pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" - }, - "pg-pool": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.2.tgz", - "integrity": "sha512-ORJoFxAlmmros8igi608iVEbQNNZlp89diFVx6yV5v+ehmpMY9sK6QgpmgoXbmkNaBAx8cOOZh9g80kJv1ooyA==" - }, - "pg-protocol": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.4.0.tgz", - "integrity": "sha512-El+aXWcwG/8wuFICMQjM5ZSAm6OWiJicFdNYo+VY3QP+8vI4SvLIWVe51PppTzMhikUJR+PsyIFKqfdXPz/yxA==" - }, - "pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "requires": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - } - }, - "pgpass": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz", - "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==", - "requires": { - "split2": "^3.1.1" - } - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" - }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -8094,10 +7366,7 @@ "p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "requires": { - "p-limit": "^2.2.0" - } + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==" }, "path-exists": { "version": "4.0.0", @@ -8106,39 +7375,11 @@ } } }, - "postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" - }, - "postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" - }, - "postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" - }, - "postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "requires": { - "xtend": "^4.0.0" - } - }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, "process-on-spawn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", @@ -8202,15 +7443,6 @@ "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==" }, - "randexp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", - "integrity": "sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==", - "requires": { - "drange": "^1.0.2", - "ret": "^0.2.0" - } - }, "random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", @@ -8266,43 +7498,6 @@ "util-deprecate": "^1.0.1" } }, - "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", - "requires": { - "picomatch": "^2.0.4" - } - }, - "redis": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/redis/-/redis-3.0.2.tgz", - "integrity": "sha512-PNhLCrjU6vKVuMOyFu7oSP296mwBkcE6lrAjruBYG5LgdSqtRBoVQIylrMyVZD/lkF24RSNNatzvYag6HRBHjQ==", - "requires": { - "denque": "^1.4.1", - "redis-commands": "^1.5.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0" - } - }, - "redis-commands": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.6.0.tgz", - "integrity": "sha512-2jnZ0IkjZxvguITjFTrGiLyzQZcTvaw8DAaCXxZq/dsHXz7KfMQ3OUJy7Tz9vnRtZRVz6VRCPDvruvU8Ts44wQ==" - }, - "redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" - }, - "redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "requires": { - "redis-errors": "^1.0.0" - } - }, "regexpp": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", @@ -8402,16 +7597,6 @@ } } }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, "resolve": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", @@ -8422,27 +7607,6 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" }, - "ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==" - }, - "rethinkdb": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/rethinkdb/-/rethinkdb-2.4.2.tgz", - "integrity": "sha512-6DzwqEpFc8cqesAdo07a845oBRxLiHvWzopTKBo/uY2ypGWIsJQFJk3wjRDtSEhczxJqLS0jnf37rwgzYAw8NQ==", - "requires": { - "bluebird": ">= 2.3.2 < 3" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, "safe-buffer": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", @@ -8453,11 +7617,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, "security": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/security/-/security-1.0.0.tgz", @@ -8518,11 +7677,6 @@ "send": "0.17.1" } }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, "set-cookie-parser": { "version": "2.4.6", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.4.6.tgz", @@ -8551,31 +7705,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, - "simple-git": { - "version": "2.27.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-2.27.0.tgz", - "integrity": "sha512-/Q4aolzErYrIx6SgyH421jmtv5l1DaAw+KYWMWy229+isW6yld/nHGxJ2xUR/aeX3SuYJnbucyUigERwaw4Xow==", - "requires": { - "@kwsites/file-exists": "^1.1.1", - "@kwsites/promise-deferred": "^1.1.1", - "debug": "^4.3.1" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, "sinon": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.0.tgz", @@ -8811,24 +7940,11 @@ } } }, - "split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "requires": { - "readable-stream": "^3.0.0" - } - }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, - "sqlstring": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", - "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" - }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", @@ -8850,34 +7966,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "string.prototype.trimend": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", - "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", - "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - } - }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -8898,24 +7986,11 @@ "is-hexadecimal": "^1.0.0" } }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - }, "strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, "superagent": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", @@ -9053,14 +8128,6 @@ } } }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, "swagger-parser": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-9.0.1.tgz", @@ -9137,62 +8204,6 @@ "readable-stream": "^3.1.1" } }, - "tarn": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/tarn/-/tarn-1.1.5.tgz", - "integrity": "sha512-PMtJ3HCLAZeedWjJPgGnCvcphbCOMbtZpjKgLq3qM5Qq9aQud+XHrL0WlrlgnTyS8U+jrjGbEXprFcQrxPy52g==" - }, - "tedious": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/tedious/-/tedious-6.7.0.tgz", - "integrity": "sha512-8qr7+sB0h4SZVQBRWUgHmYuOEflAOl2eihvxk0fVNvpvGJV4V5UC/YmSvebyfgyfwWcPO22/AnSbYVZZqf9wuQ==", - "requires": { - "@azure/ms-rest-nodeauth": "2.0.2", - "@types/node": "^12.12.17", - "@types/readable-stream": "^2.3.5", - "bl": "^3.0.0", - "depd": "^2.0.0", - "iconv-lite": "^0.5.0", - "jsbi": "^3.1.1", - "native-duplexpair": "^1.0.0", - "punycode": "^2.1.0", - "readable-stream": "^3.4.0", - "sprintf-js": "^1.1.2" - }, - "dependencies": { - "@types/node": { - "version": "12.19.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.9.tgz", - "integrity": "sha512-yj0DOaQeUrk3nJ0bd3Y5PeDRJ6W0r+kilosLA+dzF3dola/o9hxhMSg2sFvVcA2UHS5JSOsZp4S0c1OEXc4m1Q==" - }, - "bl": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.1.tgz", - "integrity": "sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ==", - "requires": { - "readable-stream": "^3.0.1" - } - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "iconv-lite": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", - "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" - } - } - }, "terser": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/terser/-/terser-4.7.0.tgz", @@ -9283,14 +8294,6 @@ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } - }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -9310,16 +8313,6 @@ "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==" }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" - }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -9341,11 +8334,6 @@ "prelude-ls": "^1.2.1" } }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" - }, "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -9368,84 +8356,6 @@ "is-typedarray": "^1.0.0" } }, - "ueberdb2": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/ueberdb2/-/ueberdb2-0.5.6.tgz", - "integrity": "sha512-stLhNkWlxUMAO33JjEh8JCRuZvHYeDQjbo6K1C3I7R37AlMKNu9GWXSZm1wQDnAqpXAXeMVh3owBsAdj0YvOrg==", - "requires": { - "async": "^3.2.0", - "cassandra-driver": "^4.5.1", - "chai": "^4.2.0", - "channels": "0.0.4", - "cli-table": "^0.3.1", - "dirty": "^1.1.0", - "elasticsearch": "^16.7.1", - "mocha": "^7.1.2", - "mssql": "^6.2.3", - "mysql": "2.18.1", - "nano": "^8.2.2", - "pg": "^8.0.3", - "randexp": "^0.5.3", - "redis": "^3.0.2", - "rethinkdb": "^2.4.2", - "rimraf": "^3.0.2", - "simple-git": "^2.4.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "mocha": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz", - "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==", - "requires": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.5", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, "uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -9644,111 +8554,11 @@ "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz", "integrity": "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==" }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, "write": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", @@ -9773,116 +8583,16 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz", "integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w==" }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" - }, - "xmldom": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.4.0.tgz", - "integrity": "sha512-2E93k08T30Ugs+34HBSTQLVtpi6mCddaY8uO+pMNk1pqSjV5vElzn4mmh6KLxN3hki8rNcHSYzILoh3TEWORvA==" - }, "xmlhttprequest-ssl": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" }, - "xpath.js": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xpath.js/-/xpath.js-1.1.0.tgz", - "integrity": "sha512-jg+qkfS4K8E7965sqaUl8mRngXiKb3WZGfONgE18pr03FUQiuSV6G+Ej4tS55B+rIQSFEIw3phdVAQ4pPqNWfQ==" - }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, - "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==" - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", - "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" - } - }, "yeast": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", @@ -9911,11 +8621,15 @@ } } }, + "errs": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/errs/-/errs-0.3.2.tgz", + "integrity": "sha1-eYCZstvTfKK8dJ5TinwTB9C1BJk=" + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { "version": "7.15.0", @@ -10159,17 +8873,30 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "fast-levenshtein": { "version": "2.0.6", @@ -10202,23 +8929,62 @@ "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==", "dev": true }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "optional": true, + "requires": { + "minipass": "^2.6.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "optional": true + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "optional": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "optional": true, + "requires": { + "glob": "^7.1.3" + } + } + } }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "functional-red-black-tree": { "version": "1.0.1", @@ -10226,11 +8992,71 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -10244,7 +9070,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -10258,20 +9083,85 @@ "type-fest": "^0.8.1" } }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "optional": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + } + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "optional": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } }, "ignore": { "version": "4.0.6", @@ -10279,6 +9169,15 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "ignore-walk": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, "import-fresh": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", @@ -10299,7 +9198,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -10308,8 +9206,18 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "optional": true + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" }, "is-core-module": { "version": "2.1.0", @@ -10323,29 +9231,40 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, "requires": { "is-extglob": "^2.1.1" } }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "js-tokens": { "version": "4.0.0", @@ -10363,11 +9282,25 @@ "esprima": "^4.0.0" } }, + "jsbi": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.1.4.tgz", + "integrity": "sha512-52QRRFSsi9impURE8ZUbzAMCLjPm4THO7H2fcuIvaaeFTbSysvkodbQQXIVsNgq/ypDbq6dJiuGKL0vZ/i9hUg==" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -10375,6 +9308,41 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "kebab-case": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/kebab-case/-/kebab-case-1.0.0.tgz", @@ -10394,8 +9362,12 @@ "lodash": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "long": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/long/-/long-2.4.0.tgz", + "integrity": "sha1-n6GAux2VAM3CnEFWdmoZleH0Uk8=" }, "lru-cache": { "version": "6.0.0", @@ -10406,20 +9378,135 @@ "yallist": "^4.0.0" } }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, + "mime-db": { + "version": "1.45.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", + "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==" + }, + "mime-types": { + "version": "2.1.28", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", + "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", + "requires": { + "mime-db": "1.45.0" + } + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "optional": true + } + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "optional": true, + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "mongodb": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.3.tgz", + "integrity": "sha512-rOZuR0QkodZiM+UbQE5kDsJykBqWi0CL4Ec2i1nrGrUI3KO11r6Fbxskqmq3JK2NH7aW4dcccBuUujAP0ERl5w==", + "requires": { + "bl": "^2.2.1", + "bson": "^1.1.4", + "denque": "^1.4.1", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "mssql": { + "version": "7.0.0-beta.2", + "resolved": "https://registry.npmjs.org/mssql/-/mssql-7.0.0-beta.2.tgz", + "integrity": "sha512-7fOp+QzFf24ir/gGeSvyyGlQKfxZj6tx88vsk4UiQw/t/zpJ9PLjOBOoi6Ff+Tw/CZ1aJTa83MPm+CRYJ/UCQA==", + "requires": { + "debug": "^4", + "tarn": "^3.0.1", + "tedious": "^9.2.3" + } + }, + "mysql": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", + "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", + "requires": { + "bignumber.js": "9.0.0", + "readable-stream": "2.3.7", + "safe-buffer": "5.1.2", + "sqlstring": "2.3.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "nano": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/nano/-/nano-8.2.3.tgz", + "integrity": "sha512-nubyTQeZ/p+xf3ZFFMd7WrZwpcy9tUDrbaXw9HFBsM6zBY5gXspvOjvG2Zz3emT6nfJtP/h7F2/ESfsVVXnuMw==", + "requires": { + "@types/request": "^2.48.4", + "cloudant-follow": "^0.18.2", + "debug": "^4.1.1", + "errs": "^0.3.2", + "request": "^2.88.0" + } + }, + "native-duplexpair": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz", + "integrity": "sha1-eJkHjmS/PIo9cyYBs9QP8F21j6A=" }, "natural-compare": { "version": "1.4.0", @@ -10427,11 +9514,228 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "needle": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.6.0.tgz", + "integrity": "sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg==", + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "node-addon-api": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz", + "integrity": "sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==", + "optional": true + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "optional": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "optional": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "optional": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "node-pre-gyp": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", + "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + }, + "dependencies": { + "nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "optional": true + }, + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "optional": true + } + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "optional": true, + "requires": { + "abbrev": "1" + } + }, + "npm-bundled": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "optional": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "optional": true + }, + "npm-packlist": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "optional": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "optional": true + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -10450,6 +9754,33 @@ "word-wrap": "^1.2.3" } }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "optional": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -10462,8 +9793,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { "version": "3.1.1", @@ -10477,23 +9807,119 @@ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pg": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.5.1.tgz", + "integrity": "sha512-9wm3yX9lCfjvA98ybCyw2pADUivyNWT/yIP4ZcDVpMN0og70BUWYEGXPCTAQdGTAqnytfRADb7NERrY1qxhIqw==", + "requires": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.4.0", + "pg-pool": "^3.2.2", + "pg-protocol": "^1.4.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + } + }, + "pg-connection-string": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.4.0.tgz", + "integrity": "sha512-3iBXuv7XKvxeMrIgym7njT+HlZkwZqqGX4Bu9cci8xHZNT+Um1gWKqCsAzcC0d95rcKMU5WBg6YRUcHyV0HZKQ==" + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-pool": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.2.tgz", + "integrity": "sha512-ORJoFxAlmmros8igi608iVEbQNNZlp89diFVx6yV5v+ehmpMY9sK6QgpmgoXbmkNaBAx8cOOZh9g80kJv1ooyA==" + }, + "pg-protocol": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.4.0.tgz", + "integrity": "sha512-El+aXWcwG/8wuFICMQjM5ZSAm6OWiJicFdNYo+VY3QP+8vI4SvLIWVe51PppTzMhikUJR+PsyIFKqfdXPz/yxA==" + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz", + "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==", + "requires": { + "split2": "^3.1.1" + } + }, + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "requires": { + "xtend": "^4.0.0" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "ramda": { "version": "0.27.1", @@ -10501,12 +9927,151 @@ "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", "dev": true }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "redis": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/redis/-/redis-3.0.2.tgz", + "integrity": "sha512-PNhLCrjU6vKVuMOyFu7oSP296mwBkcE6lrAjruBYG5LgdSqtRBoVQIylrMyVZD/lkF24RSNNatzvYag6HRBHjQ==", + "requires": { + "denque": "^1.4.1", + "redis-commands": "^1.5.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0" + } + }, + "redis-commands": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.6.0.tgz", + "integrity": "sha512-2jnZ0IkjZxvguITjFTrGiLyzQZcTvaw8DAaCXxZq/dsHXz7KfMQ3OUJy7Tz9vnRtZRVz6VRCPDvruvU8Ts44wQ==" + }, + "redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" + }, + "redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", + "requires": { + "redis-errors": "^1.0.0" + } + }, "regexpp": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", "dev": true }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" + }, + "dependencies": { + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, "resolve": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", @@ -10523,15 +10088,46 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "rethinkdb": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/rethinkdb/-/rethinkdb-2.4.2.tgz", + "integrity": "sha512-6DzwqEpFc8cqesAdo07a845oBRxLiHvWzopTKBo/uY2ypGWIsJQFJk3wjRDtSEhczxJqLS0jnf37rwgzYAw8NQ==", + "requires": { + "bluebird": ">= 2.3.2 < 3" + } + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "requires": { "glob": "^7.1.3" } }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, "semver": { "version": "7.3.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", @@ -10541,6 +10137,11 @@ "lru-cache": "^6.0.0" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -10556,6 +10157,22 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "optional": true + }, + "simple-git": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-2.31.0.tgz", + "integrity": "sha512-/+rmE7dYZMbRAfEmn8EUIOwlM2G7UdzpkC60KF86YAfXGnmGtsPrKsym0hKvLBdFLLW019C+aZld1+6iIVy5xA==", + "requires": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.3.1" + } + }, "slice-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", @@ -10567,12 +10184,73 @@ "is-fullwidth-code-point": "^2.0.0" } }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "optional": true, + "requires": { + "memory-pager": "^1.0.2" + } + }, + "split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "requires": { + "readable-stream": "^3.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "sqlite3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.1.tgz", + "integrity": "sha512-kh2lTIcYNfmVcvhVJihsYuPj9U0xzBbh6bmqILO2hkryWSC9RRhzYmkIDtJkJ+d8Kg4wZRJ0T1reyHUEspICfg==", + "optional": true, + "requires": { + "node-addon-api": "^3.0.0", + "node-gyp": "3.x", + "node-pre-gyp": "^0.11.0" + } + }, + "sqlstring": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", + "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -10601,6 +10279,21 @@ } } }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -10637,12 +10330,104 @@ "string-width": "^3.0.0" } }, + "tar": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", + "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", + "optional": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.12", + "inherits": "2" + } + }, + "tarn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.1.tgz", + "integrity": "sha512-6usSlV9KyHsspvwu2duKH+FMUhqJnAh6J5J/4MITl8s94iSUQTLkJggdiewKv4RyARQccnigV48Z+khiuVZDJw==" + }, + "tedious": { + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/tedious/-/tedious-9.2.3.tgz", + "integrity": "sha512-+mI2r/5mqxpTHKBZ/SW+NNH2MK5i3Pwwkw0gF5ZrS2wf2uT/03bLSss8nm7xh604abJXyjx0sirhwH63H328qA==", + "requires": { + "@azure/ms-rest-nodeauth": "^3.0.6", + "@js-joda/core": "^3.1.0", + "adal-node": "^0.1.28", + "bl": "^3.0.0", + "depd": "^2.0.0", + "iconv-lite": "^0.6.2", + "jsbi": "^3.1.3", + "native-duplexpair": "^1.0.0", + "punycode": "^2.1.0", + "readable-stream": "^3.6.0", + "sprintf-js": "^1.1.2" + }, + "dependencies": { + "bl": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.1.tgz", + "integrity": "sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ==", + "requires": { + "readable-stream": "^3.0.1" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + } + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "requires": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -10658,21 +10443,66 @@ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, + "ueberdb2": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/ueberdb2/-/ueberdb2-1.2.5.tgz", + "integrity": "sha512-Bts6kmVvhVDWiZjD1JAT1qYknHHK6t9L7kGIFIedGAZRNQ3lRw2XJdf9hKbFpN2HM0J3S/aJoLrZO5BLk3UiaA==", + "requires": { + "async": "^3.2.0", + "cassandra-driver": "^4.5.1", + "channels": "0.0.4", + "dirty": "^1.1.0", + "elasticsearch": "^16.7.1", + "mongodb": "^3.6.3", + "mssql": "^7.0.0-beta.2", + "mysql": "2.18.1", + "nano": "^8.2.2", + "pg": "^8.0.3", + "redis": "^3.0.2", + "rethinkdb": "^2.4.2", + "simple-git": "^2.4.0", + "sqlite3": "^5.0.1" + } + }, + "underscore": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.0.tgz", + "integrity": "sha512-21rQzss/XPMjolTiIezSu3JAjgagXKROtNrYFEOWK109qY1Uv2tVjPTZ1ci2HgvQDA16gHYSthQIJfB+XId/rQ==" + }, "uri-js": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", - "dev": true, "requires": { "punycode": "^2.1.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, "v8-compile-cache": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", "dev": true }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -10682,6 +10512,38 @@ "isexe": "^2.0.0" } }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -10691,8 +10553,36 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + }, + "xmldom": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.4.0.tgz", + "integrity": "sha512-2E93k08T30Ugs+34HBSTQLVtpi6mCddaY8uO+pMNk1pqSjV5vElzn4mmh6KLxN3hki8rNcHSYzILoh3TEWORvA==" + }, + "xpath.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xpath.js/-/xpath.js-1.1.0.tgz", + "integrity": "sha512-jg+qkfS4K8E7965sqaUl8mRngXiKb3WZGfONgE18pr03FUQiuSV6G+Ej4tS55B+rIQSFEIw3phdVAQ4pPqNWfQ==" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "yallist": { "version": "4.0.0", From 57be60d1d46c2037844e37479c7defea78f184ef Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 23 Jan 2021 18:04:20 -0500 Subject: [PATCH 072/357] GitHub workflows: Break list of plugins across multiple lines This makes it easier to review changes to the list. --- .github/workflows/backend-tests.yml | 16 ++++++++++++++-- .github/workflows/frontend-tests.yml | 17 +++++++++++++++-- .github/workflows/load-test.yml | 14 +++++++++++++- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index e50491d23..245dffb97 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -52,8 +52,20 @@ jobs: - name: Install all dependencies and symlink for ep_etherpad-lite run: bin/installDeps.sh - - name: Install etherpad plugins - run: npm install ep_align ep_author_hover ep_cursortrace ep_font_size ep_hash_auth ep_headings2 ep_markdown ep_readonly_guest ep_spellcheck ep_subscript_and_superscript ep_table_of_contents + - name: Install Etherpad plugins + run: > + npm install + ep_align + ep_author_hover + ep_cursortrace + ep_font_size + ep_hash_auth + ep_headings2 + ep_markdown + ep_readonly_guest + ep_spellcheck + ep_subscript_and_superscript + ep_table_of_contents # configures some settings and runs npm run test - name: Run the backend tests diff --git a/.github/workflows/frontend-tests.yml b/.github/workflows/frontend-tests.yml index 3b178622e..52fa0af1b 100644 --- a/.github/workflows/frontend-tests.yml +++ b/.github/workflows/frontend-tests.yml @@ -58,8 +58,21 @@ jobs: - name: Install all dependencies and symlink for ep_etherpad-lite run: bin/installDeps.sh - - name: Install etherpad plugins - run: npm install ep_align ep_author_hover ep_cursortrace ep_font_size ep_hash_auth ep_headings2 ep_markdown ep_readonly_guest ep_spellcheck ep_subscript_and_superscript ep_table_of_contents ep_set_title_on_pad + - name: Install Etherpad plugins + run: > + npm install + ep_align + ep_author_hover + ep_cursortrace + ep_font_size + ep_hash_auth + ep_headings2 + ep_markdown + ep_readonly_guest + ep_set_title_on_pad + ep_spellcheck + ep_subscript_and_superscript + ep_table_of_contents - name: export GIT_HASH to env id: environment diff --git a/.github/workflows/load-test.yml b/.github/workflows/load-test.yml index 095adc785..c590da06c 100644 --- a/.github/workflows/load-test.yml +++ b/.github/workflows/load-test.yml @@ -46,7 +46,19 @@ jobs: run: sudo npm install -g etherpad-load-test - name: Install etherpad plugins - run: npm install ep_align ep_author_hover ep_cursortrace ep_font_size ep_hash_auth ep_headings2 ep_markdown ep_readonly_guest ep_spellcheck ep_subscript_and_superscript ep_table_of_contents + run: > + npm install + ep_align + ep_author_hover + ep_cursortrace + ep_font_size + ep_hash_auth + ep_headings2 + ep_markdown + ep_readonly_guest + ep_spellcheck + ep_subscript_and_superscript + ep_table_of_contents # configures some settings and runs npm run test - name: Run load test From e32a62346832140107de53428532597f0b9bc6c2 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 23 Jan 2021 18:12:18 -0500 Subject: [PATCH 073/357] GitHub workflows: Synchronize plugin lists --- .github/workflows/backend-tests.yml | 1 + .github/workflows/load-test.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index 245dffb97..962b9e80b 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -63,6 +63,7 @@ jobs: ep_headings2 ep_markdown ep_readonly_guest + ep_set_title_on_pad ep_spellcheck ep_subscript_and_superscript ep_table_of_contents diff --git a/.github/workflows/load-test.yml b/.github/workflows/load-test.yml index c590da06c..89f766253 100644 --- a/.github/workflows/load-test.yml +++ b/.github/workflows/load-test.yml @@ -56,6 +56,7 @@ jobs: ep_headings2 ep_markdown ep_readonly_guest + ep_set_title_on_pad ep_spellcheck ep_subscript_and_superscript ep_table_of_contents From f7b11336003b43fb2fb88bef28dfd40adf578c8e Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 23 Jan 2021 18:06:50 -0500 Subject: [PATCH 074/357] GitHub workflows: Install Etherpad deps after installing plugins --- .github/workflows/backend-tests.yml | 13 ++++++++++--- .github/workflows/frontend-tests.yml | 13 ++++++++++--- .github/workflows/load-test.yml | 13 ++++++++++--- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index 962b9e80b..8c9c45f28 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -49,9 +49,6 @@ jobs: sudo apt update sudo apt install -y --no-install-recommends libreoffice libreoffice-pdfimport - - name: Install all dependencies and symlink for ep_etherpad-lite - run: bin/installDeps.sh - - name: Install Etherpad plugins run: > npm install @@ -68,6 +65,16 @@ jobs: ep_subscript_and_superscript ep_table_of_contents + # This must be run after installing the plugins, otherwise npm will try to + # hoist common dependencies by removing them from src/node_modules and + # installing them in the top-level node_modules. As of v6.14.10, npm's hoist + # logic appears to be buggy, because it sometimes removes dependencies from + # src/node_modules but fails to add them to the top-level node_modules. Even + # if npm correctly hoists the dependencies, the hoisting seems to confuse + # tools such as `npm outdated`, `npm update`, and some ESLint rules. + - name: Install all dependencies and symlink for ep_etherpad-lite + run: bin/installDeps.sh + # configures some settings and runs npm run test - name: Run the backend tests run: tests/frontend/travis/runnerBackend.sh diff --git a/.github/workflows/frontend-tests.yml b/.github/workflows/frontend-tests.yml index 52fa0af1b..0052cafe0 100644 --- a/.github/workflows/frontend-tests.yml +++ b/.github/workflows/frontend-tests.yml @@ -55,9 +55,6 @@ jobs: TRAVIS_JOB_NUMBER: ${{ github.run_id }}-${{ github.run_number }}-${{ github.job }} run: tests/frontend/travis/sauce_tunnel.sh - - name: Install all dependencies and symlink for ep_etherpad-lite - run: bin/installDeps.sh - - name: Install Etherpad plugins run: > npm install @@ -74,6 +71,16 @@ jobs: ep_subscript_and_superscript ep_table_of_contents + # This must be run after installing the plugins, otherwise npm will try to + # hoist common dependencies by removing them from src/node_modules and + # installing them in the top-level node_modules. As of v6.14.10, npm's hoist + # logic appears to be buggy, because it sometimes removes dependencies from + # src/node_modules but fails to add them to the top-level node_modules. Even + # if npm correctly hoists the dependencies, the hoisting seems to confuse + # tools such as `npm outdated`, `npm update`, and some ESLint rules. + - name: Install all dependencies and symlink for ep_etherpad-lite + run: bin/installDeps.sh + - name: export GIT_HASH to env id: environment run: echo "::set-output name=sha_short::$(git rev-parse --short ${{ github.sha }})" diff --git a/.github/workflows/load-test.yml b/.github/workflows/load-test.yml index 89f766253..ab0fb3bd0 100644 --- a/.github/workflows/load-test.yml +++ b/.github/workflows/load-test.yml @@ -39,9 +39,6 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 - - name: Install all dependencies and symlink for ep_etherpad-lite - run: bin/installDeps.sh - - name: Install etherpad-load-test run: sudo npm install -g etherpad-load-test @@ -61,6 +58,16 @@ jobs: ep_subscript_and_superscript ep_table_of_contents + # This must be run after installing the plugins, otherwise npm will try to + # hoist common dependencies by removing them from src/node_modules and + # installing them in the top-level node_modules. As of v6.14.10, npm's hoist + # logic appears to be buggy, because it sometimes removes dependencies from + # src/node_modules but fails to add them to the top-level node_modules. Even + # if npm correctly hoists the dependencies, the hoisting seems to confuse + # tools such as `npm outdated`, `npm update`, and some ESLint rules. + - name: Install all dependencies and symlink for ep_etherpad-lite + run: bin/installDeps.sh + # configures some settings and runs npm run test - name: Run load test run: tests/frontend/travis/runnerLoadTest.sh From f0cafe88f2f86c4ead2f7cac2d0cc1a5cd0a286e Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 25 Jan 2021 18:04:09 +0100 Subject: [PATCH 075/357] Localisation updates from https://translatewiki.net. --- src/locales/ru.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/locales/ru.json b/src/locales/ru.json index 007fb4c51..792162e57 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -18,21 +18,39 @@ "Арсен Асхат" ] }, + "admin.page-title": "Панель администратора — Etherpad", "admin_plugins": "Менеджер плагинов", "admin_plugins.available": "Доступные плагины", "admin_plugins.available_not-found": "Плагины не найдены.", + "admin_plugins.available_fetching": "Получение…", "admin_plugins.available_install.value": "Установить", + "admin_plugins.available_search.placeholder": "Искать плагины для установки", "admin_plugins.description": "Описание", "admin_plugins.installed": "Установленные плагины", + "admin_plugins.installed_fetching": "Получение установленных плагинов…", "admin_plugins.installed_nothing": "Вы еще не установили ни одного плагина.", "admin_plugins.installed_uninstall.value": "Удалить", "admin_plugins.last-update": "Последнее обновление", + "admin_plugins.name": "Название", + "admin_plugins.page-title": "Менеджер плагинов — Etherpad", "admin_plugins.version": "Версия", + "admin_plugins_info": "Информация об устранении неполадок", "admin_plugins_info.hooks": "Установленные крючки", + "admin_plugins_info.hooks_client": "Клиентские хуки", + "admin_plugins_info.hooks_server": "Серверные хуки", + "admin_plugins_info.parts": "Установленные части", + "admin_plugins_info.plugins": "Установленные плагины", + "admin_plugins_info.page-title": "Информация о плагине — Etherpad", + "admin_plugins_info.version": "Версия Etherpad", + "admin_plugins_info.version_latest": "Последняя доступная версия", "admin_plugins_info.version_number": "Номер версии", "admin_settings": "Настройки", "admin_settings.current": "Текущая конфигурация", + "admin_settings.current_example-devel": "Пример шаблона настроек для среда разработки", + "admin_settings.current_example-prod": "Пример шаблона настроек для боевой среды", + "admin_settings.current_restart.value": "Перезагрузить Etherpad", "admin_settings.current_save.value": "Сохранить настройки", + "admin_settings.page-title": "Настройки — Etherpad", "index.newPad": "Создать", "index.createOpenPad": "или создать/открыть документ с именем:", "index.openPad": "откройте существующий документ с именем:", From d9225f326fce37ab33979b811e6b5bcc1ef32ef8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 076/357] lint: src/node/db/API.js --- src/node/db/API.js | 170 ++++++++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 78 deletions(-) diff --git a/src/node/db/API.js b/src/node/db/API.js index 2c58a69a3..ea77ac7df 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -1,3 +1,4 @@ +'use strict'; /** * This module provides all API functions */ @@ -18,8 +19,8 @@ * limitations under the License. */ -const Changeset = require('ep_etherpad-lite/static/js/Changeset'); -const customError = require('../utils/customError'); +const Changeset = require('../../static/js/Changeset'); +const CustomError = require('../utils/customError'); const padManager = require('./PadManager'); const padMessageHandler = require('../handler/PadMessageHandler'); const readOnlyManager = require('./ReadOnlyManager'); @@ -101,7 +102,7 @@ Example returns: } */ -exports.getAttributePool = async function (padID) { +exports.getAttributePool = async (padID) => { const pad = await getPadSafe(padID, true); return {pool: pad.pool}; }; @@ -119,7 +120,7 @@ Example returns: } */ -exports.getRevisionChangeset = async function (padID, rev) { +exports.getRevisionChangeset = async (padID, rev) => { // try to parse the revision number if (rev !== undefined) { rev = checkValidRev(rev); @@ -133,7 +134,7 @@ exports.getRevisionChangeset = async function (padID, rev) { if (rev !== undefined) { // check if this is a valid revision if (rev > head) { - throw new customError('rev is higher than the head revision of the pad', 'apierror'); + throw new CustomError('rev is higher than the head revision of the pad', 'apierror'); } // get the changeset for this revision @@ -152,7 +153,7 @@ Example returns: {code: 0, message:"ok", data: {text:"Welcome Text"}} {code: 1, message:"padID does not exist", data: null} */ -exports.getText = async function (padID, rev) { +exports.getText = async (padID, rev) => { // try to parse the revision number if (rev !== undefined) { rev = checkValidRev(rev); @@ -166,7 +167,7 @@ exports.getText = async function (padID, rev) { if (rev !== undefined) { // check if this is a valid revision if (rev > head) { - throw new customError('rev is higher than the head revision of the pad', 'apierror'); + throw new CustomError('rev is higher than the head revision of the pad', 'apierror'); } // get the text of this revision @@ -188,10 +189,10 @@ Example returns: {code: 1, message:"padID does not exist", data: null} {code: 1, message:"text too long", data: null} */ -exports.setText = async function (padID, text) { +exports.setText = async (padID, text) => { // text is required if (typeof text !== 'string') { - throw new customError('text is not a string', 'apierror'); + throw new CustomError('text is not a string', 'apierror'); } // get the pad @@ -212,10 +213,10 @@ Example returns: {code: 1, message:"padID does not exist", data: null} {code: 1, message:"text too long", data: null} */ -exports.appendText = async function (padID, text) { +exports.appendText = async (padID, text) => { // text is required if (typeof text !== 'string') { - throw new customError('text is not a string', 'apierror'); + throw new CustomError('text is not a string', 'apierror'); } const pad = await getPadSafe(padID, true); @@ -233,7 +234,7 @@ Example returns: {code: 0, message:"ok", data: {text:"Welcome Text"}} {code: 1, message:"padID does not exist", data: null} */ -exports.getHTML = async function (padID, rev) { +exports.getHTML = async (padID, rev) => { if (rev !== undefined) { rev = checkValidRev(rev); } @@ -245,7 +246,7 @@ exports.getHTML = async function (padID, rev) { // check if this is a valid revision const head = pad.getHeadRevisionNumber(); if (rev > head) { - throw new customError('rev is higher than the head revision of the pad', 'apierror'); + throw new CustomError('rev is higher than the head revision of the pad', 'apierror'); } } @@ -265,10 +266,10 @@ Example returns: {code: 0, message:"ok", data: null} {code: 1, message:"padID does not exist", data: null} */ -exports.setHTML = async function (padID, html) { +exports.setHTML = async (padID, html) => { // html string is required if (typeof html !== 'string') { - throw new customError('html is not a string', 'apierror'); + throw new CustomError('html is not a string', 'apierror'); } // get the pad @@ -278,7 +279,7 @@ exports.setHTML = async function (padID, html) { try { await importHtml.setPadHTML(pad, cleanText(html)); } catch (e) { - throw new customError('HTML is malformed', 'apierror'); + throw new CustomError('HTML is malformed', 'apierror'); } // update the clients on the pad @@ -294,23 +295,25 @@ getChatHistory(padId, start, end), returns a part of or the whole chat-history o Example returns: -{"code":0,"message":"ok","data":{"messages":[{"text":"foo","authorID":"a.foo","time":1359199533759,"userName":"test"}, - {"text":"bar","authorID":"a.foo","time":1359199534622,"userName":"test"}]}} +{"code":0,"message":"ok","data":{"messages":[ + {"text":"foo","authorID":"a.foo","time":1359199533759,"userName":"test"}, + {"text":"bar","authorID":"a.foo","time":1359199534622,"userName":"test"} +]}} {code: 1, message:"start is higher or equal to the current chatHead", data: null} {code: 1, message:"padID does not exist", data: null} */ -exports.getChatHistory = async function (padID, start, end) { +exports.getChatHistory = async (padID, start, end) => { if (start && end) { if (start < 0) { - throw new customError('start is below zero', 'apierror'); + throw new CustomError('start is below zero', 'apierror'); } if (end < 0) { - throw new customError('end is below zero', 'apierror'); + throw new CustomError('end is below zero', 'apierror'); } if (start > end) { - throw new customError('start is higher than end', 'apierror'); + throw new CustomError('start is higher than end', 'apierror'); } } @@ -320,16 +323,16 @@ exports.getChatHistory = async function (padID, start, end) { const chatHead = pad.chatHead; // fall back to getting the whole chat-history if a parameter is missing - if (!start || !end) { + if (!start || !end) { start = 0; end = pad.chatHead; } if (start > chatHead) { - throw new customError('start is higher than the current chatHead', 'apierror'); + throw new CustomError('start is higher than the current chatHead', 'apierror'); } if (end > chatHead) { - throw new customError('end is higher than the current chatHead', 'apierror'); + throw new CustomError('end is higher than the current chatHead', 'apierror'); } // the the whole message-log and return it to the client @@ -339,21 +342,22 @@ exports.getChatHistory = async function (padID, start, end) { }; /** -appendChatMessage(padID, text, authorID, time), creates a chat message for the pad id, time is a timestamp +appendChatMessage(padID, text, authorID, time), creates a chat message for the pad id, +time is a timestamp Example returns: {code: 0, message:"ok", data: null} {code: 1, message:"padID does not exist", data: null} */ -exports.appendChatMessage = async function (padID, text, authorID, time) { +exports.appendChatMessage = async (padID, text, authorID, time) => { // text is required if (typeof text !== 'string') { - throw new customError('text is not a string', 'apierror'); + throw new CustomError('text is not a string', 'apierror'); } // if time is not an integer value set time to current timestamp - if (time === undefined || !is_int(time)) { + if (time === undefined || !isInt(time)) { time = Date.now(); } @@ -375,7 +379,7 @@ Example returns: {code: 0, message:"ok", data: {revisions: 56}} {code: 1, message:"padID does not exist", data: null} */ -exports.getRevisionsCount = async function (padID) { +exports.getRevisionsCount = async (padID) => { // get the pad const pad = await getPadSafe(padID, true); return {revisions: pad.getHeadRevisionNumber()}; @@ -389,7 +393,7 @@ Example returns: {code: 0, message:"ok", data: {savedRevisions: 42}} {code: 1, message:"padID does not exist", data: null} */ -exports.getSavedRevisionsCount = async function (padID) { +exports.getSavedRevisionsCount = async (padID) => { // get the pad const pad = await getPadSafe(padID, true); return {savedRevisions: pad.getSavedRevisionsNumber()}; @@ -403,7 +407,7 @@ Example returns: {code: 0, message:"ok", data: {savedRevisions: [2, 42, 1337]}} {code: 1, message:"padID does not exist", data: null} */ -exports.listSavedRevisions = async function (padID) { +exports.listSavedRevisions = async (padID) => { // get the pad const pad = await getPadSafe(padID, true); return {savedRevisions: pad.getSavedRevisionsList()}; @@ -417,7 +421,7 @@ Example returns: {code: 0, message:"ok", data: null} {code: 1, message:"padID does not exist", data: null} */ -exports.saveRevision = async function (padID, rev) { +exports.saveRevision = async (padID, rev) => { // check if rev is a number if (rev !== undefined) { rev = checkValidRev(rev); @@ -430,7 +434,7 @@ exports.saveRevision = async function (padID, rev) { // the client asked for a special revision if (rev !== undefined) { if (rev > head) { - throw new customError('rev is higher than the head revision of the pad', 'apierror'); + throw new CustomError('rev is higher than the head revision of the pad', 'apierror'); } } else { rev = pad.getHeadRevisionNumber(); @@ -448,7 +452,7 @@ Example returns: {code: 0, message:"ok", data: {lastEdited: 1340815946602}} {code: 1, message:"padID does not exist", data: null} */ -exports.getLastEdited = async function (padID) { +exports.getLastEdited = async (padID) => { // get the pad const pad = await getPadSafe(padID, true); const lastEdited = await pad.getLastEdit(); @@ -463,16 +467,16 @@ Example returns: {code: 0, message:"ok", data: null} {code: 1, message:"pad does already exist", data: null} */ -exports.createPad = async function (padID, text) { +exports.createPad = async (padID, text) => { if (padID) { // ensure there is no $ in the padID if (padID.indexOf('$') !== -1) { - throw new customError("createPad can't create group pads", 'apierror'); + throw new CustomError("createPad can't create group pads", 'apierror'); } // check for url special characters if (padID.match(/(\/|\?|&|#)/)) { - throw new customError('malformed padID: Remove special characters', 'apierror'); + throw new CustomError('malformed padID: Remove special characters', 'apierror'); } } @@ -488,7 +492,7 @@ Example returns: {code: 0, message:"ok", data: null} {code: 1, message:"padID does not exist", data: null} */ -exports.deletePad = async function (padID) { +exports.deletePad = async (padID) => { const pad = await getPadSafe(padID, true); await pad.remove(); }; @@ -501,10 +505,10 @@ exports.deletePad = async function (padID) { {code:0, message:"ok", data:null} {code: 1, message:"padID does not exist", data: null} */ -exports.restoreRevision = async function (padID, rev) { +exports.restoreRevision = async (padID, rev) => { // check if rev is a number if (rev === undefined) { - throw new customError('rev is not defined', 'apierror'); + throw new CustomError('rev is not defined', 'apierror'); } rev = checkValidRev(rev); @@ -513,7 +517,7 @@ exports.restoreRevision = async function (padID, rev) { // check if this is a valid revision if (rev > pad.getHeadRevisionNumber()) { - throw new customError('rev is higher than the head revision of the pad', 'apierror'); + throw new CustomError('rev is higher than the head revision of the pad', 'apierror'); } const atext = await pad.getInternalRevisionAText(rev); @@ -521,7 +525,7 @@ exports.restoreRevision = async function (padID, rev) { const oldText = pad.text(); atext.text += '\n'; - function eachAttribRun(attribs, func) { + const eachAttribRun = (attribs, func) => { const attribsIter = Changeset.opIterator(attribs); let textIndex = 0; const newTextStart = 0; @@ -534,7 +538,7 @@ exports.restoreRevision = async function (padID, rev) { } textIndex = nextIndex; } - } + }; // create a new changeset with a helper builder object const builder = Changeset.builder(oldText.length); @@ -569,7 +573,7 @@ Example returns: {code: 0, message:"ok", data: {padID: destinationID}} {code: 1, message:"padID does not exist", data: null} */ -exports.copyPad = async function (sourceID, destinationID, force) { +exports.copyPad = async (sourceID, destinationID, force) => { const pad = await getPadSafe(sourceID, true); await pad.copy(destinationID, force); }; @@ -583,7 +587,7 @@ Example returns: {code: 0, message:"ok", data: {padID: destinationID}} {code: 1, message:"padID does not exist", data: null} */ -exports.copyPadWithoutHistory = async function (sourceID, destinationID, force) { +exports.copyPadWithoutHistory = async (sourceID, destinationID, force) => { const pad = await getPadSafe(sourceID, true); await pad.copyPadWithoutHistory(destinationID, force); }; @@ -597,7 +601,7 @@ Example returns: {code: 0, message:"ok", data: {padID: destinationID}} {code: 1, message:"padID does not exist", data: null} */ -exports.movePad = async function (sourceID, destinationID, force) { +exports.movePad = async (sourceID, destinationID, force) => { const pad = await getPadSafe(sourceID, true); await pad.copy(destinationID, force); await pad.remove(); @@ -611,7 +615,7 @@ Example returns: {code: 0, message:"ok", data: null} {code: 1, message:"padID does not exist", data: null} */ -exports.getReadOnlyID = async function (padID) { +exports.getReadOnlyID = async (padID) => { // we don't need the pad object, but this function does all the security stuff for us await getPadSafe(padID, true); @@ -629,11 +633,11 @@ Example returns: {code: 0, message:"ok", data: {padID: padID}} {code: 1, message:"padID does not exist", data: null} */ -exports.getPadID = async function (roID) { +exports.getPadID = async (roID) => { // get the PadId const padID = await readOnlyManager.getPadId(roID); - if (padID === null) { - throw new customError('padID does not exist', 'apierror'); + if (padID == null) { + throw new CustomError('padID does not exist', 'apierror'); } return {padID}; @@ -647,7 +651,7 @@ Example returns: {code: 0, message:"ok", data: null} {code: 1, message:"padID does not exist", data: null} */ -exports.setPublicStatus = async function (padID, publicStatus) { +exports.setPublicStatus = async (padID, publicStatus) => { // ensure this is a group pad checkGroupPad(padID, 'publicStatus'); @@ -670,7 +674,7 @@ Example returns: {code: 0, message:"ok", data: {publicStatus: true}} {code: 1, message:"padID does not exist", data: null} */ -exports.getPublicStatus = async function (padID) { +exports.getPublicStatus = async (padID) => { // ensure this is a group pad checkGroupPad(padID, 'publicStatus'); @@ -687,7 +691,7 @@ Example returns: {code: 0, message:"ok", data: {authorIDs : ["a.s8oes9dhwrvt0zif", "a.akf8finncvomlqva"]} {code: 1, message:"padID does not exist", data: null} */ -exports.listAuthorsOfPad = async function (padID) { +exports.listAuthorsOfPad = async (padID) => { // get the pad const pad = await getPadSafe(padID, true); const authorIDs = pad.getAllAuthors(); @@ -717,7 +721,7 @@ Example returns: {code: 1, message:"padID does not exist"} */ -exports.sendClientsMessage = async function (padID, msg) { +exports.sendClientsMessage = async (padID, msg) => { const pad = await getPadSafe(padID, true); padMessageHandler.handleCustomMessage(padID, msg); }; @@ -730,7 +734,7 @@ Example returns: {"code":0,"message":"ok","data":null} {"code":4,"message":"no or wrong API Key","data":null} */ -exports.checkToken = async function () { +exports.checkToken = async () => { }; /** @@ -741,7 +745,7 @@ Example returns: {code: 0, message:"ok", data: {chatHead: 42}} {code: 1, message:"padID does not exist", data: null} */ -exports.getChatHead = async function (padID) { +exports.getChatHead = async (padID) => { // get the pad const pad = await getPadSafe(padID, true); return {chatHead: pad.chatHead}; @@ -751,11 +755,21 @@ exports.getChatHead = async function (padID) { createDiffHTML(padID, startRev, endRev) returns an object of diffs from 2 points in a pad Example returns: - -{"code":0,"message":"ok","data":{"html":"Welcome to Etherpad!

        This pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!

        Get involved with Etherpad at http://etherpad.org
        aw

        ","authors":["a.HKIv23mEbachFYfH",""]}} +{ + "code": 0, + "message": "ok", + "data": { + "html": "...", + "authors": [ + "a.HKIv23mEbachFYfH", + "" + ] + } +} {"code":4,"message":"no or wrong API Key","data":null} + */ -exports.createDiffHTML = async function (padID, startRev, endRev) { +exports.createDiffHTML = async (padID, startRev, endRev) => { // check if startRev is a number if (startRev !== undefined) { startRev = checkValidRev(startRev); @@ -768,8 +782,9 @@ exports.createDiffHTML = async function (padID, startRev, endRev) { // get the pad const pad = await getPadSafe(padID, true); + let padDiff; try { - var padDiff = new PadDiff(pad, startRev, endRev); + padDiff = new PadDiff(pad, startRev, endRev); } catch (e) { throw {stop: e.message}; } @@ -793,7 +808,7 @@ exports.createDiffHTML = async function (padID, startRev, endRev) { {"code":4,"message":"no or wrong API Key","data":null} */ -exports.getStats = async function () { +exports.getStats = async () => { const sessionInfos = padMessageHandler.sessioninfos; const sessionKeys = Object.keys(sessionInfos); @@ -813,20 +828,18 @@ exports.getStats = async function () { **************************** */ // checks if a number is an int -function is_int(value) { - return (parseFloat(value) == parseInt(value, 10)) && !isNaN(value); -} +const isInt = (value) => (parseFloat(value) === parseInt(value, 10)) && !isNaN(value); // gets a pad safe async function getPadSafe(padID, shouldExist, text) { // check if padID is a string if (typeof padID !== 'string') { - throw new customError('padID is not a string', 'apierror'); + throw new CustomError('padID is not a string', 'apierror'); } // check if the padID maches the requirements if (!padManager.isValidPadId(padID)) { - throw new customError('padID did not match requirements', 'apierror'); + throw new CustomError('padID did not match requirements', 'apierror'); } // check if the pad exists @@ -834,12 +847,12 @@ async function getPadSafe(padID, shouldExist, text) { if (!exists && shouldExist) { // does not exist, but should - throw new customError('padID does not exist', 'apierror'); + throw new CustomError('padID does not exist', 'apierror'); } if (exists && !shouldExist) { // does exist, but shouldn't - throw new customError('padID does already exist', 'apierror'); + throw new CustomError('padID does already exist', 'apierror'); } // pad exists, let's get it @@ -848,33 +861,34 @@ async function getPadSafe(padID, shouldExist, text) { // checks if a rev is a legal number // pre-condition is that `rev` is not undefined -function checkValidRev(rev) { +const checkValidRev = (rev) => { if (typeof rev !== 'number') { rev = parseInt(rev, 10); } // check if rev is a number if (isNaN(rev)) { - throw new customError('rev is not a number', 'apierror'); + throw new CustomError('rev is not a number', 'apierror'); } // ensure this is not a negative number if (rev < 0) { - throw new customError('rev is not a negative number', 'apierror'); + throw new CustomError('rev is not a negative number', 'apierror'); } // ensure this is not a float value - if (!is_int(rev)) { - throw new customError('rev is a float value', 'apierror'); + if (!isInt(rev)) { + throw new CustomError('rev is a float value', 'apierror'); } return rev; -} +}; // checks if a padID is part of a group -function checkGroupPad(padID, field) { +const checkGroupPad = (padID, field) => { // ensure this is a group pad if (padID && padID.indexOf('$') === -1) { - throw new customError(`You can only get/set the ${field} of pads that belong to a group`, 'apierror'); + throw new CustomError( + `You can only get/set the ${field} of pads that belong to a group`, 'apierror'); } -} +}; From 8aa729a36ffdae54bf9e7ff01895e2906760b630 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 077/357] lint: src/node/db/AuthorManager.js --- src/node/db/AuthorManager.js | 189 +++++++++++++++++------------------ 1 file changed, 89 insertions(+), 100 deletions(-) diff --git a/src/node/db/AuthorManager.js b/src/node/db/AuthorManager.js index f4ef903cf..3fde1e4b1 100644 --- a/src/node/db/AuthorManager.js +++ b/src/node/db/AuthorManager.js @@ -1,3 +1,4 @@ +'use strict'; /** * The AuthorManager controlls all information about the Pad authors */ @@ -19,85 +20,83 @@ */ const db = require('./DB'); -const customError = require('../utils/customError'); -const randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString; +const CustomError = require('../utils/customError'); +const randomString = require('../../static/js/pad_utils').randomString; -exports.getColorPalette = function () { - return [ - '#ffc7c7', - '#fff1c7', - '#e3ffc7', - '#c7ffd5', - '#c7ffff', - '#c7d5ff', - '#e3c7ff', - '#ffc7f1', - '#ffa8a8', - '#ffe699', - '#cfff9e', - '#99ffb3', - '#a3ffff', - '#99b3ff', - '#cc99ff', - '#ff99e5', - '#e7b1b1', - '#e9dcAf', - '#cde9af', - '#bfedcc', - '#b1e7e7', - '#c3cdee', - '#d2b8ea', - '#eec3e6', - '#e9cece', - '#e7e0ca', - '#d3e5c7', - '#bce1c5', - '#c1e2e2', - '#c1c9e2', - '#cfc1e2', - '#e0bdd9', - '#baded3', - '#a0f8eb', - '#b1e7e0', - '#c3c8e4', - '#cec5e2', - '#b1d5e7', - '#cda8f0', - '#f0f0a8', - '#f2f2a6', - '#f5a8eb', - '#c5f9a9', - '#ececbb', - '#e7c4bc', - '#daf0b2', - '#b0a0fd', - '#bce2e7', - '#cce2bb', - '#ec9afe', - '#edabbd', - '#aeaeea', - '#c4e7b1', - '#d722bb', - '#f3a5e7', - '#ffa8a8', - '#d8c0c5', - '#eaaedd', - '#adc6eb', - '#bedad1', - '#dee9af', - '#e9afc2', - '#f8d2a0', - '#b3b3e6', - ]; -}; +exports.getColorPalette = () => [ + '#ffc7c7', + '#fff1c7', + '#e3ffc7', + '#c7ffd5', + '#c7ffff', + '#c7d5ff', + '#e3c7ff', + '#ffc7f1', + '#ffa8a8', + '#ffe699', + '#cfff9e', + '#99ffb3', + '#a3ffff', + '#99b3ff', + '#cc99ff', + '#ff99e5', + '#e7b1b1', + '#e9dcAf', + '#cde9af', + '#bfedcc', + '#b1e7e7', + '#c3cdee', + '#d2b8ea', + '#eec3e6', + '#e9cece', + '#e7e0ca', + '#d3e5c7', + '#bce1c5', + '#c1e2e2', + '#c1c9e2', + '#cfc1e2', + '#e0bdd9', + '#baded3', + '#a0f8eb', + '#b1e7e0', + '#c3c8e4', + '#cec5e2', + '#b1d5e7', + '#cda8f0', + '#f0f0a8', + '#f2f2a6', + '#f5a8eb', + '#c5f9a9', + '#ececbb', + '#e7c4bc', + '#daf0b2', + '#b0a0fd', + '#bce2e7', + '#cce2bb', + '#ec9afe', + '#edabbd', + '#aeaeea', + '#c4e7b1', + '#d722bb', + '#f3a5e7', + '#ffa8a8', + '#d8c0c5', + '#eaaedd', + '#adc6eb', + '#bedad1', + '#dee9af', + '#e9afc2', + '#f8d2a0', + '#b3b3e6', +]; /** * Checks if the author exists */ -exports.doesAuthorExist = async function (authorID) { +exports.doesAuthorExist = async (authorID) => { const author = await db.get(`globalAuthor:${authorID}`); - return author !== null; + return author != null; }; /* exported for backwards compatibility */ @@ -107,7 +106,7 @@ exports.doesAuthorExists = exports.doesAuthorExist; * Returns the AuthorID for a token. * @param {String} token The token */ -exports.getAuthor4Token = async function (token) { +exports.getAuthor4Token = async (token) => { const author = await mapAuthorWithDBKey('token2author', token); // return only the sub value authorID @@ -119,7 +118,7 @@ exports.getAuthor4Token = async function (token) { * @param {String} token The mapper * @param {String} name The name of the author (optional) */ -exports.createAuthorIfNotExistsFor = async function (authorMapper, name) { +exports.createAuthorIfNotExistsFor = async (authorMapper, name) => { const author = await mapAuthorWithDBKey('mapper2author', authorMapper); if (name) { @@ -140,7 +139,7 @@ async function mapAuthorWithDBKey(mapperkey, mapper) { // try to map to an author const author = await db.get(`${mapperkey}:${mapper}`); - if (author === null) { + if (author == null) { // there is no author with this mapper, so create one const author = await exports.createAuthor(null); @@ -163,7 +162,7 @@ async function mapAuthorWithDBKey(mapperkey, mapper) { * Internal function that creates the database entry for an author * @param {String} name The name of the author */ -exports.createAuthor = function (name) { +exports.createAuthor = (name) => { // create the new author name const author = `a.${randomString(16)}`; @@ -185,50 +184,40 @@ exports.createAuthor = function (name) { * Returns the Author Obj of the author * @param {String} author The id of the author */ -exports.getAuthor = function (author) { - // NB: result is already a Promise - return db.get(`globalAuthor:${author}`); -}; +exports.getAuthor = (author) => db.get(`globalAuthor:${author}`); /** * Returns the color Id of the author * @param {String} author The id of the author */ -exports.getAuthorColorId = function (author) { - return db.getSub(`globalAuthor:${author}`, ['colorId']); -}; +exports.getAuthorColorId = (author) => db.getSub(`globalAuthor:${author}`, ['colorId']); /** * Sets the color Id of the author * @param {String} author The id of the author * @param {String} colorId The color id of the author */ -exports.setAuthorColorId = function (author, colorId) { - return db.setSub(`globalAuthor:${author}`, ['colorId'], colorId); -}; +exports.setAuthorColorId = (author, colorId) => db.setSub( + `globalAuthor:${author}`, ['colorId'], colorId); /** * Returns the name of the author * @param {String} author The id of the author */ -exports.getAuthorName = function (author) { - return db.getSub(`globalAuthor:${author}`, ['name']); -}; +exports.getAuthorName = (author) => db.getSub(`globalAuthor:${author}`, ['name']); /** * Sets the name of the author * @param {String} author The id of the author * @param {String} name The name of the author */ -exports.setAuthorName = function (author, name) { - return db.setSub(`globalAuthor:${author}`, ['name'], name); -}; +exports.setAuthorName = (author, name) => db.setSub(`globalAuthor:${author}`, ['name'], name); /** * Returns an array of all pads this author contributed to * @param {String} author The id of the author */ -exports.listPadsOfAuthor = async function (authorID) { +exports.listPadsOfAuthor = async (authorID) => { /* There are two other places where this array is manipulated: * (1) When the author is added to a pad, the author object is also updated * (2) When a pad is deleted, each author of that pad is also updated @@ -237,9 +226,9 @@ exports.listPadsOfAuthor = async function (authorID) { // get the globalAuthor const author = await db.get(`globalAuthor:${authorID}`); - if (author === null) { + if (author == null) { // author does not exist - throw new customError('authorID does not exist', 'apierror'); + throw new CustomError('authorID does not exist', 'apierror'); } // everything is fine, return the pad IDs @@ -253,11 +242,11 @@ exports.listPadsOfAuthor = async function (authorID) { * @param {String} author The id of the author * @param {String} padID The id of the pad the author contributes to */ -exports.addPad = async function (authorID, padID) { +exports.addPad = async (authorID, padID) => { // get the entry const author = await db.get(`globalAuthor:${authorID}`); - if (author === null) return; + if (author == null) return; /* * ACHTUNG: padIDs can also be undefined, not just null, so it is not possible @@ -280,12 +269,12 @@ exports.addPad = async function (authorID, padID) { * @param {String} author The id of the author * @param {String} padID The id of the pad the author contributes to */ -exports.removePad = async function (authorID, padID) { +exports.removePad = async (authorID, padID) => { const author = await db.get(`globalAuthor:${authorID}`); - if (author === null) return; + if (author == null) return; - if (author.padIDs !== null) { + if (author.padIDs != null) { // remove pad from author delete author.padIDs[padID]; await db.set(`globalAuthor:${authorID}`, author); From 5ecb3f9f3742800afdc45be3b3c90818a488ac80 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 078/357] lint: src/node/db/GroupManager.js --- src/node/db/GroupManager.js | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/node/db/GroupManager.js b/src/node/db/GroupManager.js index 1330acc43..203e21a35 100644 --- a/src/node/db/GroupManager.js +++ b/src/node/db/GroupManager.js @@ -1,3 +1,4 @@ +'use strict'; /** * The Group Manager provides functions to manage groups in the database */ @@ -18,13 +19,13 @@ * limitations under the License. */ -const customError = require('../utils/customError'); -const randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString; +const CustomError = require('../utils/customError'); +const randomString = require('../../static/js/pad_utils').randomString; const db = require('./DB'); const padManager = require('./PadManager'); const sessionManager = require('./SessionManager'); -exports.listAllGroups = async function () { +exports.listAllGroups = async () => { let groups = await db.get('groups'); groups = groups || {}; @@ -32,17 +33,20 @@ exports.listAllGroups = async function () { return {groupIDs}; }; -exports.deleteGroup = async function (groupID) { +exports.deleteGroup = async (groupID) => { const group = await db.get(`group:${groupID}`); // ensure group exists if (group == null) { // group does not exist - throw new customError('groupID does not exist', 'apierror'); + throw new CustomError('groupID does not exist', 'apierror'); } // iterate through all pads of this group and delete them (in parallel) - await Promise.all(Object.keys(group.pads).map((padID) => padManager.getPad(padID).then((pad) => pad.remove()))); + await Promise.all(Object.keys(group.pads) + .map((padID) => padManager.getPad(padID) + .then((pad) => pad.remove()) + )); // iterate through group2sessions and delete all sessions const group2sessions = await db.get(`group2sessions:${groupID}`); @@ -76,14 +80,14 @@ exports.deleteGroup = async function (groupID) { await db.set('groups', newGroups); }; -exports.doesGroupExist = async function (groupID) { +exports.doesGroupExist = async (groupID) => { // try to get the group entry const group = await db.get(`group:${groupID}`); return (group != null); }; -exports.createGroup = async function () { +exports.createGroup = async () => { // search for non existing groupID const groupID = `g.${randomString(16)}`; @@ -103,10 +107,10 @@ exports.createGroup = async function () { return {groupID}; }; -exports.createGroupIfNotExistsFor = async function (groupMapper) { +exports.createGroupIfNotExistsFor = async (groupMapper) => { // ensure mapper is optional if (typeof groupMapper !== 'string') { - throw new customError('groupMapper is not a string', 'apierror'); + throw new CustomError('groupMapper is not a string', 'apierror'); } // try to get a group for this mapper @@ -128,7 +132,7 @@ exports.createGroupIfNotExistsFor = async function (groupMapper) { return result; }; -exports.createGroupPad = async function (groupID, padName, text) { +exports.createGroupPad = async (groupID, padName, text) => { // create the padID const padID = `${groupID}$${padName}`; @@ -136,7 +140,7 @@ exports.createGroupPad = async function (groupID, padName, text) { const groupExists = await exports.doesGroupExist(groupID); if (!groupExists) { - throw new customError('groupID does not exist', 'apierror'); + throw new CustomError('groupID does not exist', 'apierror'); } // ensure pad doesn't exist already @@ -144,7 +148,7 @@ exports.createGroupPad = async function (groupID, padName, text) { if (padExists) { // pad exists already - throw new customError('padName does already exist', 'apierror'); + throw new CustomError('padName does already exist', 'apierror'); } // create the pad @@ -156,12 +160,12 @@ exports.createGroupPad = async function (groupID, padName, text) { return {padID}; }; -exports.listPads = async function (groupID) { +exports.listPads = async (groupID) => { const exists = await exports.doesGroupExist(groupID); // ensure the group exists if (!exists) { - throw new customError('groupID does not exist', 'apierror'); + throw new CustomError('groupID does not exist', 'apierror'); } // group exists, let's get the pads From f0c26c9ba2fccc987d267ae81096d2d50abbb0e3 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 079/357] lint: src/node/db/Pad.js --- src/node/db/Pad.js | 68 +++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/src/node/db/Pad.js b/src/node/db/Pad.js index c39b3c69e..67842c14d 100644 --- a/src/node/db/Pad.js +++ b/src/node/db/Pad.js @@ -1,21 +1,21 @@ +'use strict'; /** * The pad object, defined with joose */ -const Changeset = require('ep_etherpad-lite/static/js/Changeset'); -const AttributePool = require('ep_etherpad-lite/static/js/AttributePool'); +const Changeset = require('../../static/js/Changeset'); +const AttributePool = require('../../static/js/AttributePool'); const db = require('./DB'); const settings = require('../utils/Settings'); const authorManager = require('./AuthorManager'); const padManager = require('./PadManager'); const padMessageHandler = require('../handler/PadMessageHandler'); const groupManager = require('./GroupManager'); -const customError = require('../utils/customError'); +const CustomError = require('../utils/customError'); const readOnlyManager = require('./ReadOnlyManager'); -const crypto = require('crypto'); const randomString = require('../utils/randomstring'); -const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); +const hooks = require('../../static/js/pluginfw/hooks'); const promises = require('../utils/promises'); // serialization/deserialization attributes @@ -23,13 +23,14 @@ const attributeBlackList = ['id']; const jsonableList = ['pool']; /** - * Copied from the Etherpad source code. It converts Windows line breaks to Unix line breaks and convert Tabs to spaces + * Copied from the Etherpad source code. It converts Windows line breaks to Unix + * line breaks and convert Tabs to spaces * @param txt */ -exports.cleanText = function (txt) { - return txt.replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\t/g, ' ').replace(/\xa0/g, ' '); -}; - +exports.cleanText = (txt) => txt.replace(/\r\n/g, '\n') + .replace(/\r/g, '\n') + .replace(/\t/g, ' ') + .replace(/\xa0/g, ' '); const Pad = function Pad(id) { this.atext = Changeset.makeAText('\n'); @@ -56,7 +57,7 @@ Pad.prototype.getSavedRevisionsNumber = function getSavedRevisionsNumber() { }; Pad.prototype.getSavedRevisionsList = function getSavedRevisionsList() { - const savedRev = new Array(); + const savedRev = []; for (const rev in this.savedRevisions) { savedRev.push(this.savedRevisions[rev].revNum); } @@ -85,11 +86,11 @@ Pad.prototype.appendRevision = async function appendRevision(aChangeset, author) newRevData.meta.timestamp = Date.now(); // ex. getNumForAuthor - if (author != '') { + if (author !== '') { this.pool.putAttrib(['author', author || '']); } - if (newRev % 100 == 0) { + if (newRev % 100 === 0) { newRevData.meta.pool = this.pool; newRevData.meta.atext = this.atext; } @@ -104,7 +105,7 @@ Pad.prototype.appendRevision = async function appendRevision(aChangeset, author) p.push(authorManager.addPad(author, this.id)); } - if (this.head == 0) { + if (this.head === 0) { hooks.callAll('padCreate', {pad: this, author}); } else { hooks.callAll('padUpdate', {pad: this, author, revs: newRev, changeset: aChangeset}); @@ -153,7 +154,7 @@ Pad.prototype.getAllAuthors = function getAllAuthors() { const authors = []; for (const key in this.pool.numToAttrib) { - if (this.pool.numToAttrib[key][0] == 'author' && this.pool.numToAttrib[key][1] != '') { + if (this.pool.numToAttrib[key][0] === 'author' && this.pool.numToAttrib[key][1] !== '') { authors.push(this.pool.numToAttrib[key][1]); } } @@ -177,9 +178,10 @@ Pad.prototype.getInternalRevisionAText = async function getInternalRevisionAText // get all needed changesets const changesets = []; - await Promise.all(neededChangesets.map((item) => this.getRevisionChangeset(item).then((changeset) => { - changesets[item] = changeset; - }))); + await Promise.all( + neededChangesets.map((item) => this.getRevisionChangeset(item).then((changeset) => { + changesets[item] = changeset; + }))); // we should have the atext by now let atext = await p_atext; @@ -204,10 +206,11 @@ Pad.prototype.getAllAuthorColors = async function getAllAuthorColors() { const returnTable = {}; const colorPalette = authorManager.getColorPalette(); - await Promise.all(authors.map((author) => authorManager.getAuthorColorId(author).then((colorId) => { - // colorId might be a hex color or an number out of the palette - returnTable[author] = colorPalette[colorId] || colorId; - }))); + await Promise.all( + authors.map((author) => authorManager.getAuthorColorId(author).then((colorId) => { + // colorId might be a hex color or an number out of the palette + returnTable[author] = colorPalette[colorId] || colorId; + }))); return returnTable; }; @@ -227,7 +230,7 @@ Pad.prototype.getValidRevisionRange = function getValidRevisionRange(startRev, e endRev = head; } - if (startRev !== null && endRev !== null) { + if (startRev != null && endRev != null) { return {startRev, endRev}; } return null; @@ -251,7 +254,7 @@ Pad.prototype.setText = async function setText(newText) { // We want to ensure the pad still ends with a \n, but otherwise keep // getText() and setText() consistent. let changeset; - if (newText[newText.length - 1] == '\n') { + if (newText[newText.length - 1] === '\n') { changeset = Changeset.makeSplice(oldText, 0, oldText.length, newText); } else { changeset = Changeset.makeSplice(oldText, 0, oldText.length - 1, newText); @@ -304,9 +307,10 @@ Pad.prototype.getChatMessages = async function getChatMessages(start, end) { // get all entries out of the database const entries = []; - await Promise.all(neededEntries.map((entryObject) => this.getChatMessage(entryObject.entryNum).then((entry) => { - entries[entryObject.order] = entry; - }))); + await Promise.all( + neededEntries.map((entryObject) => this.getChatMessage(entryObject.entryNum).then((entry) => { + entries[entryObject.order] = entry; + }))); // sort out broken chat entries // it looks like in happened in the past that the chat head was @@ -384,14 +388,16 @@ Pad.prototype.copy = async function copy(destinationID, force) { // copy all chat messages const chatHead = this.chatHead; for (let i = 0; i <= chatHead; ++i) { - const p = db.get(`pad:${sourceID}:chat:${i}`).then((chat) => db.set(`pad:${destinationID}:chat:${i}`, chat)); + const p = db.get(`pad:${sourceID}:chat:${i}`) + .then((chat) => db.set(`pad:${destinationID}:chat:${i}`, chat)); promises.push(p); } // copy all revisions const revHead = this.head; for (let i = 0; i <= revHead; ++i) { - const p = db.get(`pad:${sourceID}:revs:${i}`).then((rev) => db.set(`pad:${destinationID}:revs:${i}`, rev)); + const p = db.get(`pad:${sourceID}:revs:${i}`) + .then((rev) => db.set(`pad:${destinationID}:revs:${i}`, rev)); promises.push(p); } @@ -426,7 +432,7 @@ Pad.prototype.checkIfGroupExistAndReturnIt = async function checkIfGroupExistAnd // group does not exist if (!groupExists) { - throw new customError('groupID does not exist for destinationID', 'apierror'); + throw new CustomError('groupID does not exist for destinationID', 'apierror'); } } return destGroupID; @@ -446,7 +452,7 @@ Pad.prototype.removePadIfForceIsTrueAndAlreadyExist = async function removePadIf if (exists) { if (!force) { console.error('erroring out without force'); - throw new customError('destinationID already exists', 'apierror'); + throw new CustomError('destinationID already exists', 'apierror'); } // exists and forcing From e06b9442e01ea2413dfe7e901206ceb3638337ea Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 080/357] lint: src/node/db/PadManager.js --- src/node/db/PadManager.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/node/db/PadManager.js b/src/node/db/PadManager.js index 9334b92a4..11e8cb1a5 100644 --- a/src/node/db/PadManager.js +++ b/src/node/db/PadManager.js @@ -1,3 +1,4 @@ +'use strict'; /** * The Pad Manager is a Factory for pad Objects */ @@ -18,7 +19,7 @@ * limitations under the License. */ -const customError = require('../utils/customError'); +const CustomError = require('../utils/customError'); const Pad = require('../db/Pad').Pad; const db = require('./DB'); @@ -109,22 +110,22 @@ const padList = { * @param id A String with the id of the pad * @param {Function} callback */ -exports.getPad = async function (id, text) { +exports.getPad = async (id, text) => { // check if this is a valid padId if (!exports.isValidPadId(id)) { - throw new customError(`${id} is not a valid padId`, 'apierror'); + throw new CustomError(`${id} is not a valid padId`, 'apierror'); } // check if this is a valid text if (text != null) { // check if text is a string if (typeof text !== 'string') { - throw new customError('text is not a string', 'apierror'); + throw new CustomError('text is not a string', 'apierror'); } // check if text is less than 100k chars if (text.length > 100000) { - throw new customError('text must be less than 100k chars', 'apierror'); + throw new CustomError('text must be less than 100k chars', 'apierror'); } } @@ -146,14 +147,14 @@ exports.getPad = async function (id, text) { return pad; }; -exports.listAllPads = async function () { +exports.listAllPads = async () => { const padIDs = await padList.getPads(); return {padIDs}; }; // checks if a pad exists -exports.doesPadExist = async function (padId) { +exports.doesPadExist = async (padId) => { const value = await db.get(`pad:${padId}`); return (value != null && value.atext); @@ -189,9 +190,7 @@ exports.sanitizePadId = async function sanitizePadId(padId) { return padId; }; -exports.isValidPadId = function (padId) { - return /^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/.test(padId); -}; +exports.isValidPadId = (padId) => /^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/.test(padId); /** * Removes the pad from database and unloads it. @@ -204,6 +203,6 @@ exports.removePad = async (padId) => { }; // removes a pad from the cache -exports.unloadPad = function (padId) { +exports.unloadPad = (padId) => { globalPads.remove(padId); }; From 5ce255c789c80ff373afc076fc868482258beed2 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 081/357] lint: src/node/db/ReadOnlyManager.js --- src/node/db/ReadOnlyManager.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/node/db/ReadOnlyManager.js b/src/node/db/ReadOnlyManager.js index 54f8c592e..5dabe2e06 100644 --- a/src/node/db/ReadOnlyManager.js +++ b/src/node/db/ReadOnlyManager.js @@ -1,3 +1,4 @@ +'use strict'; /** * The ReadOnlyManager manages the database and rendering releated to read only pads */ @@ -27,15 +28,13 @@ const randomString = require('../utils/randomstring'); * checks if the id pattern matches a read-only pad id * @param {String} the pad's id */ -exports.isReadOnlyId = function (id) { - return id.indexOf('r.') === 0; -}; +exports.isReadOnlyId = (id) => id.indexOf('r.') === 0; /** * returns a read only id for a pad * @param {String} padId the id of the pad */ -exports.getReadOnlyId = async function (padId) { +exports.getReadOnlyId = async (padId) => { // check if there is a pad2readonly entry let readOnlyId = await db.get(`pad2readonly:${padId}`); @@ -53,15 +52,13 @@ exports.getReadOnlyId = async function (padId) { * returns the padId for a read only id * @param {String} readOnlyId read only id */ -exports.getPadId = function (readOnlyId) { - return db.get(`readonly2pad:${readOnlyId}`); -}; +exports.getPadId = (readOnlyId) => db.get(`readonly2pad:${readOnlyId}`); /** * returns the padId and readonlyPadId in an object for any id * @param {String} padIdOrReadonlyPadId read only id or real pad id */ -exports.getIds = async function (id) { +exports.getIds = async (id) => { const readonly = (id.indexOf('r.') === 0); // Might be null, if this is an unknown read-only id From 93bc21b5f30ecd98e959001ba92837b7a61193d6 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 082/357] lint: src/node/db/SecurityManager.js --- src/node/db/SecurityManager.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/node/db/SecurityManager.js b/src/node/db/SecurityManager.js index 64091dbdc..24d966d18 100644 --- a/src/node/db/SecurityManager.js +++ b/src/node/db/SecurityManager.js @@ -1,3 +1,4 @@ +'use strict'; /** * Controls the security of pad access */ @@ -19,7 +20,7 @@ */ const authorManager = require('./AuthorManager'); -const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks.js'); +const hooks = require('../../static/js/pluginfw/hooks.js'); const padManager = require('./PadManager'); const sessionManager = require('./SessionManager'); const settings = require('../utils/Settings'); @@ -47,7 +48,7 @@ const DENY = Object.freeze({accessStatus: 'deny'}); * WARNING: Tokens and session IDs MUST be kept secret, otherwise users will be able to impersonate * each other (which might allow them to gain privileges). */ -exports.checkAccess = async function (padID, sessionCookie, token, userSettings) { +exports.checkAccess = async (padID, sessionCookie, token, userSettings) => { if (!padID) { authLogger.debug('access denied: missing padID'); return DENY; From 3681f72afd10a20715147a8c76b0bc9bcde49491 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 083/357] lint: src/node/db/SessionManager.js --- src/node/db/SessionManager.js | 55 ++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/src/node/db/SessionManager.js b/src/node/db/SessionManager.js index 8f1daab52..d46ed9fae 100644 --- a/src/node/db/SessionManager.js +++ b/src/node/db/SessionManager.js @@ -1,5 +1,7 @@ +'use strict'; /** - * The Session Manager provides functions to manage session in the database, it only provides session management for sessions created by the API + * The Session Manager provides functions to manage session in the database, + * it only provides session management for sessions created by the API */ /* @@ -18,7 +20,7 @@ * limitations under the License. */ -const customError = require('../utils/customError'); +const CustomError = require('../utils/customError'); const promises = require('../utils/promises'); const randomString = require('../utils/randomstring'); const db = require('./DB'); @@ -40,7 +42,8 @@ exports.findAuthorID = async (groupID, sessionCookie) => { * Sometimes, RFC 6265-compliant web servers may send back a cookie whose * value is enclosed in double quotes, such as: * - * Set-Cookie: sessionCookie="s.37cf5299fbf981e14121fba3a588c02b,s.2b21517bf50729d8130ab85736a11346"; Version=1; Path=/; Domain=localhost; Discard + * Set-Cookie: sessionCookie="s.37cf5299fbf981e14121fba3a588c02b, + * s.2b21517bf50729d8130ab85736a11346"; Version=1; Path=/; Domain=localhost; Discard * * Where the double quotes at the start and the end of the header value are * just delimiters. This is perfectly legal: Etherpad parsing logic should @@ -78,26 +81,26 @@ exports.findAuthorID = async (groupID, sessionCookie) => { return sessionInfo.authorID; }; -exports.doesSessionExist = async function (sessionID) { +exports.doesSessionExist = async (sessionID) => { // check if the database entry of this session exists const session = await db.get(`session:${sessionID}`); - return (session !== null); + return (session != null); }; /** * Creates a new session between an author and a group */ -exports.createSession = async function (groupID, authorID, validUntil) { +exports.createSession = async (groupID, authorID, validUntil) => { // check if the group exists const groupExists = await groupManager.doesGroupExist(groupID); if (!groupExists) { - throw new customError('groupID does not exist', 'apierror'); + throw new CustomError('groupID does not exist', 'apierror'); } // check if the author exists const authorExists = await authorManager.doesAuthorExist(authorID); if (!authorExists) { - throw new customError('authorID does not exist', 'apierror'); + throw new CustomError('authorID does not exist', 'apierror'); } // try to parse validUntil if it's not a number @@ -107,22 +110,22 @@ exports.createSession = async function (groupID, authorID, validUntil) { // check it's a valid number if (isNaN(validUntil)) { - throw new customError('validUntil is not a number', 'apierror'); + throw new CustomError('validUntil is not a number', 'apierror'); } // ensure this is not a negative number if (validUntil < 0) { - throw new customError('validUntil is a negative number', 'apierror'); + throw new CustomError('validUntil is a negative number', 'apierror'); } // ensure this is not a float value - if (!is_int(validUntil)) { - throw new customError('validUntil is a float value', 'apierror'); + if (!isInt(validUntil)) { + throw new CustomError('validUntil is a float value', 'apierror'); } // check if validUntil is in the future if (validUntil < Math.floor(Date.now() / 1000)) { - throw new customError('validUntil is in the past', 'apierror'); + throw new CustomError('validUntil is in the past', 'apierror'); } // generate sessionID @@ -170,13 +173,13 @@ exports.createSession = async function (groupID, authorID, validUntil) { return {sessionID}; }; -exports.getSessionInfo = async function (sessionID) { +exports.getSessionInfo = async (sessionID) => { // check if the database entry of this session exists const session = await db.get(`session:${sessionID}`); if (session == null) { // session does not exist - throw new customError('sessionID does not exist', 'apierror'); + throw new CustomError('sessionID does not exist', 'apierror'); } // everything is fine, return the sessioninfos @@ -186,11 +189,11 @@ exports.getSessionInfo = async function (sessionID) { /** * Deletes a session */ -exports.deleteSession = async function (sessionID) { +exports.deleteSession = async (sessionID) => { // ensure that the session exists const session = await db.get(`session:${sessionID}`); if (session == null) { - throw new customError('sessionID does not exist', 'apierror'); + throw new CustomError('sessionID does not exist', 'apierror'); } // everything is fine, use the sessioninfos @@ -217,22 +220,22 @@ exports.deleteSession = async function (sessionID) { } }; -exports.listSessionsOfGroup = async function (groupID) { +exports.listSessionsOfGroup = async (groupID) => { // check that the group exists const exists = await groupManager.doesGroupExist(groupID); if (!exists) { - throw new customError('groupID does not exist', 'apierror'); + throw new CustomError('groupID does not exist', 'apierror'); } const sessions = await listSessionsWithDBKey(`group2sessions:${groupID}`); return sessions; }; -exports.listSessionsOfAuthor = async function (authorID) { +exports.listSessionsOfAuthor = async (authorID) => { // check that the author exists const exists = await authorManager.doesAuthorExist(authorID); if (!exists) { - throw new customError('authorID does not exist', 'apierror'); + throw new CustomError('authorID does not exist', 'apierror'); } const sessions = await listSessionsWithDBKey(`author2sessions:${authorID}`); @@ -241,7 +244,7 @@ exports.listSessionsOfAuthor = async function (authorID) { // this function is basically the code listSessionsOfAuthor and listSessionsOfGroup has in common // required to return null rather than an empty object if there are none -async function listSessionsWithDBKey(dbkey) { +const listSessionsWithDBKey = async (dbkey) => { // get the group2sessions entry const sessionObject = await db.get(dbkey); const sessions = sessionObject ? sessionObject.sessionIDs : null; @@ -252,7 +255,7 @@ async function listSessionsWithDBKey(dbkey) { const sessionInfo = await exports.getSessionInfo(sessionID); sessions[sessionID] = sessionInfo; } catch (err) { - if (err == 'apierror: sessionID does not exist') { + if (err === 'apierror: sessionID does not exist') { console.warn(`Found bad session ${sessionID} in ${dbkey}`); sessions[sessionID] = null; } else { @@ -262,9 +265,7 @@ async function listSessionsWithDBKey(dbkey) { } return sessions; -} +}; // checks if a number is an int -function is_int(value) { - return (parseFloat(value) == parseInt(value)) && !isNaN(value); -} +const isInt = (value) => (parseFloat(value) === parseInt(value)) && !isNaN(value); From 8fb6912fc986c815c6515d38037d7f66d13e67c5 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 084/357] lint: src/node/db/SessionStore.js --- src/node/db/SessionStore.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/node/db/SessionStore.js b/src/node/db/SessionStore.js index 91bd75561..2c5d1ca25 100644 --- a/src/node/db/SessionStore.js +++ b/src/node/db/SessionStore.js @@ -1,3 +1,4 @@ +'use strict'; /* * Stores session data in the database * Source; https://github.com/edy-b/SciFlowWriter/blob/develop/available_plugins/ep_sciflowwriter/db/DirtyStore.js @@ -7,9 +8,9 @@ * express-session, which can't actually use promises anyway. */ -const DB = require('ep_etherpad-lite/node/db/DB'); -const Store = require('ep_etherpad-lite/node_modules/express-session').Store; -const log4js = require('ep_etherpad-lite/node_modules/log4js'); +const DB = require('./DB'); +const Store = require('express-session').Store; +const log4js = require('log4js'); const logger = log4js.getLogger('SessionStore'); From ee9bb019b2e62658b2912fe43cc475c401dfdc91 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 085/357] lint: src/node/easysync_tests.js --- src/node/easysync_tests.js | 315 +++++++++++++++++++++---------------- 1 file changed, 180 insertions(+), 135 deletions(-) diff --git a/src/node/easysync_tests.js b/src/node/easysync_tests.js index c8a5c9853..78d9f61e8 100644 --- a/src/node/easysync_tests.js +++ b/src/node/easysync_tests.js @@ -1,6 +1,8 @@ +'use strict'; /** - * I found this tests in the old Etherpad and used it to test if the Changeset library can be run on node.js. - * It has no use for ep-lite, but I thought I keep it cause it may help someone to understand the Changeset library + * I found this tests in the old Etherpad and used it to test if the Changeset library can be run on + * node.js. It has no use for ep-lite, but I thought I keep it cause it may help someone to + * understand the Changeset library * https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2_tests.js */ @@ -21,52 +23,47 @@ */ -const Changeset = require('ep_etherpad-lite/static/js/Changeset'); -const AttributePool = require('ep_etherpad-lite/static/js/AttributePool'); +const Changeset = require('../static/js/Changeset'); +const AttributePool = require('../static/js/AttributePool'); function random() { - this.nextInt = function (maxValue) { - return Math.floor(Math.random() * maxValue); - }; - - this.nextDouble = function (maxValue) { - return Math.random(); - }; + this.nextInt = (maxValue) => Math.floor(Math.random() * maxValue); + this.nextDouble = (maxValue) => Math.random(); } -function runTests() { - function print(str) { +const runTests = () => { + const print = (str) => { console.log(str); - } + }; - function assert(code, optMsg) { - if (!eval(code)) throw new Error(`FALSE: ${optMsg || code}`); - } + const assert = (code, optMsg) => { + if (!eval(code)) throw new Error(`FALSE: ${optMsg || code}`); /* eslint-disable-line no-eval */ + }; - function literal(v) { + const literal = (v) => { if ((typeof v) === 'string') { return `"${v.replace(/[\\\"]/g, '\\$1').replace(/\n/g, '\\n')}"`; } else { return JSON.stringify(v); } - } + }; - function assertEqualArrays(a, b) { + const assertEqualArrays = (a, b) => { assert(`JSON.stringify(${literal(a)}) == JSON.stringify(${literal(b)})`); - } + }; - function assertEqualStrings(a, b) { + const assertEqualStrings = (a, b) => { assert(`${literal(a)} == ${literal(b)}`); - } + }; - function throughIterator(opsStr) { + const throughIterator = (opsStr) => { const iter = Changeset.opIterator(opsStr); const assem = Changeset.opAssembler(); while (iter.hasNext()) { assem.append(iter.next()); } return assem.toString(); - } + }; - function throughSmartAssembler(opsStr) { + const throughSmartAssembler = (opsStr) => { const iter = Changeset.opIterator(opsStr); const assem = Changeset.smartOpAssembler(); while (iter.hasNext()) { @@ -74,50 +71,50 @@ function runTests() { } assem.endDocument(); return assem.toString(); - } + }; - (function () { + (() => { print('> throughIterator'); const x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1'; assert(`throughIterator(${literal(x)}) == ${literal(x)}`); })(); - (function () { + (() => { print('> throughSmartAssembler'); const x = '-c*3*4+6|3=az*asdf0*1*2*3+1=1-1+1*0+1=1-1+1|c=c-1'; assert(`throughSmartAssembler(${literal(x)}) == ${literal(x)}`); })(); - function applyMutations(mu, arrayOfArrays) { + const applyMutations = (mu, arrayOfArrays) => { arrayOfArrays.forEach((a) => { const result = mu[a[0]].apply(mu, a.slice(1)); - if (a[0] == 'remove' && a[3]) { + if (a[0] === 'remove' && a[3]) { assertEqualStrings(a[3], result); } }); - } + }; - function mutationsToChangeset(oldLen, arrayOfArrays) { + const mutationsToChangeset = (oldLen, arrayOfArrays) => { const assem = Changeset.smartOpAssembler(); const op = Changeset.newOp(); const bank = Changeset.stringAssembler(); let oldPos = 0; let newLen = 0; arrayOfArrays.forEach((a) => { - if (a[0] == 'skip') { + if (a[0] === 'skip') { op.opcode = '='; op.chars = a[1]; op.lines = (a[2] || 0); assem.append(op); oldPos += op.chars; newLen += op.chars; - } else if (a[0] == 'remove') { + } else if (a[0] === 'remove') { op.opcode = '-'; op.chars = a[1]; op.lines = (a[2] || 0); assem.append(op); oldPos += op.chars; - } else if (a[0] == 'insert') { + } else if (a[0] === 'insert') { op.opcode = '+'; bank.append(a[1]); op.chars = a[1].length; @@ -129,9 +126,9 @@ function runTests() { newLen += oldLen - oldPos; assem.endDocument(); return Changeset.pack(oldLen, newLen, assem.toString(), bank.toString()); - } + }; - function runMutationTest(testId, origLines, muts, correct) { + const runMutationTest = (testId, origLines, muts, correct) => { print(`> runMutationTest#${testId}`); let lines = origLines.slice(); const mu = Changeset.textLinesMutator(lines); @@ -149,7 +146,7 @@ function runTests() { // print(literal(cs)); const outText = Changeset.applyToText(cs, inText); assertEqualStrings(correctText, outText); - } + }; runMutationTest(1, ['apple\n', 'banana\n', 'cabbage\n', 'duffle\n', 'eggplant\n'], [ ['remove', 1, 0, 'a'], @@ -220,7 +217,7 @@ function runTests() { ['skip', 1, 1, true], ], ['banana\n', 'cabbage\n', 'duffle\n']); - function poolOrArray(attribs) { + const poolOrArray = (attribs) => { if (attribs.getAttrib) { return attribs; // it's already an attrib pool } else { @@ -231,23 +228,25 @@ function runTests() { }); return p; } - } + }; - function runApplyToAttributionTest(testId, attribs, cs, inAttr, outCorrect) { + const runApplyToAttributionTest = (testId, attribs, cs, inAttr, outCorrect) => { print(`> applyToAttribution#${testId}`); const p = poolOrArray(attribs); const result = Changeset.applyToAttribution( Changeset.checkRep(cs), inAttr, p); assertEqualStrings(outCorrect, result); - } + }; // turn cactus\n into actusabcd\n - runApplyToAttributionTest(1, ['bold,', 'bold,true'], 'Z:7>3-1*0=1*1=1=3+4$abcd', '+1*1+1|1+5', '+1*1+1|1+8'); + runApplyToAttributionTest(1, + ['bold,', 'bold,true'], 'Z:7>3-1*0=1*1=1=3+4$abcd', '+1*1+1|1+5', '+1*1+1|1+8'); // turn "david\ngreenspan\n" into "david\ngreen\n" - runApplyToAttributionTest(2, ['bold,', 'bold,true'], 'Z:g<4*1|1=6*1=5-4$', '|2+g', '*1|1+6*1+5|1+1'); + runApplyToAttributionTest(2, + ['bold,', 'bold,true'], 'Z:g<4*1|1=6*1=5-4$', '|2+g', '*1|1+6*1+5|1+1'); - (function () { + (() => { print('> mutatorHasMore'); const lines = ['1\n', '2\n', '3\n', '4\n']; let mu; @@ -288,7 +287,7 @@ function runTests() { assert(`${mu.hasMore()} == false`); })(); - function runMutateAttributionTest(testId, attribs, cs, alines, outCorrect) { + const runMutateAttributionTest = (testId, attribs, cs, alines, outCorrect) => { print(`> runMutateAttributionTest#${testId}`); const p = poolOrArray(attribs); const alines2 = Array.prototype.slice.call(alines); @@ -298,30 +297,35 @@ function runTests() { print(`> runMutateAttributionTest#${testId}.applyToAttribution`); - function removeQuestionMarks(a) { - return a.replace(/\?/g, ''); - } + const removeQuestionMarks = (a) => a.replace(/\?/g, ''); const inMerged = Changeset.joinAttributionLines(alines.map(removeQuestionMarks)); const correctMerged = Changeset.joinAttributionLines(outCorrect.map(removeQuestionMarks)); const mergedResult = Changeset.applyToAttribution(cs, inMerged, p); assertEqualStrings(correctMerged, mergedResult); - } + }; // turn 123\n 456\n 789\n into 123\n 456\n 789\n - runMutateAttributionTest(1, ['bold,true'], 'Z:c>0|1=4=1*0=1$', ['|1+4', '|1+4', '|1+4'], ['|1+4', '+1*0+1|1+2', '|1+4']); + runMutateAttributionTest(1, + ['bold,true'], 'Z:c>0|1=4=1*0=1$', ['|1+4', '|1+4', '|1+4'], ['|1+4', '+1*0+1|1+2', '|1+4']); // make a document bold - runMutateAttributionTest(2, ['bold,true'], 'Z:c>0*0|3=c$', ['|1+4', '|1+4', '|1+4'], ['*0|1+4', '*0|1+4', '*0|1+4']); + runMutateAttributionTest(2, + ['bold,true'], 'Z:c>0*0|3=c$', ['|1+4', '|1+4', '|1+4'], ['*0|1+4', '*0|1+4', '*0|1+4']); // clear bold on document - runMutateAttributionTest(3, ['bold,', 'bold,true'], 'Z:c>0*0|3=c$', ['*1+1+1*1+1|1+1', '+1*1+1|1+2', '*1+1+1*1+1|1+1'], ['|1+4', '|1+4', '|1+4']); + runMutateAttributionTest(3, + ['bold,', 'bold,true'], 'Z:c>0*0|3=c$', + ['*1+1+1*1+1|1+1', '+1*1+1|1+2', '*1+1+1*1+1|1+1'], ['|1+4', '|1+4', '|1+4']); // add a character on line 3 of a document with 5 blank lines, and make sure // the optimization that skips purely-kept lines is working; if any attribution string // with a '?' is parsed it will cause an error. - runMutateAttributionTest(4, ['foo,bar', 'line,1', 'line,2', 'line,3', 'line,4', 'line,5'], 'Z:5>1|2=2+1$x', ['?*1|1+1', '?*2|1+1', '*3|1+1', '?*4|1+1', '?*5|1+1'], ['?*1|1+1', '?*2|1+1', '+1*3|1+1', '?*4|1+1', '?*5|1+1']); + runMutateAttributionTest(4, + ['foo,bar', 'line,1', 'line,2', 'line,3', 'line,4', 'line,5'], + 'Z:5>1|2=2+1$x', ['?*1|1+1', '?*2|1+1', '*3|1+1', '?*4|1+1', '?*5|1+1'], + ['?*1|1+1', '?*2|1+1', '+1*3|1+1', '?*4|1+1', '?*5|1+1']); - const testPoolWithChars = (function () { + const testPoolWithChars = (() => { const p = new AttributePool(); p.putAttrib(['char', 'newline']); for (let i = 1; i < 36; i++) { @@ -332,39 +336,66 @@ function runTests() { })(); // based on runMutationTest#1 - runMutateAttributionTest(5, testPoolWithChars, 'Z:11>7-2*t+1*u+1|2=b|2+a=2*b+1*o+1*t+1*0|1+1*b+1*u+1=3|1-3-6$' + 'tucream\npie\nbot\nbu', ['*a+1*p+2*l+1*e+1*0|1+1', '*b+1*a+1*n+1*a+1*n+1*a+1*0|1+1', '*c+1*a+1*b+2*a+1*g+1*e+1*0|1+1', '*d+1*u+1*f+2*l+1*e+1*0|1+1', '*e+1*g+2*p+1*l+1*a+1*n+1*t+1*0|1+1'], ['*t+1*u+1*p+1*l+1*e+1*0|1+1', '*b+1*a+1*n+1*a+1*n+1*a+1*0|1+1', '|1+6', '|1+4', '*c+1*a+1*b+1*o+1*t+1*0|1+1', '*b+1*u+1*b+2*a+1*0|1+1', '*e+1*g+2*p+1*l+1*a+1*n+1*t+1*0|1+1']); + runMutateAttributionTest(5, testPoolWithChars, + 'Z:11>7-2*t+1*u+1|2=b|2+a=2*b+1*o+1*t+1*0|1+1*b+1*u+1=3|1-3-6$' + 'tucream\npie\nbot\nbu', ['*a+1*p+2*l+1*e+1*0|1+1', '*b+1*a+1*n+1*a+1*n+1*a+1*0|1+1', '*c+1*a+1*b+2*a+1*g+1*e+1*0|1+1', '*d+1*u+1*f+2*l+1*e+1*0|1+1', '*e+1*g+2*p+1*l+1*a+1*n+1*t+1*0|1+1'], ['*t+1*u+1*p+1*l+1*e+1*0|1+1', '*b+1*a+1*n+1*a+1*n+1*a+1*0|1+1', '|1+6', '|1+4', '*c+1*a+1*b+1*o+1*t+1*0|1+1', '*b+1*u+1*b+2*a+1*0|1+1', '*e+1*g+2*p+1*l+1*a+1*n+1*t+1*0|1+1']); // based on runMutationTest#3 - runMutateAttributionTest(6, testPoolWithChars, 'Z:117=1|4+7$\n2\n3\n4\n', ['*1+1*5|1+2'], ['*1+1|1+1', '|1+2', '|1+2', '|1+2', '*5|1+2']); + runMutateAttributionTest(7, testPoolWithChars, 'Z:3>7=1|4+7$\n2\n3\n4\n', + ['*1+1*5|1+2'], ['*1+1|1+1', '|1+2', '|1+2', '|1+2', '*5|1+2']); // based on runMutationTest#5 - runMutateAttributionTest(8, testPoolWithChars, 'Z:a<7=1|4-7$', ['*1|1+2', '*2|1+2', '*3|1+2', '*4|1+2', '*5|1+2'], ['*1+1*5|1+2']); + runMutateAttributionTest(8, testPoolWithChars, 'Z:a<7=1|4-7$', + ['*1|1+2', '*2|1+2', '*3|1+2', '*4|1+2', '*5|1+2'], ['*1+1*5|1+2']); // based on runMutationTest#6 - runMutateAttributionTest(9, testPoolWithChars, 'Z:k<7*0+1*10|2=8|2-8$0', ['*1+1*2+1*3+1|1+1', '*a+1*b+1*c+1|1+1', '*d+1*e+1*f+1|1+1', '*g+1*h+1*i+1|1+1', '?*x+1*y+1*z+1|1+1'], ['*0+1|1+4', '|1+4', '?*x+1*y+1*z+1|1+1']); + runMutateAttributionTest(9, testPoolWithChars, 'Z:k<7*0+1*10|2=8|2-8$0', + [ + '*1+1*2+1*3+1|1+1', + '*a+1*b+1*c+1|1+1', + '*d+1*e+1*f+1|1+1', + '*g+1*h+1*i+1|1+1', + '?*x+1*y+1*z+1|1+1', + ], + ['*0+1|1+4', '|1+4', '?*x+1*y+1*z+1|1+1']); - runMutateAttributionTest(10, testPoolWithChars, 'Z:6>4=1+1=1+1|1=1+1=1*0+1$abcd', ['|1+3', '|1+3'], ['|1+5', '+2*0+1|1+2']); + runMutateAttributionTest(10, testPoolWithChars, 'Z:6>4=1+1=1+1|1=1+1=1*0+1$abcd', + ['|1+3', '|1+3'], ['|1+5', '+2*0+1|1+2']); - runMutateAttributionTest(11, testPoolWithChars, 'Z:s>1|1=4=6|1+1$\n', ['*0|1+4', '*0|1+8', '*0+5|1+1', '*0|1+1', '*0|1+5', '*0|1+1', '*0|1+1', '*0|1+1', '|1+1'], ['*0|1+4', '*0+6|1+1', '*0|1+2', '*0+5|1+1', '*0|1+1', '*0|1+5', '*0|1+1', '*0|1+1', '*0|1+1', '|1+1']); + runMutateAttributionTest(11, testPoolWithChars, 'Z:s>1|1=4=6|1+1$\n', + ['*0|1+4', '*0|1+8', '*0+5|1+1', '*0|1+1', '*0|1+5', '*0|1+1', '*0|1+1', '*0|1+1', '|1+1'], + [ + '*0|1+4', + '*0+6|1+1', + '*0|1+2', + '*0+5|1+1', + '*0|1+1', + '*0|1+5', + '*0|1+1', + '*0|1+1', + '*0|1+1', + '|1+1', + ]); - function randomInlineString(len, rand) { + const randomInlineString = (len, rand) => { const assem = Changeset.stringAssembler(); for (let i = 0; i < len; i++) { assem.append(String.fromCharCode(rand.nextInt(26) + 97)); } return assem.toString(); - } + }; - function randomMultiline(approxMaxLines, approxMaxCols, rand) { + const randomMultiline = (approxMaxLines, approxMaxCols, rand) => { const numParts = rand.nextInt(approxMaxLines * 2) + 1; const txt = Changeset.stringAssembler(); txt.append(rand.nextInt(2) ? '\n' : ''); for (let i = 0; i < numParts; i++) { - if ((i % 2) == 0) { + if ((i % 2) === 0) { if (rand.nextInt(10)) { txt.append(randomInlineString(rand.nextInt(approxMaxCols) + 1, rand)); } else { @@ -375,9 +406,9 @@ function runTests() { } } return txt.toString(); - } + }; - function randomStringOperation(numCharsLeft, rand) { + const randomStringOperation = (numCharsLeft, rand) => { let result; switch (rand.nextInt(9)) { case 0: @@ -476,26 +507,26 @@ function runTests() { result.skip = Math.min(result.skip, maxOrig); } return result; - } + }; - function randomTwoPropAttribs(opcode, rand) { + const randomTwoPropAttribs = (opcode, rand) => { // assumes attrib pool like ['apple,','apple,true','banana,','banana,true'] - if (opcode == '-' || rand.nextInt(3)) { + if (opcode === '-' || rand.nextInt(3)) { return ''; } else if (rand.nextInt(3)) { - if (opcode == '+' || rand.nextInt(2)) { + if (opcode === '+' || rand.nextInt(2)) { return `*${Changeset.numToString(rand.nextInt(2) * 2 + 1)}`; } else { return `*${Changeset.numToString(rand.nextInt(2) * 2)}`; } - } else if (opcode == '+' || rand.nextInt(4) == 0) { + } else if (opcode === '+' || rand.nextInt(4) === 0) { return '*1*3'; } else { return ['*0*2', '*0*3', '*1*2'][rand.nextInt(3)]; } - } + }; - function randomTestChangeset(origText, rand, withAttribs) { + const randomTestChangeset = (origText, rand, withAttribs) => { const charBank = Changeset.stringAssembler(); let textLeft = origText; // always keep final newline const outTextAssem = Changeset.stringAssembler(); @@ -504,13 +535,13 @@ function runTests() { const nextOp = Changeset.newOp(); - function appendMultilineOp(opcode, txt) { + const appendMultilineOp = (opcode, txt) => { nextOp.opcode = opcode; if (withAttribs) { nextOp.attribs = randomTwoPropAttribs(opcode, rand); } txt.replace(/\n|[^\n]+/g, (t) => { - if (t == '\n') { + if (t === '\n') { nextOp.chars = 1; nextOp.lines = 1; opAssem.append(nextOp); @@ -521,26 +552,26 @@ function runTests() { } return ''; }); - } + }; - function doOp() { + const doOp = () => { const o = randomStringOperation(textLeft.length, rand); if (o.insert) { - var txt = o.insert; + const txt = o.insert; charBank.append(txt); outTextAssem.append(txt); appendMultilineOp('+', txt); } else if (o.skip) { - var txt = textLeft.substring(0, o.skip); + const txt = textLeft.substring(0, o.skip); textLeft = textLeft.substring(o.skip); outTextAssem.append(txt); appendMultilineOp('=', txt); } else if (o.remove) { - var txt = textLeft.substring(0, o.remove); + const txt = textLeft.substring(0, o.remove); textLeft = textLeft.substring(o.remove); appendMultilineOp('-', txt); } - } + }; while (textLeft.length > 1) doOp(); for (let i = 0; i < 5; i++) doOp(); // do some more (only insertions will happen) @@ -549,9 +580,9 @@ function runTests() { const cs = Changeset.pack(oldLen, outText.length, opAssem.toString(), charBank.toString()); Changeset.checkRep(cs); return [cs, outText]; - } + }; - function testCompose(randomSeed) { + const testCompose = (randomSeed) => { const rand = new random(); print(`> testCompose#${randomSeed}`); @@ -583,9 +614,9 @@ function runTests() { assertEqualStrings(text2, Changeset.applyToText(change12, startText)); assertEqualStrings(text3, Changeset.applyToText(change23, text1)); assertEqualStrings(text3, Changeset.applyToText(change123, startText)); - } + }; - for (var i = 0; i < 30; i++) testCompose(i); + for (let i = 0; i < 30; i++) testCompose(i); (function simpleComposeAttributesTest() { print('> simpleComposeAttributesTest'); @@ -607,12 +638,12 @@ function runTests() { p.putAttrib(['y', 'abc']); p.putAttrib(['y', 'def']); - function testFollow(a, b, afb, bfa, merge) { + const testFollow = (a, b, afb, bfa, merge) => { assertEqualStrings(afb, Changeset.followAttributes(a, b, p)); assertEqualStrings(bfa, Changeset.followAttributes(b, a, p)); assertEqualStrings(merge, Changeset.composeAttributes(a, afb, true, p)); assertEqualStrings(merge, Changeset.composeAttributes(b, bfa, true, p)); - } + }; testFollow('', '', '', '', ''); testFollow('*0', '', '', '*0', '*0'); @@ -624,7 +655,7 @@ function runTests() { testFollow('*0*4', '*2', '', '*0*4', '*0*4'); })(); - function testFollow(randomSeed) { + const testFollow = (randomSeed) => { const rand = new random(); print(`> testFollow#${randomSeed}`); @@ -642,37 +673,37 @@ function runTests() { const merge2 = Changeset.checkRep(Changeset.compose(cs2, bfa)); assertEqualStrings(merge1, merge2); - } + }; - for (var i = 0; i < 30; i++) testFollow(i); + for (let i = 0; i < 30; i++) testFollow(i); - function testSplitJoinAttributionLines(randomSeed) { + const testSplitJoinAttributionLines = (randomSeed) => { const rand = new random(); print(`> testSplitJoinAttributionLines#${randomSeed}`); const doc = `${randomMultiline(10, 20, rand)}\n`; - function stringToOps(str) { + const stringToOps = (str) => { const assem = Changeset.mergingOpAssembler(); const o = Changeset.newOp('+'); o.chars = 1; for (let i = 0; i < str.length; i++) { const c = str.charAt(i); - o.lines = (c == '\n' ? 1 : 0); - o.attribs = (c == 'a' || c == 'b' ? `*${c}` : ''); + o.lines = (c === '\n' ? 1 : 0); + o.attribs = (c === 'a' || c === 'b' ? `*${c}` : ''); assem.append(o); } return assem.toString(); - } + }; const theJoined = stringToOps(doc); const theSplit = doc.match(/[^\n]*\n/g).map(stringToOps); assertEqualArrays(theSplit, Changeset.splitAttributionLines(theJoined, doc)); assertEqualStrings(theJoined, Changeset.joinAttributionLines(theSplit)); - } + }; - for (var i = 0; i < 10; i++) testSplitJoinAttributionLines(i); + for (let i = 0; i < 10; i++) testSplitJoinAttributionLines(i); (function testMoveOpsToNewPool() { print('> testMoveOpsToNewPool'); @@ -685,8 +716,10 @@ function runTests() { pool2.putAttrib(['foo', 'bar']); - assertEqualStrings(Changeset.moveOpsToNewPool('Z:1>2*1+1*0+1$ab', pool1, pool2), 'Z:1>2*0+1*1+1$ab'); - assertEqualStrings(Changeset.moveOpsToNewPool('*1+1*0+1', pool1, pool2), '*0+1*1+1'); + assertEqualStrings( + Changeset.moveOpsToNewPool('Z:1>2*1+1*0+1$ab', pool1, pool2), 'Z:1>2*0+1*1+1$ab'); + assertEqualStrings( + Changeset.moveOpsToNewPool('*1+1*0+1', pool1, pool2), '*0+1*1+1'); })(); @@ -709,14 +742,15 @@ function runTests() { assertEqualArrays(correctSplices, Changeset.toSplices(cs)); })(); - function testCharacterRangeFollow(testId, cs, oldRange, insertionsAfter, correctNewRange) { + const testCharacterRangeFollow = (testId, cs, oldRange, insertionsAfter, correctNewRange) => { print(`> testCharacterRangeFollow#${testId}`); + cs = Changeset.checkRep(cs); + assertEqualArrays(correctNewRange, Changeset.characterRangeFollow( + cs, oldRange[0], oldRange[1], insertionsAfter)); + }; - var cs = Changeset.checkRep(cs); - assertEqualArrays(correctNewRange, Changeset.characterRangeFollow(cs, oldRange[0], oldRange[1], insertionsAfter)); - } - - testCharacterRangeFollow(1, 'Z:z>9*0=1=4-3+9=1|1-4-4+1*0+a$123456789abcdefghijk', [7, 10], false, [14, 15]); + testCharacterRangeFollow(1, 'Z:z>9*0=1=4-3+9=1|1-4-4+1*0+a$123456789abcdefghijk', + [7, 10], false, [14, 15]); testCharacterRangeFollow(2, 'Z:bc<6|x=b4|2-6$', [400, 407], false, [400, 401]); testCharacterRangeFollow(3, 'Z:4>0-3+3$abc', [0, 3], false, [3, 3]); testCharacterRangeFollow(4, 'Z:4>0-3+3$abc', [0, 3], true, [0, 0]); @@ -735,23 +769,31 @@ function runTests() { p.putAttrib(['name', 'david']); p.putAttrib(['color', 'green']); - assertEqualStrings('david', Changeset.opAttributeValue(Changeset.stringOp('*0*1+1'), 'name', p)); - assertEqualStrings('david', Changeset.opAttributeValue(Changeset.stringOp('*0+1'), 'name', p)); - assertEqualStrings('', Changeset.opAttributeValue(Changeset.stringOp('*1+1'), 'name', p)); - assertEqualStrings('', Changeset.opAttributeValue(Changeset.stringOp('+1'), 'name', p)); - assertEqualStrings('green', Changeset.opAttributeValue(Changeset.stringOp('*0*1+1'), 'color', p)); - assertEqualStrings('green', Changeset.opAttributeValue(Changeset.stringOp('*1+1'), 'color', p)); - assertEqualStrings('', Changeset.opAttributeValue(Changeset.stringOp('*0+1'), 'color', p)); - assertEqualStrings('', Changeset.opAttributeValue(Changeset.stringOp('+1'), 'color', p)); + assertEqualStrings('david', + Changeset.opAttributeValue(Changeset.stringOp('*0*1+1'), 'name', p)); + assertEqualStrings('david', + Changeset.opAttributeValue(Changeset.stringOp('*0+1'), 'name', p)); + assertEqualStrings('', + Changeset.opAttributeValue(Changeset.stringOp('*1+1'), 'name', p)); + assertEqualStrings('', + Changeset.opAttributeValue(Changeset.stringOp('+1'), 'name', p)); + assertEqualStrings('green', + Changeset.opAttributeValue(Changeset.stringOp('*0*1+1'), 'color', p)); + assertEqualStrings('green', + Changeset.opAttributeValue(Changeset.stringOp('*1+1'), 'color', p)); + assertEqualStrings('', + Changeset.opAttributeValue(Changeset.stringOp('*0+1'), 'color', p)); + assertEqualStrings('', + Changeset.opAttributeValue(Changeset.stringOp('+1'), 'color', p)); })(); - function testAppendATextToAssembler(testId, atext, correctOps) { + const testAppendATextToAssembler = (testId, atext, correctOps) => { print(`> testAppendATextToAssembler#${testId}`); const assem = Changeset.smartOpAssembler(); Changeset.appendATextToAssembler(atext, assem); assertEqualStrings(correctOps, assem.toString()); - } + }; testAppendATextToAssembler(1, { text: '\n', @@ -786,13 +828,13 @@ function runTests() { attribs: '|2+2*x|2+5', }, '|2+2*x|1+1*x+3'); - function testMakeAttribsString(testId, pool, opcode, attribs, correctString) { + const testMakeAttribsString = (testId, pool, opcode, attribs, correctString) => { print(`> testMakeAttribsString#${testId}`); const p = poolOrArray(pool); const str = Changeset.makeAttribsString(opcode, attribs, p); assertEqualStrings(correctString, str); - } + }; testMakeAttribsString(1, ['bold,'], '+', [ ['bold', ''], @@ -809,12 +851,12 @@ function runTests() { ['abc', 'def'], ], '*0*1'); - function testSubattribution(testId, astr, start, end, correctOutput) { + const testSubattribution = (testId, astr, start, end, correctOutput) => { print(`> testSubattribution#${testId}`); const str = Changeset.subattribution(astr, start, end); assertEqualStrings(correctOutput, str); - } + }; testSubattribution(1, '+1', 0, 0, ''); testSubattribution(2, '+1', 0, 1, '+1'); @@ -859,39 +901,42 @@ function runTests() { testSubattribution(41, '*0+2+1*1|1+3', 2, 6, '+1*1|1+3'); testSubattribution(42, '*0+2+1*1+3', 2, 6, '+1*1+3'); - function testFilterAttribNumbers(testId, cs, filter, correctOutput) { + const testFilterAttribNumbers = (testId, cs, filter, correctOutput) => { print(`> testFilterAttribNumbers#${testId}`); const str = Changeset.filterAttribNumbers(cs, filter); assertEqualStrings(correctOutput, str); - } + }; - testFilterAttribNumbers(1, '*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6', (n) => (n % 2) == 0, '*0+1+2+3+4*2+5*0*2*c+6'); - testFilterAttribNumbers(2, '*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6', (n) => (n % 2) == 1, '*1+1+2+3*1+4+5*1*b+6'); + testFilterAttribNumbers(1, '*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6', + (n) => (n % 2) === 0, '*0+1+2+3+4*2+5*0*2*c+6'); + testFilterAttribNumbers(2, '*0*1+1+2+3*1+4*2+5*0*2*1*b*c+6', + (n) => (n % 2) === 1, '*1+1+2+3*1+4+5*1*b+6'); - function testInverse(testId, cs, lines, alines, pool, correctOutput) { + const testInverse = (testId, cs, lines, alines, pool, correctOutput) => { print(`> testInverse#${testId}`); pool = poolOrArray(pool); const str = Changeset.inverse(Changeset.checkRep(cs), lines, alines, pool); assertEqualStrings(correctOutput, str); - } + }; // take "FFFFTTTTT" and apply "-FT--FFTT", the inverse of which is "--F--TT--" - testInverse(1, 'Z:9>0=1*0=1*1=1=2*0=2*1|1=2$', null, ['+4*1+5'], ['bold,', 'bold,true'], 'Z:9>0=2*0=1=2*1=2$'); + testInverse(1, 'Z:9>0=1*0=1*1=1=2*0=2*1|1=2$', null, + ['+4*1+5'], ['bold,', 'bold,true'], 'Z:9>0=2*0=1=2*1=2$'); - function testMutateTextLines(testId, cs, lines, correctLines) { + const testMutateTextLines = (testId, cs, lines, correctLines) => { print(`> testMutateTextLines#${testId}`); const a = lines.slice(); Changeset.mutateTextLines(cs, a); assertEqualArrays(correctLines, a); - } + }; testMutateTextLines(1, 'Z:4<1|1-2-1|1+1+1$\nc', ['a\n', 'b\n'], ['\n', 'c\n']); testMutateTextLines(2, 'Z:4>0|1-2-1|2+3$\nc\n', ['a\n', 'b\n'], ['\n', 'c\n', '\n']); - function testInverseRandom(randomSeed) { + const testInverseRandom = (randomSeed) => { const rand = new random(); print(`> testInverseRandom#${randomSeed}`); @@ -928,9 +973,9 @@ function runTests() { // print(lines.map(function(s) { return '3: '+s.slice(0,-1); }).join('\n')); assertEqualArrays(origLines, lines); assertEqualArrays(origALines, alines); - } + }; - for (var i = 0; i < 30; i++) testInverseRandom(i); -} + for (let i = 0; i < 30; i++) testInverseRandom(i); +}; runTests(); From a7d9a703cdce8488c3ddf446a3bbf44b6a053469 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 086/357] lint: src/node/handler/APIHandler.js --- src/node/handler/APIHandler.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/node/handler/APIHandler.js b/src/node/handler/APIHandler.js index 0708cc009..6bc6e5378 100644 --- a/src/node/handler/APIHandler.js +++ b/src/node/handler/APIHandler.js @@ -1,3 +1,4 @@ +'use strict'; /** * The API Handler handles all API http requests */ @@ -37,7 +38,8 @@ try { apikey = fs.readFileSync(apikeyFilename, 'utf8'); apiHandlerLogger.info(`Api key file read from: "${apikeyFilename}"`); } catch (e) { - apiHandlerLogger.info(`Api key file "${apikeyFilename}" not found. Creating with random contents.`); + apiHandlerLogger.info( + `Api key file "${apikeyFilename}" not found. Creating with random contents.`); apikey = randomString(32); fs.writeFileSync(apikeyFilename, apikey, 'utf8'); } From 2fe5d1f8739a4847bb9a0740881d229d1f7e0bde Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 087/357] lint: src/node/handler/ExportHandler.js --- src/node/handler/ExportHandler.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/node/handler/ExportHandler.js b/src/node/handler/ExportHandler.js index 0a92633f7..fbb9e57da 100644 --- a/src/node/handler/ExportHandler.js +++ b/src/node/handler/ExportHandler.js @@ -1,3 +1,4 @@ +'use strict'; /** * Handles the export requests */ @@ -25,7 +26,7 @@ const exportEtherpad = require('../utils/ExportEtherpad'); const fs = require('fs'); const settings = require('../utils/Settings'); const os = require('os'); -const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); +const hooks = require('../../static/js/pluginfw/hooks'); const TidyHtml = require('../utils/TidyHtml'); const util = require('util'); @@ -49,7 +50,7 @@ const tempDirectory = os.tmpdir(); /** * do a requested export */ -async function doExport(req, res, padId, readOnlyId, type) { +const doExport = async (req, res, padId, readOnlyId, type) => { // avoid naming the read-only file as the original pad's id let fileName = readOnlyId ? readOnlyId : padId; @@ -104,7 +105,6 @@ async function doExport(req, res, padId, readOnlyId, type) { const result = await hooks.aCallAll('exportConvert', {srcFile, destFile, req, res}); if (result.length > 0) { // console.log("export handled by plugin", destFile); - handledByPlugin = true; } else { // @TODO no Promise interface for convertors (yet) await new Promise((resolve, reject) => { @@ -115,7 +115,6 @@ async function doExport(req, res, padId, readOnlyId, type) { } // send the file - const sendFile = util.promisify(res.sendFile); await res.sendFile(destFile, null); // clean up temporary files @@ -128,9 +127,9 @@ async function doExport(req, res, padId, readOnlyId, type) { await fsp_unlink(destFile); } -} +}; -exports.doExport = function (req, res, padId, readOnlyId, type) { +exports.doExport = (req, res, padId, readOnlyId, type) => { doExport(req, res, padId, readOnlyId, type).catch((err) => { if (err !== 'stop') { throw err; From 841d45cbe1157e3c5a4fceb9903a5f98c29c7dbd Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 088/357] lint: src/node/handler/ImportHandler.js --- src/node/handler/ImportHandler.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index c25623d76..d2f98f47d 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -1,3 +1,4 @@ +'use strict'; /** * Handles the import requests */ @@ -30,7 +31,7 @@ const os = require('os'); const importHtml = require('../utils/ImportHtml'); const importEtherpad = require('../utils/ImportEtherpad'); const log4js = require('log4js'); -const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks.js'); +const hooks = require('../../static/js/pluginfw/hooks.js'); const util = require('util'); const fsp_exists = util.promisify(fs.exists); @@ -42,7 +43,7 @@ let convertor = null; let exportExtension = 'htm'; // load abiword only if it is enabled and if soffice is disabled -if (settings.abiword != null && settings.soffice === null) { +if (settings.abiword != null && settings.soffice == null) { convertor = require('../utils/Abiword'); } @@ -57,7 +58,7 @@ const tmpDirectory = os.tmpdir(); /** * do a requested import */ -async function doImport(req, res, padId) { +const doImport = async (req, res, padId) => { const apiLogger = log4js.getLogger('ImportHandler'); // pipe to a file @@ -112,7 +113,8 @@ async function doImport(req, res, padId) { // ensure this is a file ending we know, else we change the file ending to .txt // this allows us to accept source code files like .c or .java const fileEnding = path.extname(srcFile).toLowerCase(); - const knownFileEndings = ['.txt', '.doc', '.docx', '.pdf', '.odt', '.html', '.htm', '.etherpad', '.rtf']; + const knownFileEndings = + ['.txt', '.doc', '.docx', '.pdf', '.odt', '.html', '.htm', '.etherpad', '.rtf']; const fileEndingUnknown = (knownFileEndings.indexOf(fileEnding) < 0); if (fileEndingUnknown) { @@ -146,7 +148,7 @@ async function doImport(req, res, padId) { const headCount = _pad.head; if (headCount >= 10) { - apiLogger.warn("Direct database Import attempt of a pad that already has content, we won't be doing this"); + apiLogger.warn('Aborting direct database import attempt of a pad that already has content'); throw 'padHasData'; } @@ -251,9 +253,9 @@ async function doImport(req, res, padId) { if (await fsp_exists(destFile)) { fsp_unlink(destFile); } -} +}; -exports.doImport = function (req, res, padId) { +exports.doImport = (req, res, padId) => { /** * NB: abuse the 'req' object by storing an additional * 'directDatabaseAccess' property on it so that it can @@ -266,7 +268,10 @@ exports.doImport = function (req, res, padId) { let status = 'ok'; doImport(req, res, padId).catch((err) => { // check for known errors and replace the status - if (err == 'uploadFailed' || err == 'convertFailed' || err == 'padHasData' || err == 'maxFileSize') { + if (err === 'uploadFailed' || + err === 'convertFailed' || + err === 'padHasData' || + err === 'maxFileSize') { status = err; } else { throw err; From 532bde71f77bb04f11b7ddd26d9db4b9e3e555ca Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 089/357] lint: src/node/handler/PadMessageHandler.js --- src/node/handler/PadMessageHandler.js | 190 +++++++++++++++----------- 1 file changed, 110 insertions(+), 80 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 279b08dfa..99cac5ca4 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -1,3 +1,4 @@ +'use strict'; /** * The MessageHandler handles all Messages that comes from Socket.IO and controls the sessions */ @@ -18,22 +19,20 @@ * limitations under the License. */ -/* global exports, process, require */ - const padManager = require('../db/PadManager'); -const Changeset = require('ep_etherpad-lite/static/js/Changeset'); -const AttributePool = require('ep_etherpad-lite/static/js/AttributePool'); -const AttributeManager = require('ep_etherpad-lite/static/js/AttributeManager'); +const Changeset = require('../../static/js/Changeset'); +const AttributePool = require('../../static/js/AttributePool'); +const AttributeManager = require('../../static/js/AttributeManager'); const authorManager = require('../db/AuthorManager'); const readOnlyManager = require('../db/ReadOnlyManager'); const settings = require('../utils/Settings'); const securityManager = require('../db/SecurityManager'); -const plugins = require('ep_etherpad-lite/static/js/pluginfw/plugin_defs.js'); +const plugins = require('../../static/js/pluginfw/plugin_defs.js'); const log4js = require('log4js'); const messageLogger = log4js.getLogger('message'); const accessLogger = log4js.getLogger('access'); const _ = require('underscore'); -const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks.js'); +const hooks = require('../../static/js/pluginfw/hooks.js'); const channels = require('channels'); const stats = require('../stats'); const assert = require('assert').strict; @@ -65,7 +64,9 @@ stats.gauge('totalUsers', () => Object.keys(socketio.sockets.sockets).length); /** * A changeset queue per pad that is processed by handleUserChanges() */ -const padChannels = new channels.channels(({socket, message}, callback) => nodeify(handleUserChanges(socket, message), callback)); +const padChannels = new channels.channels( + ({socket, message}, callback) => nodeify(handleUserChanges(socket, message), callback) +); /** * Saves the Socket class we need to send and receive data from the client @@ -76,7 +77,7 @@ let socketio; * This Method is called by server.js to tell the message handler on which socket it should send * @param socket_io The Socket */ -exports.setSocketIO = function (socket_io) { +exports.setSocketIO = (socket_io) => { socketio = socket_io; }; @@ -94,7 +95,7 @@ exports.handleConnect = (socket) => { /** * Kicks all sessions from a pad */ -exports.kickSessionsFromPad = function (padID) { +exports.kickSessionsFromPad = (padID) => { if (typeof socketio.sockets.clients !== 'function') return; // skip if there is nobody on this pad @@ -114,7 +115,8 @@ exports.handleDisconnect = async (socket) => { // save the padname of this session const session = sessioninfos[socket.id]; - // if this connection was already etablished with a handshake, send a disconnect message to the others + // if this connection was already etablished with a handshake, + // send a disconnect message to the others if (session && session.author) { const {session: {user} = {}} = socket.client.request; accessLogger.info(`${'[LEAVE]' + @@ -192,7 +194,8 @@ exports.handleMessage = async (socket, message) => { const auth = thisSession.auth; if (!auth) { - console.error('Auth was never applied to a session. If you are using the stress-test tool then restart Etherpad and the Stress test tool.'); + console.error('Auth was never applied to a session. If you are using the ' + + 'stress-test tool then restart Etherpad and the Stress test tool.'); return; } @@ -234,7 +237,7 @@ exports.handleMessage = async (socket, message) => { } // Call handleMessage hook. If a plugin returns null, the message will be dropped. - if ((await hooks.aCallAll('handleMessage', context)).some((m) => m === null)) { + if ((await hooks.aCallAll('handleMessage', context)).some((m) => m == null)) { return; } @@ -283,11 +286,11 @@ exports.handleMessage = async (socket, message) => { * @param socket the socket.io Socket object for the client * @param message the message from the client */ -async function handleSaveRevisionMessage(socket, message) { +const handleSaveRevisionMessage = async (socket, message) => { const {padId, author: authorId} = sessioninfos[socket.id]; const pad = await padManager.getPad(padId); await pad.addSavedRevision(pad.head, authorId); -} +}; /** * Handles a custom message, different to the function below as it handles @@ -296,7 +299,7 @@ async function handleSaveRevisionMessage(socket, message) { * @param msg {Object} the message we're sending * @param sessionID {string} the socketIO session to which we're sending this message */ -exports.handleCustomObjectMessage = function (msg, sessionID) { +exports.handleCustomObjectMessage = (msg, sessionID) => { if (msg.data.type === 'CUSTOM') { if (sessionID) { // a sessionID is targeted: directly to this sessionID @@ -314,7 +317,7 @@ exports.handleCustomObjectMessage = function (msg, sessionID) { * @param padID {Pad} the pad to which we're sending this message * @param msgString {String} the message we're sending */ -exports.handleCustomMessage = function (padID, msgString) { +exports.handleCustomMessage = (padID, msgString) => { const time = Date.now(); const msg = { type: 'COLLABROOM', @@ -331,12 +334,12 @@ exports.handleCustomMessage = function (padID, msgString) { * @param socket the socket.io Socket object for the client * @param message the message from the client */ -async function handleChatMessage(socket, message) { +const handleChatMessage = async (socket, message) => { const time = Date.now(); const text = message.data.text; const {padId, author: authorId} = sessioninfos[socket.id]; await exports.sendChatMessageToPadClients(time, authorId, text, padId); -} +}; /** * Sends a chat message to all clients of this pad @@ -345,7 +348,7 @@ async function handleChatMessage(socket, message) { * @param text the text of the chat message * @param padId the padId to send the chat message to */ -exports.sendChatMessageToPadClients = async function (time, userId, text, padId) { +exports.sendChatMessageToPadClients = async (time, userId, text, padId) => { // get the pad const pad = await padManager.getPad(padId); @@ -371,7 +374,7 @@ exports.sendChatMessageToPadClients = async function (time, userId, text, padId) * @param socket the socket.io Socket object for the client * @param message the message from the client */ -async function handleGetChatMessages(socket, message) { +const handleGetChatMessages = async (socket, message) => { if (message.data.start == null) { messageLogger.warn('Dropped message, GetChatMessages Message has no start!'); return; @@ -387,7 +390,8 @@ async function handleGetChatMessages(socket, message) { const count = end - start; if (count < 0 || count > 100) { - messageLogger.warn('Dropped message, GetChatMessages Message, client requested invalid amount of messages!'); + messageLogger.warn( + 'Dropped message, GetChatMessages Message, client requested invalid amount of messages!'); return; } @@ -405,14 +409,14 @@ async function handleGetChatMessages(socket, message) { // send the messages back to the client socket.json.send(infoMsg); -} +}; /** * Handles a handleSuggestUserName, that means a user have suggest a userName for a other user * @param socket the socket.io Socket object for the client * @param message the message from the client */ -function handleSuggestUserName(socket, message) { +const handleSuggestUserName = (socket, message) => { // check if all ok if (message.data.payload.newName == null) { messageLogger.warn('Dropped message, suggestUserName Message has no newName!'); @@ -433,14 +437,15 @@ function handleSuggestUserName(socket, message) { socket.json.send(message); } }); -} +}; /** - * Handles a USERINFO_UPDATE, that means that a user have changed his color or name. Anyway, we get both informations + * Handles a USERINFO_UPDATE, that means that a user have changed his color or name. + * Anyway, we get both informations * @param socket the socket.io Socket object for the client * @param message the message from the client */ -async function handleUserInfoUpdate(socket, message) { +const handleUserInfoUpdate = async (socket, message) => { // check if all ok if (message.data.userInfo == null) { messageLogger.warn('Dropped message, USERINFO_UPDATE Message has no userInfo!'); @@ -463,7 +468,8 @@ async function handleUserInfoUpdate(socket, message) { const author = session.author; // Check colorId is a Hex color - const isColor = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(message.data.userInfo.colorId); // for #f00 (Thanks Smamatti) + // for #f00 (Thanks Smamatti) + const isColor = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(message.data.userInfo.colorId); if (!isColor) { messageLogger.warn(`Dropped message, USERINFO_UPDATE Color is malformed.${message.data}`); return; @@ -496,7 +502,7 @@ async function handleUserInfoUpdate(socket, message) { // Block until the authorManager has stored the new attributes. await p; -} +}; /** * Handles a USER_CHANGES message, where the client submits its local @@ -512,7 +518,7 @@ async function handleUserInfoUpdate(socket, message) { * @param socket the socket.io Socket object for the client * @param message the message from the client */ -async function handleUserChanges(socket, message) { +const handleUserChanges = async (socket, message) => { // This one's no longer pending, as we're gonna process it now stats.counter('pendingEdits').dec(); @@ -578,7 +584,8 @@ async function handleUserChanges(socket, message) { // + can add text with attribs // = can change or add attribs - // - can have attribs, but they are discarded and don't show up in the attribs - but do show up in the pool + // - can have attribs, but they are discarded and don't show up in the attribs - + // but do show up in the pool op.attribs.split('*').forEach((attr) => { if (!attr) return; @@ -586,9 +593,11 @@ async function handleUserChanges(socket, message) { attr = wireApool.getAttrib(attr); if (!attr) return; - // the empty author is used in the clearAuthorship functionality so this should be the only exception + // the empty author is used in the clearAuthorship functionality so this + // should be the only exception if ('author' === attr[0] && (attr[1] !== thisSession.author && attr[1] !== '')) { - throw new Error(`Author ${thisSession.author} tried to submit changes as author ${attr[1]} in changeset ${changeset}`); + throw new Error(`Author ${thisSession.author} tried to submit changes as author ` + + `${attr[1]} in changeset ${changeset}`); } }); } @@ -628,7 +637,7 @@ async function handleUserChanges(socket, message) { if (baseRev + 1 === r && c === changeset) { socket.json.send({disconnect: 'badChangeset'}); stats.meter('failedChangesets').mark(); - throw new Error("Won't apply USER_CHANGES, because it contains an already accepted changeset"); + throw new Error("Won't apply USER_CHANGES, as it contains an already accepted changeset"); } changeset = Changeset.follow(c, changeset, false, apool); @@ -672,9 +681,9 @@ async function handleUserChanges(socket, message) { } stopWatch.end(); -} +}; -exports.updatePadClients = async function (pad) { +exports.updatePadClients = async (pad) => { // skip this if no-one is on this pad const roomSockets = _getRoomSockets(pad.id); if (roomSockets.length === 0) return; @@ -682,9 +691,12 @@ exports.updatePadClients = async function (pad) { // since all clients usually get the same set of changesets, store them in local cache // to remove unnecessary roundtrip to the datalayer // NB: note below possibly now accommodated via the change to promises/async - // TODO: in REAL world, if we're working without datalayer cache, all requests to revisions will be fired - // BEFORE first result will be landed to our cache object. The solution is to replace parallel processing - // via async.forEach with sequential for() loop. There is no real benefits of running this in parallel, + // TODO: in REAL world, if we're working without datalayer cache, + // all requests to revisions will be fired + // BEFORE first result will be landed to our cache object. + // The solution is to replace parallel processing + // via async.forEach with sequential for() loop. There is no real + // benefits of running this in parallel, // but benefit of reusing cached revision object is HUGE const revCache = {}; @@ -737,7 +749,7 @@ exports.updatePadClients = async function (pad) { /** * Copied from the Etherpad Source Code. Don't know what this method does excatly... */ -function _correctMarkersInPad(atext, apool) { +const _correctMarkersInPad = (atext, apool) => { const text = atext.text; // collect char positions of line markers (e.g. bullets) in new atext @@ -746,9 +758,11 @@ function _correctMarkersInPad(atext, apool) { const iter = Changeset.opIterator(atext.attribs); let offset = 0; while (iter.hasNext()) { - var op = iter.next(); + const op = iter.next(); - const hasMarker = _.find(AttributeManager.lineAttributes, (attribute) => Changeset.opAttributeValue(op, attribute, apool)) !== undefined; + const hasMarker = _.find( + AttributeManager.lineAttributes, + (attribute) => Changeset.opAttributeValue(op, attribute, apool)) !== undefined; if (hasMarker) { for (let i = 0; i < op.chars; i++) { @@ -778,9 +792,9 @@ function _correctMarkersInPad(atext, apool) { }); return builder.toString(); -} +}; -async function handleSwitchToPad(socket, message, _authorID) { +const handleSwitchToPad = async (socket, message, _authorID) => { const currentSessionInfo = sessioninfos[socket.id]; const padId = currentSessionInfo.padId; @@ -816,10 +830,10 @@ async function handleSwitchToPad(socket, message, _authorID) { const newSessionInfo = sessioninfos[socket.id]; createSessionInfoAuth(newSessionInfo, message); await handleClientReady(socket, message, authorID); -} +}; // Creates/replaces the auth object in the given session info. -function createSessionInfoAuth(sessionInfo, message) { +const createSessionInfoAuth = (sessionInfo, message) => { // Remember this information since we won't // have the cookie in further socket.io messages. // This information will be used to check if @@ -830,15 +844,16 @@ function createSessionInfoAuth(sessionInfo, message) { padID: message.padId, token: message.token, }; -} +}; /** - * Handles a CLIENT_READY. A CLIENT_READY is the first message from the client to the server. The Client sends his token + * Handles a CLIENT_READY. A CLIENT_READY is the first message from the client + * to the server. The Client sends his token * and the pad it wants to enter. The Server answers with the inital values (clientVars) of the pad * @param socket the socket.io Socket object for the client * @param message the message from the client */ -async function handleClientReady(socket, message, authorID) { +const handleClientReady = async (socket, message, authorID) => { // check if all ok if (!message.token) { messageLogger.warn('Dropped message, CLIENT_READY Message has no token!'); @@ -884,9 +899,11 @@ async function handleClientReady(socket, message, authorID) { const historicalAuthorData = {}; await Promise.all(authors.map((authorId) => authorManager.getAuthor(authorId).then((author) => { if (!author) { - messageLogger.error('There is no author for authorId: ', authorId, '. This is possibly related to https://github.com/ether/etherpad-lite/issues/2802'); + messageLogger.error(`There is no author for authorId: ${authorId}. ` + + 'This is possibly related to https://github.com/ether/etherpad-lite/issues/2802'); } else { - historicalAuthorData[authorId] = {name: author.name, colorId: author.colorId}; // Filter author attribs (e.g. don't send author's pads to all clients) + // Filter author attribs (e.g. don't send author's pads to all clients) + historicalAuthorData[authorId] = {name: author.name, colorId: author.colorId}; } }))); @@ -931,7 +948,8 @@ async function handleClientReady(socket, message, authorID) { // Save the revision in sessioninfos, we take the revision from the info the client send to us sessionInfo.rev = message.client_rev; - // During the client reconnect, client might miss some revisions from other clients. By using client revision, + // During the client reconnect, client might miss some revisions from other clients. + // By using client revision, // this below code sends all the revisions missed during the client reconnect const revisionsNeeded = []; const changesets = {}; @@ -987,12 +1005,13 @@ async function handleClientReady(socket, message, authorID) { } } else { // This is a normal first connect - + let atext; + let apool; // prepare all values for the wire, there's a chance that this throws, if the pad is corrupted try { - var atext = Changeset.cloneAText(pad.atext); + atext = Changeset.cloneAText(pad.atext); const attribsForWire = Changeset.prepareForWire(atext.attribs, pad.pool); - var apool = attribsForWire.pool.toJsonable(); + apool = attribsForWire.pool.toJsonable(); atext.attribs = attribsForWire.translated; } catch (e) { console.error(e.stack || e); @@ -1147,12 +1166,12 @@ async function handleClientReady(socket, message, authorID) { socket.json.send(msg); })); } -} +}; /** * Handles a request for a rough changeset, the timeslider client needs it */ -async function handleChangesetRequest(socket, message) { +const handleChangesetRequest = async (socket, message) => { // check if all ok if (message.data == null) { messageLogger.warn('Dropped message, changeset request has no data!'); @@ -1197,15 +1216,16 @@ async function handleChangesetRequest(socket, message) { data.requestID = message.data.requestID; socket.json.send({type: 'CHANGESET_REQ', data}); } catch (err) { - console.error(`Error while handling a changeset request for ${padIds.padId}`, err.toString(), message.data); + console.error(`Error while handling a changeset request for ${padIds.padId}`, + err.toString(), message.data); } -} +}; /** * Tries to rebuild the getChangestInfo function of the original Etherpad * https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L144 */ -async function getChangesetInfo(padId, startNum, endNum, granularity) { +const getChangesetInfo = async (padId, startNum, endNum, granularity) => { const pad = await padManager.getPad(padId); const head_revision = pad.getHeadRevisionNumber(); @@ -1237,15 +1257,25 @@ async function getChangesetInfo(padId, startNum, endNum, granularity) { // get all needed composite Changesets const composedChangesets = {}; - const p1 = Promise.all(compositesChangesetNeeded.map((item) => composePadChangesets(padId, item.start, item.end).then((changeset) => { - composedChangesets[`${item.start}/${item.end}`] = changeset; - }))); + const p1 = Promise.all( + compositesChangesetNeeded.map( + (item) => composePadChangesets( + padId, item.start, item.end + ).then( + (changeset) => { + composedChangesets[`${item.start}/${item.end}`] = changeset; + } + ) + ) + ); // get all needed revision Dates const revisionDate = []; - const p2 = Promise.all(revTimesNeeded.map((revNum) => pad.getRevisionDate(revNum).then((revDate) => { - revisionDate[revNum] = Math.floor(revDate / 1000); - }))); + const p2 = Promise.all(revTimesNeeded.map((revNum) => pad.getRevisionDate(revNum) + .then((revDate) => { + revisionDate[revNum] = Math.floor(revDate / 1000); + }) + )); // get the lines let lines; @@ -1288,13 +1318,13 @@ async function getChangesetInfo(padId, startNum, endNum, granularity) { return {forwardsChangesets, backwardsChangesets, apool: apool.toJsonable(), actualEndNum: endNum, timeDeltas, start: startNum, granularity}; -} +}; /** * Tries to rebuild the getPadLines function of the original Etherpad * https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L263 */ -async function getPadLines(padId, revNum) { +const getPadLines = async (padId, revNum) => { const pad = await padManager.getPad(padId); // get the atext @@ -1310,13 +1340,13 @@ async function getPadLines(padId, revNum) { textlines: Changeset.splitTextLines(atext.text), alines: Changeset.splitAttributionLines(atext.attribs, atext.text), }; -} +}; /** * Tries to rebuild the composePadChangeset function of the original Etherpad * https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L241 */ -async function composePadChangesets(padId, startNum, endNum) { +const composePadChangesets = async (padId, startNum, endNum) => { const pad = await padManager.getPad(padId); // fetch all changesets we need @@ -1333,7 +1363,9 @@ async function composePadChangesets(padId, startNum, endNum) { // get all changesets const changesets = {}; - await Promise.all(changesetsNeeded.map((revNum) => pad.getRevisionChangeset(revNum).then((changeset) => changesets[revNum] = changeset))); + await Promise.all(changesetsNeeded.map( + (revNum) => pad.getRevisionChangeset(revNum).then((changeset) => changesets[revNum] = changeset) + )); // compose Changesets let r; @@ -1351,9 +1383,9 @@ async function composePadChangesets(padId, startNum, endNum) { console.warn('failed to compose cs in pad:', padId, ' startrev:', startNum, ' current rev:', r); throw e; } -} +}; -function _getRoomSockets(padID) { +const _getRoomSockets = (padID) => { const roomSockets = []; const room = socketio.sockets.adapter.rooms[padID]; @@ -1364,21 +1396,19 @@ function _getRoomSockets(padID) { } return roomSockets; -} +}; /** * Get the number of users in a pad */ -exports.padUsersCount = function (padID) { - return { - padUsersCount: _getRoomSockets(padID).length, - }; -}; +exports.padUsersCount = (padID) => ({ + padUsersCount: _getRoomSockets(padID).length, +}); /** * Get the list of users in a pad */ -exports.padUsers = async function (padID) { +exports.padUsers = async (padID) => { const padUsers = []; // iterate over all clients (in parallel) From acf889b7de88dca6d679e57e4bac04386c35c33b Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 090/357] lint: src/node/handler/SocketIORouter.js --- src/node/handler/SocketIORouter.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/node/handler/SocketIORouter.js b/src/node/handler/SocketIORouter.js index 56e5c5be4..42d710a39 100644 --- a/src/node/handler/SocketIORouter.js +++ b/src/node/handler/SocketIORouter.js @@ -1,3 +1,4 @@ +'use strict'; /** * This is the Socket.IO Router. It routes the Messages between the * components of the Server. The components are at the moment: pad and timeslider @@ -21,9 +22,6 @@ const log4js = require('log4js'); const messageLogger = log4js.getLogger('message'); -const securityManager = require('../db/SecurityManager'); -const readOnlyManager = require('../db/ReadOnlyManager'); -const settings = require('../utils/Settings'); /** * Saves all components @@ -37,7 +35,7 @@ let socket; /** * adds a component */ -exports.addComponent = function (moduleName, module) { +exports.addComponent = (moduleName, module) => { // save the component components[moduleName] = module; @@ -48,14 +46,14 @@ exports.addComponent = function (moduleName, module) { /** * sets the socket.io and adds event functions for routing */ -exports.setSocketIO = function (_socket) { +exports.setSocketIO = (_socket) => { // save this socket internaly socket = _socket; socket.sockets.on('connection', (client) => { // wrap the original send function to log the messages client._send = client.send; - client.send = function (message) { + client.send = (message) => { messageLogger.debug(`to ${client.id}: ${JSON.stringify(message)}`); client._send(message); }; @@ -66,7 +64,7 @@ exports.setSocketIO = function (_socket) { } client.on('message', async (message) => { - if (message.protocolVersion && message.protocolVersion != 2) { + if (message.protocolVersion && message.protocolVersion !== 2) { messageLogger.warn(`Protocolversion header is not correct: ${JSON.stringify(message)}`); return; } From 3a586a7aadd76e1a8ff55361e5bf3083ef13f0a7 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 091/357] lint: src/node/hooks/express/admin.js --- src/node/hooks/express/admin.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/node/hooks/express/admin.js b/src/node/hooks/express/admin.js index 417939600..763698332 100644 --- a/src/node/hooks/express/admin.js +++ b/src/node/hooks/express/admin.js @@ -1,8 +1,9 @@ -const eejs = require('ep_etherpad-lite/node/eejs'); +'use strict'; +const eejs = require('../../eejs'); -exports.expressCreateServer = function (hook_name, args, cb) { +exports.expressCreateServer = (hookName, args, cb) => { args.app.get('/admin', (req, res) => { - if ('/' != req.path[req.path.length - 1]) return res.redirect('./admin/'); + if ('/' !== req.path[req.path.length - 1]) return res.redirect('./admin/'); res.send(eejs.require('ep_etherpad-lite/templates/admin/index.html', {req})); }); return cb(); From fbc70c12768791c6a70a2589db4eccc1722d0a15 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 092/357] lint: src/node/hooks/express/adminplugins.js --- src/node/hooks/express/adminplugins.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index 0a6d97808..e2129c116 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -4,7 +4,6 @@ const eejs = require('../../eejs'); const settings = require('../../utils/Settings'); const installer = require('../../../static/js/pluginfw/installer'); const plugins = require('../../../static/js/pluginfw/plugin_defs'); -const _ = require('underscore'); const semver = require('semver'); const UpdateCheck = require('../../utils/UpdateCheck'); @@ -51,7 +50,7 @@ exports.socketio = (hookName, args, cb) => { try { const results = await installer.getAvailablePlugins(/* maxCacheAge:*/ 60 * 10); - const updatable = _(plugins.plugins).keys().filter((plugin) => { + const updatable = Object.keys(plugins.plugins).filter((plugin) => { if (!results[plugin]) return false; const latestVersion = results[plugin].version; From 4de2844af2d38dcb30e2203806e92faf9c8dc007 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 093/357] lint: src/node/hooks/express/apicalls.js --- src/node/hooks/express/apicalls.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/node/hooks/express/apicalls.js b/src/node/hooks/express/apicalls.js index c87998e94..b72ed11e5 100644 --- a/src/node/hooks/express/apicalls.js +++ b/src/node/hooks/express/apicalls.js @@ -1,9 +1,11 @@ +'use strict'; + const log4js = require('log4js'); const clientLogger = log4js.getLogger('client'); const formidable = require('formidable'); const apiHandler = require('../../handler/APIHandler'); -exports.expressCreateServer = function (hook_name, args, cb) { +exports.expressCreateServer = (hookName, args, cb) => { // The Etherpad client side sends information about how a disconnect happened args.app.post('/ep/pad/connection-diagnostic-info', (req, res) => { new formidable.IncomingForm().parse(req, (err, fields, files) => { @@ -15,8 +17,9 @@ exports.expressCreateServer = function (hook_name, args, cb) { // The Etherpad client side sends information about client side javscript errors args.app.post('/jserror', (req, res) => { new formidable.IncomingForm().parse(req, (err, fields, files) => { + let data; try { - var data = JSON.parse(fields.errorInfo); + data = JSON.parse(fields.errorInfo); } catch (e) { return res.end(); } From 3cf6e1f015aad2e9104e8dc74d1d32fc9ca605b4 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 094/357] lint: src/node/hooks/express/errorhandling.js --- src/node/hooks/express/errorhandling.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/node/hooks/express/errorhandling.js b/src/node/hooks/express/errorhandling.js index 4a20b70d2..884ca9be0 100644 --- a/src/node/hooks/express/errorhandling.js +++ b/src/node/hooks/express/errorhandling.js @@ -1,6 +1,8 @@ -const stats = require('ep_etherpad-lite/node/stats'); +'use strict'; -exports.expressCreateServer = function (hook_name, args, cb) { +const stats = require('../../stats'); + +exports.expressCreateServer = (hook_name, args, cb) => { exports.app = args.app; // Handle errors From 3571eb7c3213b69ae4f0aa303eb68af191e82a21 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 095/357] lint: src/node/hooks/express/importexport.js --- src/node/hooks/express/importexport.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/node/hooks/express/importexport.js b/src/node/hooks/express/importexport.js index 7a6c38655..4dbe816f6 100644 --- a/src/node/hooks/express/importexport.js +++ b/src/node/hooks/express/importexport.js @@ -1,39 +1,43 @@ -const assert = require('assert').strict; +'use strict'; + const hasPadAccess = require('../../padaccess'); const settings = require('../../utils/Settings'); const exportHandler = require('../../handler/ExportHandler'); const importHandler = require('../../handler/ImportHandler'); const padManager = require('../../db/PadManager'); const readOnlyManager = require('../../db/ReadOnlyManager'); -const authorManager = require('../../db/AuthorManager'); const rateLimit = require('express-rate-limit'); const securityManager = require('../../db/SecurityManager'); const webaccess = require('./webaccess'); -settings.importExportRateLimiting.onLimitReached = function (req, res, options) { +settings.importExportRateLimiting.onLimitReached = (req, res, options) => { // when the rate limiter triggers, write a warning in the logs - console.warn(`Import/Export rate limiter triggered on "${req.originalUrl}" for IP address ${req.ip}`); + console.warn('Import/Export rate limiter triggered on ' + + `"${req.originalUrl}" for IP address ${req.ip}`); }; const limiter = rateLimit(settings.importExportRateLimiting); -exports.expressCreateServer = function (hook_name, args, cb) { +exports.expressCreateServer = (hookName, args, cb) => { // handle export requests args.app.use('/p/:pad/:rev?/export/:type', limiter); args.app.get('/p/:pad/:rev?/export/:type', async (req, res, next) => { const types = ['pdf', 'doc', 'txt', 'html', 'odt', 'etherpad']; // send a 404 if we don't support this filetype - if (types.indexOf(req.params.type) == -1) { + if (types.indexOf(req.params.type) === -1) { return next(); } // if abiword is disabled, and this is a format we only support with abiword, output a message - if (settings.exportAvailable() == 'no' && + if (settings.exportAvailable() === 'no' && ['odt', 'pdf', 'doc'].indexOf(req.params.type) !== -1) { - console.error(`Impossible to export pad "${req.params.pad}" in ${req.params.type} format. There is no converter configured`); + console.error(`Impossible to export pad "${req.params.pad}" in ${req.params.type} format.` + + ' There is no converter configured'); - // ACHTUNG: do not include req.params.type in res.send() because there is no HTML escaping and it would lead to an XSS - res.send('This export is not enabled at this Etherpad instance. Set the path to Abiword or soffice (LibreOffice) in settings.json to enable this feature'); + // ACHTUNG: do not include req.params.type in res.send() because there is + // no HTML escaping and it would lead to an XSS + res.send('This export is not enabled at this Etherpad instance. Set the path to Abiword' + + ' or soffice (LibreOffice) in settings.json to enable this feature'); return; } From 18ebf7b69afd8b56745efcc2571687c5cf782cc8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 096/357] lint: src/node/hooks/express/isValidJSONPName.js --- src/node/hooks/express/isValidJSONPName.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/node/hooks/express/isValidJSONPName.js b/src/node/hooks/express/isValidJSONPName.js index 442c963e9..c8ca5bea1 100644 --- a/src/node/hooks/express/isValidJSONPName.js +++ b/src/node/hooks/express/isValidJSONPName.js @@ -1,3 +1,5 @@ +'use strict'; + const RESERVED_WORDS = [ 'abstract', 'arguments', @@ -65,9 +67,9 @@ const RESERVED_WORDS = [ 'yield', ]; -const regex = /^[a-zA-Z_$][0-9a-zA-Z_$]*(?:\[(?:".+"|\'.+\'|\d+)\])*?$/; +const regex = /^[a-zA-Z_$][0-9a-zA-Z_$]*(?:\[(?:".+"|'.+'|\d+)\])*?$/; -module.exports.check = function (inputStr) { +module.exports.check = (inputStr) => { let isValid = true; inputStr.split('.').forEach((part) => { if (!regex.test(part)) { From 2f9a3ec655c46f4aee0798d5f410f117398d43e8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 097/357] lint: src/node/hooks/express/openapi.js --- src/node/hooks/express/openapi.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/node/hooks/express/openapi.js b/src/node/hooks/express/openapi.js index 8ea9529c7..344913389 100644 --- a/src/node/hooks/express/openapi.js +++ b/src/node/hooks/express/openapi.js @@ -1,3 +1,5 @@ +'use strict'; + /** * node/hooks/express/openapi.js * @@ -31,7 +33,9 @@ const OPENAPI_VERSION = '3.0.2'; // Swagger/OAS version const info = { title: 'Etherpad API', description: - 'Etherpad is a real-time collaborative editor scalable to thousands of simultaneous real time users. It provides full data export capabilities, and runs on your server, under your control.', + 'Etherpad is a real-time collaborative editor scalable to thousands of simultaneous ' + + 'real time users. It provides full data export capabilities, and runs on your server, ' + + 'under your control.', termsOfService: 'https://etherpad.org/', contact: { name: 'The Etherpad Foundation', @@ -80,7 +84,9 @@ const resources = { listSessions: { operationId: 'listSessionsOfGroup', summary: '', - responseSchema: {sessions: {type: 'array', items: {$ref: '#/components/schemas/SessionInfo'}}}, + responseSchema: { + sessions: {type: 'array', items: {$ref: '#/components/schemas/SessionInfo'}}, + }, }, list: { operationId: 'listAllGroups', @@ -109,7 +115,9 @@ const resources = { listSessions: { operationId: 'listSessionsOfAuthor', summary: 'returns all sessions of an author', - responseSchema: {sessions: {type: 'array', items: {$ref: '#/components/schemas/SessionInfo'}}}, + responseSchema: { + sessions: {type: 'array', items: {$ref: '#/components/schemas/SessionInfo'}}, + }, }, // We need an operation that return a UserInfo so it can be picked up by the codegen :( getName: { @@ -153,7 +161,8 @@ const resources = { create: { operationId: 'createPad', description: - 'creates a new (non-group) pad. Note that if you need to create a group Pad, you should call createGroupPad', + 'creates a new (non-group) pad. Note that if you need to create a group Pad, ' + + 'you should call createGroupPad', }, getText: { operationId: 'getText', @@ -607,7 +616,7 @@ exports.expressCreateServer = (hookName, args, cb) => { if (createHTTPError.isHttpError(err)) { // pass http errors thrown by handler forward throw err; - } else if (err.name == 'apierror') { + } else if (err.name === 'apierror') { // parameters were wrong and the api stopped execution, pass the error // convert to http error throw new createHTTPError.BadRequest(err.message); From 43ce0f839bdacb61b2ca096d46a3c0f0be039c51 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 098/357] lint: src/node/hooks/express/padreadonly.js --- src/node/hooks/express/padreadonly.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/node/hooks/express/padreadonly.js b/src/node/hooks/express/padreadonly.js index f17f7f0d6..4dda67b1f 100644 --- a/src/node/hooks/express/padreadonly.js +++ b/src/node/hooks/express/padreadonly.js @@ -1,8 +1,10 @@ +'use strict'; + const readOnlyManager = require('../../db/ReadOnlyManager'); const hasPadAccess = require('../../padaccess'); const exporthtml = require('../../utils/ExportHtml'); -exports.expressCreateServer = function (hook_name, args, cb) { +exports.expressCreateServer = (hookName, args, cb) => { // serve read only pad args.app.get('/ro/:id', async (req, res) => { // translate the read only pad to a padId From 72ddf35426b1bb855cdec97e2c4ed221fabd693b Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 099/357] lint: src/node/hooks/express/padurlsanitize.js --- src/node/hooks/express/padurlsanitize.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/node/hooks/express/padurlsanitize.js b/src/node/hooks/express/padurlsanitize.js index 8a287a961..c4c1b6751 100644 --- a/src/node/hooks/express/padurlsanitize.js +++ b/src/node/hooks/express/padurlsanitize.js @@ -1,7 +1,9 @@ +'use strict'; + const padManager = require('../../db/PadManager'); const url = require('url'); -exports.expressCreateServer = function (hook_name, args, cb) { +exports.expressCreateServer = (hookName, args, cb) => { // redirects browser to the pad's sanitized url if needed. otherwise, renders the html args.app.param('pad', async (req, res, next, padId) => { // ensure the padname is valid and the url doesn't end with a / @@ -17,12 +19,12 @@ exports.expressCreateServer = function (hook_name, args, cb) { next(); } else { // the pad id was sanitized, so we redirect to the sanitized version - let real_url = sanitizedPadId; - real_url = encodeURIComponent(real_url); + let realURL = sanitizedPadId; + realURL = encodeURIComponent(realURL); const query = url.parse(req.url).query; - if (query) real_url += `?${query}`; - res.header('Location', real_url); - res.status(302).send(`You should be redirected to ${real_url}`); + if (query) realURL += `?${query}`; + res.header('Location', realURL); + res.status(302).send(`You should be redirected to ${realURL}`); } }); return cb(); From 09fc7438ea626463dcddd0d154fde9a555e9a13e Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 100/357] lint: src/node/hooks/express/specialpages.js --- src/node/hooks/express/specialpages.js | 30 ++++++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/node/hooks/express/specialpages.js b/src/node/hooks/express/specialpages.js index f53ce1ac7..c11e374e9 100644 --- a/src/node/hooks/express/specialpages.js +++ b/src/node/hooks/express/specialpages.js @@ -1,14 +1,16 @@ +'use strict'; + const path = require('path'); -const eejs = require('ep_etherpad-lite/node/eejs'); -const toolbar = require('ep_etherpad-lite/node/utils/toolbar'); -const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); +const eejs = require('../../eejs'); +const toolbar = require('../../utils/toolbar'); +const hooks = require('../../../static/js/pluginfw/hooks'); const settings = require('../../utils/Settings'); const webaccess = require('./webaccess'); -exports.expressCreateServer = function (hook_name, args, cb) { +exports.expressCreateServer = (hookName, args, cb) => { // expose current stats args.app.get('/stats', (req, res) => { - res.json(require('ep_etherpad-lite/node/stats').toJSON()); + res.json(require('../../stats').toJSON()); }); // serve index.html under / @@ -24,7 +26,14 @@ exports.expressCreateServer = function (hook_name, args, cb) { // serve robots.txt args.app.get('/robots.txt', (req, res) => { - let filePath = path.join(settings.root, 'src', 'static', 'skins', settings.skinName, 'robots.txt'); + let filePath = path.join( + settings.root, + 'src', + 'static', + 'skins', + settings.skinName, + 'robots.txt' + ); res.sendFile(filePath, (err) => { // there is no custom robots.txt, send the default robots.txt which dissallows all if (err) { @@ -66,7 +75,14 @@ exports.expressCreateServer = function (hook_name, args, cb) { // serve favicon.ico from all path levels except as a pad name args.app.get(/\/favicon.ico$/, (req, res) => { - let filePath = path.join(settings.root, 'src', 'static', 'skins', settings.skinName, 'favicon.ico'); + let filePath = path.join( + settings.root, + 'src', + 'static', + 'skins', + settings.skinName, + 'favicon.ico' + ); res.sendFile(filePath, (err) => { // there is no custom favicon, send the default favicon From 6df3eadecd12e81bceb799eb7fd17c705bc0ed4c Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 101/357] lint: src/node/hooks/express/static.js --- src/node/hooks/express/static.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/node/hooks/express/static.js b/src/node/hooks/express/static.js index 2df757e64..950fe3f78 100644 --- a/src/node/hooks/express/static.js +++ b/src/node/hooks/express/static.js @@ -1,11 +1,12 @@ +'use strict'; + const minify = require('../../utils/Minify'); -const plugins = require('ep_etherpad-lite/static/js/pluginfw/plugin_defs'); +const plugins = require('../../../static/js/pluginfw/plugin_defs'); const CachingMiddleware = require('../../utils/caching_middleware'); -const settings = require('../../utils/Settings'); const Yajsml = require('etherpad-yajsml'); const _ = require('underscore'); -exports.expressCreateServer = function (hook_name, args, cb) { +exports.expressCreateServer = (hookName, args, cb) => { // Cache both minified and static. const assetCache = new CachingMiddleware(); args.app.all(/\/javascripts\/(.*)/, assetCache.handle); @@ -34,7 +35,8 @@ exports.expressCreateServer = function (hook_name, args, cb) { args.app.use(jsServer.handle.bind(jsServer)); // serve plugin definitions - // not very static, but served here so that client can do require("pluginfw/static/js/plugin-definitions.js"); + // not very static, but served here so that client can do + // require("pluginfw/static/js/plugin-definitions.js"); args.app.get('/pluginfw/plugin-definitions.json', (req, res, next) => { const clientParts = _(plugins.parts) .filter((part) => _(part).has('client_hooks')); From 2dec36bfd7254d86a3a4f7f343d417f1fcd07021 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 102/357] lint: src/node/hooks/express/tests.js --- src/node/hooks/express/tests.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/node/hooks/express/tests.js b/src/node/hooks/express/tests.js index 87edc2a09..641ca819a 100644 --- a/src/node/hooks/express/tests.js +++ b/src/node/hooks/express/tests.js @@ -1,9 +1,11 @@ +'use strict'; + const path = require('path'); const npm = require('npm'); const fs = require('fs'); const util = require('util'); -exports.expressCreateServer = function (hook_name, args, cb) { +exports.expressCreateServer = (hookName, args, cb) => { args.app.get('/tests/frontend/specs_list.js', async (req, res) => { const [coreTests, pluginTests] = await Promise.all([ exports.getCoreTests(), @@ -24,9 +26,9 @@ exports.expressCreateServer = function (hook_name, args, cb) { // path.join seems to normalize by default, but we'll just be explicit const rootTestFolder = path.normalize(path.join(npm.root, '../tests/frontend/')); - const url2FilePath = function (url) { + const url2FilePath = (url) => { let subPath = url.substr('/tests/frontend'.length); - if (subPath == '') { + if (subPath === '') { subPath = 'index.html'; } subPath = subPath.split('?')[0]; @@ -49,8 +51,9 @@ exports.expressCreateServer = function (hook_name, args, cb) { content = `describe(${JSON.stringify(specFileName)}, function(){ ${content} });`; - if(!specFilePath.endsWith('index.html')) res.setHeader('content-type', 'application/javascript'); - + if (!specFilePath.endsWith('index.html')) { + res.setHeader('content-type', 'application/javascript'); + } res.send(content); }); }); @@ -69,7 +72,7 @@ exports.expressCreateServer = function (hook_name, args, cb) { const readdir = util.promisify(fs.readdir); -exports.getPluginTests = async function (callback) { +exports.getPluginTests = async (callback) => { const moduleDir = 'node_modules/'; const specPath = '/static/tests/frontend/specs/'; const staticDir = '/static/plugins/'; @@ -88,7 +91,4 @@ exports.getPluginTests = async function (callback) { return Promise.all(promises).then(() => pluginSpecs); }; -exports.getCoreTests = function () { - // get the core test specs - return readdir('tests/frontend/specs'); -}; +exports.getCoreTests = () => readdir('tests/frontend/specs'); From 6054f6d93f7a902ba7027d7c913ecc25d3a9e05d Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 103/357] lint: src/node/hooks/i18n.js --- src/node/hooks/i18n.js | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/node/hooks/i18n.js b/src/node/hooks/i18n.js index 610c3f68f..5b4b0ec12 100644 --- a/src/node/hooks/i18n.js +++ b/src/node/hooks/i18n.js @@ -1,24 +1,23 @@ +'use strict'; + const languages = require('languages4translatewiki'); const fs = require('fs'); const path = require('path'); const _ = require('underscore'); const npm = require('npm'); -const plugins = require('ep_etherpad-lite/static/js/pluginfw/plugin_defs.js').plugins; -const semver = require('semver'); +const plugins = require('../../static/js/pluginfw/plugin_defs.js').plugins; const existsSync = require('../utils/path_exists'); -const settings = require('../utils/Settings') -; - +const settings = require('../utils/Settings'); // returns all existing messages merged together and grouped by langcode // {es: {"foo": "string"}, en:...} -function getAllLocales() { +const getAllLocales = () => { const locales2paths = {}; // Puts the paths of all locale files contained in a given directory // into `locales2paths` (files from various dirs are grouped by lang code) // (only json files with valid language code as name) - function extractLangs(dir) { + const extractLangs = (dir) => { if (!existsSync(dir)) return; let stat = fs.lstatSync(dir); if (!stat.isDirectory() || stat.isSymbolicLink()) return; @@ -31,12 +30,12 @@ function getAllLocales() { const ext = path.extname(file); const locale = path.basename(file, ext).toLowerCase(); - if ((ext == '.json') && languages.isValid(locale)) { + if ((ext === '.json') && languages.isValid(locale)) { if (!locales2paths[locale]) locales2paths[locale] = []; locales2paths[locale].push(file); } }); - } + }; // add core supported languages first extractLangs(`${npm.root}/ep_etherpad-lite/locales`); @@ -78,29 +77,29 @@ function getAllLocales() { } return locales; -} +}; // returns a hash of all available languages availables with nativeName and direction // e.g. { es: {nativeName: "español", direction: "ltr"}, ... } -function getAvailableLangs(locales) { +const getAvailableLangs = (locales) => { const result = {}; _.each(_.keys(locales), (langcode) => { result[langcode] = languages.getLanguageInfo(langcode); }); return result; -} +}; // returns locale index that will be served in /locales.json -const generateLocaleIndex = function (locales) { +const generateLocaleIndex = (locales) => { const result = _.clone(locales); // keep English strings _.each(_.keys(locales), (langcode) => { - if (langcode != 'en') result[langcode] = `locales/${langcode}.json`; + if (langcode !== 'en') result[langcode] = `locales/${langcode}.json`; }); return JSON.stringify(result); }; -exports.expressCreateServer = function (n, args, cb) { +exports.expressCreateServer = (n, args, cb) => { // regenerate locales on server restart const locales = getAllLocales(); const localeIndex = generateLocaleIndex(locales); From 666dd7abd182f5586f89bdbe4e6d62058b5ccd63 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 104/357] lint: src/node/padaccess.js --- src/node/padaccess.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node/padaccess.js b/src/node/padaccess.js index 617056a97..241302852 100644 --- a/src/node/padaccess.js +++ b/src/node/padaccess.js @@ -1,3 +1,4 @@ +'use strict'; const securityManager = require('./db/SecurityManager'); // checks for padAccess From 7afc809073df00d74fd108e14a445b2a751358d2 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 105/357] lint: src/node/utils/Abiword.js --- src/node/utils/Abiword.js | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/node/utils/Abiword.js b/src/node/utils/Abiword.js index b75487d75..b93646cd5 100644 --- a/src/node/utils/Abiword.js +++ b/src/node/utils/Abiword.js @@ -1,3 +1,4 @@ +'use strict'; /** * Controls the communication with the Abiword application */ @@ -25,11 +26,12 @@ const os = require('os'); let doConvertTask; -// on windows we have to spawn a process for each convertion, cause the plugin abicommand doesn't exist on this platform +// on windows we have to spawn a process for each convertion, +// cause the plugin abicommand doesn't exist on this platform if (os.type().indexOf('Windows') > -1) { let stdoutBuffer = ''; - doConvertTask = function (task, callback) { + doConvertTask = (task, callback) => { // span an abiword process to perform the conversion const abiword = spawn(settings.abiword, [`--to=${task.destFile}`, task.srcFile]); @@ -46,11 +48,11 @@ if (os.type().indexOf('Windows') > -1) { // throw exceptions if abiword is dieing abiword.on('exit', (code) => { - if (code != 0) { + if (code !== 0) { return callback(`Abiword died with exit code ${code}`); } - if (stdoutBuffer != '') { + if (stdoutBuffer !== '') { console.log(stdoutBuffer); } @@ -58,17 +60,17 @@ if (os.type().indexOf('Windows') > -1) { }); }; - exports.convertFile = function (srcFile, destFile, type, callback) { + exports.convertFile = (srcFile, destFile, type, callback) => { doConvertTask({srcFile, destFile, type}, callback); }; -} -// on unix operating systems, we can start abiword with abicommand and communicate with it via stdin/stdout -// thats much faster, about factor 10 -else { + // on unix operating systems, we can start abiword with abicommand and + // communicate with it via stdin/stdout + // thats much faster, about factor 10 +} else { // spawn the abiword process let abiword; let stdoutCallback = null; - var spawnAbiword = function () { + const spawnAbiword = () => { abiword = spawn(settings.abiword, ['--plugin', 'AbiCommand']); let stdoutBuffer = ''; let firstPrompt = true; @@ -90,9 +92,9 @@ else { stdoutBuffer += data.toString(); // we're searching for the prompt, cause this means everything we need is in the buffer - if (stdoutBuffer.search('AbiWord:>') != -1) { + if (stdoutBuffer.search('AbiWord:>') !== -1) { // filter the feedback message - const err = stdoutBuffer.search('OK') != -1 ? null : stdoutBuffer; + const err = stdoutBuffer.search('OK') !== -1 ? null : stdoutBuffer; // reset the buffer stdoutBuffer = ''; @@ -110,10 +112,10 @@ else { }; spawnAbiword(); - doConvertTask = function (task, callback) { + doConvertTask = (task, callback) => { abiword.stdin.write(`convert ${task.srcFile} ${task.destFile} ${task.type}\n`); // create a callback that calls the task callback and the caller callback - stdoutCallback = function (err) { + stdoutCallback = (err) => { callback(); console.log('queue continue'); try { @@ -126,7 +128,7 @@ else { // Queue with the converts we have to do const queue = async.queue(doConvertTask, 1); - exports.convertFile = function (srcFile, destFile, type, callback) { + exports.convertFile = (srcFile, destFile, type, callback) => { queue.push({srcFile, destFile, type, callback}); }; } From 60bc849be2788230757c6c5cb9dcfdb5e1106d15 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 106/357] lint: src/node/utils/AbsolutePaths.js --- src/node/utils/AbsolutePaths.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/node/utils/AbsolutePaths.js b/src/node/utils/AbsolutePaths.js index 22294cfe2..5b364ed80 100644 --- a/src/node/utils/AbsolutePaths.js +++ b/src/node/utils/AbsolutePaths.js @@ -1,3 +1,4 @@ +'use strict'; /** * Library for deterministic relative filename expansion for Etherpad. */ @@ -40,7 +41,7 @@ let etherpadRoot = null; * @return {string[]|boolean} The shortened array, or false if there was no * overlap. */ -const popIfEndsWith = function (stringArray, lastDesiredElements) { +const popIfEndsWith = (stringArray, lastDesiredElements) => { if (stringArray.length <= lastDesiredElements.length) { absPathLogger.debug(`In order to pop "${lastDesiredElements.join(path.sep)}" from "${stringArray.join(path.sep)}", it should contain at least ${lastDesiredElements.length + 1} elements`); @@ -72,8 +73,8 @@ const popIfEndsWith = function (stringArray, lastDesiredElements) { * @return {string} The identified absolute base path. If such path cannot be * identified, prints a log and exits the application. */ -exports.findEtherpadRoot = function () { - if (etherpadRoot !== null) { +exports.findEtherpadRoot = () => { + if (etherpadRoot != null) { return etherpadRoot; } @@ -126,7 +127,7 @@ exports.findEtherpadRoot = function () { * it is returned unchanged. Otherwise it is interpreted * relative to exports.root. */ -exports.makeAbsolute = function (somePath) { +exports.makeAbsolute = (somePath) => { if (path.isAbsolute(somePath)) { return somePath; } @@ -145,7 +146,7 @@ exports.makeAbsolute = function (somePath) { * a subdirectory of the base one * @return {boolean} */ -exports.isSubdir = function (parent, arbitraryDir) { +exports.isSubdir = (parent, arbitraryDir) => { // modified from: https://stackoverflow.com/questions/37521893/determine-if-a-path-is-subdirectory-of-another-in-node-js#45242825 const relative = path.relative(parent, arbitraryDir); const isSubdir = !!relative && !relative.startsWith('..') && !path.isAbsolute(relative); From 52f60ceeaabd58b463715d8887e5a050a495b51f Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 107/357] lint: src/node/utils/Cli.js --- src/node/utils/Cli.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/node/utils/Cli.js b/src/node/utils/Cli.js index 6297a4f8c..a5cdee83a 100644 --- a/src/node/utils/Cli.js +++ b/src/node/utils/Cli.js @@ -1,3 +1,4 @@ +'use strict'; /** * The CLI module handles command line parameters */ @@ -30,22 +31,22 @@ for (let i = 0; i < argv.length; i++) { arg = argv[i]; // Override location of settings.json file - if (prevArg == '--settings' || prevArg == '-s') { + if (prevArg === '--settings' || prevArg === '-s') { exports.argv.settings = arg; } // Override location of credentials.json file - if (prevArg == '--credentials') { + if (prevArg === '--credentials') { exports.argv.credentials = arg; } // Override location of settings.json file - if (prevArg == '--sessionkey') { + if (prevArg === '--sessionkey') { exports.argv.sessionkey = arg; } // Override location of settings.json file - if (prevArg == '--apikey') { + if (prevArg === '--apikey') { exports.argv.apikey = arg; } From a4764fadeda1344572740aec74d621a9046751b8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 108/357] lint: src/node/utils/ExportEtherpad.js --- src/node/utils/ExportEtherpad.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/node/utils/ExportEtherpad.js b/src/node/utils/ExportEtherpad.js index ace298ab7..48c850af9 100644 --- a/src/node/utils/ExportEtherpad.js +++ b/src/node/utils/ExportEtherpad.js @@ -1,3 +1,4 @@ +'use strict'; /** * 2014 John McLear (Etherpad Foundation / McLear Ltd) * @@ -16,9 +17,9 @@ const db = require('../db/DB'); -const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); +const hooks = require('../../static/js/pluginfw/hooks'); -exports.getPadRaw = async function (padId) { +exports.getPadRaw = async (padId) => { const padKey = `pad:${padId}`; const padcontent = await db.get(padKey); From c44c4edc1081e583999309ac5ddb112d0d67c883 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 109/357] lint: src/node/utils/ExportHelper.js --- src/node/utils/ExportHelper.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/node/utils/ExportHelper.js b/src/node/utils/ExportHelper.js index e498d4c42..0c593eca1 100644 --- a/src/node/utils/ExportHelper.js +++ b/src/node/utils/ExportHelper.js @@ -1,3 +1,4 @@ +'use strict'; /** * Helpers for export requests */ @@ -18,9 +19,9 @@ * limitations under the License. */ -const Changeset = require('ep_etherpad-lite/static/js/Changeset'); +const Changeset = require('../../static/js/Changeset'); -exports.getPadPlainText = function (pad, revNum) { +exports.getPadPlainText = (pad, revNum) => { const _analyzeLine = exports._analyzeLine; const atext = ((revNum !== undefined) ? pad.getInternalRevisionAText(revNum) : pad.atext); const textLines = atext.text.slice(0, -1).split('\n'); @@ -43,7 +44,7 @@ exports.getPadPlainText = function (pad, revNum) { }; -exports._analyzeLine = function (text, aline, apool) { +exports._analyzeLine = (text, aline, apool) => { const line = {}; // identify list @@ -81,6 +82,5 @@ exports._analyzeLine = function (text, aline, apool) { }; -exports._encodeWhitespace = function (s) { - return s.replace(/[^\x21-\x7E\s\t\n\r]/gu, (c) => `&#${c.codePointAt(0)};`); -}; +exports._encodeWhitespace = + (s) => s.replace(/[^\x21-\x7E\s\t\n\r]/gu, (c) => `&#${c.codePointAt(0)};`); From bfabe7c297997107e2ed36c4f050a60d059e2491 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 21 Jan 2021 21:06:52 +0000 Subject: [PATCH 110/357] lint: src/node/utils/ExportHtml.js --- src/node/utils/ExportHtml.js | 169 ++++++++++++++++++++--------------- 1 file changed, 96 insertions(+), 73 deletions(-) diff --git a/src/node/utils/ExportHtml.js b/src/node/utils/ExportHtml.js index 2f5a77c9a..999b22639 100644 --- a/src/node/utils/ExportHtml.js +++ b/src/node/utils/ExportHtml.js @@ -1,3 +1,4 @@ +'use strict'; /** * Copyright 2009 Google Inc. * @@ -14,32 +15,29 @@ * limitations under the License. */ -const Changeset = require('ep_etherpad-lite/static/js/Changeset'); +const Changeset = require('../../static/js/Changeset'); const padManager = require('../db/PadManager'); const _ = require('underscore'); -const Security = require('ep_etherpad-lite/static/js/security'); -const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); -const eejs = require('ep_etherpad-lite/node/eejs'); +const Security = require('../../static/js/security'); +const hooks = require('../../static/js/pluginfw/hooks'); +const eejs = require('../eejs'); const _analyzeLine = require('./ExportHelper')._analyzeLine; const _encodeWhitespace = require('./ExportHelper')._encodeWhitespace; const padutils = require('../../static/js/pad_utils').padutils; -async function getPadHTML(pad, revNum) { +const getPadHTML = async (pad, revNum) => { let atext = pad.atext; // fetch revision atext - if (revNum != undefined) { + if (revNum !== undefined) { atext = await pad.getInternalRevisionAText(revNum); } // convert atext to html return await getHTMLFromAtext(pad, atext); -} +}; -exports.getPadHTML = getPadHTML; -exports.getHTMLFromAtext = getHTMLFromAtext; - -async function getHTMLFromAtext(pad, atext, authorColors) { +const getHTMLFromAtext = async (pad, atext, authorColors) => { const apool = pad.apool(); const textLines = atext.text.slice(0, -1).split('\n'); const attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text); @@ -72,9 +70,7 @@ async function getHTMLFromAtext(pad, atext, authorColors) { const anumMap = {}; let css = ''; - const stripDotFromAuthorID = function (id) { - return id.replace(/\./g, '_'); - }; + const stripDotFromAuthorID = (id) => id.replace(/\./g, '_'); if (authorColors) { css += 'empty
        ', - expectedHTML: 'empty

        ', - expectedText: 'empty\n\n', + wantHTML: 'empty

        ', + wantText: 'empty\n\n', }, 'indentedListsAreNotBullets': { description: 'Indented lists are represented with tabs and without bullets', input: '
        • indent
        • indent
        ', - expectedHTML: '
        • indent
        • indent

        ', - expectedText: '\tindent\n\tindent\n\n', + wantHTML: '
        • indent
        • indent

        ', + wantText: '\tindent\n\tindent\n\n', }, 'lineWithMultipleSpaces': { description: 'Multiple spaces should be collapsed', input: 'Text with more than one space.
        ', - expectedHTML: 'Text with more than one space.

        ', - expectedText: 'Text with more than one space.\n\n', + wantHTML: 'Text with more than one space.

        ', + wantText: 'Text with more than one space.\n\n', }, 'lineWithMultipleNonBreakingAndNormalSpaces': { // XXX the HTML between "than" and "one" looks strange description: 'non-breaking space should be preserved, but can be replaced when it', input: 'Text with  more   than  one space.
        ', - expectedHTML: 'Text with  more   than  one space.

        ', - expectedText: 'Text with more than one space.\n\n', + wantHTML: 'Text with  more   than  one space.

        ', + wantText: 'Text with more than one space.\n\n', }, 'multiplenbsp': { description: 'Multiple non-breaking space should be preserved', input: '  
        ', - expectedHTML: '  

        ', - expectedText: ' \n\n', + wantHTML: '  

        ', + wantText: ' \n\n', }, 'multipleNonBreakingSpaceBetweenWords': { description: 'A normal space is always inserted before a word', input: '  word1  word2   word3
        ', - expectedHTML: '  word1  word2   word3

        ', - expectedText: ' word1 word2 word3\n\n', + wantHTML: '  word1  word2   word3

        ', + wantText: ' word1 word2 word3\n\n', }, 'nonBreakingSpacePreceededBySpaceBetweenWords': { description: 'A non-breaking space preceeded by a normal space', input: '  word1  word2  word3
        ', - expectedHTML: ' word1  word2  word3

        ', - expectedText: ' word1 word2 word3\n\n', + wantHTML: ' word1  word2  word3

        ', + wantText: ' word1 word2 word3\n\n', }, 'nonBreakingSpaceFollowededBySpaceBetweenWords': { description: 'A non-breaking space followed by a normal space', input: '  word1  word2  word3
        ', - expectedHTML: '  word1  word2  word3

        ', - expectedText: ' word1 word2 word3\n\n', + wantHTML: '  word1  word2  word3

        ', + wantText: ' word1 word2 word3\n\n', }, 'spacesAfterNewline': { description: 'Collapse spaces that follow a newline', input: 'something
        something
        ', - expectedHTML: 'something
        something

        ', - expectedText: 'something\nsomething\n\n', + wantHTML: 'something
        something

        ', + wantText: 'something\nsomething\n\n', }, 'spacesAfterNewlineP': { description: 'Collapse spaces that follow a paragraph', input: 'something

        something
        ', - expectedHTML: 'something

        something

        ', - expectedText: 'something\n\nsomething\n\n', + wantHTML: 'something

        something

        ', + wantText: 'something\n\nsomething\n\n', }, 'spacesAtEndOfLine': { description: 'Collapse spaces that preceed/follow a newline', input: 'something
        something
        ', - expectedHTML: 'something
        something

        ', - expectedText: 'something\nsomething\n\n', + wantHTML: 'something
        something

        ', + wantText: 'something\nsomething\n\n', }, 'spacesAtEndOfLineP': { description: 'Collapse spaces that preceed/follow a paragraph', input: 'something

        something
        ', - expectedHTML: 'something

        something

        ', - expectedText: 'something\n\nsomething\n\n', + wantHTML: 'something

        something

        ', + wantText: 'something\n\nsomething\n\n', }, 'nonBreakingSpacesAfterNewlines': { description: 'Don\'t collapse non-breaking spaces that follow a newline', input: 'something
           something
        ', - expectedHTML: 'something
           something

        ', - expectedText: 'something\n something\n\n', + wantHTML: 'something
           something

        ', + wantText: 'something\n something\n\n', }, 'nonBreakingSpacesAfterNewlinesP': { description: 'Don\'t collapse non-breaking spaces that follow a paragraph', input: 'something

           something
        ', - expectedHTML: 'something

           something

        ', - expectedText: 'something\n\n something\n\n', + wantHTML: 'something

           something

        ', + wantText: 'something\n\n something\n\n', }, 'collapseSpacesInsideElements': { description: 'Preserve only one space when multiple are present', input: 'Need more space s !
        ', - expectedHTML: 'Need more space s !

        ', - expectedText: 'Need more space s !\n\n', + wantHTML: 'Need more space s !

        ', + wantText: 'Need more space s !\n\n', }, 'collapseSpacesAcrossNewlines': { description: 'Newlines and multiple spaces across newlines should be collapsed', @@ -155,14 +155,14 @@ const testImports = { space s !
        `, - expectedHTML: 'Need more space s !

        ', - expectedText: 'Need more space s !\n\n', + wantHTML: 'Need more space s !

        ', + wantText: 'Need more space s !\n\n', }, 'multipleNewLinesAtBeginning': { description: 'Multiple new lines and paragraphs at the beginning should be preserved', input: '

        first line

        second line
        ', - expectedHTML: '



        first line

        second line

        ', - expectedText: '\n\n\n\nfirst line\n\nsecond line\n\n', + wantHTML: '



        first line

        second line

        ', + wantText: '\n\n\n\nfirst line\n\nsecond line\n\n', }, 'multiLineParagraph': { description: 'A paragraph with multiple lines should not loose spaces when lines are combined', @@ -172,8 +172,8 @@ const testImports = { п р с т у ф х ц ч ш щ ю я ь

        `, - expectedHTML: 'а б в г ґ д е є ж з и і ї й к л м н о п р с т у ф х ц ч ш щ ю я ь

        ', - expectedText: 'а б в г ґ д е є ж з и і ї й к л м н о п р с т у ф х ц ч ш щ ю я ь\n\n', + wantHTML: 'а б в г ґ д е є ж з и і ї й к л м н о п р с т у ф х ц ч ш щ ю я ь

        ', + wantText: 'а б в г ґ д е є ж з и і ї й к л м н о п р с т у ф х ц ч ш щ ю я ь\n\n', }, 'multiLineParagraphWithPre': { // XXX why is there   before "in"? @@ -187,8 +187,8 @@ const testImports = {

        п р с т у ф х ц ч ш щ ю я ь

        `, - expectedHTML: 'а б в г ґ д е є ж з и і ї й к л м н о
        multiple
           lines
         in
              pre

        п р с т у ф х ц ч ш щ ю я ь

        ', - expectedText: 'а б в г ґ д е є ж з и і ї й к л м н о\nmultiple\n lines\n in\n pre\n\nп р с т у ф х ц ч ш щ ю я ь\n\n', + wantHTML: 'а б в г ґ д е є ж з и і ї й к л м н о
        multiple
           lines
         in
              pre

        п р с т у ф х ц ч ш щ ю я ь

        ', + wantText: 'а б в г ґ д е є ж з и і ї й к л м н о\nmultiple\n lines\n in\n pre\n\nп р с т у ф х ц ч ш щ ю я ь\n\n', }, 'preIntroducesASpace': { description: 'pre should be on a new line not preceeded by a space', @@ -196,32 +196,32 @@ const testImports = { 1
        preline
         

        `, - expectedHTML: '1
        preline


        ', - expectedText: '1\npreline\n\n\n', + wantHTML: '1
        preline


        ', + wantText: '1\npreline\n\n\n', }, 'dontDeleteSpaceInsideElements': { description: 'Preserve spaces inside elements', input: 'Need more space s !
        ', - expectedHTML: 'Need more space s !

        ', - expectedText: 'Need more space s !\n\n', + wantHTML: 'Need more space s !

        ', + wantText: 'Need more space s !\n\n', }, 'dontDeleteSpaceOutsideElements': { description: 'Preserve spaces outside elements', input: 'Need more space s !
        ', - expectedHTML: 'Need more space s !

        ', - expectedText: 'Need more space s !\n\n', + wantHTML: 'Need more space s !

        ', + wantText: 'Need more space s !\n\n', }, 'dontDeleteSpaceAtEndOfElement': { description: 'Preserve spaces at the end of an element', input: 'Need more space s !
        ', - expectedHTML: 'Need more space s !

        ', - expectedText: 'Need more space s !\n\n', + wantHTML: 'Need more space s !

        ', + wantText: 'Need more space s !\n\n', }, 'dontDeleteSpaceAtBeginOfElements': { description: 'Preserve spaces at the start of an element', input: 'Need more space s !
        ', - expectedHTML: 'Need more space s !

        ', - expectedText: 'Need more space s !\n\n', + wantHTML: 'Need more space s !

        ', + wantText: 'Need more space s !\n\n', }, }; @@ -256,17 +256,17 @@ describe(__filename, function () { it('getHTML', function (done) { api.get(`${endPoint('getHTML')}&padID=${testPadId}`) .expect((res) => { - const receivedHtml = res.body.data.html; - if (receivedHtml !== test.expectedHTML) { + const gotHtml = res.body.data.html; + if (gotHtml !== test.wantHTML) { throw new Error(`HTML received from export is not the one we were expecting. Test Name: ${testName} - Received: - ${JSON.stringify(receivedHtml)} + Got: + ${JSON.stringify(gotHtml)} - Expected: - ${JSON.stringify(test.expectedHTML)} + Want: + ${JSON.stringify(test.wantHTML)} Which is a different version of the originally imported one: ${test.input}`); @@ -279,17 +279,17 @@ describe(__filename, function () { it('getText', function (done) { api.get(`${endPoint('getText')}&padID=${testPadId}`) .expect((res) => { - const receivedText = res.body.data.text; - if (receivedText !== test.expectedText) { + const gotText = res.body.data.text; + if (gotText !== test.wantText) { throw new Error(`Text received from export is not the one we were expecting. Test Name: ${testName} - Received: - ${JSON.stringify(receivedText)} + Got: + ${JSON.stringify(gotText)} - Expected: - ${JSON.stringify(test.expectedText)} + Want: + ${JSON.stringify(test.wantText)} Which is a different version of the originally imported one: ${test.input}`); diff --git a/tests/backend/specs/contentcollector.js b/tests/backend/specs/contentcollector.js index 7014cac2a..c9ebb7482 100644 --- a/tests/backend/specs/contentcollector.js +++ b/tests/backend/specs/contentcollector.js @@ -18,17 +18,17 @@ const tests = { nestedLi: { description: 'Complex nested Li', html: '
        1. one
          1. 1.1
        2. two
        ', - expectedLineAttribs: [ + wantLineAttribs: [ '*0*1*2*3+1+3', '*0*4*2*5+1+3', '*0*1*2*5+1+3', ], - expectedText: [ + wantText: [ '*one', '*1.1', '*two', ], }, complexNest: { description: 'Complex list of different types', html: '
        • one
        • two
        • 0
        • 1
        • 2
          • 3
          • 4
        1. item
          1. item1
          2. item2
        ', - expectedLineAttribs: [ + wantLineAttribs: [ '*0*1*2+1+3', '*0*1*2+1+3', '*0*1*2+1+1', @@ -40,7 +40,7 @@ const tests = { '*0*6*2*7+1+5', '*0*6*2*7+1+5', ], - expectedText: [ + wantText: [ '*one', '*two', '*0', @@ -56,142 +56,142 @@ const tests = { ul: { description: 'Tests if uls properly get attributes', html: '
        • a
        • b
        div

        foo

        ', - expectedLineAttribs: ['*0*1*2+1+1', '*0*1*2+1+1', '+3', '+3'], - expectedText: ['*a', '*b', 'div', 'foo'], + wantLineAttribs: ['*0*1*2+1+1', '*0*1*2+1+1', '+3', '+3'], + wantText: ['*a', '*b', 'div', 'foo'], }, ulIndented: { description: 'Tests if indented uls properly get attributes', html: '
        • a
          • b
        • a

        foo

        ', - expectedLineAttribs: ['*0*1*2+1+1', '*0*3*2+1+1', '*0*1*2+1+1', '+3'], - expectedText: ['*a', '*b', '*a', 'foo'], + wantLineAttribs: ['*0*1*2+1+1', '*0*3*2+1+1', '*0*1*2+1+1', '+3'], + wantText: ['*a', '*b', '*a', 'foo'], }, ol: { description: 'Tests if ols properly get line numbers when in a normal OL', html: '
        1. a
        2. b
        3. c

        test

        ', - expectedLineAttribs: ['*0*1*2*3+1+1', '*0*1*2*3+1+1', '*0*1*2*3+1+1', '+4'], - expectedText: ['*a', '*b', '*c', 'test'], + wantLineAttribs: ['*0*1*2*3+1+1', '*0*1*2*3+1+1', '*0*1*2*3+1+1', '+4'], + wantText: ['*a', '*b', '*c', 'test'], noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?', }, lineDoBreakInOl: { description: 'A single completely empty line break within an ol should reset count if OL is closed off..', html: '
        1. should be 1

        hello

        1. should be 1
        2. should be 2

        ', - expectedLineAttribs: ['*0*1*2*3+1+b', '+5', '*0*1*2*4+1+b', '*0*1*2*4+1+b', ''], - expectedText: ['*should be 1', 'hello', '*should be 1', '*should be 2', ''], + wantLineAttribs: ['*0*1*2*3+1+b', '+5', '*0*1*2*4+1+b', '*0*1*2*4+1+b', ''], + wantText: ['*should be 1', 'hello', '*should be 1', '*should be 2', ''], noteToSelf: "Shouldn't include attribute marker in the

        line", }, testP: { description: 'A single

        should create a new line', html: '

        ', - expectedLineAttribs: ['', ''], - expectedText: ['', ''], + wantLineAttribs: ['', ''], + wantText: ['', ''], noteToSelf: '

        should create a line break but not break numbering', }, nestedOl: { description: 'Tests if ols properly get line numbers when in a normal OL', html: 'a
        1. b
          1. c
        notlist

        foo

        ', - expectedLineAttribs: ['+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1', '+7', '+3'], - expectedText: ['a', '*b', '*c', 'notlist', 'foo'], + wantLineAttribs: ['+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1', '+7', '+3'], + wantText: ['a', '*b', '*c', 'notlist', 'foo'], noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?', }, nestedOl2: { description: 'First item being an UL then subsequent being OL will fail', html: '
        • a
          1. b
          2. c
        ', - expectedLineAttribs: ['+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1'], - expectedText: ['a', '*b', '*c'], + wantLineAttribs: ['+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1'], + wantText: ['a', '*b', '*c'], noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?', disabled: true, }, lineDontBreakOL: { description: 'A single completely empty line break within an ol should NOT reset count', html: '
        1. should be 1
        2. should be 2
        3. should be 3

        ', - expectedLineAttribs: [], - expectedText: ['*should be 1', '*should be 2', '*should be 3'], + wantLineAttribs: [], + wantText: ['*should be 1', '*should be 2', '*should be 3'], noteToSelf: "

        should create a line break but not break numbering -- This is what I can't get working!", disabled: true, }, ignoreAnyTagsOutsideBody: { description: 'Content outside body should be ignored', html: 'titleempty
        ', - expectedLineAttribs: ['+5'], - expectedText: ['empty'], + wantLineAttribs: ['+5'], + wantText: ['empty'], }, lineWithMultipleSpaces: { description: 'Multiple spaces should be preserved', html: 'Text with more than one space.
        ', - expectedLineAttribs: ['+10'], - expectedText: ['Text with more than one space.'], + wantLineAttribs: ['+10'], + wantText: ['Text with more than one space.'], }, lineWithMultipleNonBreakingAndNormalSpaces: { description: 'non-breaking and normal space should be preserved', html: 'Text with  more   than  one space.
        ', - expectedLineAttribs: ['+10'], - expectedText: ['Text with more than one space.'], + wantLineAttribs: ['+10'], + wantText: ['Text with more than one space.'], }, multiplenbsp: { description: 'Multiple nbsp should be preserved', html: '  
        ', - expectedLineAttribs: ['+2'], - expectedText: [' '], + wantLineAttribs: ['+2'], + wantText: [' '], }, multipleNonBreakingSpaceBetweenWords: { description: 'Multiple nbsp between words ', html: '  word1  word2   word3
        ', - expectedLineAttribs: ['+m'], - expectedText: [' word1 word2 word3'], + wantLineAttribs: ['+m'], + wantText: [' word1 word2 word3'], }, nonBreakingSpacePreceededBySpaceBetweenWords: { description: 'A non-breaking space preceeded by a normal space', html: '  word1  word2  word3
        ', - expectedLineAttribs: ['+l'], - expectedText: [' word1 word2 word3'], + wantLineAttribs: ['+l'], + wantText: [' word1 word2 word3'], }, nonBreakingSpaceFollowededBySpaceBetweenWords: { description: 'A non-breaking space followed by a normal space', html: '  word1  word2  word3
        ', - expectedLineAttribs: ['+l'], - expectedText: [' word1 word2 word3'], + wantLineAttribs: ['+l'], + wantText: [' word1 word2 word3'], }, spacesAfterNewline: { description: 'Don\'t collapse spaces that follow a newline', html: 'something
        something
        ', - expectedLineAttribs: ['+9', '+m'], - expectedText: ['something', ' something'], + wantLineAttribs: ['+9', '+m'], + wantText: ['something', ' something'], }, spacesAfterNewlineP: { description: 'Don\'t collapse spaces that follow a empty paragraph', html: 'something

        something
        ', - expectedLineAttribs: ['+9', '', '+m'], - expectedText: ['something', '', ' something'], + wantLineAttribs: ['+9', '', '+m'], + wantText: ['something', '', ' something'], }, spacesAtEndOfLine: { description: 'Don\'t collapse spaces that preceed/follow a newline', html: 'something
        something
        ', - expectedLineAttribs: ['+l', '+m'], - expectedText: ['something ', ' something'], + wantLineAttribs: ['+l', '+m'], + wantText: ['something ', ' something'], }, spacesAtEndOfLineP: { description: 'Don\'t collapse spaces that preceed/follow a empty paragraph', html: 'something

        something
        ', - expectedLineAttribs: ['+l', '', '+m'], - expectedText: ['something ', '', ' something'], + wantLineAttribs: ['+l', '', '+m'], + wantText: ['something ', '', ' something'], }, nonBreakingSpacesAfterNewlines: { description: 'Don\'t collapse non-breaking spaces that follow a newline', html: 'something
           something
        ', - expectedLineAttribs: ['+9', '+c'], - expectedText: ['something', ' something'], + wantLineAttribs: ['+9', '+c'], + wantText: ['something', ' something'], }, nonBreakingSpacesAfterNewlinesP: { description: 'Don\'t collapse non-breaking spaces that follow a paragraph', html: 'something

           something
        ', - expectedLineAttribs: ['+9', '', '+c'], - expectedText: ['something', '', ' something'], + wantLineAttribs: ['+9', '', '+c'], + wantText: ['something', '', ' something'], }, preserveSpacesInsideElements: { description: 'Preserve all spaces when multiple are present', html: 'Need more space s !
        ', - expectedLineAttribs: ['+h*0+4+2'], - expectedText: ['Need more space s !'], + wantLineAttribs: ['+h*0+4+2'], + wantText: ['Need more space s !'], }, preserveSpacesAcrossNewlines: { description: 'Newlines and multiple spaces across newlines should be preserved', @@ -201,14 +201,14 @@ const tests = { space s !
        `, - expectedLineAttribs: ['+19*0+4+b'], - expectedText: ['Need more space s !'], + wantLineAttribs: ['+19*0+4+b'], + wantText: ['Need more space s !'], }, multipleNewLinesAtBeginning: { description: 'Multiple new lines at the beginning should be preserved', html: '

        first line

        second line
        ', - expectedLineAttribs: ['', '', '', '', '+a', '', '+b'], - expectedText: ['', '', '', '', 'first line', '', 'second line'], + wantLineAttribs: ['', '', '', '', '+a', '', '+b'], + wantText: ['', '', '', '', 'first line', '', 'second line'], }, multiLineParagraph: { description: 'A paragraph with multiple lines should not loose spaces when lines are combined', @@ -216,8 +216,8 @@ const tests = { а б в г ґ д е є ж з и і ї й к л м н о п р с т у ф х ц ч ш щ ю я ь

        `, - expectedLineAttribs: ['+1t'], - expectedText: ['а б в г ґ д е є ж з и і ї й к л м н о п р с т у ф х ц ч ш щ ю я ь'], + wantLineAttribs: ['+1t'], + wantText: ['а б в г ґ д е є ж з и і ї й к л м н о п р с т у ф х ц ч ш щ ю я ь'], }, multiLineParagraphWithPre: { description: 'lines in preformatted text should be kept intact', @@ -229,8 +229,8 @@ pre

        п р с т у ф х ц ч ш щ ю я ь

        `, - expectedLineAttribs: ['+11', '+8', '+5', '+2', '+3', '+r'], - expectedText: [ + wantLineAttribs: ['+11', '+8', '+5', '+2', '+3', '+r'], + wantText: [ 'а б в г ґ д е є ж з и і ї й к л м н о', 'multiple', 'lines', @@ -245,32 +245,32 @@ pre 1

        preline
         
        `, - expectedLineAttribs: ['+6', '+7'], - expectedText: [' 1 ', 'preline'], + wantLineAttribs: ['+6', '+7'], + wantText: [' 1 ', 'preline'], }, dontDeleteSpaceInsideElements: { description: 'Preserve spaces on the beginning and end of a element', html: 'Need more space s !
        ', - expectedLineAttribs: ['+f*0+3+1'], - expectedText: ['Need more space s !'], + wantLineAttribs: ['+f*0+3+1'], + wantText: ['Need more space s !'], }, dontDeleteSpaceOutsideElements: { description: 'Preserve spaces outside elements', html: 'Need more space s !
        ', - expectedLineAttribs: ['+g*0+1+2'], - expectedText: ['Need more space s !'], + wantLineAttribs: ['+g*0+1+2'], + wantText: ['Need more space s !'], }, dontDeleteSpaceAtEndOfElement: { description: 'Preserve spaces at the end of an element', html: 'Need more space s !
        ', - expectedLineAttribs: ['+g*0+2+1'], - expectedText: ['Need more space s !'], + wantLineAttribs: ['+g*0+2+1'], + wantText: ['Need more space s !'], }, dontDeleteSpaceAtBeginOfElements: { description: 'Preserve spaces at the start of an element', html: 'Need more space s !
        ', - expectedLineAttribs: ['+f*0+2+2'], - expectedText: ['Need more space s !'], + wantLineAttribs: ['+f*0+2+2'], + wantText: ['Need more space s !'], }, }; @@ -294,13 +294,13 @@ describe(__filename, function () { const cc = contentcollector.makeContentCollector(true, null, apool); cc.collectContent(doc); const result = cc.finish(); - const recievedAttributes = result.lineAttribs; - const expectedAttributes = testObj.expectedLineAttribs; - const recievedText = new Array(result.lines); - const expectedText = testObj.expectedText; + const gotAttributes = result.lineAttribs; + const wantAttributes = testObj.wantLineAttribs; + const gotText = new Array(result.lines); + const wantText = testObj.wantText; - assert.deepEqual(recievedText[0], expectedText); - assert.deepEqual(recievedAttributes, expectedAttributes); + assert.deepEqual(gotText[0], wantText); + assert.deepEqual(gotAttributes, wantAttributes); }); }); } From 56f617060a18d5830e29e582879030e87c2274ce Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 24 Jan 2021 01:57:11 -0500 Subject: [PATCH 142/357] tests: Fix missing call to `done` callback --- tests/backend/specs/contentcollector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/backend/specs/contentcollector.js b/tests/backend/specs/contentcollector.js index c9ebb7482..e6aff389a 100644 --- a/tests/backend/specs/contentcollector.js +++ b/tests/backend/specs/contentcollector.js @@ -284,7 +284,7 @@ describe(__filename, function () { }); } - it(testObj.description, function (done) { + it(testObj.description, async function () { const $ = cheerio.load(testObj.html); // Load HTML into Cheerio const doc = $('body')[0]; // Creates a dom-like representation of HTML // Create an empty attribute pool From 42c25b25363a97ec3ab7a1e5d1eafe345a87a14d Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 24 Jan 2021 00:52:24 -0500 Subject: [PATCH 143/357] openapi: Fix error logging --- src/node/hooks/express/openapi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/hooks/express/openapi.js b/src/node/hooks/express/openapi.js index d7059896a..444077fda 100644 --- a/src/node/hooks/express/openapi.js +++ b/src/node/hooks/express/openapi.js @@ -623,7 +623,7 @@ exports.expressCreateServer = (hookName, args, cb) => { } else { // an unknown error happened // log it and throw internal error - apiLogger.error(err); + apiLogger.error(err.stack || err.toString()); throw new createHTTPError.InternalError('internal error'); } }); From 74bb2f76ccd1474543390bd159bb52cb6119d3af Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 21 Jan 2021 02:30:29 -0500 Subject: [PATCH 144/357] contentcollector: Delete unused `domInterface` parameter --- src/static/js/ace2_inner.js | 2 +- src/static/js/contentcollector.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 95db2c14f..60b029f3b 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -1139,7 +1139,7 @@ function Ace2Inner() { lastDirtyNode = (lastDirtyNode && isNodeDirty(lastDirtyNode) && lastDirtyNode); if (firstDirtyNode && lastDirtyNode) { - const cc = makeContentCollector(isStyled, browser, rep.apool, null, className2Author); + const cc = makeContentCollector(isStyled, browser, rep.apool, className2Author); cc.notifySelection(selection); const dirtyNodes = []; for (let n = firstDirtyNode; n && diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index ab5781725..a77c4e5f0 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -32,8 +32,8 @@ const hooks = require('./pluginfw/hooks'); const sanitizeUnicode = (s) => UNorm.nfc(s); -const makeContentCollector = (collectStyles, abrowser, apool, domInterface, className2Author) => { - const dom = domInterface || { +const makeContentCollector = (collectStyles, abrowser, apool, className2Author) => { + const dom = { isNodeText: (n) => n.nodeType === 3, nodeTagName: (n) => n.tagName, nodeValue: (n) => n.nodeValue, From dd7fb1babee440e8395aa0862e96e57446199001 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 24 Jan 2021 18:03:13 -0500 Subject: [PATCH 145/357] contentcollector: Document the `dom` object --- src/static/js/contentcollector.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index a77c4e5f0..a717df3a3 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -33,26 +33,47 @@ const hooks = require('./pluginfw/hooks'); const sanitizeUnicode = (s) => UNorm.nfc(s); const makeContentCollector = (collectStyles, abrowser, apool, className2Author) => { + // This file is used both in browsers and with cheerio in Node.js (for importing HTML). Cheerio's + // Node-like objects are not 100% API compatible with the DOM Node specification; this `dom` + // object abstracts away the differences. const dom = { - isNodeText: (n) => n.nodeType === 3, + // .nodeType works with DOM and cheerio 0.22.0. Note: Cheerio 0.22.0 does not provide the + // Node.*_NODE constants, so they cannot be used here. + isNodeText: (n) => n.nodeType === 3, // Node.TEXT_NODE + // .tagName works with DOM and cheerio 0.22.0, but: + // * With DOM, .tagName is an uppercase string. + // * With cheerio 0.22.0, .tagName is a lowercase string. nodeTagName: (n) => n.tagName, + // .nodeValue works with DOM and cheerio 0.22.0. nodeValue: (n) => n.nodeValue, + // Returns the number of Node children (n.childNodes.length), not the number of Element children + // (n.children.length in DOM). nodeNumChildren: (n) => { + // .childNodes.length works with DOM and cheerio 0.22.0, except in cheerio the .childNodes + // property does not exist on text nodes (and maybe other non-element nodes). if (n.childNodes == null) return 0; return n.childNodes.length; }, + // Returns the i'th Node child (n.childNodes[i]), not the i'th Element child (n.children[i] in + // DOM). nodeChild: (n, i) => { if (n.childNodes.item == null) { + // .childNodes[] works with DOM and cheerio 0.22.0. return n.childNodes[i]; } + // .childNodes.item() works with DOM but not with cheerio 0.22.0. return n.childNodes.item(i); }, nodeProp: (n, p) => n[p], nodeAttr: (n, a) => { + // .getAttribute() works with DOM but not with cheerio 0.22.0. if (n.getAttribute != null) return n.getAttribute(a); + // .attribs[] works with cheerio 0.22.0 but not with DOM. if (n.attribs != null) return n.attribs[a]; return null; }, + // .innerHTML works with DOM but not with cheerio 0.22.0. Cheerio's Element-like objects have no + // equivalent. (Cheerio objects have an .html() method, but that isn't accessible here.) optNodeInnerHTML: (n) => n.innerHTML, }; From 99625950c8ceb78e652d2ac52e5dc4e521ae6a39 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 21 Jan 2021 02:27:15 -0500 Subject: [PATCH 146/357] contentcollector: Factor out call to `.toLowerCase()` --- src/static/js/contentcollector.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index a717df3a3..9f60b9033 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -43,7 +43,8 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) // .tagName works with DOM and cheerio 0.22.0, but: // * With DOM, .tagName is an uppercase string. // * With cheerio 0.22.0, .tagName is a lowercase string. - nodeTagName: (n) => n.tagName, + // For consistency, this function always returns a lowercase string. + nodeTagName: (n) => n.tagName && n.tagName.toLowerCase(), // .nodeValue works with DOM and cheerio 0.22.0. nodeValue: (n) => n.nodeValue, // Returns the number of Node children (n.childNodes.length), not the number of Element children @@ -88,7 +89,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) _blockElems[element] = 1; }); - const isBlockElement = (n) => !!_blockElems[(dom.nodeTagName(n) || '').toLowerCase()]; + const isBlockElement = (n) => !!_blockElems[dom.nodeTagName(n) || '']; const textify = (str) => sanitizeUnicode( str.replace(/(\n | \n)/g, ' ') @@ -406,7 +407,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) } } } else { - const tname = (dom.nodeTagName(node) || '').toLowerCase(); + const tname = dom.nodeTagName(node) || ''; if (tname === 'img') { hooks.callAll('collectContentImage', { From 8763c3bb2965874d2497f22d6bb347bc92aad22b Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 21 Jan 2021 00:30:55 -0500 Subject: [PATCH 147/357] contentcollector: Fix Element attribute accesses The `attribs` property is only available on cheerio's Element-like objects; DOM Element objects do not have an `attribs` property. Switch to `dom.nodeAttr()` to fix the logic for browsers. --- src/static/js/contentcollector.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 9f60b9033..ecd84c6bb 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -491,14 +491,14 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) cc.doAttrib(state, 'strikethrough'); } if (tname === 'ul' || tname === 'ol') { - let type = node.attribs ? node.attribs.class : null; + let type = dom.nodeAttr(node, 'class'); const rr = cls && /(?:^| )list-([a-z]+[0-9]+)\b/.exec(cls); // lists do not need to have a type, so before we make a wrong guess // check if we find a better hint within the node's children if (!rr && !type) { for (const i in node.children) { if (node.children[i] && node.children[i].name === 'ul') { - type = node.children[i].attribs.class; + type = dom.nodeAttr(node.children[i], 'class'); if (type) { break; } @@ -509,8 +509,8 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) type = rr[1]; } else { if (tname === 'ul') { - if ((type && type.match('indent')) || - (node.attribs && node.attribs.class && node.attribs.class.match('indent'))) { + const cls = dom.nodeAttr(node, 'class'); + if ((type && type.match('indent')) || (cls && cls.match('indent'))) { type = 'indent'; } else { type = 'bullet'; From 3cfec58948f652f3306ec0cb68d3fbe57ae1805b Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 24 Jan 2021 18:20:19 -0500 Subject: [PATCH 148/357] contentcollector: Rename `dom` functions for consistency with DOM spec --- src/static/js/contentcollector.js | 46 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index ecd84c6bb..884f4b693 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -39,17 +39,17 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) const dom = { // .nodeType works with DOM and cheerio 0.22.0. Note: Cheerio 0.22.0 does not provide the // Node.*_NODE constants, so they cannot be used here. - isNodeText: (n) => n.nodeType === 3, // Node.TEXT_NODE + isTextNode: (n) => n.nodeType === 3, // Node.TEXT_NODE // .tagName works with DOM and cheerio 0.22.0, but: // * With DOM, .tagName is an uppercase string. // * With cheerio 0.22.0, .tagName is a lowercase string. // For consistency, this function always returns a lowercase string. - nodeTagName: (n) => n.tagName && n.tagName.toLowerCase(), + tagName: (n) => n.tagName && n.tagName.toLowerCase(), // .nodeValue works with DOM and cheerio 0.22.0. nodeValue: (n) => n.nodeValue, // Returns the number of Node children (n.childNodes.length), not the number of Element children // (n.children.length in DOM). - nodeNumChildren: (n) => { + numChildNodes: (n) => { // .childNodes.length works with DOM and cheerio 0.22.0, except in cheerio the .childNodes // property does not exist on text nodes (and maybe other non-element nodes). if (n.childNodes == null) return 0; @@ -57,7 +57,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) }, // Returns the i'th Node child (n.childNodes[i]), not the i'th Element child (n.children[i] in // DOM). - nodeChild: (n, i) => { + childNode: (n, i) => { if (n.childNodes.item == null) { // .childNodes[] works with DOM and cheerio 0.22.0. return n.childNodes[i]; @@ -66,7 +66,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) return n.childNodes.item(i); }, nodeProp: (n, p) => n[p], - nodeAttr: (n, a) => { + getAttribute: (n, a) => { // .getAttribute() works with DOM but not with cheerio 0.22.0. if (n.getAttribute != null) return n.getAttribute(a); // .attribs[] works with cheerio 0.22.0 but not with DOM. @@ -75,7 +75,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) }, // .innerHTML works with DOM but not with cheerio 0.22.0. Cheerio's Element-like objects have no // equivalent. (Cheerio objects have an .html() method, but that isn't accessible here.) - optNodeInnerHTML: (n) => n.innerHTML, + innerHTML: (n) => n.innerHTML, }; const _blockElems = { @@ -89,7 +89,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) _blockElems[element] = 1; }); - const isBlockElement = (n) => !!_blockElems[dom.nodeTagName(n) || '']; + const isBlockElement = (n) => !!_blockElems[dom.tagName(n) || '']; const textify = (str) => sanitizeUnicode( str.replace(/(\n | \n)/g, ' ') @@ -145,13 +145,13 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) let selEnd = [-1, -1]; const _isEmpty = (node, state) => { // consider clean blank lines pasted in IE to be empty - if (dom.nodeNumChildren(node) === 0) return true; - if (dom.nodeNumChildren(node) === 1 && + if (dom.numChildNodes(node) === 0) return true; + if (dom.numChildNodes(node) === 1 && getAssoc(node, 'shouldBeEmpty') && - dom.optNodeInnerHTML(node) === ' ' && + dom.innerHTML(node) === ' ' && !getAssoc(node, 'unpasted')) { if (state) { - const child = dom.nodeChild(node, 0); + const child = dom.childNode(node, 0); _reachPoint(child, 0, state); _reachPoint(child, 1, state); } @@ -171,7 +171,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) }; const _reachBlockPoint = (nd, idx, state) => { - if (!dom.isNodeText(nd)) _reachPoint(nd, idx, state); + if (!dom.isTextNode(nd)) _reachPoint(nd, idx, state); }; const _reachPoint = (nd, idx, state) => { @@ -338,9 +338,9 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) const startLine = lines.length() - 1; _reachBlockPoint(node, 0, state); - if (dom.isNodeText(node)) { + if (dom.isTextNode(node)) { let txt = dom.nodeValue(node); - const tname = dom.nodeAttr(node.parentNode, 'name'); + const tname = dom.getAttribute(node.parentNode, 'name'); const txtFromHook = hooks.callAll('collectContentLineText', { cc: this, @@ -407,7 +407,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) } } } else { - const tname = dom.nodeTagName(node) || ''; + const tname = dom.tagName(node) || ''; if (tname === 'img') { hooks.callAll('collectContentImage', { @@ -425,7 +425,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) if (tname === 'br') { this.breakLine = true; - const tvalue = dom.nodeAttr(node, 'value'); + const tvalue = dom.getAttribute(node, 'value'); const induceLineBreak = hooks.callAll('collectContentLineBreak', { cc: this, state, @@ -443,8 +443,8 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) } else if (tname === 'script' || tname === 'style') { // ignore } else if (!isEmpty) { - let styl = dom.nodeAttr(node, 'style'); - let cls = dom.nodeAttr(node, 'class'); + let styl = dom.getAttribute(node, 'style'); + let cls = dom.getAttribute(node, 'class'); let isPre = (tname === 'pre'); if ((!isPre) && abrowser && abrowser.safari) { isPre = (styl && /\bwhite-space:\s*pre\b/i.exec(styl)); @@ -491,14 +491,14 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) cc.doAttrib(state, 'strikethrough'); } if (tname === 'ul' || tname === 'ol') { - let type = dom.nodeAttr(node, 'class'); + let type = dom.getAttribute(node, 'class'); const rr = cls && /(?:^| )list-([a-z]+[0-9]+)\b/.exec(cls); // lists do not need to have a type, so before we make a wrong guess // check if we find a better hint within the node's children if (!rr && !type) { for (const i in node.children) { if (node.children[i] && node.children[i].name === 'ul') { - type = dom.nodeAttr(node.children[i], 'class'); + type = dom.getAttribute(node.children[i], 'class'); if (type) { break; } @@ -509,7 +509,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) type = rr[1]; } else { if (tname === 'ul') { - const cls = dom.nodeAttr(node, 'class'); + const cls = dom.getAttribute(node, 'class'); if ((type && type.match('indent')) || (cls && cls.match('indent'))) { type = 'indent'; } else { @@ -581,9 +581,9 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) } } - const nc = dom.nodeNumChildren(node); + const nc = dom.numChildNodes(node); for (let i = 0; i < nc; i++) { - const c = dom.nodeChild(node, i); + const c = dom.childNode(node, i); cc.collectContent(c, state); } From d0bfb54c0ad695854e09e6a8f0bce3814f72dc99 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 20 Jan 2021 23:50:33 -0500 Subject: [PATCH 149/357] contentcollector: Avoid `for..in` iteration of object properties `for..in` iterates over inherited properties, which is almost never desired. In most cases there aren't any inherited enumerable properties so it's not that big of a deal, but in the case of HTMLCollection it's very bad because it iterates over every entry twice (once by numerical index and once by name) plus it includes the `length` property in the iteration. --- src/static/js/contentcollector.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 884f4b693..86043baf8 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -250,8 +250,8 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) const _recalcAttribString = (state) => { const lst = []; - for (const a in state.attribs) { - if (state.attribs[a]) { + for (const [a, count] of Object.entries(state.attribs)) { + if (count) { // The following splitting of the attribute name is a workaround // to enable the content collector to store key-value attributes // see https://github.com/ether/etherpad-lite/issues/2567 for more information @@ -496,9 +496,18 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) // lists do not need to have a type, so before we make a wrong guess // check if we find a better hint within the node's children if (!rr && !type) { - for (const i in node.children) { - if (node.children[i] && node.children[i].name === 'ul') { - type = dom.getAttribute(node.children[i], 'class'); + // If `node` is from the DOM (not cheerio) then it implements the ParentNode interface + // and `node.children` is a HTMLCollection. The DOM + Web IDL specs guarantee that + // HTMLCollection implements the iterable protocol, so for..of iteration should always + // work. See: https://stackoverflow.com/a/41759532. Cheerio behaves the same with + // regard to iteration. + // + // TODO: The set of Nodes included in node.children differs between the DOM and + // cheerio 0.22.0: cheerio includes all child Nodes (including non-Element Nodes) + // whereas the DOM only includes Nodes that are Elements. + for (const child of node.children) { + if (child && child.name === 'ul') { + type = dom.getAttribute(child, 'class'); if (type) { break; } From fc2420c244b682ad514363fbdb7cac3d3ae574bf Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 24 Jan 2021 18:50:50 -0500 Subject: [PATCH 150/357] contentcollector: Fix iteration over child Nodes In the DOM, `.children` only includes children that are Element objects. In cheerio 0.22.0, `.children` includes all child Nodes, not just Elements. Use `dom.numChildNodes()` and `dom.childNode()` so that browsers behave the same as cheerio. --- src/static/js/contentcollector.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 86043baf8..f940f257b 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -496,16 +496,8 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) // lists do not need to have a type, so before we make a wrong guess // check if we find a better hint within the node's children if (!rr && !type) { - // If `node` is from the DOM (not cheerio) then it implements the ParentNode interface - // and `node.children` is a HTMLCollection. The DOM + Web IDL specs guarantee that - // HTMLCollection implements the iterable protocol, so for..of iteration should always - // work. See: https://stackoverflow.com/a/41759532. Cheerio behaves the same with - // regard to iteration. - // - // TODO: The set of Nodes included in node.children differs between the DOM and - // cheerio 0.22.0: cheerio includes all child Nodes (including non-Element Nodes) - // whereas the DOM only includes Nodes that are Elements. - for (const child of node.children) { + for (let i = 0; i < dom.numChildNodes(node); i++) { + const child = dom.childNode(node, i); if (child && child.name === 'ul') { type = dom.getAttribute(child, 'class'); if (type) { From b811030846b72d5449f58510e32c75e12995c987 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 21 Jan 2021 02:34:50 -0500 Subject: [PATCH 151/357] contentcollector: Delete unnecessary truthiness check --- src/static/js/contentcollector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index f940f257b..f4fc015cf 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -498,7 +498,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) if (!rr && !type) { for (let i = 0; i < dom.numChildNodes(node); i++) { const child = dom.childNode(node, i); - if (child && child.name === 'ul') { + if (child.name === 'ul') { type = dom.getAttribute(child, 'class'); if (type) { break; From b547ce9a47eece44a1c23e22aa90e39a996d0710 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 21 Jan 2021 02:35:44 -0500 Subject: [PATCH 152/357] contentcollector: Invert logic to improve readability --- src/static/js/contentcollector.js | 42 ++++++++++++++----------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index f4fc015cf..c03ab6d64 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -251,24 +251,23 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) const _recalcAttribString = (state) => { const lst = []; for (const [a, count] of Object.entries(state.attribs)) { - if (count) { - // The following splitting of the attribute name is a workaround - // to enable the content collector to store key-value attributes - // see https://github.com/ether/etherpad-lite/issues/2567 for more information - // in long term the contentcollector should be refactored to get rid of this workaround - const ATTRIBUTE_SPLIT_STRING = '::'; + if (!count) continue; + // The following splitting of the attribute name is a workaround + // to enable the content collector to store key-value attributes + // see https://github.com/ether/etherpad-lite/issues/2567 for more information + // in long term the contentcollector should be refactored to get rid of this workaround + const ATTRIBUTE_SPLIT_STRING = '::'; - // see if attributeString is splittable - const attributeSplits = a.split(ATTRIBUTE_SPLIT_STRING); - if (attributeSplits.length > 1) { - // the attribute name follows the convention key::value - // so save it as a key value attribute - lst.push([attributeSplits[0], attributeSplits[1]]); - } else { - // the "normal" case, the attribute is just a switch - // so set it true - lst.push([a, 'true']); - } + // see if attributeString is splittable + const attributeSplits = a.split(ATTRIBUTE_SPLIT_STRING); + if (attributeSplits.length > 1) { + // the attribute name follows the convention key::value + // so save it as a key value attribute + lst.push([attributeSplits[0], attributeSplits[1]]); + } else { + // the "normal" case, the attribute is just a switch + // so set it true + lst.push([a, 'true']); } } if (state.authorLevel > 0) { @@ -498,12 +497,9 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) if (!rr && !type) { for (let i = 0; i < dom.numChildNodes(node); i++) { const child = dom.childNode(node, i); - if (child.name === 'ul') { - type = dom.getAttribute(child, 'class'); - if (type) { - break; - } - } + if (child.name !== 'ul') continue; + type = dom.getAttribute(child, 'class'); + if (type) break; } } if (rr && rr[1]) { From 4e220538a15b0b6108251b18141dc43a0beccc11 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 21 Jan 2021 00:06:43 -0500 Subject: [PATCH 153/357] contentcollector: Use destructuring to improve readability --- src/static/js/contentcollector.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index c03ab6d64..b20c2583a 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -425,7 +425,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) if (tname === 'br') { this.breakLine = true; const tvalue = dom.getAttribute(node, 'value'); - const induceLineBreak = hooks.callAll('collectContentLineBreak', { + const [startNewLine = true] = hooks.callAll('collectContentLineBreak', { cc: this, state, tname, @@ -433,9 +433,6 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) styl: null, cls: null, }); - const startNewLine = ( - typeof (induceLineBreak) === 'object' && - induceLineBreak.length === 0) ? true : induceLineBreak[0]; if (startNewLine) { cc.startNewLine(state); } From e3a47e48f9ee2391fb4e60c97eb7137855e580af Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 22 Jan 2021 02:06:06 -0500 Subject: [PATCH 154/357] contentcollector: Fix collectContentLineText hook Before, the hook always ignored the return values provided by the hook functions. Now the hook functions can change the text by either returning a string or setting `context.text` to the desired value. Also drop the `styl` and `cls` context properties. They were never documented and they were always null. --- doc/api/hooks_client-side.md | 15 ++++++++++++++- src/static/js/contentcollector.js | 24 +++++++----------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/doc/api/hooks_client-side.md b/doc/api/hooks_client-side.md index 478f5d0a5..e87536cfe 100755 --- a/doc/api/hooks_client-side.md +++ b/doc/api/hooks_client-side.md @@ -421,7 +421,20 @@ Things in context: 4. text - the text for that line This hook allows you to validate/manipulate the text before it's sent to the -server side. The return value should be the validated/manipulated text. +server side. To change the text, either: + +* Set the `text` context property to the desired value and return `undefined`. +* (Deprecated) Return a string. If a hook function changes the `text` context + property, the return value is ignored. If no hook function changes `text` but + multiple hook functions return a string, the first one wins. + +Example: + +``` +exports.collectContentLineText = (hookName, context) => { + context.text = tweakText(context.text); +}; +``` ## collectContentLineBreak diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index b20c2583a..357b50195 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -338,24 +338,14 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) _reachBlockPoint(node, 0, state); if (dom.isTextNode(node)) { - let txt = dom.nodeValue(node); const tname = dom.getAttribute(node.parentNode, 'name'); - - const txtFromHook = hooks.callAll('collectContentLineText', { - cc: this, - state, - tname, - node, - text: txt, - styl: null, - cls: null, - }); - - if (typeof (txtFromHook) === 'object') { - txt = dom.nodeValue(node); - } else if (txtFromHook) { - txt = txtFromHook; - } + const context = {cc: this, state, tname, node, text: dom.nodeValue(node)}; + // Hook functions may either return a string (deprecated) or modify context.text. If any hook + // function modifies context.text then all returned strings are ignored. If no hook functions + // modify context.text, the first hook function to return a string wins. + const [hookTxt] = + hooks.callAll('collectContentLineText', context).filter((s) => typeof s === 'string'); + let txt = context.text === dom.nodeValue(node) && hookTxt != null ? hookTxt : context.text; let rest = ''; let x = 0; // offset into original text From 1d36549152c383264877b0f9a395ab93a7408b13 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 24 Jan 2021 19:27:00 -0500 Subject: [PATCH 155/357] contentcollector: Delete unnecessary parentheses --- src/static/js/contentcollector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 357b50195..c7db55fa0 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -509,7 +509,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) // This has undesirable behavior in Chrome but is right in other browsers. // See https://github.com/ether/etherpad-lite/issues/2412 for reasoning if (!abrowser.chrome) oldListTypeOrNull = (_enterList(state, undefined) || 'none'); - } else if ((tname === 'li')) { + } else if (tname === 'li') { state.lineAttributes.start = state.start || 0; _recalcAttribString(state); if (state.lineAttributes.list.indexOf('number') !== -1) { From e3ec9d9a4c9afba34a3edb14f73c7a1fa1f4988f Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 24 Jan 2021 19:29:56 -0500 Subject: [PATCH 156/357] contentcollector: Fix parent node access The `parent` property is only available on cheerio's Node-like objects; DOM Node objects do not have a `parent` property. Switch to the `parentNode` property so that the code works in browsers as well as cheerio. --- src/static/js/contentcollector.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index c7db55fa0..0b85b434b 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -519,7 +519,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) Note how the
          item has to be inside a
        1. Because of this we don't increment the start number */ - if (node.parent && node.parent.name !== 'ol') { + if (node.parentNode && node.parentNode.name !== 'ol') { /* TODO: start number has to increment based on indentLevel(numberX) This means we have to build an object IE @@ -536,7 +536,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) } } // UL list items never modify the start value. - if (node.parent && node.parent.name === 'ul') { + if (node.parentNode && node.parentNode.name === 'ul') { state.start++; // TODO, this is hacky. // Because if the first item is an UL it will increment a list no? From 075969aea08e5cf767db489f8feeadbc0ef0ae50 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 21 Jan 2021 02:46:22 -0500 Subject: [PATCH 157/357] contentcollector: Fix Element tag name fetch The `name` property is only available on cheerio's Element-like objects; DOM Element objects do not have a `name` property. Switch to `dom.tagName()` to fix the logic for browsers. --- src/static/js/contentcollector.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 0b85b434b..89dfd8ff8 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -484,7 +484,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) if (!rr && !type) { for (let i = 0; i < dom.numChildNodes(node); i++) { const child = dom.childNode(node, i); - if (child.name !== 'ul') continue; + if (dom.tagName(child) !== 'ul') continue; type = dom.getAttribute(child, 'class'); if (type) break; } @@ -519,7 +519,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) Note how the
            item has to be inside a
          1. Because of this we don't increment the start number */ - if (node.parentNode && node.parentNode.name !== 'ol') { + if (node.parentNode && dom.tagName(node.parentNode) !== 'ol') { /* TODO: start number has to increment based on indentLevel(numberX) This means we have to build an object IE @@ -536,7 +536,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) } } // UL list items never modify the start value. - if (node.parentNode && node.parentNode.name === 'ul') { + if (node.parentNode && dom.tagName(node.parentNode) === 'ul') { state.start++; // TODO, this is hacky. // Because if the first item is an UL it will increment a list no? From 1cb5453aeb98bfd5521500bfbead23ad92f137c4 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 24 Jan 2021 02:07:33 -0500 Subject: [PATCH 158/357] contentcollector: Skip over non-Text, non-Element Nodes --- src/static/js/contentcollector.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 89dfd8ff8..28ac8cb6e 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -39,6 +39,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) const dom = { // .nodeType works with DOM and cheerio 0.22.0. Note: Cheerio 0.22.0 does not provide the // Node.*_NODE constants, so they cannot be used here. + isElementNode: (n) => n.nodeType === 1, // Node.ELEMENT_NODE isTextNode: (n) => n.nodeType === 3, // Node.TEXT_NODE // .tagName works with DOM and cheerio 0.22.0, but: // * With DOM, .tagName is an uppercase string. @@ -395,7 +396,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) cc.startNewLine(state); } } - } else { + } else if (dom.isElementNode(node)) { const tname = dom.tagName(node) || ''; if (tname === 'img') { From 275f041fbb39c0c6518f7f26d73fd760045412d0 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 25 Jan 2021 01:29:08 -0500 Subject: [PATCH 159/357] contentcollector: Simplify child node access --- src/static/js/contentcollector.js | 34 ++++++++----------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index 28ac8cb6e..ff9b5d9e4 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -48,24 +48,9 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) tagName: (n) => n.tagName && n.tagName.toLowerCase(), // .nodeValue works with DOM and cheerio 0.22.0. nodeValue: (n) => n.nodeValue, - // Returns the number of Node children (n.childNodes.length), not the number of Element children - // (n.children.length in DOM). - numChildNodes: (n) => { - // .childNodes.length works with DOM and cheerio 0.22.0, except in cheerio the .childNodes - // property does not exist on text nodes (and maybe other non-element nodes). - if (n.childNodes == null) return 0; - return n.childNodes.length; - }, - // Returns the i'th Node child (n.childNodes[i]), not the i'th Element child (n.children[i] in - // DOM). - childNode: (n, i) => { - if (n.childNodes.item == null) { - // .childNodes[] works with DOM and cheerio 0.22.0. - return n.childNodes[i]; - } - // .childNodes.item() works with DOM but not with cheerio 0.22.0. - return n.childNodes.item(i); - }, + // .childNodes works with DOM and cheerio 0.22.0, except in cheerio the .childNodes property + // does not exist on text nodes (and maybe other non-element nodes). + childNodes: (n) => n.childNodes || [], nodeProp: (n, p) => n[p], getAttribute: (n, a) => { // .getAttribute() works with DOM but not with cheerio 0.22.0. @@ -146,13 +131,13 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) let selEnd = [-1, -1]; const _isEmpty = (node, state) => { // consider clean blank lines pasted in IE to be empty - if (dom.numChildNodes(node) === 0) return true; - if (dom.numChildNodes(node) === 1 && + if (dom.childNodes(node).length === 0) return true; + if (dom.childNodes(node).length === 1 && getAssoc(node, 'shouldBeEmpty') && dom.innerHTML(node) === ' ' && !getAssoc(node, 'unpasted')) { if (state) { - const child = dom.childNode(node, 0); + const child = dom.childNodes(node)[0]; _reachPoint(child, 0, state); _reachPoint(child, 1, state); } @@ -483,8 +468,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) // lists do not need to have a type, so before we make a wrong guess // check if we find a better hint within the node's children if (!rr && !type) { - for (let i = 0; i < dom.numChildNodes(node); i++) { - const child = dom.childNode(node, i); + for (const child of dom.childNodes(node)) { if (dom.tagName(child) !== 'ul') continue; type = dom.getAttribute(child, 'class'); if (type) break; @@ -566,9 +550,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) } } - const nc = dom.numChildNodes(node); - for (let i = 0; i < nc; i++) { - const c = dom.childNode(node, i); + for (const c of dom.childNodes(node)) { cc.collectContent(c, state); } From e5b45cc98479470463cb66882f61121807d586c7 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 25 Jan 2021 00:33:18 -0500 Subject: [PATCH 160/357] contentcollector: Delete unnecessary `dom` functions And move the remaining functions out of the `makeContentCollector()` function. --- src/static/js/contentcollector.js | 107 +++++++++++++++--------------- 1 file changed, 52 insertions(+), 55 deletions(-) diff --git a/src/static/js/contentcollector.js b/src/static/js/contentcollector.js index ff9b5d9e4..7ac27168f 100644 --- a/src/static/js/contentcollector.js +++ b/src/static/js/contentcollector.js @@ -32,38 +32,31 @@ const hooks = require('./pluginfw/hooks'); const sanitizeUnicode = (s) => UNorm.nfc(s); -const makeContentCollector = (collectStyles, abrowser, apool, className2Author) => { - // This file is used both in browsers and with cheerio in Node.js (for importing HTML). Cheerio's - // Node-like objects are not 100% API compatible with the DOM Node specification; this `dom` - // object abstracts away the differences. - const dom = { - // .nodeType works with DOM and cheerio 0.22.0. Note: Cheerio 0.22.0 does not provide the - // Node.*_NODE constants, so they cannot be used here. - isElementNode: (n) => n.nodeType === 1, // Node.ELEMENT_NODE - isTextNode: (n) => n.nodeType === 3, // Node.TEXT_NODE - // .tagName works with DOM and cheerio 0.22.0, but: - // * With DOM, .tagName is an uppercase string. - // * With cheerio 0.22.0, .tagName is a lowercase string. - // For consistency, this function always returns a lowercase string. - tagName: (n) => n.tagName && n.tagName.toLowerCase(), - // .nodeValue works with DOM and cheerio 0.22.0. - nodeValue: (n) => n.nodeValue, - // .childNodes works with DOM and cheerio 0.22.0, except in cheerio the .childNodes property - // does not exist on text nodes (and maybe other non-element nodes). - childNodes: (n) => n.childNodes || [], - nodeProp: (n, p) => n[p], - getAttribute: (n, a) => { - // .getAttribute() works with DOM but not with cheerio 0.22.0. - if (n.getAttribute != null) return n.getAttribute(a); - // .attribs[] works with cheerio 0.22.0 but not with DOM. - if (n.attribs != null) return n.attribs[a]; - return null; - }, - // .innerHTML works with DOM but not with cheerio 0.22.0. Cheerio's Element-like objects have no - // equivalent. (Cheerio objects have an .html() method, but that isn't accessible here.) - innerHTML: (n) => n.innerHTML, - }; +// This file is used both in browsers and with cheerio in Node.js (for importing HTML). Cheerio's +// Node-like objects are not 100% API compatible with the DOM specification; the following functions +// abstract away the differences. +// .nodeType works with DOM and cheerio 0.22.0, but cheerio 0.22.0 does not provide the Node.*_NODE +// constants so they cannot be used here. +const isElementNode = (n) => n.nodeType === 1; // Node.ELEMENT_NODE +const isTextNode = (n) => n.nodeType === 3; // Node.TEXT_NODE +// .tagName works with DOM and cheerio 0.22.0, but: +// * With DOM, .tagName is an uppercase string. +// * With cheerio 0.22.0, .tagName is a lowercase string. +// For consistency, this function always returns a lowercase string. +const tagName = (n) => n.tagName && n.tagName.toLowerCase(); +// .childNodes works with DOM and cheerio 0.22.0, except in cheerio the .childNodes property does +// not exist on text nodes (and maybe other non-element nodes). +const childNodes = (n) => n.childNodes || []; +const getAttribute = (n, a) => { + // .getAttribute() works with DOM but not with cheerio 0.22.0. + if (n.getAttribute != null) return n.getAttribute(a); + // .attribs[] works with cheerio 0.22.0 but not with DOM. + if (n.attribs != null) return n.attribs[a]; + return null; +}; + +const makeContentCollector = (collectStyles, abrowser, apool, className2Author) => { const _blockElems = { div: 1, p: 1, @@ -75,7 +68,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) _blockElems[element] = 1; }); - const isBlockElement = (n) => !!_blockElems[dom.tagName(n) || '']; + const isBlockElement = (n) => !!_blockElems[tagName(n) || '']; const textify = (str) => sanitizeUnicode( str.replace(/(\n | \n)/g, ' ') @@ -83,7 +76,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) .replace(/\xa0/g, ' ') .replace(/\t/g, ' ')); - const getAssoc = (node, name) => dom.nodeProp(node, `_magicdom_${name}`); + const getAssoc = (node, name) => node[`_magicdom_${name}`]; const lines = (() => { const textArray = []; @@ -131,13 +124,17 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) let selEnd = [-1, -1]; const _isEmpty = (node, state) => { // consider clean blank lines pasted in IE to be empty - if (dom.childNodes(node).length === 0) return true; - if (dom.childNodes(node).length === 1 && + if (childNodes(node).length === 0) return true; + if (childNodes(node).length === 1 && getAssoc(node, 'shouldBeEmpty') && - dom.innerHTML(node) === ' ' && + // Note: The .innerHTML property exists on DOM Element objects but not on cheerio's + // Element-like objects (cheerio v0.22.0) so this equality check will always be false. + // Cheerio's Element-like objects have no equivalent to .innerHTML. (Cheerio objects have an + // .html() method, but that isn't accessible here.) + node.innerHTML === ' ' && !getAssoc(node, 'unpasted')) { if (state) { - const child = dom.childNodes(node)[0]; + const child = childNodes(node)[0]; _reachPoint(child, 0, state); _reachPoint(child, 1, state); } @@ -157,7 +154,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) }; const _reachBlockPoint = (nd, idx, state) => { - if (!dom.isTextNode(nd)) _reachPoint(nd, idx, state); + if (!isTextNode(nd)) _reachPoint(nd, idx, state); }; const _reachPoint = (nd, idx, state) => { @@ -323,15 +320,15 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) const startLine = lines.length() - 1; _reachBlockPoint(node, 0, state); - if (dom.isTextNode(node)) { - const tname = dom.getAttribute(node.parentNode, 'name'); - const context = {cc: this, state, tname, node, text: dom.nodeValue(node)}; + if (isTextNode(node)) { + const tname = getAttribute(node.parentNode, 'name'); + const context = {cc: this, state, tname, node, text: node.nodeValue}; // Hook functions may either return a string (deprecated) or modify context.text. If any hook // function modifies context.text then all returned strings are ignored. If no hook functions // modify context.text, the first hook function to return a string wins. const [hookTxt] = hooks.callAll('collectContentLineText', context).filter((s) => typeof s === 'string'); - let txt = context.text === dom.nodeValue(node) && hookTxt != null ? hookTxt : context.text; + let txt = context.text === node.nodeValue && hookTxt != null ? hookTxt : context.text; let rest = ''; let x = 0; // offset into original text @@ -381,8 +378,8 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) cc.startNewLine(state); } } - } else if (dom.isElementNode(node)) { - const tname = dom.tagName(node) || ''; + } else if (isElementNode(node)) { + const tname = tagName(node) || ''; if (tname === 'img') { hooks.callAll('collectContentImage', { @@ -400,7 +397,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) if (tname === 'br') { this.breakLine = true; - const tvalue = dom.getAttribute(node, 'value'); + const tvalue = getAttribute(node, 'value'); const [startNewLine = true] = hooks.callAll('collectContentLineBreak', { cc: this, state, @@ -415,8 +412,8 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) } else if (tname === 'script' || tname === 'style') { // ignore } else if (!isEmpty) { - let styl = dom.getAttribute(node, 'style'); - let cls = dom.getAttribute(node, 'class'); + let styl = getAttribute(node, 'style'); + let cls = getAttribute(node, 'class'); let isPre = (tname === 'pre'); if ((!isPre) && abrowser && abrowser.safari) { isPre = (styl && /\bwhite-space:\s*pre\b/i.exec(styl)); @@ -463,14 +460,14 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) cc.doAttrib(state, 'strikethrough'); } if (tname === 'ul' || tname === 'ol') { - let type = dom.getAttribute(node, 'class'); + let type = getAttribute(node, 'class'); const rr = cls && /(?:^| )list-([a-z]+[0-9]+)\b/.exec(cls); // lists do not need to have a type, so before we make a wrong guess // check if we find a better hint within the node's children if (!rr && !type) { - for (const child of dom.childNodes(node)) { - if (dom.tagName(child) !== 'ul') continue; - type = dom.getAttribute(child, 'class'); + for (const child of childNodes(node)) { + if (tagName(child) !== 'ul') continue; + type = getAttribute(child, 'class'); if (type) break; } } @@ -478,7 +475,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) type = rr[1]; } else { if (tname === 'ul') { - const cls = dom.getAttribute(node, 'class'); + const cls = getAttribute(node, 'class'); if ((type && type.match('indent')) || (cls && cls.match('indent'))) { type = 'indent'; } else { @@ -504,7 +501,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) Note how the
              item has to be inside a
            1. Because of this we don't increment the start number */ - if (node.parentNode && dom.tagName(node.parentNode) !== 'ol') { + if (node.parentNode && tagName(node.parentNode) !== 'ol') { /* TODO: start number has to increment based on indentLevel(numberX) This means we have to build an object IE @@ -521,7 +518,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) } } // UL list items never modify the start value. - if (node.parentNode && dom.tagName(node.parentNode) === 'ul') { + if (node.parentNode && tagName(node.parentNode) === 'ul') { state.start++; // TODO, this is hacky. // Because if the first item is an UL it will increment a list no? @@ -550,7 +547,7 @@ const makeContentCollector = (collectStyles, abrowser, apool, className2Author) } } - for (const c of dom.childNodes(node)) { + for (const c of childNodes(node)) { cc.collectContent(c, state); } From 53092fa7af7f6c54596a4ce33bcd580eaedaf2f0 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 28 Jan 2021 00:35:46 -0500 Subject: [PATCH 161/357] db/Pad: Call padCopy and padRemove hooks asynchronously --- src/node/db/Pad.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/node/db/Pad.js b/src/node/db/Pad.js index 67842c14d..965f2793f 100644 --- a/src/node/db/Pad.js +++ b/src/node/db/Pad.js @@ -418,7 +418,7 @@ Pad.prototype.copy = async function copy(destinationID, force) { await padManager.getPad(destinationID, null); // this runs too early. // let the plugins know the pad was copied - hooks.callAll('padCopy', {originalPad: this, destinationID}); + await hooks.aCallAll('padCopy', {originalPad: this, destinationID}); return {padID: destinationID}; }; @@ -520,7 +520,7 @@ Pad.prototype.copyPadWithoutHistory = async function copyPadWithoutHistory(desti const changeset = Changeset.pack(oldLength, newLength, assem.toString(), newText); newPad.appendRevision(changeset); - hooks.callAll('padCopy', {originalPad: this, destinationID}); + await hooks.aCallAll('padCopy', {originalPad: this, destinationID}); return {padID: destinationID}; }; @@ -574,7 +574,7 @@ Pad.prototype.remove = async function remove() { // delete the pad entry and delete pad from padManager p.push(padManager.removePad(padID)); - hooks.callAll('padRemove', {padID}); + p.push(hooks.aCallAll('padRemove', {padID})); await Promise.all(p); }; From 7f392e12e971df679038dbc84a2d18b90bf999b8 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 28 Jan 2021 00:41:23 -0500 Subject: [PATCH 162/357] tests: Create a `src/tests/` symlink that points to `tests/` This makes it possible for plugin backend tests to do `require('ep_etherpad-lite/tests/backend/common')` to access the API key (among other things). Eventually we probably should reverse these (move `tests/` to `src/tests/` and make `tests/` a symlink to `src/tests/`) and move `bin/` to `src/bin/` so that we can avoid the top-level `package.json` mess. --- src/tests | 1 + 1 file changed, 1 insertion(+) create mode 120000 src/tests diff --git a/src/tests b/src/tests new file mode 120000 index 000000000..6dd24e02b --- /dev/null +++ b/src/tests @@ -0,0 +1 @@ +../tests \ No newline at end of file From 989f42204f3169c4f7992ebd356f608a156084f2 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 28 Jan 2021 18:48:11 +0100 Subject: [PATCH 163/357] Localisation updates from https://translatewiki.net. --- src/locales/de.json | 23 +++++++++++++++++++++-- src/locales/fi.json | 3 ++- src/locales/sl.json | 8 ++++++-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/locales/de.json b/src/locales/de.json index 781abe099..439e9c9e3 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -11,24 +11,42 @@ "Sebastian Wallroth", "Thargon", "Tim.krieger", - "Wikinaut" + "Wikinaut", + "Zunkelty" ] }, + "admin.page-title": "Admin Dashboard - Etherpad", "admin_plugins": "Plugins verwalten", "admin_plugins.available": "Verfügbare Plugins", "admin_plugins.available_not-found": "Keine Plugins gefunden.", + "admin_plugins.available_fetching": "Wird abgerufen...", "admin_plugins.available_install.value": "Installieren", + "admin_plugins.available_search.placeholder": "Suche nach Plugins zum Installieren", "admin_plugins.description": "Beschreibung", + "admin_plugins.installed": "Installierte Plugins", + "admin_plugins.installed_fetching": "Rufe installierte Plugins ab...", "admin_plugins.installed_nothing": "Du hast bisher noch keine Plugins installiert.", + "admin_plugins.installed_uninstall.value": "Deinstallieren", "admin_plugins.last-update": "Letze Aktualisierung", "admin_plugins.name": "Name", + "admin_plugins.page-title": "Plugin Manager - Etherpad", "admin_plugins.version": "Version", + "admin_plugins_info": "Hilfestellung", "admin_plugins_info.hooks": "Installierte Hooks", + "admin_plugins_info.hooks_client": "Client-seitige Hooks", + "admin_plugins_info.hooks_server": "Server-seitige Hooks", + "admin_plugins_info.parts": "Installierte Teile", "admin_plugins_info.plugins": "Installierte Plugins", + "admin_plugins_info.page-title": "Plugin Informationen - Etherpad", + "admin_plugins_info.version": "Etherpad Version", + "admin_plugins_info.version_latest": "Neueste Version", "admin_plugins_info.version_number": "Versionsnummer", "admin_settings": "Einstellungen", "admin_settings.current": "Derzeitige Konfiguration", + "admin_settings.current_example-devel": "Beispielhafte Entwicklungseinstellungs-Templates", + "admin_settings.current_restart.value": "Etherpad neustarten", "admin_settings.current_save.value": "Einstellungen speichern", + "admin_settings.page-title": "Einstellungen - Etherpad", "index.newPad": "Neues Pad", "index.createOpenPad": "oder ein Pad mit folgendem Namen erstellen/öffnen:", "index.openPad": "Öffne ein vorhandenes Pad mit folgendem Namen:", @@ -78,7 +96,7 @@ "pad.importExport.exportopen": "ODF (Open Document Format)", "pad.importExport.abiword.innerHTML": "Du kannst nur aus reinen Text- oder HTML-Formaten importieren. Für umfangreichere Importfunktionen muss AbiWord oder LibreOffice auf dem Server installiert werden.", "pad.modals.connected": "Verbunden.", - "pad.modals.reconnecting": "Wiederherstellen der Verbindung …", + "pad.modals.reconnecting": "Dein Pad wird neu verbunden...", "pad.modals.forcereconnect": "Erneutes Verbinden erzwingen", "pad.modals.reconnecttimer": "Versuche Neuverbindung in", "pad.modals.cancel": "Abbrechen", @@ -102,6 +120,7 @@ "pad.modals.deleted.explanation": "Dieses Pad wurde entfernt.", "pad.modals.rateLimited": "Begrenzte Rate.", "pad.modals.rateLimited.explanation": "Sie haben zu viele Nachrichten an dieses Pad gesendet, so dass die Verbindung unterbrochen wurde.", + "pad.modals.rejected.explanation": "Der Server hat eine Nachricht abgelehnt, die von deinem Browser gesendet wurde.", "pad.modals.disconnected": "Ihre Verbindung wurde getrennt.", "pad.modals.disconnected.explanation": "Die Verbindung zum Server wurde unterbrochen.", "pad.modals.disconnected.cause": "Möglicherweise ist der Server nicht erreichbar. Bitte benachrichtige den Dienstadministrator, falls dies weiterhin passiert.", diff --git a/src/locales/fi.json b/src/locales/fi.json index 2af040a3d..9f569d44b 100644 --- a/src/locales/fi.json +++ b/src/locales/fi.json @@ -18,6 +18,7 @@ "VezonThunder" ] }, + "admin_plugins.available": "Saatavilla olevat liitännäiset", "admin_plugins.available_install.value": "Lataa", "admin_plugins.available_search.placeholder": "Etsi asennettavia laajennuksia", "admin_plugins.description": "Kuvaus", @@ -92,7 +93,7 @@ "pad.importExport.exportopen": "ODF (Open Document Format)", "pad.importExport.abiword.innerHTML": "Tuonti on tuettu vain HTML- ja raakatekstitiedostoista. Monipuoliset tuontiominaisuudet ovat käytettävissä asentamalla AbiWordin tai LibreOfficen.", "pad.modals.connected": "Yhdistetty.", - "pad.modals.reconnecting": "Muodostetaan yhteyttä muistioon uudelleen...", + "pad.modals.reconnecting": "Muodostetaan yhteyttä muistioon uudelleen…", "pad.modals.forcereconnect": "Pakota yhdistämään uudelleen", "pad.modals.reconnecttimer": "Yritetään yhdistää uudelleen", "pad.modals.cancel": "Peruuta", diff --git a/src/locales/sl.json b/src/locales/sl.json index 0314f07ce..5ceb49d34 100644 --- a/src/locales/sl.json +++ b/src/locales/sl.json @@ -9,6 +9,10 @@ "Upwinxp" ] }, + "admin_plugins.last-update": "Zadnja posodobitev", + "admin_plugins.name": "Ime", + "admin_plugins.version": "Različica", + "admin_settings": "Nastavitve", "index.newPad": "Nov dokument", "index.createOpenPad": "ali pa ustvari/odpri dokument z imenom:", "pad.toolbar.bold.title": "Krepko (Ctrl-B)", @@ -54,7 +58,7 @@ "pad.importExport.exportword": "DOC (zapis Microsoft Word)", "pad.importExport.exportpdf": "PDF (zapis Acrobat PDF)", "pad.importExport.exportopen": "ODF (zapis Open Document)", - "pad.importExport.abiword.innerHTML": "Uvoziti je mogoče le običajno neoblikovano besedilo in zapise HTML. Za naprednejše zmožnosti namestite program AbiWord.", + "pad.importExport.abiword.innerHTML": "Uvoziti je mogoče le neoblikovano besedilo in zapise HTML. Za naprednejše možnosti uvoza namestite program AbiWord.", "pad.modals.connected": "Povezano.", "pad.modals.reconnecting": "Poteka povezovanje z dokumentom ...", "pad.modals.forcereconnect": "Vsili ponovno povezavo", @@ -64,7 +68,7 @@ "pad.modals.userdup.explanation": "Videti je, da je ta dokument odprt v več kot enem oknu brskalnika na tem računalniku.", "pad.modals.userdup.advice": "Ponovno vzpostavite povezavo in uporabljajte to okno.", "pad.modals.unauth": "Nepooblaščen dostop", - "pad.modals.unauth.explanation": "Med pregledovanjem te strani so se dovoljenja za ogled spremenila. Poskusite se ponovno povezati.", + "pad.modals.unauth.explanation": "Med ogledovanjem strani so se dovoljenja za ogled spremenila. Poskusite se znova povezati.", "pad.modals.looping.explanation": "Zaznane so težave pri komunikaciji s strežnikom za usklajevanje.", "pad.modals.looping.cause": "Morda ste se povezali preko neustrezno nastavljenega požarnega zidu ali posredniškega strežnika.", "pad.modals.initsocketfail": "Strežnik je nedosegljiv.", From f6eb6bd266ff90094159540decd5bb721ab2d21b Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 21 Dec 2020 20:25:17 +0000 Subject: [PATCH 164/357] remove dead object code --- src/static/js/ace2_common.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/static/js/ace2_common.js b/src/static/js/ace2_common.js index 9055b34e3..9322a1d41 100644 --- a/src/static/js/ace2_common.js +++ b/src/static/js/ace2_common.js @@ -26,12 +26,6 @@ function isNodeText(node) { return (node.nodeType == 3); } -function object(o) { - const f = function () {}; - f.prototype = o; - return new f(); -} - function getAssoc(obj, name) { return obj[`_magicdom_${name}`]; } @@ -75,7 +69,6 @@ function htmlPrettyEscape(str) { const noop = function () {}; exports.isNodeText = isNodeText; -exports.object = object; exports.getAssoc = getAssoc; exports.setAssoc = setAssoc; exports.binarySearch = binarySearch; From 5d7645e36add1862fbd5b07c4ee4efac4d454956 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 15 Dec 2020 15:37:28 +0000 Subject: [PATCH 165/357] lint: ace2_common.js linting --- src/static/js/ace2_common.js | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/static/js/ace2_common.js b/src/static/js/ace2_common.js index 9322a1d41..d3f86f699 100644 --- a/src/static/js/ace2_common.js +++ b/src/static/js/ace2_common.js @@ -1,3 +1,5 @@ +'use strict'; + /** * This code is mostly from the old Etherpad. Please help us to comment this code. * This helps other people to understand this code better and helps them to improve it. @@ -22,27 +24,23 @@ const Security = require('./security'); -function isNodeText(node) { - return (node.nodeType == 3); -} +const isNodeText = (node) => (node.nodeType === 3); -function getAssoc(obj, name) { - return obj[`_magicdom_${name}`]; -} +const getAssoc = (obj, name) => obj[`_magicdom_${name}`]; -function setAssoc(obj, name, value) { +const setAssoc = (obj, name, value) => { // note that in IE designMode, properties of a node can get // copied to new nodes that are spawned during editing; also, // properties representable in HTML text can survive copy-and-paste obj[`_magicdom_${name}`] = value; -} +}; // "func" is a function over 0..(numItems-1) that is monotonically // "increasing" with index (false, then true). Finds the boundary // between false and true, a number between 0 and numItems inclusive. -function binarySearch(numItems, func) { +const binarySearch = (numItems, func) => { if (numItems < 1) return 0; if (func(0)) return 0; if (!func(numItems - 1)) return numItems; @@ -54,19 +52,17 @@ function binarySearch(numItems, func) { else low = x; } return high; -} +}; -function binarySearchInfinite(expectedLength, func) { +const binarySearchInfinite = (expectedLength, func) => { let i = 0; while (!func(i)) i += expectedLength; return binarySearch(i, func); -} +}; -function htmlPrettyEscape(str) { - return Security.escapeHTML(str).replace(/\r?\n/g, '\\n'); -} +const htmlPrettyEscape = (str) => Security.escapeHTML(str).replace(/\r?\n/g, '\\n'); -const noop = function () {}; +const noop = () => {}; exports.isNodeText = isNodeText; exports.getAssoc = getAssoc; From b02ab430fe0337abf588c9a57cb446262d244954 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 29 Jan 2021 01:00:33 -0500 Subject: [PATCH 166/357] Bump eslint-config-etherpad to 1.0.24 --- bin/plugins/checkPlugin.js | 6 +- package-lock.json | 174 ++++++++++++++++++++++++------------- package.json | 6 +- src/package-lock.json | 12 +-- src/package.json | 6 +- 5 files changed, 129 insertions(+), 75 deletions(-) diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index 3b8c0b31a..cbe146aa1 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -220,12 +220,12 @@ fs.readdir(pluginPath, (err, rootFiles) => { } updateDeps(parsedPackageJSON, 'devDependencies', { - 'eslint': '^7.17.0', - 'eslint-config-etherpad': '^1.0.22', + 'eslint': '^7.18.0', + 'eslint-config-etherpad': '^1.0.24', 'eslint-plugin-eslint-comments': '^3.2.0', 'eslint-plugin-mocha': '^8.0.0', 'eslint-plugin-node': '^11.1.0', - 'eslint-plugin-prefer-arrow': '^1.2.2', + 'eslint-plugin-prefer-arrow': '^1.2.3', 'eslint-plugin-promise': '^4.2.1', 'eslint-plugin-you-dont-need-lodash-underscore': '^6.10.0', }); diff --git a/package-lock.json b/package-lock.json index 4ac678d3f..4dcc411d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -74,9 +74,9 @@ } }, "@eslint/eslintrc": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz", - "integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", + "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -86,7 +86,7 @@ "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", - "lodash": "^4.17.19", + "lodash": "^4.17.20", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" } @@ -321,9 +321,9 @@ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, "async": { @@ -743,9 +743,9 @@ } }, "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "engine.io": { @@ -2450,6 +2450,11 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" } } }, @@ -8410,13 +8415,13 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { - "version": "7.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.15.0.tgz", - "integrity": "sha512-Vr64xFDT8w30wFll643e7cGrIkPEU50yIiI36OdSIDoSGguIeaLzBo0vpGvzo9RECUqq7htURfwEtKqwytkqzA==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", + "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.2.2", + "@eslint/eslintrc": "^0.3.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -8440,7 +8445,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.19", + "lodash": "^4.17.20", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -8449,15 +8454,15 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^5.2.3", + "table": "^6.0.4", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" } }, "eslint-config-etherpad": { - "version": "1.0.20", - "resolved": "https://registry.npmjs.org/eslint-config-etherpad/-/eslint-config-etherpad-1.0.20.tgz", - "integrity": "sha512-dDEmWphxOmYe7XC0Uevzb0lK7o1jDBGwYMMCdNeZlgo2EfJljnijPgodlimM4R+4OsnfegEMY6rdWoXjzdd5Rw==", + "version": "1.0.24", + "resolved": "https://registry.npmjs.org/eslint-config-etherpad/-/eslint-config-etherpad-1.0.24.tgz", + "integrity": "sha512-zM92/lricP0ALURQWhSFKk8gwDUVkgNiup/Gv5lly6bHj9OIMpK86SlLv/NPkQZYM609pyQjKIeiObsiCSdQsw==", "dev": true }, "eslint-plugin-es": { @@ -8527,9 +8532,9 @@ } }, "eslint-plugin-prefer-arrow": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.2.tgz", - "integrity": "sha512-C8YMhL+r8RMeMdYAw/rQtE6xNdMulj+zGWud/qIGnlmomiPRaLDGLMeskZ3alN6uMBojmooRimtdrXebLN4svQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.3.tgz", + "integrity": "sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==", "dev": true }, "eslint-plugin-promise": { @@ -8702,9 +8707,9 @@ } }, "flatted": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz", - "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", "dev": true }, "forever-agent": { @@ -8977,9 +8982,9 @@ } }, "import-fresh": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", - "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -9869,6 +9874,12 @@ } } }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, "require_optional": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", @@ -9992,14 +10003,46 @@ } }, "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + } } }, "socket.io": { @@ -10176,30 +10219,21 @@ } }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } } } }, @@ -10243,15 +10277,35 @@ } }, "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", + "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", "dev": true, "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" + "ajv": "^7.0.2", + "lodash": "^4.17.20", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "ajv": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.0.3.tgz", + "integrity": "sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } } }, "tar": { diff --git a/package.json b/package.json index 1704f8816..41e472831 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,12 @@ "ep_etherpad-lite": "file:src" }, "devDependencies": { - "eslint": "^7.15.0", - "eslint-config-etherpad": "^1.0.20", + "eslint": "^7.18.0", + "eslint-config-etherpad": "^1.0.24", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-mocha": "^8.0.0", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prefer-arrow": "^1.2.2", + "eslint-plugin-prefer-arrow": "^1.2.3", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0" }, diff --git a/src/package-lock.json b/src/package-lock.json index f4f145474..3a5a35ad0 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -2176,9 +2176,9 @@ } }, "eslint-config-etherpad": { - "version": "1.0.23", - "resolved": "https://registry.npmjs.org/eslint-config-etherpad/-/eslint-config-etherpad-1.0.23.tgz", - "integrity": "sha512-/yMp7aK5zg309DgNIDotl+CtxrDq5ovwVz5ScmjOZgRgaG1rHhtUX5BFewOaoRlYGg+F2xYkqZ5Zhh9fgjs/jg==", + "version": "1.0.24", + "resolved": "https://registry.npmjs.org/eslint-config-etherpad/-/eslint-config-etherpad-1.0.24.tgz", + "integrity": "sha512-zM92/lricP0ALURQWhSFKk8gwDUVkgNiup/Gv5lly6bHj9OIMpK86SlLv/NPkQZYM609pyQjKIeiObsiCSdQsw==", "dev": true }, "eslint-plugin-es": { @@ -2258,9 +2258,9 @@ } }, "eslint-plugin-prefer-arrow": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.2.tgz", - "integrity": "sha512-C8YMhL+r8RMeMdYAw/rQtE6xNdMulj+zGWud/qIGnlmomiPRaLDGLMeskZ3alN6uMBojmooRimtdrXebLN4svQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.3.tgz", + "integrity": "sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==", "dev": true }, "eslint-plugin-promise": { diff --git a/src/package.json b/src/package.json index dffcd3fed..dd6525d36 100644 --- a/src/package.json +++ b/src/package.json @@ -78,12 +78,12 @@ "etherpad-lite": "node/server.js" }, "devDependencies": { - "eslint": "^7.15.0", - "eslint-config-etherpad": "^1.0.20", + "eslint": "^7.18.0", + "eslint-config-etherpad": "^1.0.24", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-mocha": "^8.0.0", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prefer-arrow": "^1.2.2", + "eslint-plugin-prefer-arrow": "^1.2.3", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0", "etherpad-cli-client": "0.0.9", From c1ef12b8da5fe2bd4be130fd9f777cf088bf05d4 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 29 Jan 2021 01:14:03 -0500 Subject: [PATCH 167/357] lint: Re-run eslint --fix --- src/node/handler/PadMessageHandler.js | 2 +- src/static/js/pad_automatic_reconnect.js | 2 +- src/static/js/pluginfw/tsort.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 99cac5ca4..01981d1f0 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -1217,7 +1217,7 @@ const handleChangesetRequest = async (socket, message) => { socket.json.send({type: 'CHANGESET_REQ', data}); } catch (err) { console.error(`Error while handling a changeset request for ${padIds.padId}`, - err.toString(), message.data); + err.toString(), message.data); } }; diff --git a/src/static/js/pad_automatic_reconnect.js b/src/static/js/pad_automatic_reconnect.js index db803e896..9d9ee953a 100644 --- a/src/static/js/pad_automatic_reconnect.js +++ b/src/static/js/pad_automatic_reconnect.js @@ -113,7 +113,7 @@ const reconnectionTries = { nextTry() { // double the time to try to reconnect on every time reconnection fails - const nextCounterFactor = Math.pow(2, this.counter); + const nextCounterFactor = 2 ** this.counter; this.counter++; return nextCounterFactor; diff --git a/src/static/js/pluginfw/tsort.js b/src/static/js/pluginfw/tsort.js index f988de3ad..e584ada29 100644 --- a/src/static/js/pluginfw/tsort.js +++ b/src/static/js/pluginfw/tsort.js @@ -55,7 +55,7 @@ const tsort = (edges) => { Object.keys(nodes).forEach(visit); return sorted; -} +}; /** * TEST From 8efc87f33a88cb1dec4b8e88ca2193e3eb63a225 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 29 Jan 2021 02:55:02 -0500 Subject: [PATCH 168/357] AttributeManager: Fix bogus `this` during attribute removal Before this commit, the callback passed to `.map()` during attribute removal was a normal function, not an arrow function. This meant that the value of `this` in the function body depended on how the callback was invoked. In this case, the callback was invoked without any explicit context (it was not called as a method, nor was it called via `.call()`, `.apply()`, or `.bind()`). Without any explicit context, the value of `this` depends on strict mode. Currently the function is in sloppy mode, so `this` refers to the "global this" object (a.k.a., `window`). It doesn't make sense for the callback to reference `window.author`, so I'm assuming the previous behavior was a bug. Now the function is an arrow function, so the value of `this` comes from the enclosing lexical context, which in this case is the AttributeManager object. I believe that was the original intention. --- src/static/js/AttributeManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index c1257a9bc..32df255da 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -349,7 +349,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ const hasMarker = this.lineHasMarker(lineNum); let found = false; - const attribs = _(this.getAttributesOnLine(lineNum)).map(function (attrib) { + const attribs = _(this.getAttributesOnLine(lineNum)).map((attrib) => { if (attrib[0] === attributeName && (!attributeValue || attrib[0] === attributeValue)) { found = true; return [attributeName, '']; From 462530eafb6116b490177ff3b7f6a7f2ce7cbe74 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 29 Jan 2021 03:01:05 -0500 Subject: [PATCH 169/357] AttributeManager: Fix attribute name during attribute removal Before this change, the `author` attribute was silently discarded during `.map()` iteration and the name of the attribute to remove was included twice with two different values. --- src/static/js/AttributeManager.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index 32df255da..336364196 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -352,10 +352,10 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ const attribs = _(this.getAttributesOnLine(lineNum)).map((attrib) => { if (attrib[0] === attributeName && (!attributeValue || attrib[0] === attributeValue)) { found = true; - return [attributeName, '']; + return [attrib[0], '']; } else if (attrib[0] === 'author') { // update last author to make changes to line attributes on this line - return [attributeName, this.author]; + return [attrib[0], this.author]; } return attrib; }); From 873987f98988ec7708f091482a4828e89f59ccfd Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 29 Jan 2021 03:05:08 -0500 Subject: [PATCH 170/357] tests: Asyncify indentation test This makes it much easier to see why a test is failing. Before, a `helper.waitFor()` failure would simply cause the test to time out. Now an exception is displayed. --- tests/frontend/specs/indentation.js | 50 ++++++++++++++--------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/tests/frontend/specs/indentation.js b/tests/frontend/specs/indentation.js index f35b7ca00..c52f5f406 100644 --- a/tests/frontend/specs/indentation.js +++ b/tests/frontend/specs/indentation.js @@ -130,41 +130,39 @@ describe('indentation button', function () { }); }); - it("issue #2772 shows '*' when multiple indented lines receive a style and are outdented", function (done) { + it("issue #2772 shows '*' when multiple indented lines receive a style and are outdented", async function () { const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; // make sure pad has more than one line inner$('div').first().sendkeys('First{enter}Second{enter}'); - helper.waitFor(() => inner$('div').first().text().trim() === 'First').done(() => { - // indent first 2 lines - const $lines = inner$('div'); - const $firstLine = $lines.first(); - const $secondLine = $lines.slice(1, 2); - helper.selectLines($firstLine, $secondLine); + await helper.waitForPromise(() => inner$('div').first().text().trim() === 'First'); - const $indentButton = chrome$('.buttonicon-indent'); - $indentButton.click(); + // indent first 2 lines + const $lines = inner$('div'); + const $firstLine = $lines.first(); + let $secondLine = $lines.slice(1, 2); + helper.selectLines($firstLine, $secondLine); - helper.waitFor(() => inner$('div').first().find('ul li').length === 1).done(() => { - // apply bold - const $boldButton = chrome$('.buttonicon-bold'); - $boldButton.click(); + const $indentButton = chrome$('.buttonicon-indent'); + $indentButton.click(); - helper.waitFor(() => inner$('div').first().find('b').length === 1).done(() => { - // outdent first 2 lines - const $outdentButton = chrome$('.buttonicon-outdent'); - $outdentButton.click(); - helper.waitFor(() => inner$('div').first().find('ul li').length === 0).done(() => { - // check if '*' is displayed - const $secondLine = inner$('div').slice(1, 2); - expect($secondLine.text().trim()).to.be('Second'); + await helper.waitForPromise(() => inner$('div').first().find('ul li').length === 1); - done(); - }); - }); - }); - }); + // apply bold + const $boldButton = chrome$('.buttonicon-bold'); + $boldButton.click(); + + await helper.waitForPromise(() => inner$('div').first().find('b').length === 1); + + // outdent first 2 lines + const $outdentButton = chrome$('.buttonicon-outdent'); + $outdentButton.click(); + await helper.waitForPromise(() => inner$('div').first().find('ul li').length === 0); + + // check if '*' is displayed + $secondLine = inner$('div').slice(1, 2); + expect($secondLine.text().trim()).to.be('Second'); }); /* From 0897a28e70a29f084ca6a7a3b6b9bc8a242fa06c Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 15 Dec 2020 15:42:10 +0000 Subject: [PATCH 171/357] lint: AttributeManager --- src/static/js/AttributeManager.js | 52 +++++++++++++++++-------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/static/js/AttributeManager.js b/src/static/js/AttributeManager.js index 336364196..19424b0a7 100644 --- a/src/static/js/AttributeManager.js +++ b/src/static/js/AttributeManager.js @@ -1,3 +1,5 @@ +'use strict'; + const Changeset = require('./Changeset'); const ChangesetUtils = require('./ChangesetUtils'); const _ = require('./underscore'); @@ -72,10 +74,12 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ const rowChangeset = this._setAttributesOnRangeByLine(row, startCol, endCol, attribs); - // compose changesets of all rows into a single changeset, as the range might not be continuous + // compose changesets of all rows into a single changeset + // as the range might not be continuous // due to the presence of line markers on the rows if (allChangesets) { - allChangesets = Changeset.compose(allChangesets.toString(), rowChangeset.toString(), this.rep.apool); + allChangesets = Changeset.compose( + allChangesets.toString(), rowChangeset.toString(), this.rep.apool); } else { allChangesets = rowChangeset; } @@ -118,7 +122,8 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ _setAttributesOnRangeByLine(row, startCol, endCol, attribs) { const builder = Changeset.builder(this.rep.lines.totalWidth()); ChangesetUtils.buildKeepToStartOfRange(this.rep, builder, [row, startCol]); - ChangesetUtils.buildKeepRange(this.rep, builder, [row, startCol], [row, endCol], attribs, this.rep.apool); + ChangesetUtils.buildKeepRange( + this.rep, builder, [row, startCol], [row, endCol], attribs, this.rep.apool); return builder; }, @@ -127,9 +132,8 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ @param lineNum: the number of the line */ lineHasMarker(lineNum) { - const that = this; - - return _.find(lineAttributes, (attribute) => that.getAttributeOnLine(lineNum, attribute) != '') !== undefined; + return lineAttributes.find( + (attribute) => this.getAttributeOnLine(lineNum, attribute) !== '') !== undefined; }, /* @@ -184,7 +188,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ if (!(rep.selStart && rep.selEnd)) return; // If we're looking for the caret attribute not the selection // has the user already got a selection or is this purely a caret location? - const isNotSelection = (rep.selStart[0] == rep.selEnd[0] && rep.selEnd[1] === rep.selStart[1]); + const isNotSelection = (rep.selStart[0] === rep.selEnd[0] && rep.selEnd[1] === rep.selStart[1]); if (isNotSelection) { if (prevChar) { // If it's not the start of the line @@ -198,21 +202,18 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ [attributeName, 'true'], ], rep.apool); const withItRegex = new RegExp(`${withIt.replace(/\*/g, '\\*')}(\\*|$)`); - function hasIt(attribs) { - return withItRegex.test(attribs); - } + const hasIt = (attribs) => withItRegex.test(attribs); - return rangeHasAttrib(rep.selStart, rep.selEnd); - - function rangeHasAttrib(selStart, selEnd) { + const rangeHasAttrib = (selStart, selEnd) => { // if range is collapsed -> no attribs in range - if (selStart[1] == selEnd[1] && selStart[0] == selEnd[0]) return false; + if (selStart[1] === selEnd[1] && selStart[0] === selEnd[0]) return false; - if (selStart[0] != selEnd[0]) { // -> More than one line selected - var hasAttrib = true; + if (selStart[0] !== selEnd[0]) { // -> More than one line selected + let hasAttrib = true; // from selStart to the end of the first line - hasAttrib = hasAttrib && rangeHasAttrib(selStart, [selStart[0], rep.lines.atIndex(selStart[0]).text.length]); + hasAttrib = hasAttrib && rangeHasAttrib( + selStart, [selStart[0], rep.lines.atIndex(selStart[0]).text.length]); // for all lines in between for (let n = selStart[0] + 1; n < selEnd[0]; n++) { @@ -230,7 +231,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ const lineNum = selStart[0]; const start = selStart[1]; const end = selEnd[1]; - var hasAttrib = true; + let hasAttrib = true; // Iterate over attribs on this line @@ -244,7 +245,8 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ if (!hasIt(op.attribs)) { // does op overlap selection? if (!(opEndInLine <= start || opStartInLine >= end)) { - hasAttrib = false; // since it's overlapping but hasn't got the attrib -> range hasn't got it + // since it's overlapping but hasn't got the attrib -> range hasn't got it + hasAttrib = false; break; } } @@ -252,7 +254,8 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ } return hasAttrib; - } + }; + return rangeHasAttrib(rep.selStart, rep.selEnd); }, /* @@ -349,7 +352,7 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ const hasMarker = this.lineHasMarker(lineNum); let found = false; - const attribs = _(this.getAttributesOnLine(lineNum)).map((attrib) => { + const attribs = this.getAttributesOnLine(lineNum).map((attrib) => { if (attrib[0] === attributeName && (!attributeValue || attrib[0] === attributeValue)) { found = true; return [attrib[0], '']; @@ -373,7 +376,8 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ if (hasMarker && !countAttribsWithMarker) { ChangesetUtils.buildRemoveRange(this.rep, builder, [lineNum, 0], [lineNum, 1]); } else { - ChangesetUtils.buildKeepRange(this.rep, builder, [lineNum, 0], [lineNum, 1], attribs, this.rep.apool); + ChangesetUtils.buildKeepRange( + this.rep, builder, [lineNum, 0], [lineNum, 1], attribs, this.rep.apool); } return this.applyChangeset(builder); @@ -394,7 +398,9 @@ AttributeManager.prototype = _(AttributeManager.prototype).extend({ }, hasAttributeOnSelectionOrCaretPosition(attributeName) { - const hasSelection = ((this.rep.selStart[0] !== this.rep.selEnd[0]) || (this.rep.selEnd[1] !== this.rep.selStart[1])); + const hasSelection = ( + (this.rep.selStart[0] !== this.rep.selEnd[0]) || (this.rep.selEnd[1] !== this.rep.selStart[1]) + ); let hasAttrib; if (hasSelection) { hasAttrib = this.getAttributeOnSelection(attributeName); From 89bcfa2b4af8b2c034cae74c384f0b9890d13c76 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 29 Jan 2021 03:20:41 -0500 Subject: [PATCH 172/357] tests: Stop using nyc There are some problems with nyc: * The coverage numbers aren't useful in our case because most of the code is executed outside the test process (the test code is mostly API client logic). * nyc messes with line numbers, which makes it much harder to debug problems. * We're seeing frequent SIGABRT crashes while nyc is printing the results table. I'm not sure if nyc is the cause of the crashes, or if it's making a race condition worse, or if the crashes have nothing to do with nyc, but we don't lose much by removing it so we might as well see if the crash frequency improves. --- src/package-lock.json | 984 +----------------------------------------- src/package.json | 5 +- 2 files changed, 4 insertions(+), 985 deletions(-) diff --git a/src/package-lock.json b/src/package-lock.json index 3a5a35ad0..3e04ec287 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -101,182 +101,12 @@ "@babel/highlight": "^7.10.4" } }, - "@babel/core": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", - "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.10", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.5", - "@babel/parser": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.10", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", - "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.11", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/helper-function-name": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", - "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/types": "^7.12.11" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", - "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", - "dev": true, - "requires": { - "@babel/types": "^7.12.10" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", - "dev": true, - "requires": { - "@babel/types": "^7.12.7" - } - }, - "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.5" - } - }, - "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "lodash": "^4.17.19" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", - "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", - "dev": true, - "requires": { - "@babel/types": "^7.12.10" - } - }, - "@babel/helper-replace-supers": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", - "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.7", - "@babel/helper-optimise-call-expression": "^7.12.10", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.11" - } - }, - "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", - "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", - "dev": true, - "requires": { - "@babel/types": "^7.12.11" - } - }, "@babel/helper-validator-identifier": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", "dev": true }, - "@babel/helpers": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", - "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", - "dev": true, - "requires": { - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.5", - "@babel/types": "^7.12.5" - } - }, "@babel/highlight": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", @@ -319,74 +149,6 @@ } } }, - "@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", - "dev": true - }, - "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" - } - }, - "@babel/traverse": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", - "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.11", - "@babel/generator": "^7.12.11", - "@babel/helper-function-name": "^7.12.11", - "@babel/helper-split-export-declaration": "^7.12.11", - "@babel/parser": "^7.12.11", - "@babel/types": "^7.12.12", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, "@eslint/eslintrc": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", @@ -428,67 +190,6 @@ } } }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true - }, "@js-joda/core": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-3.2.0.tgz", @@ -715,16 +416,6 @@ "humanize-ms": "^1.2.1" } }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -762,15 +453,6 @@ "picomatch": "^2.0.4" } }, - "append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "dev": true, - "requires": { - "default-require-extensions": "^3.0.0" - } - }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -859,12 +541,6 @@ } } }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", - "dev": true - }, "are-we-there-yet": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", @@ -1217,18 +893,6 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, - "caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "dev": true, - "requires": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - } - }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -1356,12 +1020,6 @@ "source-map": "~0.6.0" } }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", @@ -1476,12 +1134,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, "component-bind": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", @@ -1578,23 +1230,6 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } - } - }, "cookie": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", @@ -1721,15 +1356,6 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, - "default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", - "dev": true, - "requires": { - "strip-bom": "^4.0.0" - } - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -2018,12 +1644,6 @@ "is-symbol": "^1.0.2" } }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -2550,17 +2170,6 @@ "unpipe": "~1.0.0" } }, - "find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - } - }, "find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -2611,16 +2220,6 @@ "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", "dev": true }, - "foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - } - }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -2651,12 +2250,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, - "fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", - "dev": true - }, "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -2724,12 +2317,6 @@ "wide-align": "^1.1.0" } }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2747,12 +2334,6 @@ "has-symbols": "^1.0.1" } }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -2872,16 +2453,6 @@ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "optional": true }, - "hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", - "dev": true, - "requires": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - } - }, "hast-util-embedded": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-1.0.6.tgz", @@ -2951,12 +2522,6 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, "html-void-elements": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz", @@ -3065,12 +2630,6 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, "indexof": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", @@ -3227,12 +2786,6 @@ "has-symbols": "^1.0.1" } }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, "is-symbol": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", @@ -3247,12 +2800,6 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", @@ -3268,133 +2815,6 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, - "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "dev": true, - "requires": { - "append-transform": "^2.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", - "dev": true, - "requires": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", - "p-map": "^3.0.0", - "rimraf": "^3.0.0", - "uuid": "^3.3.3" - }, - "dependencies": { - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, "js-cookie": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", @@ -3425,12 +2845,6 @@ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -3452,15 +2866,6 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, - "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, "jsonminify": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/jsonminify/-/jsonminify-0.4.1.tgz", @@ -3631,12 +3036,6 @@ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, "lodash.foreach": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", @@ -3788,23 +3187,6 @@ } } }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, "measured-core": { "version": "1.11.2", "resolved": "https://registry.npmjs.org/measured-core/-/measured-core-1.11.2.tgz", @@ -4281,15 +3663,6 @@ } } }, - "node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "dev": true, - "requires": { - "process-on-spawn": "^1.0.0" - } - }, "nodeify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/nodeify/-/nodeify-1.0.1.tgz", @@ -7448,198 +6821,6 @@ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, - "nyc": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.0.1.tgz", - "integrity": "sha512-n0MBXYBYRqa67IVt62qW1r/d9UH/Qtr7SF1w/nQLJ9KxvWF6b2xCHImRAixHN9tnMMYHC2P14uo6KddNGwMgGg==", - "dev": true, - "requires": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "decamelize": "^1.2.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -7802,33 +6983,12 @@ "p-limit": "^2.0.0" } }, - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - } - }, "packet-reader": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", @@ -7956,51 +7116,6 @@ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - } - } - }, "postgres-array": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", @@ -8035,15 +7150,6 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "dev": true, - "requires": { - "fromentries": "^1.2.0" - } - }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -8260,15 +7366,6 @@ "xtend": "^4.0.0" } }, - "release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "dev": true, - "requires": { - "es6-error": "^4.0.1" - } - }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -8475,7 +7572,8 @@ "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "optional": true }, "simple-git": { "version": "2.31.0", @@ -8726,40 +7824,6 @@ "memory-pager": "^1.0.2" } }, - "spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "dev": true, - "requires": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "rimraf": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - }, - "dependencies": { - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, "split2": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", @@ -8868,12 +7932,6 @@ "ansi-regex": "^2.0.0" } }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -9145,17 +8203,6 @@ "source-map-support": "~0.5.12" } }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -9207,12 +8254,6 @@ "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -9294,15 +8335,6 @@ "mime-types": "~2.1.24" } }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, "ueberdb2": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/ueberdb2/-/ueberdb2-1.2.5.tgz", @@ -9622,18 +8654,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, "ws": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", diff --git a/src/package.json b/src/package.json index dd6525d36..bc3e4d22f 100644 --- a/src/package.json +++ b/src/package.json @@ -89,7 +89,6 @@ "etherpad-cli-client": "0.0.9", "mocha": "7.1.2", "mocha-froth": "^0.2.10", - "nyc": "15.0.1", "set-cookie-parser": "^2.4.6", "sinon": "^9.2.0", "superagent": "^3.8.3", @@ -148,8 +147,8 @@ }, "scripts": { "lint": "eslint .", - "test": "nyc mocha --timeout 120000 --recursive ../tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs", - "test-container": "nyc mocha --timeout 5000 ../tests/container/specs/api" + "test": "mocha --timeout 120000 --recursive ../tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs", + "test-container": "mocha --timeout 5000 ../tests/container/specs/api" }, "version": "1.8.7", "license": "Apache-2.0" From f72ce463ef4aafecc82d6ddbbc609a0961f3ca00 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 21 Dec 2020 23:11:17 +0000 Subject: [PATCH 173/357] lint: undomodule --- src/static/js/undomodule.js | 116 ++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 59 deletions(-) diff --git a/src/static/js/undomodule.js b/src/static/js/undomodule.js index 44efa0449..b8270b805 100644 --- a/src/static/js/undomodule.js +++ b/src/static/js/undomodule.js @@ -1,3 +1,5 @@ +'use strict'; + /** * This code is mostly from the old Etherpad. Please help us to comment this code. * This helps other people to understand this code better and helps them to improve it. @@ -23,8 +25,8 @@ const Changeset = require('./Changeset'); const _ = require('./underscore'); -var undoModule = (function () { - const stack = (function () { +const undoModule = (() => { + const stack = (() => { const stackElements = []; // two types of stackElements: // 1) { elementType: UNDOABLE_EVENT, eventType: "anything", [backset: ,] @@ -36,7 +38,7 @@ var undoModule = (function () { const UNDOABLE_EVENT = 'undoableEvent'; const EXTERNAL_CHANGE = 'externalChange'; - function clearStack() { + const clearStack = () => { stackElements.length = 0; stackElements.push( { @@ -44,22 +46,23 @@ var undoModule = (function () { eventType: 'bottom', }); numUndoableEvents = 1; - } + }; clearStack(); - function pushEvent(event) { + const pushEvent = (event) => { const e = _.extend( {}, event); e.elementType = UNDOABLE_EVENT; stackElements.push(e); numUndoableEvents++; // dmesg("pushEvent backset: "+event.backset); - } + }; - function pushExternalChange(cs) { + const pushExternalChange = (cs) => { const idx = stackElements.length - 1; - if (stackElements[idx].elementType == EXTERNAL_CHANGE) { - stackElements[idx].changeset = Changeset.compose(stackElements[idx].changeset, cs, getAPool()); + if (stackElements[idx].elementType === EXTERNAL_CHANGE) { + stackElements[idx].changeset = + Changeset.compose(stackElements[idx].changeset, cs, getAPool()); } else { stackElements.push( { @@ -67,14 +70,14 @@ var undoModule = (function () { changeset: cs, }); } - } + }; - function _exposeEvent(nthFromTop) { + const _exposeEvent = (nthFromTop) => { // precond: 0 <= nthFromTop < numUndoableEvents const targetIndex = stackElements.length - 1 - nthFromTop; let idx = stackElements.length - 1; - while (idx > targetIndex || stackElements[idx].elementType == EXTERNAL_CHANGE) { - if (stackElements[idx].elementType == EXTERNAL_CHANGE) { + while (idx > targetIndex || stackElements[idx].elementType === EXTERNAL_CHANGE) { + if (stackElements[idx].elementType === EXTERNAL_CHANGE) { const ex = stackElements[idx]; const un = stackElements[idx - 1]; if (un.backset) { @@ -86,15 +89,16 @@ var undoModule = (function () { const newSel = Changeset.characterRangeFollow(excs, un.selStart, un.selEnd); un.selStart = newSel[0]; un.selEnd = newSel[1]; - if (un.selStart == un.selEnd) { + if (un.selStart === un.selEnd) { un.selFocusAtStart = false; } } } stackElements[idx - 1] = ex; stackElements[idx] = un; - if (idx >= 2 && stackElements[idx - 2].elementType == EXTERNAL_CHANGE) { - ex.changeset = Changeset.compose(stackElements[idx - 2].changeset, ex.changeset, getAPool()); + if (idx >= 2 && stackElements[idx - 2].elementType === EXTERNAL_CHANGE) { + ex.changeset = + Changeset.compose(stackElements[idx - 2].changeset, ex.changeset, getAPool()); stackElements.splice(idx - 2, 1); idx--; } @@ -102,24 +106,22 @@ var undoModule = (function () { idx--; } } - } + }; - function getNthFromTop(n) { + const getNthFromTop = (n) => { // precond: 0 <= n < numEvents() _exposeEvent(n); return stackElements[stackElements.length - 1 - n]; - } + }; - function numEvents() { - return numUndoableEvents; - } + const numEvents = () => numUndoableEvents; - function popEvent() { + const popEvent = () => { // precond: numEvents() > 0 _exposeEvent(0); numUndoableEvents--; return stackElements.pop(); - } + }; return { numEvents, @@ -134,12 +136,12 @@ var undoModule = (function () { // invariant: stack always has at least one undoable event let undoPtr = 0; // zero-index from top of stack, 0 == top - function clearHistory() { + const clearHistory = () => { stack.clearStack(); undoPtr = 0; - } + }; - function _charOccurrences(str, c) { + const _charOccurrences = (str, c) => { let i = 0; let count = 0; while (i >= 0 && i < str.length) { @@ -150,13 +152,11 @@ var undoModule = (function () { } } return count; - } + }; - function _opcodeOccurrences(cs, opcode) { - return _charOccurrences(Changeset.unpack(cs).ops, opcode); - } + const _opcodeOccurrences = (cs, opcode) => _charOccurrences(Changeset.unpack(cs).ops, opcode); - function _mergeChangesets(cs1, cs2) { + const _mergeChangesets = (cs1, cs2) => { if (!cs1) return cs2; if (!cs2) return cs1; @@ -170,40 +170,40 @@ var undoModule = (function () { const plusCount2 = _opcodeOccurrences(cs2, '+'); const minusCount1 = _opcodeOccurrences(cs1, '-'); const minusCount2 = _opcodeOccurrences(cs2, '-'); - if (plusCount1 == 1 && plusCount2 == 1 && minusCount1 == 0 && minusCount2 == 0) { - var merge = Changeset.compose(cs1, cs2, getAPool()); - var plusCount3 = _opcodeOccurrences(merge, '+'); - var minusCount3 = _opcodeOccurrences(merge, '-'); - if (plusCount3 == 1 && minusCount3 == 0) { + if (plusCount1 === 1 && plusCount2 === 1 && minusCount1 === 0 && minusCount2 === 0) { + const merge = Changeset.compose(cs1, cs2, getAPool()); + const plusCount3 = _opcodeOccurrences(merge, '+'); + const minusCount3 = _opcodeOccurrences(merge, '-'); + if (plusCount3 === 1 && minusCount3 === 0) { return merge; } - } else if (plusCount1 == 0 && plusCount2 == 0 && minusCount1 == 1 && minusCount2 == 1) { - var merge = Changeset.compose(cs1, cs2, getAPool()); - var plusCount3 = _opcodeOccurrences(merge, '+'); - var minusCount3 = _opcodeOccurrences(merge, '-'); - if (plusCount3 == 0 && minusCount3 == 1) { + } else if (plusCount1 === 0 && plusCount2 === 0 && minusCount1 === 1 && minusCount2 === 1) { + const merge = Changeset.compose(cs1, cs2, getAPool()); + const plusCount3 = _opcodeOccurrences(merge, '+'); + const minusCount3 = _opcodeOccurrences(merge, '-'); + if (plusCount3 === 0 && minusCount3 === 1) { return merge; } } return null; - } + }; - function reportEvent(event) { + const reportEvent = (event) => { const topEvent = stack.getNthFromTop(0); - function applySelectionToTop() { + const applySelectionToTop = () => { if ((typeof event.selStart) === 'number') { topEvent.selStart = event.selStart; topEvent.selEnd = event.selEnd; topEvent.selFocusAtStart = event.selFocusAtStart; } - } + }; if ((!event.backset) || Changeset.isIdentity(event.backset)) { applySelectionToTop(); } else { let merged = false; - if (topEvent.eventType == event.eventType) { + if (topEvent.eventType === event.eventType) { const merge = _mergeChangesets(event.backset, topEvent.backset); if (merge) { topEvent.backset = merge; @@ -225,15 +225,15 @@ var undoModule = (function () { } undoPtr = 0; } - } + }; - function reportExternalChange(changeset) { + const reportExternalChange = (changeset) => { if (changeset && !Changeset.isIdentity(changeset)) { stack.pushExternalChange(changeset); } - } + }; - function _getSelectionInfo(event) { + const _getSelectionInfo = (event) => { if ((typeof event.selStart) !== 'number') { return null; } else { @@ -243,7 +243,7 @@ var undoModule = (function () { selFocusAtStart: event.selFocusAtStart, }; } - } + }; // For "undo" and "redo", the change event must be returned // by eventFunc and NOT reported through the normal mechanism. @@ -251,7 +251,7 @@ var undoModule = (function () { // or can be called with no arguments to mean that no undo is possible. // "eventFunc" will be called exactly once. - function performUndo(eventFunc) { + const performUndo = (eventFunc) => { if (undoPtr < stack.numEvents() - 1) { const backsetEvent = stack.getNthFromTop(undoPtr); const selectionEvent = stack.getNthFromTop(undoPtr + 1); @@ -259,9 +259,9 @@ var undoModule = (function () { stack.pushEvent(undoEvent); undoPtr += 2; } else { eventFunc(); } - } + }; - function performRedo(eventFunc) { + const performRedo = (eventFunc) => { if (undoPtr >= 2) { const backsetEvent = stack.getNthFromTop(0); const selectionEvent = stack.getNthFromTop(1); @@ -269,11 +269,9 @@ var undoModule = (function () { stack.popEvent(); undoPtr -= 2; } else { eventFunc(); } - } + }; - function getAPool() { - return undoModule.apool; - } + const getAPool = () => undoModule.apool; return { clearHistory, From 29179e512f7fa41b64a4de555b427c7b507997d4 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 21 Dec 2020 23:23:16 +0000 Subject: [PATCH 174/357] lint: skiplist --- src/static/js/skiplist.js | 177 ++++++++++++++------------------------ 1 file changed, 65 insertions(+), 112 deletions(-) diff --git a/src/static/js/skiplist.js b/src/static/js/skiplist.js index bf7a74f97..4ea740104 100644 --- a/src/static/js/skiplist.js +++ b/src/static/js/skiplist.js @@ -1,3 +1,5 @@ +'use strict'; + /** * This code is mostly from the old Etherpad. Please help us to comment this code. * This helps other people to understand this code better and helps them to improve it. @@ -28,15 +30,13 @@ const noop = Ace2Common.noop; function SkipList() { let PROFILER = window.PROFILER; if (!PROFILER) { - PROFILER = function () { - return { - start: noop, - mark: noop, - literal: noop, - end: noop, - cancel: noop, - }; - }; + PROFILER = () => ({ + start: noop, + mark: noop, + literal: noop, + end: noop, + cancel: noop, + }); } // if there are N elements in the skiplist, "start" is element -1 and "end" is element N @@ -68,7 +68,7 @@ function SkipList() { // this point. - function _getPoint(targetLoc) { + const _getPoint = (targetLoc) => { const numLevels = start.levels; let lvl = numLevels - 1; let i = -1; @@ -99,13 +99,11 @@ function SkipList() { idxs, loc: targetLoc, widthSkips, - toString() { - return `getPoint(${targetLoc})`; - }, + toString: () => `getPoint(${targetLoc})`, }; - } + }; - function _getNodeAtOffset(targetOffset) { + const _getNodeAtOffset = (targetOffset) => { let i = 0; let n = start; let lvl = start.levels - 1; @@ -117,16 +115,14 @@ function SkipList() { lvl--; } if (n === start) return (start.downPtrs[0] || null); - else if (n === end) return (targetOffset == totalWidth ? (end.upPtrs[0] || null) : null); + else if (n === end) return (targetOffset === totalWidth ? (end.upPtrs[0] || null) : null); return n; - } + }; - function _entryWidth(e) { - return (e && e.width) || 0; - } + const _entryWidth = (e) => (e && e.width) || 0; - function _insertKeyAtPoint(point, newKey, entry) { - const p = PROFILER('insertKey', false); + const _insertKeyAtPoint = (point, newKey, entry) => { + const p = PROFILER('insertKey', false); // eslint-disable-line new-cap const newNode = { key: newKey, levels: 0, @@ -145,10 +141,10 @@ function SkipList() { // The new node will have at least level 1 // With a proability of 0.01^(n-1) the nodes level will be >= n - while (newNode.levels == 0 || Math.random() < 0.01) { - var lvl = newNode.levels; + while (newNode.levels === 0 || Math.random() < 0.01) { + const lvl = newNode.levels; newNode.levels++; - if (lvl == pNodes.length) { + if (lvl === pNodes.length) { // assume we have just passed the end of point.nodes, and reached one level greater // than the skiplist currently supports pNodes[lvl] = start; @@ -162,7 +158,7 @@ function SkipList() { point.widthSkips[lvl] = 0; } const me = newNode; - var up = pNodes[lvl]; + const up = pNodes[lvl]; const down = up.downPtrs[lvl]; const skip1 = pLoc - pIdxs[lvl]; const skip2 = up.downSkips[lvl] + 1 - skip1; @@ -179,8 +175,8 @@ function SkipList() { } p.mark('loop2'); p.literal(pNodes.length, 'PNL'); - for (var lvl = newNode.levels; lvl < pNodes.length; lvl++) { - var up = pNodes[lvl]; + for (let lvl = newNode.levels; lvl < pNodes.length; lvl++) { + const up = pNodes[lvl]; up.downSkips[lvl]++; up.downSkipWidths[lvl] += newWidth; } @@ -189,30 +185,17 @@ function SkipList() { numNodes++; totalWidth += newWidth; p.end(); - } + }; - function _getNodeAtPoint(point) { - return point.nodes[0].downPtrs[0]; - } + const _getNodeAtPoint = (point) => point.nodes[0].downPtrs[0]; - function _incrementPoint(point) { - point.loc++; - for (let i = 0; i < point.nodes.length; i++) { - if (point.idxs[i] + point.nodes[i].downSkips[i] < point.loc) { - point.idxs[i] += point.nodes[i].downSkips[i]; - point.widthSkips[i] += point.nodes[i].downSkipWidths[i]; - point.nodes[i] = point.nodes[i].downPtrs[i]; - } - } - } - - function _deleteKeyAtPoint(point) { + const _deleteKeyAtPoint = (point) => { const elem = point.nodes[0].downPtrs[0]; const elemWidth = _entryWidth(elem.entry); for (let i = 0; i < point.nodes.length; i++) { if (i < elem.levels) { - var up = elem.upPtrs[i]; - var down = elem.downPtrs[i]; + const up = elem.upPtrs[i]; + const down = elem.downPtrs[i]; const totalSkip = up.downSkips[i] + elem.downSkips[i] - 1; up.downPtrs[i] = down; down.upPtrs[i] = up; @@ -220,8 +203,7 @@ function SkipList() { const totalWidthSkip = up.downSkipWidths[i] + elem.downSkipWidths[i] - elemWidth; up.downSkipWidths[i] = totalWidthSkip; } else { - var up = point.nodes[i]; - var down = up.downPtrs[i]; + const up = point.nodes[i]; up.downSkips[i]--; up.downSkipWidths[i] -= elemWidth; } @@ -229,9 +211,9 @@ function SkipList() { delete keyToNodeMap[`$KEY$${elem.key}`]; numNodes--; totalWidth -= elemWidth; - } + }; - function _propagateWidthChange(node) { + const _propagateWidthChange = (node) => { const oldWidth = node.downSkipWidths[0]; const newWidth = _entryWidth(node.entry); const widthChange = newWidth - oldWidth; @@ -245,9 +227,9 @@ function SkipList() { } } totalWidth += widthChange; - } + }; - function _getNodeIndex(node, byWidth) { + const _getNodeIndex = (node, byWidth) => { let dist = (byWidth ? 0 : -1); let n = node; while (n !== start) { @@ -257,27 +239,26 @@ function SkipList() { else dist += n.downSkips[lvl]; } return dist; - } + }; - function _getNodeByKey(key) { - return keyToNodeMap[`$KEY$${key}`]; - } + const _getNodeByKey = (key) => keyToNodeMap[`$KEY$${key}`]; // Returns index of first entry such that entryFunc(entry) is truthy, // or length() if no such entry. Assumes all falsy entries come before // all truthy entries. - function _search(entryFunc) { + const _search = (entryFunc) => { let low = start; let lvl = start.levels - 1; let lowIndex = -1; - function f(node) { + const f = (node) => { if (node === start) return false; else if (node === end) return true; else return entryFunc(node.entry); - } + }; + while (lvl >= 0) { let nextLow = low.downPtrs[lvl]; while (!f(nextLow)) { @@ -288,7 +269,7 @@ function SkipList() { lvl--; } return lowIndex + 1; - } + }; /* The skip-list contains "entries", JavaScript objects that each must have a unique "key" property @@ -296,16 +277,14 @@ that is a string. */ const self = this; _.extend(this, { - length() { - return numNodes; - }, - atIndex(i) { + length: () => numNodes, + atIndex: (i) => { if (i < 0) console.warn(`atIndex(${i})`); if (i >= numNodes) console.warn(`atIndex(${i}>=${numNodes})`); return _getNodeAtPoint(_getPoint(i)).entry; }, // differs from Array.splice() in that new elements are in an array, not varargs - splice(start, deleteCount, newEntryArray) { + splice: (start, deleteCount, newEntryArray) => { if (start < 0) console.warn(`splice(${start}, ...)`); if (start + deleteCount > numNodes) { console.warn(`splice(${start}, ${deleteCount}, ...), N=${numNodes}`); @@ -315,26 +294,22 @@ that is a string. if (!newEntryArray) newEntryArray = []; const pt = _getPoint(start); - for (var i = 0; i < deleteCount; i++) { + for (let i = 0; i < deleteCount; i++) { _deleteKeyAtPoint(pt); } - for (var i = (newEntryArray.length - 1); i >= 0; i--) { + for (let i = (newEntryArray.length - 1); i >= 0; i--) { const entry = newEntryArray[i]; _insertKeyAtPoint(pt, entry.key, entry); const node = _getNodeByKey(entry.key); node.entry = entry; } }, - next(entry) { - return _getNodeByKey(entry.key).downPtrs[0].entry || null; - }, - prev(entry) { - return _getNodeByKey(entry.key).upPtrs[0].entry || null; - }, - push(entry) { + next: (entry) => _getNodeByKey(entry.key).downPtrs[0].entry || null, + prev: (entry) => _getNodeByKey(entry.key).upPtrs[0].entry || null, + push: (entry) => { self.splice(numNodes, 0, [entry]); }, - slice(start, end) { + slice: (start, end) => { // act like Array.slice() if (start === undefined) start = 0; else if (start < 0) start += numNodes; @@ -346,7 +321,7 @@ that is a string. if (end < 0) end = 0; if (end > numNodes) end = numNodes; - dmesg(String([start, end, numNodes])); + window.dmesg(String([start, end, numNodes])); if (end <= start) return []; let n = self.atIndex(start); const array = [n]; @@ -356,56 +331,34 @@ that is a string. } return array; }, - atKey(key) { - return _getNodeByKey(key).entry; - }, - indexOfKey(key) { - return _getNodeIndex(_getNodeByKey(key)); - }, - indexOfEntry(entry) { - return self.indexOfKey(entry.key); - }, - containsKey(key) { - return !!(_getNodeByKey(key)); - }, + atKey: (key) => _getNodeByKey(key).entry, + indexOfKey: (key) => _getNodeIndex(_getNodeByKey(key)), + indexOfEntry: (entry) => self.indexOfKey(entry.key), + containsKey: (key) => !!(_getNodeByKey(key)), // gets the last entry starting at or before the offset - atOffset(offset) { - return _getNodeAtOffset(offset).entry; - }, - keyAtOffset(offset) { - return self.atOffset(offset).key; - }, - offsetOfKey(key) { - return _getNodeIndex(_getNodeByKey(key), true); - }, - offsetOfEntry(entry) { - return self.offsetOfKey(entry.key); - }, - setEntryWidth(entry, width) { + atOffset: (offset) => _getNodeAtOffset(offset).entry, + keyAtOffset: (offset) => self.atOffset(offset).key, + offsetOfKey: (key) => _getNodeIndex(_getNodeByKey(key), true), + offsetOfEntry: (entry) => self.offsetOfKey(entry.key), + setEntryWidth: (entry, width) => { entry.width = width; _propagateWidthChange(_getNodeByKey(entry.key)); }, - totalWidth() { - return totalWidth; - }, - offsetOfIndex(i) { + totalWidth: () => totalWidth, + offsetOfIndex: (i) => { if (i < 0) return 0; if (i >= numNodes) return totalWidth; return self.offsetOfEntry(self.atIndex(i)); }, - indexOfOffset(offset) { + indexOfOffset: (offset) => { if (offset <= 0) return 0; if (offset >= totalWidth) return numNodes; return self.indexOfEntry(self.atOffset(offset)); }, - search(entryFunc) { - return _search(entryFunc); - }, + search: (entryFunc) => _search(entryFunc), // debugToString: _debugToString, debugGetPoint: _getPoint, - debugDepth() { - return start.levels; - }, + debugDepth: () => start.levels, }); } From 5bcd6f44a5a8d2aca76fbb863ce0c165f0990f16 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 30 Jan 2021 07:32:12 +0000 Subject: [PATCH 175/357] lint: skin-variants (#4603) * lint: skin-variants * for squash: Fix attachment of event listener Before this PR the statement was outside the function. I'm assuming the move into the function body was accidental, so move it back out. * for squash: Preserve order of function calls Co-authored-by: Richard Hansen --- src/static/js/skin_variants.js | 42 +++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/static/js/skin_variants.js b/src/static/js/skin_variants.js index d5d663c08..a7902545e 100644 --- a/src/static/js/skin_variants.js +++ b/src/static/js/skin_variants.js @@ -1,19 +1,14 @@ -// Specific hash to display the skin variants builder popup -if (window.location.hash.toLowerCase() == '#skinvariantsbuilder') { - $('#skin-variants').addClass('popup-show'); +'use strict'; - $('.skin-variant').change(() => { - updateSkinVariantsClasses(); - }); +// Specific hash to display the skin variants builder popup +if (window.location.hash.toLowerCase() === '#skinvariantsbuilder') { + $('#skin-variants').addClass('popup-show'); const containers = ['editor', 'background', 'toolbar']; const colors = ['super-light', 'light', 'dark', 'super-dark']; - updateCheckboxFromSkinClasses(); - updateSkinVariantsClasses(); - // add corresponding classes when config change - function updateSkinVariantsClasses() { + const updateSkinVariantsClasses = () => { const domsToUpdate = [ $('html'), $('iframe[name=ace_outer]').contents().find('html'), @@ -27,23 +22,21 @@ if (window.location.hash.toLowerCase() == '#skinvariantsbuilder') { domsToUpdate.forEach((el) => { el.removeClass('full-width-editor'); }); - const new_classes = []; + const newClasses = []; $('select.skin-variant-color').each(function () { - new_classes.push(`${$(this).val()}-${$(this).data('container')}`); + newClasses.push(`${$(this).val()}-${$(this).data('container')}`); }); - if ($('#skin-variant-full-width').is(':checked')) new_classes.push('full-width-editor'); + if ($('#skin-variant-full-width').is(':checked')) newClasses.push('full-width-editor'); - domsToUpdate.forEach((el) => { el.addClass(new_classes.join(' ')); }); + domsToUpdate.forEach((el) => { el.addClass(newClasses.join(' ')); }); - $('#skin-variants-result').val(`"skinVariants": "${new_classes.join(' ')}",`); - } + $('#skin-variants-result').val(`"skinVariants": "${newClasses.join(' ')}",`); + }; // run on init - function updateCheckboxFromSkinClasses() { + const updateCheckboxFromSkinClasses = () => { $('html').attr('class').split(' ').forEach((classItem) => { - var container = classItem.split('-').slice(-1); - - var container = classItem.substring(classItem.lastIndexOf('-') + 1, classItem.length); + const container = classItem.substring(classItem.lastIndexOf('-') + 1, classItem.length); if (containers.indexOf(container) > -1) { const color = classItem.substring(0, classItem.lastIndexOf('-')); $(`.skin-variant-color[data-container="${container}"`).val(color); @@ -51,5 +44,12 @@ if (window.location.hash.toLowerCase() == '#skinvariantsbuilder') { }); $('#skin-variant-full-width').prop('checked', $('html').hasClass('full-width-editor')); - } + }; + + $('.skin-variant').change(() => { + updateSkinVariantsClasses(); + }); + + updateCheckboxFromSkinClasses(); + updateSkinVariantsClasses(); } From f03c4bd7f7c5108284167943f26a209d87daa2e4 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 8 Jan 2021 17:47:38 -0500 Subject: [PATCH 176/357] bin scripts: compare against null, not undefined --- bin/checkAllPads.js | 4 ++-- bin/checkPad.js | 8 ++++---- bin/checkPadDeltas.js | 4 +--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/bin/checkAllPads.js b/bin/checkAllPads.js index 6e1f14842..8426d4429 100644 --- a/bin/checkAllPads.js +++ b/bin/checkAllPads.js @@ -30,7 +30,7 @@ npm.load({}, async () => { const pad = await padManager.getPad(padId); // check if the pad has a pool - if (pad.pool === undefined) { + if (pad.pool == null) { console.error(`[${pad.id}] Missing attribute pool`); continue; } @@ -66,7 +66,7 @@ npm.load({}, async () => { } // check if there is a atext in the keyRevisions - if (revisions[keyRev].meta === undefined || revisions[keyRev].meta.atext === undefined) { + if (revisions[keyRev].meta == null || revisions[keyRev].meta.atext == null) { console.error(`[${pad.id}] Missing atext in revision ${keyRev}`); continue; } diff --git a/bin/checkPad.js b/bin/checkPad.js index de1c51402..374a3c856 100644 --- a/bin/checkPad.js +++ b/bin/checkPad.js @@ -59,12 +59,12 @@ npm.load({}, async () => { } // check if the pad has a pool - if (pad.pool === undefined) throw new Error('Attribute pool is missing'); + if (pad.pool == null) throw new Error('Attribute pool is missing'); // check if there is an atext in the keyRevisions - if (revisions[keyRev] === undefined || - revisions[keyRev].meta === undefined || - revisions[keyRev].meta.atext === undefined) { + if (revisions[keyRev] == null || + revisions[keyRev].meta == null || + revisions[keyRev].meta.atext == null) { console.error(`No atext in key revision ${keyRev}`); continue; } diff --git a/bin/checkPadDeltas.js b/bin/checkPadDeltas.js index ecbb20846..c53275a28 100644 --- a/bin/checkPadDeltas.js +++ b/bin/checkPadDeltas.js @@ -55,9 +55,7 @@ npm.load({}, async () => { const revision = await db.get(`pad:${padId}:revs:${revNum}`); // check if there is a atext in the keyRevisions if (~keyRevisions.indexOf(revNum) && - (revision === undefined || - revision.meta === undefined || - revision.meta.atext === undefined)) { + (revision == null || revision.meta == null || revision.meta.atext == null)) { console.error(`No atext in key revision ${revNum}`); continue; } From 92cd2cc760f369b14f48f0f3c3cf4feccf9568b1 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 9 Jan 2021 02:28:45 -0500 Subject: [PATCH 177/357] bin scripts: Use destructuring instead of long condition checks --- bin/checkAllPads.js | 4 ++-- bin/checkPad.js | 6 ++---- bin/checkPadDeltas.js | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/bin/checkAllPads.js b/bin/checkAllPads.js index 8426d4429..063da96a5 100644 --- a/bin/checkAllPads.js +++ b/bin/checkAllPads.js @@ -66,13 +66,13 @@ npm.load({}, async () => { } // check if there is a atext in the keyRevisions - if (revisions[keyRev].meta == null || revisions[keyRev].meta.atext == null) { + let {meta: {atext} = {}} = revisions[keyRev]; + if (atext == null) { console.error(`[${pad.id}] Missing atext in revision ${keyRev}`); continue; } const apool = pad.pool; - let atext = revisions[keyRev].meta.atext; for (let rev = keyRev + 1; rev <= keyRev + 100 && rev <= head; rev++) { try { const cs = revisions[rev].changeset; diff --git a/bin/checkPad.js b/bin/checkPad.js index 374a3c856..42e5946c1 100644 --- a/bin/checkPad.js +++ b/bin/checkPad.js @@ -62,15 +62,13 @@ npm.load({}, async () => { if (pad.pool == null) throw new Error('Attribute pool is missing'); // check if there is an atext in the keyRevisions - if (revisions[keyRev] == null || - revisions[keyRev].meta == null || - revisions[keyRev].meta.atext == null) { + let {meta: {atext} = {}} = revisions[keyRev] || {}; + if (atext == null) { console.error(`No atext in key revision ${keyRev}`); continue; } const apool = pad.pool; - let atext = revisions[keyRev].meta.atext; for (let rev = keyRev + 1; rev <= keyRev + 100 && rev <= head; rev++) { checkRevisionCount++; diff --git a/bin/checkPadDeltas.js b/bin/checkPadDeltas.js index c53275a28..19d4ba4db 100644 --- a/bin/checkPadDeltas.js +++ b/bin/checkPadDeltas.js @@ -54,8 +54,8 @@ npm.load({}, async () => { // console.log('Fetching', revNum) const revision = await db.get(`pad:${padId}:revs:${revNum}`); // check if there is a atext in the keyRevisions - if (~keyRevisions.indexOf(revNum) && - (revision == null || revision.meta == null || revision.meta.atext == null)) { + const {meta: {atext: revAtext} = {}} = revision || {}; + if (~keyRevisions.indexOf(revNum) && revAtext == null) { console.error(`No atext in key revision ${revNum}`); continue; } From efdcaae5269450ef49fdd6efb6fce9cc5fee2a6f Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 9 Jan 2021 02:44:59 -0500 Subject: [PATCH 178/357] bin scripts: Promisify npm.load --- bin/checkAllPads.js | 11 +++++++---- bin/checkPad.js | 11 +++++++---- bin/checkPadDeltas.js | 8 +++++--- bin/extractPadData.js | 8 ++++---- bin/importSqlFile.js | 9 ++++++--- bin/migrateDirtyDBtoRealDB.js | 7 +++++-- bin/repairPad.js | 8 +++++--- 7 files changed, 39 insertions(+), 23 deletions(-) diff --git a/bin/checkAllPads.js b/bin/checkAllPads.js index 063da96a5..3e2ea8407 100644 --- a/bin/checkAllPads.js +++ b/bin/checkAllPads.js @@ -7,11 +7,14 @@ // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); +const npm = require('ep_etherpad-lite/node_modules/npm'); +const util = require('util'); + if (process.argv.length !== 2) throw new Error('Use: node bin/checkAllPads.js'); -// load and initialize NPM -const npm = require('ep_etherpad-lite/node_modules/npm'); -npm.load({}, async () => { +(async () => { + await util.promisify(npm.load)({}); + try { // initialize the database require('ep_etherpad-lite/node/utils/Settings'); @@ -92,4 +95,4 @@ npm.load({}, async () => { console.trace(err); throw err; } -}); +})(); diff --git a/bin/checkPad.js b/bin/checkPad.js index 42e5946c1..56464648b 100644 --- a/bin/checkPad.js +++ b/bin/checkPad.js @@ -7,15 +7,18 @@ // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); +const npm = require('ep_etherpad-lite/node_modules/npm'); +const util = require('util'); + if (process.argv.length !== 3) throw new Error('Use: node bin/checkPad.js $PADID'); // get the padID const padId = process.argv[2]; let checkRevisionCount = 0; -// load and initialize NPM; -const npm = require('ep_etherpad-lite/node_modules/npm'); -npm.load({}, async () => { +(async () => { + await util.promisify(npm.load)({}); + try { // initialize database require('ep_etherpad-lite/node/utils/Settings'); @@ -86,4 +89,4 @@ npm.load({}, async () => { console.trace(err); throw err; } -}); +})(); diff --git a/bin/checkPadDeltas.js b/bin/checkPadDeltas.js index 19d4ba4db..45ee27d72 100644 --- a/bin/checkPadDeltas.js +++ b/bin/checkPadDeltas.js @@ -12,12 +12,14 @@ if (process.argv.length !== 3) throw new Error('Use: node bin/checkPadDeltas.js // get the padID const padId = process.argv[2]; -// load and initialize NPM; const expect = require('../tests/frontend/lib/expect'); const diff = require('ep_etherpad-lite/node_modules/diff'); const npm = require('ep_etherpad-lite/node_modules/npm'); +const util = require('util'); + +(async () => { + await util.promisify(npm.load)({}); -npm.load({}, async () => { // initialize database require('ep_etherpad-lite/node/utils/Settings'); const db = require('ep_etherpad-lite/node/db/DB'); @@ -102,4 +104,4 @@ npm.load({}, async () => { } })); } -}); +})(); diff --git a/bin/extractPadData.js b/bin/extractPadData.js index 3b182a571..58fe50a42 100644 --- a/bin/extractPadData.js +++ b/bin/extractPadData.js @@ -16,9 +16,10 @@ if (process.argv.length !== 3) throw new Error('Use: node extractPadData.js $PAD const padId = process.argv[2]; const npm = require('ep_etherpad-lite/node_modules/npm'); +const util = require('util'); -npm.load({}, async (err) => { - if (err) throw err; +(async () => { + await util.promisify(npm.load)({}); try { // initialize database @@ -29,7 +30,6 @@ npm.load({}, async (err) => { // load extra modules const dirtyDB = require('ep_etherpad-lite/node_modules/dirty'); const padManager = require('ep_etherpad-lite/node/db/PadManager'); - const util = require('util'); // initialize output database const dirty = dirtyDB(`${padId}.db`); @@ -71,4 +71,4 @@ npm.load({}, async (err) => { console.error(err); throw err; } -}); +})(); diff --git a/bin/importSqlFile.js b/bin/importSqlFile.js index 35fe4f323..b02e9e10c 100644 --- a/bin/importSqlFile.js +++ b/bin/importSqlFile.js @@ -4,6 +4,9 @@ // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); +const npm = require('ep_etherpad-lite/node_modules/npm'); +const util = require('util'); + const startTime = Date.now(); const log = (str) => { @@ -43,10 +46,10 @@ const unescape = (val) => { return val; }; +(async () => { + await util.promisify(npm.load)({}); -require('ep_etherpad-lite/node_modules/npm').load({}, (er, npm) => { const fs = require('fs'); - const ueberDB = require('ep_etherpad-lite/node_modules/ueberdb2'); const settings = require('ep_etherpad-lite/node/utils/Settings'); const log4js = require('ep_etherpad-lite/node_modules/log4js'); @@ -106,4 +109,4 @@ require('ep_etherpad-lite/node_modules/npm').load({}, (er, npm) => { }); } }); -}); +})(); diff --git a/bin/migrateDirtyDBtoRealDB.js b/bin/migrateDirtyDBtoRealDB.js index 48760b8ba..c79d9b747 100644 --- a/bin/migrateDirtyDBtoRealDB.js +++ b/bin/migrateDirtyDBtoRealDB.js @@ -4,9 +4,12 @@ // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); +const npm = require('ep_etherpad-lite/node_modules/npm'); const util = require('util'); -require('ep_etherpad-lite/node_modules/npm').load({}, async (er, npm) => { +(async () => { + await util.promisify(npm.load)({}); + process.chdir(`${npm.root}/..`); // This script requires that you have modified your settings.json file @@ -56,4 +59,4 @@ require('ep_etherpad-lite/node_modules/npm').load({}, async (er, npm) => { await util.promisify(db.close.bind(db))(); console.log('Finished.'); -}); +})(); diff --git a/bin/repairPad.js b/bin/repairPad.js index e083f30b9..ff2da9776 100644 --- a/bin/repairPad.js +++ b/bin/repairPad.js @@ -18,8 +18,10 @@ const padId = process.argv[2]; let valueCount = 0; const npm = require('ep_etherpad-lite/node_modules/npm'); -npm.load({}, async (err) => { - if (err) throw err; +const util = require('util'); + +(async () => { + await util.promisify(npm.load)({}); // intialize database require('ep_etherpad-lite/node/utils/Settings'); @@ -56,4 +58,4 @@ npm.load({}, async (err) => { } console.info(`Finished: Replaced ${valueCount} values in the database`); -}); +})(); From 0a617679012728d7e48d28658cd08341d77808f6 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 17 Jan 2021 03:16:01 -0500 Subject: [PATCH 179/357] bin scripts: Delete redundant exception log messages The exception will cause Node.js to print the error message and stack trace so there's no point in logging it ourselves. --- bin/checkAllPads.js | 131 ++++++++++++++++++++---------------------- bin/checkPad.js | 107 ++++++++++++++++------------------ bin/extractPadData.js | 79 ++++++++++++------------- 3 files changed, 151 insertions(+), 166 deletions(-) diff --git a/bin/checkAllPads.js b/bin/checkAllPads.js index 3e2ea8407..356112e59 100644 --- a/bin/checkAllPads.js +++ b/bin/checkAllPads.js @@ -15,84 +15,79 @@ if (process.argv.length !== 2) throw new Error('Use: node bin/checkAllPads.js'); (async () => { await util.promisify(npm.load)({}); - try { - // initialize the database - require('ep_etherpad-lite/node/utils/Settings'); - const db = require('ep_etherpad-lite/node/db/DB'); - await db.init(); + // initialize the database + require('ep_etherpad-lite/node/utils/Settings'); + const db = require('ep_etherpad-lite/node/db/DB'); + await db.init(); - // load modules - const Changeset = require('ep_etherpad-lite/static/js/Changeset'); - const padManager = require('ep_etherpad-lite/node/db/PadManager'); + // load modules + const Changeset = require('ep_etherpad-lite/static/js/Changeset'); + const padManager = require('ep_etherpad-lite/node/db/PadManager'); - let revTestedCount = 0; + let revTestedCount = 0; - // get all pads - const res = await padManager.listAllPads(); - for (const padId of res.padIDs) { - const pad = await padManager.getPad(padId); + // get all pads + const res = await padManager.listAllPads(); + for (const padId of res.padIDs) { + const pad = await padManager.getPad(padId); - // check if the pad has a pool - if (pad.pool == null) { - console.error(`[${pad.id}] Missing attribute pool`); + // check if the pad has a pool + if (pad.pool == null) { + console.error(`[${pad.id}] Missing attribute pool`); + continue; + } + // create an array with key kevisions + // key revisions always save the full pad atext + const head = pad.getHeadRevisionNumber(); + const keyRevisions = []; + for (let rev = 0; rev < head; rev += 100) { + keyRevisions.push(rev); + } + + // run through all key revisions + for (const keyRev of keyRevisions) { + // create an array of revisions we need till the next keyRevision or the End + const revisionsNeeded = []; + for (let rev = keyRev; rev <= keyRev + 100 && rev <= head; rev++) { + revisionsNeeded.push(rev); + } + + // this array will hold all revision changesets + const revisions = []; + + // run through all needed revisions and get them from the database + for (const revNum of revisionsNeeded) { + const revision = await db.get(`pad:${pad.id}:revs:${revNum}`); + revisions[revNum] = revision; + } + + // check if the revision exists + if (revisions[keyRev] == null) { + console.error(`[${pad.id}] Missing revision ${keyRev}`); continue; } - // create an array with key kevisions - // key revisions always save the full pad atext - const head = pad.getHeadRevisionNumber(); - const keyRevisions = []; - for (let rev = 0; rev < head; rev += 100) { - keyRevisions.push(rev); + + // check if there is a atext in the keyRevisions + let {meta: {atext} = {}} = revisions[keyRev]; + if (atext == null) { + console.error(`[${pad.id}] Missing atext in revision ${keyRev}`); + continue; } - // run through all key revisions - for (const keyRev of keyRevisions) { - // create an array of revisions we need till the next keyRevision or the End - const revisionsNeeded = []; - for (let rev = keyRev; rev <= keyRev + 100 && rev <= head; rev++) { - revisionsNeeded.push(rev); - } - - // this array will hold all revision changesets - const revisions = []; - - // run through all needed revisions and get them from the database - for (const revNum of revisionsNeeded) { - const revision = await db.get(`pad:${pad.id}:revs:${revNum}`); - revisions[revNum] = revision; - } - - // check if the revision exists - if (revisions[keyRev] == null) { - console.error(`[${pad.id}] Missing revision ${keyRev}`); - continue; - } - - // check if there is a atext in the keyRevisions - let {meta: {atext} = {}} = revisions[keyRev]; - if (atext == null) { - console.error(`[${pad.id}] Missing atext in revision ${keyRev}`); - continue; - } - - const apool = pad.pool; - for (let rev = keyRev + 1; rev <= keyRev + 100 && rev <= head; rev++) { - try { - const cs = revisions[rev].changeset; - atext = Changeset.applyToAText(cs, atext, apool); - revTestedCount++; - } catch (e) { - console.error(`[${pad.id}] Bad changeset at revision ${rev} - ${e.message}`); - } + const apool = pad.pool; + for (let rev = keyRev + 1; rev <= keyRev + 100 && rev <= head; rev++) { + try { + const cs = revisions[rev].changeset; + atext = Changeset.applyToAText(cs, atext, apool); + revTestedCount++; + } catch (e) { + console.error(`[${pad.id}] Bad changeset at revision ${rev} - ${e.message}`); } } } - if (revTestedCount === 0) { - throw new Error('No revisions tested'); - } - console.log(`Finished: Tested ${revTestedCount} revisions`); - } catch (err) { - console.trace(err); - throw err; } + if (revTestedCount === 0) { + throw new Error('No revisions tested'); + } + console.log(`Finished: Tested ${revTestedCount} revisions`); })(); diff --git a/bin/checkPad.js b/bin/checkPad.js index 56464648b..20b3fa226 100644 --- a/bin/checkPad.js +++ b/bin/checkPad.js @@ -19,74 +19,69 @@ let checkRevisionCount = 0; (async () => { await util.promisify(npm.load)({}); - try { - // initialize database - require('ep_etherpad-lite/node/utils/Settings'); - const db = require('ep_etherpad-lite/node/db/DB'); - await db.init(); + // initialize database + require('ep_etherpad-lite/node/utils/Settings'); + const db = require('ep_etherpad-lite/node/db/DB'); + await db.init(); - // load modules - const Changeset = require('ep_etherpad-lite/static/js/Changeset'); - const padManager = require('ep_etherpad-lite/node/db/PadManager'); + // load modules + const Changeset = require('ep_etherpad-lite/static/js/Changeset'); + const padManager = require('ep_etherpad-lite/node/db/PadManager'); - const exists = await padManager.doesPadExists(padId); - if (!exists) throw new Error('Pad does not exist'); + const exists = await padManager.doesPadExists(padId); + if (!exists) throw new Error('Pad does not exist'); - // get the pad - const pad = await padManager.getPad(padId); + // get the pad + const pad = await padManager.getPad(padId); - // create an array with key revisions - // key revisions always save the full pad atext - const head = pad.getHeadRevisionNumber(); - const keyRevisions = []; - for (let rev = 0; rev < head; rev += 100) { - keyRevisions.push(rev); + // create an array with key revisions + // key revisions always save the full pad atext + const head = pad.getHeadRevisionNumber(); + const keyRevisions = []; + for (let rev = 0; rev < head; rev += 100) { + keyRevisions.push(rev); + } + + // run through all key revisions + for (let keyRev of keyRevisions) { + keyRev = parseInt(keyRev); + // create an array of revisions we need till the next keyRevision or the End + const revisionsNeeded = []; + for (let rev = keyRev; rev <= keyRev + 100 && rev <= head; rev++) { + revisionsNeeded.push(rev); } - // run through all key revisions - for (let keyRev of keyRevisions) { - keyRev = parseInt(keyRev); - // create an array of revisions we need till the next keyRevision or the End - const revisionsNeeded = []; - for (let rev = keyRev; rev <= keyRev + 100 && rev <= head; rev++) { - revisionsNeeded.push(rev); - } + // this array will hold all revision changesets + const revisions = []; - // this array will hold all revision changesets - const revisions = []; + // run through all needed revisions and get them from the database + for (const revNum of revisionsNeeded) { + const revision = await db.get(`pad:${padId}:revs:${revNum}`); + revisions[revNum] = revision; + } - // run through all needed revisions and get them from the database - for (const revNum of revisionsNeeded) { - const revision = await db.get(`pad:${padId}:revs:${revNum}`); - revisions[revNum] = revision; - } + // check if the pad has a pool + if (pad.pool == null) throw new Error('Attribute pool is missing'); - // check if the pad has a pool - if (pad.pool == null) throw new Error('Attribute pool is missing'); + // check if there is an atext in the keyRevisions + let {meta: {atext} = {}} = revisions[keyRev] || {}; + if (atext == null) { + console.error(`No atext in key revision ${keyRev}`); + continue; + } - // check if there is an atext in the keyRevisions - let {meta: {atext} = {}} = revisions[keyRev] || {}; - if (atext == null) { - console.error(`No atext in key revision ${keyRev}`); + const apool = pad.pool; + + for (let rev = keyRev + 1; rev <= keyRev + 100 && rev <= head; rev++) { + checkRevisionCount++; + try { + const cs = revisions[rev].changeset; + atext = Changeset.applyToAText(cs, atext, apool); + } catch (e) { + console.error(`Bad changeset at revision ${rev} - ${e.message}`); continue; } - - const apool = pad.pool; - - for (let rev = keyRev + 1; rev <= keyRev + 100 && rev <= head; rev++) { - checkRevisionCount++; - try { - const cs = revisions[rev].changeset; - atext = Changeset.applyToAText(cs, atext, apool); - } catch (e) { - console.error(`Bad changeset at revision ${rev} - ${e.message}`); - continue; - } - } - console.log(`Finished: Checked ${checkRevisionCount} revisions`); } - } catch (err) { - console.trace(err); - throw err; + console.log(`Finished: Checked ${checkRevisionCount} revisions`); } })(); diff --git a/bin/extractPadData.js b/bin/extractPadData.js index 58fe50a42..181a8c6bf 100644 --- a/bin/extractPadData.js +++ b/bin/extractPadData.js @@ -21,54 +21,49 @@ const util = require('util'); (async () => { await util.promisify(npm.load)({}); - try { - // initialize database - require('ep_etherpad-lite/node/utils/Settings'); - const db = require('ep_etherpad-lite/node/db/DB'); - await db.init(); + // initialize database + require('ep_etherpad-lite/node/utils/Settings'); + const db = require('ep_etherpad-lite/node/db/DB'); + await db.init(); - // load extra modules - const dirtyDB = require('ep_etherpad-lite/node_modules/dirty'); - const padManager = require('ep_etherpad-lite/node/db/PadManager'); + // load extra modules + const dirtyDB = require('ep_etherpad-lite/node_modules/dirty'); + const padManager = require('ep_etherpad-lite/node/db/PadManager'); - // initialize output database - const dirty = dirtyDB(`${padId}.db`); + // initialize output database + const dirty = dirtyDB(`${padId}.db`); - // Promise wrapped get and set function - const wrapped = db.db.db.wrappedDB; - const get = util.promisify(wrapped.get.bind(wrapped)); - const set = util.promisify(dirty.set.bind(dirty)); + // Promise wrapped get and set function + const wrapped = db.db.db.wrappedDB; + const get = util.promisify(wrapped.get.bind(wrapped)); + const set = util.promisify(dirty.set.bind(dirty)); - // array in which required key values will be accumulated - const neededDBValues = [`pad:${padId}`]; + // array in which required key values will be accumulated + const neededDBValues = [`pad:${padId}`]; - // get the actual pad object - const pad = await padManager.getPad(padId); + // get the actual pad object + const pad = await padManager.getPad(padId); - // add all authors - neededDBValues.push(...pad.getAllAuthors().map((author) => `globalAuthor:${author}`)); + // add all authors + neededDBValues.push(...pad.getAllAuthors().map((author) => `globalAuthor:${author}`)); - // add all revisions - for (let rev = 0; rev <= pad.head; ++rev) { - neededDBValues.push(`pad:${padId}:revs:${rev}`); - } - - // add all chat values - for (let chat = 0; chat <= pad.chatHead; ++chat) { - neededDBValues.push(`pad:${padId}:chat:${chat}`); - } - - for (const dbkey of neededDBValues) { - let dbvalue = await get(dbkey); - if (dbvalue && typeof dbvalue !== 'object') { - dbvalue = JSON.parse(dbvalue); - } - await set(dbkey, dbvalue); - } - - console.log('finished'); - } catch (err) { - console.error(err); - throw err; + // add all revisions + for (let rev = 0; rev <= pad.head; ++rev) { + neededDBValues.push(`pad:${padId}:revs:${rev}`); } + + // add all chat values + for (let chat = 0; chat <= pad.chatHead; ++chat) { + neededDBValues.push(`pad:${padId}:chat:${chat}`); + } + + for (const dbkey of neededDBValues) { + let dbvalue = await get(dbkey); + if (dbvalue && typeof dbvalue !== 'object') { + dbvalue = JSON.parse(dbvalue); + } + await set(dbkey, dbvalue); + } + + console.log('finished'); })(); From c622894fe0b6b7d17f5e286b9c2b2d85a3408b04 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 9 Jan 2021 03:08:50 -0500 Subject: [PATCH 180/357] bin scripts: Promisify db.init and db.close --- bin/importSqlFile.js | 59 +++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/bin/importSqlFile.js b/bin/importSqlFile.js index b02e9e10c..5927d82ce 100644 --- a/bin/importSqlFile.js +++ b/bin/importSqlFile.js @@ -71,42 +71,35 @@ const unescape = (val) => { if (!sqlFile) throw new Error('Use: node importSqlFile.js $SQLFILE'); log('initializing db'); - db.init((err) => { - // there was an error while initializing the database, output it and stop - if (err) { - throw err; - } else { - log('done'); + await util.promisify(db.init.bind(db))(); + log('done'); - log('open output file...'); - const lines = fs.readFileSync(sqlFile, 'utf8').split('\n'); + log('open output file...'); + const lines = fs.readFileSync(sqlFile, 'utf8').split('\n'); - const count = lines.length; - let keyNo = 0; + const count = lines.length; + let keyNo = 0; - process.stdout.write(`Start importing ${count} keys...\n`); - lines.forEach((l) => { - if (l.substr(0, 27) === 'REPLACE INTO store VALUES (') { - const pos = l.indexOf("', '"); - const key = l.substr(28, pos - 28); - let value = l.substr(pos + 3); - value = value.substr(0, value.length - 2); - console.log(`key: ${key} val: ${value}`); - console.log(`unval: ${unescape(value)}`); - db.set(key, unescape(value), null); - keyNo++; - if (keyNo % 1000 === 0) { - process.stdout.write(` ${keyNo}/${count}\n`); - } - } - }); - process.stdout.write('\n'); - process.stdout.write('done. waiting for db to finish transaction. ' + - 'depended on dbms this may take some time..\n'); - - db.close(() => { - log(`finished, imported ${keyNo} keys.`); - }); + process.stdout.write(`Start importing ${count} keys...\n`); + lines.forEach((l) => { + if (l.substr(0, 27) === 'REPLACE INTO store VALUES (') { + const pos = l.indexOf("', '"); + const key = l.substr(28, pos - 28); + let value = l.substr(pos + 3); + value = value.substr(0, value.length - 2); + console.log(`key: ${key} val: ${value}`); + console.log(`unval: ${unescape(value)}`); + db.set(key, unescape(value), null); + keyNo++; + if (keyNo % 1000 === 0) { + process.stdout.write(` ${keyNo}/${count}\n`); + } } }); + process.stdout.write('\n'); + process.stdout.write('done. waiting for db to finish transaction. ' + + 'depended on dbms this may take some time..\n'); + + await util.promisify(db.close.bind(db))(); + log(`finished, imported ${keyNo} keys.`); })(); From 69efd16a6db1794ae3a719740d296a035e79c7f0 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 17 Jan 2021 03:58:28 -0500 Subject: [PATCH 181/357] bin/rebuildPad.js: Add missing calls to `util.callbackify` --- bin/rebuildPad.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/rebuildPad.js b/bin/rebuildPad.js index 0034fe336..83c9bf342 100644 --- a/bin/rebuildPad.js +++ b/bin/rebuildPad.js @@ -29,7 +29,7 @@ async.series([ (callback) => { // Get a handle into the database db = require('ep_etherpad-lite/node/db/DB'); - db.init(callback); + util.callbackify(db.init)(callback); }, (callback) => { Pad = require('ep_etherpad-lite/node/db/Pad').Pad; @@ -44,10 +44,10 @@ async.series([ if (!PadManager.isValidPadId(newPadId)) { throw new Error('Cannot create a pad with that id as it is invalid'); } - PadManager.doesPadExists(newPadId, (err, exists) => { + util.callbackify(PadManager.doesPadExist)(newPadId, (err, exists) => { if (exists) throw new Error('Cannot create a pad with that id as it already exists'); }); - PadManager.getPad(padId, (err, pad) => { + util.callbackify(PadManager.getPad)(padId, (err, pad) => { oldPad = pad; newPad = new Pad(newPadId); callback(); From 72c2abab8d1d5477075c7c09117c55136cd461aa Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 17 Jan 2021 04:19:02 -0500 Subject: [PATCH 182/357] bin/rebuildPad.js: Fix sequencing of asynchronous functions --- bin/rebuildPad.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/bin/rebuildPad.js b/bin/rebuildPad.js index 83c9bf342..0f929ef75 100644 --- a/bin/rebuildPad.js +++ b/bin/rebuildPad.js @@ -45,9 +45,14 @@ async.series([ throw new Error('Cannot create a pad with that id as it is invalid'); } util.callbackify(PadManager.doesPadExist)(newPadId, (err, exists) => { + if (err != null) return callback(err); if (exists) throw new Error('Cannot create a pad with that id as it already exists'); + callback(); }); + }, + (callback) => { util.callbackify(PadManager.getPad)(padId, (err, pad) => { + if (err) return callback(err); oldPad = pad; newPad = new Pad(newPadId); callback(); @@ -102,12 +107,11 @@ async.series([ newPad.savedRevisions = newSavedRevisions; callback(); }, + // Save the source pad + (callback) => db.db.set(`pad:${newPadId}`, newPad, callback), (callback) => { - // Save the source pad - db.db.set(`pad:${newPadId}`, newPad, (err) => { - console.log(`Created: Source Pad: pad:${newPadId}`); - util.callbackify(newPad.saveToDatabase.bind(newPad))(callback); - }); + console.log(`Created: Source Pad: pad:${newPadId}`); + util.callbackify(newPad.saveToDatabase.bind(newPad))(callback); }, ], (err) => { if (err) throw err; From 5b519b9a9cf2d5fe8412bbba56ac649f8c0fc618 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 29 Jan 2021 17:01:04 -0500 Subject: [PATCH 183/357] bin/rebuildPad.js: Asyncify --- bin/rebuildPad.js | 160 ++++++++++++++++++++-------------------------- 1 file changed, 68 insertions(+), 92 deletions(-) diff --git a/bin/rebuildPad.js b/bin/rebuildPad.js index 0f929ef75..3e6e6e5fa 100644 --- a/bin/rebuildPad.js +++ b/bin/rebuildPad.js @@ -13,7 +13,6 @@ if (process.argv.length !== 4 && process.argv.length !== 5) { throw new Error('Use: node bin/repairPad.js $PADID $REV [$NEWPADID]'); } -const async = require('ep_etherpad-lite/node_modules/async'); const npm = require('ep_etherpad-lite/node_modules/npm'); const util = require('util'); @@ -21,99 +20,76 @@ const padId = process.argv[2]; const newRevHead = process.argv[3]; const newPadId = process.argv[4] || `${padId}-rebuilt`; -let db, oldPad, newPad; -let Pad, PadManager; +(async () => { + await util.promisify(npm.load)({}); -async.series([ - (callback) => npm.load({}, callback), - (callback) => { - // Get a handle into the database - db = require('ep_etherpad-lite/node/db/DB'); - util.callbackify(db.init)(callback); - }, - (callback) => { - Pad = require('ep_etherpad-lite/node/db/Pad').Pad; - PadManager = require('ep_etherpad-lite/node/db/PadManager'); - // Get references to the original pad and to a newly created pad - // HACK: This is a standalone script, so we want to write everything - // out to the database immediately. The only problem with this is - // that a driver (like the mysql driver) can hardcode these values. - db.db.db.settings = {cache: 0, writeInterval: 0, json: true}; - // Validate the newPadId if specified and that a pad with that ID does - // not already exist to avoid overwriting it. - if (!PadManager.isValidPadId(newPadId)) { - throw new Error('Cannot create a pad with that id as it is invalid'); + const db = require('ep_etherpad-lite/node/db/DB'); + await db.init(); + + const Pad = require('ep_etherpad-lite/node/db/Pad').Pad; + const PadManager = require('ep_etherpad-lite/node/db/PadManager'); + // Get references to the original pad and to a newly created pad + // HACK: This is a standalone script, so we want to write everything + // out to the database immediately. The only problem with this is + // that a driver (like the mysql driver) can hardcode these values. + db.db.db.settings = {cache: 0, writeInterval: 0, json: true}; + // Validate the newPadId if specified and that a pad with that ID does + // not already exist to avoid overwriting it. + if (!PadManager.isValidPadId(newPadId)) { + throw new Error('Cannot create a pad with that id as it is invalid'); + } + const exists = await PadManager.doesPadExist(newPadId); + if (exists) throw new Error('Cannot create a pad with that id as it already exists'); + + const oldPad = await PadManager.getPad(padId); + const newPad = new Pad(newPadId); + + // Clone all Chat revisions + const chatHead = oldPad.chatHead; + await Promise.all([...Array(chatHead + 1).keys()].map(async (i) => { + const chat = await db.get(`pad:${padId}:chat:${i}`); + await db.set(`pad:${newPadId}:chat:${i}`, chat); + console.log(`Created: Chat Revision: pad:${newPadId}:chat:${i}`); + })); + + // Rebuild Pad from revisions up to and including the new revision head + const AuthorManager = require('ep_etherpad-lite/node/db/AuthorManager'); + const Changeset = require('ep_etherpad-lite/static/js/Changeset'); + // Author attributes are derived from changesets, but there can also be + // non-author attributes with specific mappings that changesets depend on + // and, AFAICT, cannot be recreated any other way + newPad.pool.numToAttrib = oldPad.pool.numToAttrib; + for (let curRevNum = 0; curRevNum <= newRevHead; curRevNum++) { + const rev = await db.get(`pad:${padId}:revs:${curRevNum}`); + if (rev.meta) { + throw new Error('The specified revision number could not be found.'); } - util.callbackify(PadManager.doesPadExist)(newPadId, (err, exists) => { - if (err != null) return callback(err); - if (exists) throw new Error('Cannot create a pad with that id as it already exists'); - callback(); - }); - }, - (callback) => { - util.callbackify(PadManager.getPad)(padId, (err, pad) => { - if (err) return callback(err); - oldPad = pad; - newPad = new Pad(newPadId); - callback(); - }); - }, - (callback) => { - // Clone all Chat revisions - const chatHead = oldPad.chatHead; - for (let i = 0, curHeadNum = 0; i <= chatHead; i++) { - db.db.get(`pad:${padId}:chat:${i}`, (err, chat) => { - db.db.set(`pad:${newPadId}:chat:${curHeadNum++}`, chat); - console.log(`Created: Chat Revision: pad:${newPadId}:chat:${curHeadNum}`); - }); + const newRevNum = ++newPad.head; + const newRevId = `pad:${newPad.id}:revs:${newRevNum}`; + await Promise.all([ + db.set(newRevId, rev), + AuthorManager.addPad(rev.meta.author, newPad.id), + ]); + newPad.atext = Changeset.applyToAText(rev.changeset, newPad.atext, newPad.pool); + console.log(`Created: Revision: pad:${newPad.id}:revs:${newRevNum}`); + } + + // Add saved revisions up to the new revision head + console.log(newPad.head); + const newSavedRevisions = []; + for (const savedRev of oldPad.savedRevisions) { + if (savedRev.revNum <= newRevHead) { + newSavedRevisions.push(savedRev); + console.log(`Added: Saved Revision: ${savedRev.revNum}`); } - callback(); - }, - (callback) => { - // Rebuild Pad from revisions up to and including the new revision head - const AuthorManager = require('ep_etherpad-lite/node/db/AuthorManager'); - const Changeset = require('ep_etherpad-lite/static/js/Changeset'); - // Author attributes are derived from changesets, but there can also be - // non-author attributes with specific mappings that changesets depend on - // and, AFAICT, cannot be recreated any other way - newPad.pool.numToAttrib = oldPad.pool.numToAttrib; - for (let curRevNum = 0; curRevNum <= newRevHead; curRevNum++) { - db.db.get(`pad:${padId}:revs:${curRevNum}`, (err, rev) => { - if (rev.meta) { - throw new Error('The specified revision number could not be found.'); - } - const newRevNum = ++newPad.head; - const newRevId = `pad:${newPad.id}:revs:${newRevNum}`; - db.db.set(newRevId, rev); - AuthorManager.addPad(rev.meta.author, newPad.id); - newPad.atext = Changeset.applyToAText(rev.changeset, newPad.atext, newPad.pool); - console.log(`Created: Revision: pad:${newPad.id}:revs:${newRevNum}`); - if (newRevNum === newRevHead) { - callback(); - } - }); - } - }, - (callback) => { - // Add saved revisions up to the new revision head - console.log(newPad.head); - const newSavedRevisions = []; - for (const savedRev of oldPad.savedRevisions) { - if (savedRev.revNum <= newRevHead) { - newSavedRevisions.push(savedRev); - console.log(`Added: Saved Revision: ${savedRev.revNum}`); - } - } - newPad.savedRevisions = newSavedRevisions; - callback(); - }, + } + newPad.savedRevisions = newSavedRevisions; + // Save the source pad - (callback) => db.db.set(`pad:${newPadId}`, newPad, callback), - (callback) => { - console.log(`Created: Source Pad: pad:${newPadId}`); - util.callbackify(newPad.saveToDatabase.bind(newPad))(callback); - }, -], (err) => { - if (err) throw err; + await db.set(`pad:${newPadId}`, newPad); + + console.log(`Created: Source Pad: pad:${newPadId}`); + await newPad.saveToDatabase(); + console.info('finished'); -}); +})(); From 809dc6e367f1db7f25e08dcc04fda5eb7e9b1b0f Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 29 Jan 2021 17:15:00 -0500 Subject: [PATCH 184/357] bin/rebuildPad.js: PadManager must be loaded before Pad There is a circular dependency between the two; loading PadManager first ensures that PadManager's Pad variable is not undefined. --- bin/rebuildPad.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/rebuildPad.js b/bin/rebuildPad.js index 3e6e6e5fa..f5043b85b 100644 --- a/bin/rebuildPad.js +++ b/bin/rebuildPad.js @@ -26,8 +26,8 @@ const newPadId = process.argv[4] || `${padId}-rebuilt`; const db = require('ep_etherpad-lite/node/db/DB'); await db.init(); - const Pad = require('ep_etherpad-lite/node/db/Pad').Pad; const PadManager = require('ep_etherpad-lite/node/db/PadManager'); + const Pad = require('ep_etherpad-lite/node/db/Pad').Pad; // Get references to the original pad and to a newly created pad // HACK: This is a standalone script, so we want to write everything // out to the database immediately. The only problem with this is From bf209ddad38ce12f446093092c7f5899a6d7144b Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 29 Jan 2021 17:19:40 -0500 Subject: [PATCH 185/357] bin/rebuildPad.js: Close the database when done This prevents loss of data due to unflushed writes. --- bin/rebuildPad.js | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/rebuildPad.js b/bin/rebuildPad.js index f5043b85b..f541a4ce4 100644 --- a/bin/rebuildPad.js +++ b/bin/rebuildPad.js @@ -91,5 +91,6 @@ const newPadId = process.argv[4] || `${padId}-rebuilt`; console.log(`Created: Source Pad: pad:${newPadId}`); await newPad.saveToDatabase(); + await db.shutdown(); console.info('finished'); })(); From 846e3e9fbdce80c3ff84e7ae94d5d7d698e4978a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 29 Jan 2021 17:20:25 -0500 Subject: [PATCH 186/357] bin/rebuildPad.js: Don't overwrite DB settings There's no need, and setting `json` to true breaks databases that do their own serialization of ECMAScript objects. --- bin/rebuildPad.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/bin/rebuildPad.js b/bin/rebuildPad.js index f541a4ce4..870bbcc4a 100644 --- a/bin/rebuildPad.js +++ b/bin/rebuildPad.js @@ -28,11 +28,6 @@ const newPadId = process.argv[4] || `${padId}-rebuilt`; const PadManager = require('ep_etherpad-lite/node/db/PadManager'); const Pad = require('ep_etherpad-lite/node/db/Pad').Pad; - // Get references to the original pad and to a newly created pad - // HACK: This is a standalone script, so we want to write everything - // out to the database immediately. The only problem with this is - // that a driver (like the mysql driver) can hardcode these values. - db.db.db.settings = {cache: 0, writeInterval: 0, json: true}; // Validate the newPadId if specified and that a pad with that ID does // not already exist to avoid overwriting it. if (!PadManager.isValidPadId(newPadId)) { From 0ad0160b7ce8e17053c6c255cd34ccd845d291fc Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 29 Jan 2021 17:23:11 -0500 Subject: [PATCH 187/357] bin/rebuildPad.js: Fix check for existing rev --- bin/rebuildPad.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bin/rebuildPad.js b/bin/rebuildPad.js index 870bbcc4a..1bc942fe2 100644 --- a/bin/rebuildPad.js +++ b/bin/rebuildPad.js @@ -56,9 +56,7 @@ const newPadId = process.argv[4] || `${padId}-rebuilt`; newPad.pool.numToAttrib = oldPad.pool.numToAttrib; for (let curRevNum = 0; curRevNum <= newRevHead; curRevNum++) { const rev = await db.get(`pad:${padId}:revs:${curRevNum}`); - if (rev.meta) { - throw new Error('The specified revision number could not be found.'); - } + if (!rev || !rev.meta) throw new Error('The specified revision number could not be found.'); const newRevNum = ++newPad.head; const newRevId = `pad:${newPad.id}:revs:${newRevNum}`; await Promise.all([ From 86ceb2b610a2a9a15fedc70b5690017e41fee5fe Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 14 Dec 2020 20:45:12 -0500 Subject: [PATCH 188/357] server: Exit on unhandled Promise rejection --- src/node/server.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/node/server.js b/src/node/server.js index 3e3b25d21..d14fca92d 100755 --- a/src/node/server.js +++ b/src/node/server.js @@ -77,6 +77,9 @@ exports.start = async () => { } process.on('uncaughtException', exports.exit); + // As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an + // unhandled rejection into an uncaught exception, which does cause Node.js to exit. + process.on('unhandledRejection', (err) => { throw err; }); /* * Connect graceful shutdown with sigint and uncaught exception From d339f2a6715f154bae7d68e52393377f0030f18f Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 22 Dec 2020 00:45:33 -0500 Subject: [PATCH 189/357] server: Perform init after adding uncaught exception handler This avoids an unnecessary `try` block. --- src/node/server.js | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/node/server.js b/src/node/server.js index d14fca92d..ece5ed25b 100755 --- a/src/node/server.js +++ b/src/node/server.js @@ -60,22 +60,6 @@ exports.start = async () => { stats.gauge('memoryUsage', () => process.memoryUsage().rss); stats.gauge('memoryUsageHeap', () => process.memoryUsage().heapUsed); - await util.promisify(npm.load)(); - - try { - await db.init(); - await plugins.update(); - console.info(`Installed plugins: ${plugins.formatPluginsWithVersion()}`); - console.debug(`Installed parts:\n${plugins.formatParts()}`); - console.debug(`Installed hooks:\n${plugins.formatHooks()}`); - await hooks.aCallAll('loadSettings', {settings}); - await hooks.aCallAll('createServer'); - } catch (e) { - console.error(`exception thrown: ${e.message}`); - if (e.stack) console.log(e.stack); - process.exit(1); - } - process.on('uncaughtException', exports.exit); // As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an // unhandled rejection into an uncaught exception, which does cause Node.js to exit. @@ -105,6 +89,15 @@ exports.start = async () => { // Pass undefined to exports.exit because this is not an abnormal termination. process.on('SIGTERM', () => exports.exit()); + await util.promisify(npm.load)(); + await db.init(); + await plugins.update(); + console.info(`Installed plugins: ${plugins.formatPluginsWithVersion()}`); + console.debug(`Installed parts:\n${plugins.formatParts()}`); + console.debug(`Installed hooks:\n${plugins.formatHooks()}`); + await hooks.aCallAll('loadSettings', {settings}); + await hooks.aCallAll('createServer'); + // Return the HTTP server to make it easier to write tests. return express.server; }; From 725023fe5883ebc67723948a98e7c06042216fc0 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 22 Dec 2020 01:01:37 -0500 Subject: [PATCH 190/357] server: Refactor `stop()` to avoid no-async-promise-executor lint error Also log when Etherpad has stopped. --- src/node/server.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/node/server.js b/src/node/server.js index ece5ed25b..38ffce22c 100755 --- a/src/node/server.js +++ b/src/node/server.js @@ -106,12 +106,15 @@ exports.stop = async () => { if (stopped) return; stopped = true; console.log('Stopping Etherpad...'); - await new Promise(async (resolve, reject) => { - const id = setTimeout(() => reject(new Error('Timed out waiting for shutdown tasks')), 3000); - await hooks.aCallAll('shutdown'); - clearTimeout(id); - resolve(); - }); + let timeout = null; + await Promise.race([ + hooks.aCallAll('shutdown'), + new Promise((resolve, reject) => { + timeout = setTimeout(() => reject(new Error('Timed out waiting for shutdown tasks')), 3000); + }), + ]); + clearTimeout(timeout); + console.log('Etherpad stopped'); }; exports.exit = async (err) => { From ecdb105bfed80f22ab67ec3640cc09e0019233c6 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 22 Dec 2020 01:08:50 -0500 Subject: [PATCH 191/357] server: Refine process lifetime management Define states and use them to properly handle multiple calls to `start()`, `stop()`, and `exit()`. (Multiple calls to `exit()` can happen if there is an uncaught exception or signal during shutdown.) This should also make it easier to add support for cleanly restarting the server after a shutdown (for tests or via an `/admin` page). --- src/node/server.js | 127 +++++++++++++++++++++++++++++++++------- tests/backend/common.js | 2 +- 2 files changed, 108 insertions(+), 21 deletions(-) diff --git a/src/node/server.js b/src/node/server.js index 38ffce22c..4cfca5fd1 100755 --- a/src/node/server.js +++ b/src/node/server.js @@ -44,13 +44,38 @@ const plugins = require('../static/js/pluginfw/plugins'); const settings = require('./utils/Settings'); const util = require('util'); -let started = false; -let stopped = false; +const State = { + INITIAL: 1, + STARTING: 2, + RUNNING: 3, + STOPPING: 4, + STOPPED: 5, + EXITING: 6, + WAITING_FOR_EXIT: 7, +}; +let state = State.INITIAL; + +const runningCallbacks = []; exports.start = async () => { - if (started) return express.server; - started = true; - if (stopped) throw new Error('restart not supported'); + switch (state) { + case State.INITIAL: + break; + case State.STARTING: + await new Promise((resolve) => runningCallbacks.push(resolve)); + // fall through + case State.RUNNING: + return express.server; + case State.STOPPING: + case State.STOPPED: + case State.EXITING: + case State.WAITING_FOR_EXIT: + throw new Error('restart not supported'); + default: + throw new Error(`unknown State: ${state.toString()}`); + } + console.log('Starting Etherpad...'); + state = State.STARTING; // Check if Etherpad version is up-to-date UpdateCheck.check(); @@ -86,8 +111,7 @@ exports.start = async () => { process.on('SIGINT', exports.exit); // When running as PID1 (e.g. in docker container) allow graceful shutdown on SIGTERM c.f. #3265. - // Pass undefined to exports.exit because this is not an abnormal termination. - process.on('SIGTERM', () => exports.exit()); + process.on('SIGTERM', exports.exit); await util.promisify(npm.load)(); await db.init(); @@ -98,14 +122,36 @@ exports.start = async () => { await hooks.aCallAll('loadSettings', {settings}); await hooks.aCallAll('createServer'); + console.log('Etherpad is running'); + state = State.RUNNING; + while (runningCallbacks.length > 0) setImmediate(runningCallbacks.pop()); + // Return the HTTP server to make it easier to write tests. return express.server; }; +const stoppedCallbacks = []; exports.stop = async () => { - if (stopped) return; - stopped = true; + switch (state) { + case State.STARTING: + await exports.start(); + // Don't fall through to State.RUNNING in case another caller is also waiting for startup. + return await exports.stop(); + case State.RUNNING: + break; + case State.STOPPING: + await new Promise((resolve) => stoppedCallbacks.push(resolve)); + // fall through + case State.INITIAL: + case State.STOPPED: + case State.EXITING: + case State.WAITING_FOR_EXIT: + return; + default: + throw new Error(`unknown State: ${state.toString()}`); + } console.log('Stopping Etherpad...'); + state = State.STOPPING; let timeout = null; await Promise.race([ hooks.aCallAll('shutdown'), @@ -115,21 +161,62 @@ exports.stop = async () => { ]); clearTimeout(timeout); console.log('Etherpad stopped'); + state = State.STOPPED; + while (stoppedCallbacks.length > 0) setImmediate(stoppedCallbacks.pop()); }; -exports.exit = async (err) => { - let exitCode = 0; - if (err) { - exitCode = 1; - console.error(err.stack ? err.stack : err); +const exitCallbacks = []; +let exitCalled = false; +exports.exit = async (err = null) => { + /* eslint-disable no-process-exit */ + if (err === 'SIGTERM') { + // Termination from SIGTERM is not treated as an abnormal termination. + console.log('Received SIGTERM signal'); + err = null; + } else if (err != null) { + console.error(err.stack || err.toString()); + process.exitCode = 1; + if (exitCalled) { + console.error('Error occurred while waiting to exit. Forcing an immediate unclean exit...'); + process.exit(1); + } } - try { - await exports.stop(); - } catch (err) { - exitCode = 1; - console.error(err.stack ? err.stack : err); + exitCalled = true; + switch (state) { + case State.STARTING: + case State.RUNNING: + case State.STOPPING: + await exports.stop(); + // Don't fall through to State.STOPPED in case another caller is also waiting for stop(). + // Don't pass err to exports.exit() because this err has already been processed. (If err is + // passed again to exit() then exit() will think that a second error occurred while exiting.) + return await exports.exit(); + case State.INITIAL: + case State.STOPPED: + break; + case State.EXITING: + await new Promise((resolve) => exitCallbacks.push(resolve)); + // fall through + case State.WAITING_FOR_EXIT: + return; + default: + throw new Error(`unknown State: ${state.toString()}`); } - process.exit(exitCode); + console.log('Exiting...'); + state = State.EXITING; + while (exitCallbacks.length > 0) setImmediate(exitCallbacks.pop()); + // Node.js should exit on its own without further action. Add a timeout to force Node.js to exit + // just in case something failed to get cleaned up during the shutdown hook. unref() is called on + // the timeout so that the timeout itself does not prevent Node.js from exiting. + setTimeout(() => { + console.error('Something that should have been cleaned up during the shutdown hook (such as ' + + 'a timer, worker thread, or open connection) is preventing Node.js from exiting'); + console.error('Forcing an unclean exit...'); + process.exit(1); + }, 5000).unref(); + console.log('Waiting for Node.js to exit...'); + state = State.WAITING_FOR_EXIT; + /* eslint-enable no-process-exit */ }; if (require.main === module) exports.start(); diff --git a/tests/backend/common.js b/tests/backend/common.js index 22a84c097..642f83afe 100644 --- a/tests/backend/common.js +++ b/tests/backend/common.js @@ -50,10 +50,10 @@ exports.init = async function () { after(async function () { webaccess.authnFailureDelayMs = backups.authnFailureDelayMs; - await server.stop(); // Note: This does not unset settings that were added. Object.assign(settings, backups.settings); log4js.setGlobalLogLevel(logLevel); + await server.exit(); }); return exports.agent; From ba81ead1018e2c78ae724226bb7a0b845544b62f Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 22 Dec 2020 01:21:43 -0500 Subject: [PATCH 192/357] server: Remove all other signal listeners --- src/node/server.js | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/node/server.js b/src/node/server.js index 4cfca5fd1..1a6f9cbe5 100755 --- a/src/node/server.js +++ b/src/node/server.js @@ -56,6 +56,13 @@ const State = { let state = State.INITIAL; +const removeSignalListener = (signal, listener) => { + console.debug(`Removing ${signal} listener because it might interfere with shutdown tasks. ` + + `Function code:\n${listener.toString()}\n` + + `Current stack:\n${(new Error()).stack.split('\n').slice(1).join('\n')}`); + process.off(signal, listener); +}; + const runningCallbacks = []; exports.start = async () => { switch (state) { @@ -90,28 +97,21 @@ exports.start = async () => { // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); - /* - * Connect graceful shutdown with sigint and uncaught exception - * - * Until Etherpad 1.7.5, process.on('SIGTERM') and process.on('SIGINT') were - * not hooked up under Windows, because old nodejs versions did not support - * them. - * - * According to nodejs 6.x documentation, it is now safe to do so. This - * allows to gracefully close the DB connection when hitting CTRL+C under - * Windows, for example. - * - * Source: https://nodejs.org/docs/latest-v6.x/api/process.html#process_signal_events - * - * - SIGTERM is not supported on Windows, it can be listened on. - * - SIGINT from the terminal is supported on all platforms, and can usually - * be generated with +C (though this may be configurable). It is not - * generated when terminal raw mode is enabled. - */ - process.on('SIGINT', exports.exit); - - // When running as PID1 (e.g. in docker container) allow graceful shutdown on SIGTERM c.f. #3265. - process.on('SIGTERM', exports.exit); + for (const signal of ['SIGINT', 'SIGTERM']) { + // Forcibly remove other signal listeners to prevent them from terminating node before we are + // done cleaning up. See https://github.com/andywer/threads.js/pull/329 for an example of a + // problematic listener. This means that exports.exit is solely responsible for performing all + // necessary cleanup tasks. + for (const listener of process.listeners(signal)) { + removeSignalListener(signal, listener); + } + process.on(signal, exports.exit); + // Prevent signal listeners from being added in the future. + process.on('newListener', (event, listener) => { + if (event !== signal) return; + removeSignalListener(signal, listener); + }); + } await util.promisify(npm.load)(); await db.init(); From 877f0c5883a33c9e513e81fc56d2fa6c3ed66c9e Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 15 Dec 2020 03:23:12 -0500 Subject: [PATCH 193/357] server: Use wtfnode to log reasons why node isn't exiting --- package-lock.json | 8 +++++++- src/node/server.js | 5 +++++ src/package-lock.json | 5 +++++ src/package.json | 3 ++- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4dcc411d9..67ca85767 100644 --- a/package-lock.json +++ b/package-lock.json @@ -870,7 +870,8 @@ "tinycon": "0.0.1", "ueberdb2": "^1.2.5", "underscore": "1.8.3", - "unorm": "1.4.1" + "unorm": "1.4.1", + "wtfnode": "^0.8.4" }, "dependencies": { "@apidevtools/json-schema-ref-parser": { @@ -10577,6 +10578,11 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, + "wtfnode": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/wtfnode/-/wtfnode-0.8.4.tgz", + "integrity": "sha512-64GEKtMt/MUBuAm+8kHqP74ojjafzu00aT0JKsmkIwYmjRQ/odO0yhbzKLm+Z9v1gMla+8dwITRKzTAlHsB+Og==" + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/src/node/server.js b/src/node/server.js index 1a6f9cbe5..bbe4c7aeb 100755 --- a/src/node/server.js +++ b/src/node/server.js @@ -27,6 +27,10 @@ const log4js = require('log4js'); log4js.replaceConsole(); +// wtfnode should be loaded after log4js.replaceConsole() so that it uses log4js for logging, and it +// should be above everything else so that it can hook in before resources are used. +const wtfnode = require('wtfnode'); + /* * early check for version compatibility before calling * any modules that require newer versions of NodeJS @@ -211,6 +215,7 @@ exports.exit = async (err = null) => { setTimeout(() => { console.error('Something that should have been cleaned up during the shutdown hook (such as ' + 'a timer, worker thread, or open connection) is preventing Node.js from exiting'); + wtfnode.dump(); console.error('Forcing an unclean exit...'); process.exit(1); }, 5000).unref(); diff --git a/src/package-lock.json b/src/package-lock.json index 3e04ec287..07441bde4 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -8659,6 +8659,11 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==" }, + "wtfnode": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/wtfnode/-/wtfnode-0.8.4.tgz", + "integrity": "sha512-64GEKtMt/MUBuAm+8kHqP74ojjafzu00aT0JKsmkIwYmjRQ/odO0yhbzKLm+Z9v1gMla+8dwITRKzTAlHsB+Og==" + }, "xml2js": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", diff --git a/src/package.json b/src/package.json index bc3e4d22f..816f5b587 100644 --- a/src/package.json +++ b/src/package.json @@ -72,7 +72,8 @@ "tinycon": "0.0.1", "ueberdb2": "^1.2.5", "underscore": "1.8.3", - "unorm": "1.4.1" + "unorm": "1.4.1", + "wtfnode": "^0.8.4" }, "bin": { "etherpad-lite": "node/server.js" From 0cc8405e9c8e2c7acfe5607c46907287c0a52006 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 19 Jan 2021 16:37:12 +0000 Subject: [PATCH 194/357] Bump minimum required Node.js version to 10.17.0 This makes it possible to use fs.promises. --- CHANGELOG.md | 4 ++++ README.md | 4 ++-- bin/plugins/checkPlugin.js | 2 +- doc/plugins.md | 2 +- package.json | 2 +- src/node/server.js | 4 ++-- src/package.json | 2 +- 7 files changed, 12 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b39ce09ec..42c2692b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ # Changes for the next release + +### Compatibility changes +* Node.js 10.17.0 or newer is now required. + ### Notable new features * Database performance is significantly improved. diff --git a/README.md b/README.md index 10dbd36cd..5a66ddd78 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Etherpad is a real-time collaborative editor [scalable to thousands of simultane # Installation ## Requirements -- `nodejs` >= **10.13.0**. +- `nodejs` >= **10.17.0**. ## GNU/Linux and other UNIX-like systems @@ -25,7 +25,7 @@ git clone --branch master https://github.com/ether/etherpad-lite.git && cd ether ``` ### Manual install -You'll need git and [node.js](https://nodejs.org) installed (minimum required Node version: **10.13.0**). +You'll need git and [node.js](https://nodejs.org) installed (minimum required Node version: **10.17.0**). **As any user (we recommend creating a separate user called etherpad):** diff --git a/bin/plugins/checkPlugin.js b/bin/plugins/checkPlugin.js index cbe146aa1..0b736af80 100755 --- a/bin/plugins/checkPlugin.js +++ b/bin/plugins/checkPlugin.js @@ -263,7 +263,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.warn('No engines or node engine in package.json'); if (autoFix) { const engines = { - node: '>=10.13.0', + node: '^10.17.0 || >=11.14.0', }; parsedPackageJSON.engines = engines; writePackageJson(parsedPackageJSON); diff --git a/doc/plugins.md b/doc/plugins.md index 2062378bb..d8239c68a 100644 --- a/doc/plugins.md +++ b/doc/plugins.md @@ -225,7 +225,7 @@ publish your plugin. "author": "USERNAME (REAL NAME) ", "contributors": [], "dependencies": {"MODULE": "0.3.20"}, - "engines": { "node": ">= 10.13.0"} + "engines": { "node": "^10.17.0 || >=11.14.0"} } ``` diff --git a/package.json b/package.json index 41e472831..03cb677ba 100644 --- a/package.json +++ b/package.json @@ -100,6 +100,6 @@ "lint": "eslint ." }, "engines": { - "node": ">=10.13.0" + "node": "^10.17.0 || >=11.14.0" } } diff --git a/src/node/server.js b/src/node/server.js index bbe4c7aeb..a0d9e2adc 100755 --- a/src/node/server.js +++ b/src/node/server.js @@ -36,8 +36,8 @@ const wtfnode = require('wtfnode'); * any modules that require newer versions of NodeJS */ const NodeVersion = require('./utils/NodeVersion'); -NodeVersion.enforceMinNodeVersion('10.13.0'); -NodeVersion.checkDeprecationStatus('10.13.0', '1.8.3'); +NodeVersion.enforceMinNodeVersion('10.17.0'); +NodeVersion.checkDeprecationStatus('10.17.0', '1.8.8'); const UpdateCheck = require('./utils/UpdateCheck'); const db = require('./db/DB'); diff --git a/src/package.json b/src/package.json index 816f5b587..8399c19bd 100644 --- a/src/package.json +++ b/src/package.json @@ -139,7 +139,7 @@ "root": true }, "engines": { - "node": ">=10.13.0", + "node": "^10.17.0 || >=11.14.0", "npm": ">=5.5.1" }, "repository": { From b3dda3b11c23f36942d283c0974a3664268f6242 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 19 Jan 2021 16:37:12 +0000 Subject: [PATCH 195/357] lint: src/static/js/pluginfw/*.js --- src/static/js/pluginfw/hooks.js | 56 ++++++++++----------- src/static/js/pluginfw/installer.js | 72 ++++++++++++++------------- src/static/js/pluginfw/plugin_defs.js | 2 + src/static/js/pluginfw/plugins.js | 51 ++++++++----------- src/static/js/pluginfw/shared.js | 34 +++++++------ 5 files changed, 109 insertions(+), 106 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 6243a9305..e61079eb6 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -1,4 +1,4 @@ -/* global exports, require */ +'use strict'; const _ = require('underscore'); const pluginDefs = require('./plugin_defs'); @@ -15,30 +15,28 @@ exports.deprecationNotices = {}; const deprecationWarned = {}; -function checkDeprecation(hook) { +const checkDeprecation = (hook) => { const notice = exports.deprecationNotices[hook.hook_name]; if (notice == null) return; if (deprecationWarned[hook.hook_fn_name]) return; console.warn(`${hook.hook_name} hook used by the ${hook.part.plugin} plugin ` + `(${hook.hook_fn_name}) is deprecated: ${notice}`); deprecationWarned[hook.hook_fn_name] = true; -} +}; exports.bubbleExceptions = true; -const hookCallWrapper = function (hook, hook_name, args, cb) { - if (cb === undefined) cb = function (x) { return x; }; +const hookCallWrapper = (hook, hook_name, args, cb) => { + if (cb === undefined) cb = (x) => x; checkDeprecation(hook); // Normalize output to list for both sync and async cases - const normalize = function (x) { + const normalize = (x) => { if (x === undefined) return []; return x; }; - const normalizedhook = function () { - return normalize(hook.hook_fn(hook_name, args, (x) => cb(normalize(x)))); - }; + const normalizedhook = () => normalize(hook.hook_fn(hook_name, args, (x) => cb(normalize(x)))); if (exports.bubbleExceptions) { return normalizedhook(); @@ -51,7 +49,7 @@ const hookCallWrapper = function (hook, hook_name, args, cb) { } }; -exports.syncMapFirst = function (lst, fn) { +exports.syncMapFirst = (lst, fn) => { let i; let result; for (i = 0; i < lst.length; i++) { @@ -61,11 +59,11 @@ exports.syncMapFirst = function (lst, fn) { return []; }; -exports.mapFirst = function (lst, fn, cb, predicate) { +exports.mapFirst = (lst, fn, cb, predicate) => { if (predicate == null) predicate = (x) => (x != null && x.length > 0); let i = 0; - var next = function () { + const next = () => { if (i >= lst.length) return cb(null, []); fn(lst[i++], (err, result) => { if (err) return cb(err); @@ -104,7 +102,7 @@ exports.mapFirst = function (lst, fn, cb, predicate) { // // See the tests in tests/backend/specs/hooks.js for examples of supported and prohibited behaviors. // -function callHookFnSync(hook, context) { +const callHookFnSync = (hook, context) => { checkDeprecation(hook); // This var is used to keep track of whether the hook function already settled. @@ -190,7 +188,7 @@ function callHookFnSync(hook, context) { settle(null, val, 'returned value'); return outcome.val; -} +}; // Invokes all registered hook functions synchronously. // @@ -203,7 +201,7 @@ function callHookFnSync(hook, context) { // 1. Collect all values returned by the hook functions into an array. // 2. Convert each `undefined` entry into `[]`. // 3. Flatten one level. -exports.callAll = function (hookName, context) { +exports.callAll = (hookName, context) => { if (context == null) context = {}; const hooks = pluginDefs.hooks[hookName] || []; return _.flatten(hooks.map((hook) => { @@ -248,7 +246,7 @@ exports.callAll = function (hookName, context) { // // See the tests in tests/backend/specs/hooks.js for examples of supported and prohibited behaviors. // -async function callHookFnAsync(hook, context) { +const callHookFnAsync = async (hook, context) => { checkDeprecation(hook); return await new Promise((resolve, reject) => { // This var is used to keep track of whether the hook function already settled. @@ -326,7 +324,7 @@ async function callHookFnAsync(hook, context) { (val) => settle(null, val, 'returned value'), (err) => settle(err, null, 'Promise rejection')); }); -} +}; // Invokes all registered hook functions asynchronously. // @@ -350,20 +348,22 @@ exports.aCallAll = async (hookName, context, cb) => { const hooks = pluginDefs.hooks[hookName] || []; let resultsPromise = Promise.all(hooks.map((hook) => callHookFnAsync(hook, context) // `undefined` (but not `null`!) is treated the same as []. - .then((result) => (result === undefined) ? [] : result))).then((results) => _.flatten(results, 1)); + .then((result) => (result === undefined) ? [] : result))) + .then((results) => _.flatten(results, 1)); if (cb != null) resultsPromise = resultsPromise.then((val) => cb(null, val), cb); return await resultsPromise; }; -exports.callFirst = function (hook_name, args) { +exports.callFirst = (hook_name, args) => { if (!args) args = {}; if (pluginDefs.hooks[hook_name] === undefined) return []; - return exports.syncMapFirst(pluginDefs.hooks[hook_name], (hook) => hookCallWrapper(hook, hook_name, args)); + return exports.syncMapFirst(pluginDefs.hooks[hook_name], + (hook) => hookCallWrapper(hook, hook_name, args)); }; -function aCallFirst(hook_name, args, cb, predicate) { +const aCallFirst = (hook_name, args, cb, predicate) => { if (!args) args = {}; - if (!cb) cb = function () {}; + if (!cb) cb = () => {}; if (pluginDefs.hooks[hook_name] === undefined) return cb(null, []); exports.mapFirst( pluginDefs.hooks[hook_name], @@ -373,10 +373,10 @@ function aCallFirst(hook_name, args, cb, predicate) { cb, predicate ); -} +}; /* return a Promise if cb is not supplied */ -exports.aCallFirst = function (hook_name, args, cb, predicate) { +exports.aCallFirst = (hook_name, args, cb, predicate) => { if (cb === undefined) { return new Promise((resolve, reject) => { aCallFirst(hook_name, args, (err, res) => err ? reject(err) : resolve(res), predicate); @@ -386,10 +386,10 @@ exports.aCallFirst = function (hook_name, args, cb, predicate) { } }; -exports.callAllStr = function (hook_name, args, sep, pre, post) { - if (sep == undefined) sep = ''; - if (pre == undefined) pre = ''; - if (post == undefined) post = ''; +exports.callAllStr = (hook_name, args, sep, pre, post) => { + if (sep === undefined) sep = ''; + if (pre === undefined) pre = ''; + if (post === undefined) post = ''; const newCallhooks = []; const callhooks = exports.callAll(hook_name, args); for (let i = 0, ii = callhooks.length; i < ii; i++) { diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js index 7d29b91b1..ae5c06e10 100644 --- a/src/static/js/pluginfw/installer.js +++ b/src/static/js/pluginfw/installer.js @@ -1,6 +1,8 @@ +'use strict'; + const log4js = require('log4js'); -const plugins = require('ep_etherpad-lite/static/js/pluginfw/plugins'); -const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); +const plugins = require('./plugins'); +const hooks = require('./hooks'); const npm = require('npm'); const request = require('request'); const util = require('util'); @@ -13,22 +15,22 @@ const loadNpm = async () => { npm.on('log', log4js.getLogger('npm').log); }; +const onAllTasksFinished = () => { + hooks.aCallAll('restartServer', {}, () => {}); +}; + let tasks = 0; function wrapTaskCb(cb) { tasks++; - return function () { - cb && cb.apply(this, arguments); + return function (...args) { + cb && cb.apply(this, args); tasks--; - if (tasks == 0) onAllTasksFinished(); + if (tasks === 0) onAllTasksFinished(); }; } -function onAllTasksFinished() { - hooks.aCallAll('restartServer', {}, () => {}); -} - exports.uninstall = async (pluginName, cb = null) => { cb = wrapTaskCb(cb); try { @@ -60,7 +62,7 @@ exports.install = async (pluginName, cb = null) => { exports.availablePlugins = null; let cacheTimestamp = 0; -exports.getAvailablePlugins = function (maxCacheAge) { +exports.getAvailablePlugins = (maxCacheAge) => { const nowTimestamp = Math.round(Date.now() / 1000); return new Promise((resolve, reject) => { @@ -87,31 +89,33 @@ exports.getAvailablePlugins = function (maxCacheAge) { }; -exports.search = function (searchTerm, maxCacheAge) { - return exports.getAvailablePlugins(maxCacheAge).then((results) => { - const res = {}; +exports.search = (searchTerm, maxCacheAge) => exports.getAvailablePlugins(maxCacheAge).then( + (results) => { + const res = {}; - if (searchTerm) { - searchTerm = searchTerm.toLowerCase(); - } - - for (const pluginName in results) { - // for every available plugin - if (pluginName.indexOf(plugins.prefix) != 0) continue; // TODO: Also search in keywords here! - - if (searchTerm && !~results[pluginName].name.toLowerCase().indexOf(searchTerm) && - (typeof results[pluginName].description !== 'undefined' && !~results[pluginName].description.toLowerCase().indexOf(searchTerm)) - ) { - if (typeof results[pluginName].description === 'undefined') { - console.debug('plugin without Description: %s', results[pluginName].name); - } - - continue; + if (searchTerm) { + searchTerm = searchTerm.toLowerCase(); } - res[pluginName] = results[pluginName]; - } + for (const pluginName in results) { + // for every available plugin + // TODO: Also search in keywords here! + if (pluginName.indexOf(plugins.prefix) !== 0) continue; - return res; - }); -}; + if (searchTerm && !~results[pluginName].name.toLowerCase().indexOf(searchTerm) && + (typeof results[pluginName].description !== 'undefined' && + !~results[pluginName].description.toLowerCase().indexOf(searchTerm)) + ) { + if (typeof results[pluginName].description === 'undefined') { + console.debug('plugin without Description: %s', results[pluginName].name); + } + + continue; + } + + res[pluginName] = results[pluginName]; + } + + return res; + } +); diff --git a/src/static/js/pluginfw/plugin_defs.js b/src/static/js/pluginfw/plugin_defs.js index 95bbcb95c..768d99c3e 100644 --- a/src/static/js/pluginfw/plugin_defs.js +++ b/src/static/js/pluginfw/plugin_defs.js @@ -1,3 +1,5 @@ +'use strict'; + // This module contains processed plugin definitions. The data structures in this file are set by // plugins.js (server) or client_plugins.js (client). diff --git a/src/static/js/pluginfw/plugins.js b/src/static/js/pluginfw/plugins.js index 52fbdd271..4acdee7bd 100644 --- a/src/static/js/pluginfw/plugins.js +++ b/src/static/js/pluginfw/plugins.js @@ -1,6 +1,7 @@ +'use strict'; + const fs = require('fs').promises; const hooks = require('./hooks'); -const npm = require('npm/lib/npm.js'); const readInstalled = require('./read-installed.js'); const path = require('path'); const tsort = require('./tsort'); @@ -13,11 +14,9 @@ const defs = require('./plugin_defs'); exports.prefix = 'ep_'; -exports.formatPlugins = function () { - return _.keys(defs.plugins).join(', '); -}; +exports.formatPlugins = () => Object.keys(defs.plugins).join(', '); -exports.formatPluginsWithVersion = function () { +exports.formatPluginsWithVersion = () => { const plugins = []; _.forEach(defs.plugins, (plugin) => { if (plugin.package.name !== 'ep_etherpad-lite') { @@ -28,17 +27,16 @@ exports.formatPluginsWithVersion = function () { return plugins.join(', '); }; -exports.formatParts = function () { - return _.map(defs.parts, (part) => part.full_name).join('\n'); -}; +exports.formatParts = () => _.map(defs.parts, (part) => part.full_name).join('\n'); -exports.formatHooks = function (hook_set_name) { +exports.formatHooks = (hook_set_name) => { const res = []; const hooks = pluginUtils.extractHooks(defs.parts, hook_set_name || 'hooks'); _.chain(hooks).keys().forEach((hook_name) => { _.forEach(hooks[hook_name], (hook) => { - res.push(`
              ${hook.hook_name}
              ${hook.hook_fn_name} from ${hook.part.full_name}
              `); + res.push(`
              ${hook.hook_name}
              ${hook.hook_fn_name} ` + + `from ${hook.part.full_name}
              `); }); }); return `
              ${res.join('\n')}
              `; @@ -57,7 +55,7 @@ const callInit = async () => { })); }; -exports.pathNormalization = function (part, hook_fn_name, hook_name) { +exports.pathNormalization = (part, hook_fn_name, hook_name) => { const tmp = hook_fn_name.split(':'); // hook_fn_name might be something like 'C:\\foo.js:myFunc'. // If there is a single colon assume it's 'filename:funcname' not 'C:\\filename'. const functionName = (tmp.length > 1 ? tmp.pop() : null) || hook_name; @@ -67,7 +65,7 @@ exports.pathNormalization = function (part, hook_fn_name, hook_name) { return `${fileName}:${functionName}`; }; -exports.update = async function () { +exports.update = async () => { const packages = await exports.getPackages(); const parts = {}; // Key is full name. sortParts converts this into a topologically sorted array. const plugins = {}; @@ -83,13 +81,14 @@ exports.update = async function () { await callInit(); }; -exports.getPackages = async function () { - // Load list of installed NPM packages, flatten it to a list, and filter out only packages with names that +exports.getPackages = async () => { + // Load list of installed NPM packages, flatten it to a list, + // and filter out only packages with names that const dir = settings.root; const data = await util.promisify(readInstalled)(dir); const packages = {}; - function flatten(deps) { + const flatten = (deps) => { _.chain(deps).keys().each((name) => { if (name.indexOf(exports.prefix) === 0) { packages[name] = _.clone(deps[name]); @@ -102,7 +101,7 @@ exports.getPackages = async function () { // I don't think we need recursion // if (deps[name].dependencies !== undefined) flatten(deps[name].dependencies); }); - } + }; const tmp = {}; tmp[data.name] = data; @@ -110,7 +109,7 @@ exports.getPackages = async function () { return packages; }; -async function loadPlugin(packages, plugin_name, plugins, parts) { +const loadPlugin = async (packages, plugin_name, plugins, parts) => { const plugin_path = path.resolve(packages[plugin_name].path, 'ep.json'); try { const data = await fs.readFile(plugin_path); @@ -129,9 +128,9 @@ async function loadPlugin(packages, plugin_name, plugins, parts) { } catch (er) { console.error(`Unable to load plugin definition file ${plugin_path}`); } -} +}; -function partsToParentChildList(parts) { +const partsToParentChildList = (parts) => { const res = []; _.chain(parts).keys().forEach((name) => { _.each(parts[name].post || [], (child_name) => { @@ -145,15 +144,9 @@ function partsToParentChildList(parts) { } }); return res; -} +}; // Used only in Node, so no need for _ -function sortParts(parts) { - return tsort( - partsToParentChildList(parts) - ).filter( - (name) => parts[name] !== undefined - ).map( - (name) => parts[name] - ); -} +const sortParts = (parts) => tsort(partsToParentChildList(parts)) + .filter((name) => parts[name] !== undefined) + .map((name) => parts[name]); diff --git a/src/static/js/pluginfw/shared.js b/src/static/js/pluginfw/shared.js index 749706812..981cd2558 100644 --- a/src/static/js/pluginfw/shared.js +++ b/src/static/js/pluginfw/shared.js @@ -1,3 +1,4 @@ +'use strict'; const _ = require('underscore'); const defs = require('./plugin_defs'); @@ -8,13 +9,13 @@ const disabledHookReasons = { }, }; -function loadFn(path, hookName) { +const loadFn = (path, hookName) => { let functionName; const parts = path.split(':'); // on windows: C:\foo\bar:xyz - if (parts[0].length == 1) { - if (parts.length == 3) { + if (parts[0].length === 1) { + if (parts.length === 3) { functionName = parts.pop(); } path = parts.join(':'); @@ -30,9 +31,9 @@ function loadFn(path, hookName) { fn = fn[name]; }); return fn; -} +}; -function extractHooks(parts, hook_set_name, normalizer) { +const extractHooks = (parts, hook_set_name, normalizer) => { const hooks = {}; _.each(parts, (part) => { _.chain(part[hook_set_name] || {}) @@ -50,20 +51,23 @@ function extractHooks(parts, hook_set_name, normalizer) { const disabledReason = (disabledHookReasons[hook_set_name] || {})[hook_name]; if (disabledReason) { - console.error(`Hook ${hook_set_name}/${hook_name} is disabled. Reason: ${disabledReason}`); + console.error( + `Hook ${hook_set_name}/${hook_name} is disabled. Reason: ${disabledReason}`); console.error(`The hook function ${hook_fn_name} from plugin ${part.plugin} ` + - 'will never be called, which may cause the plugin to fail'); - console.error(`Please update the ${part.plugin} plugin to not use the ${hook_name} hook`); + 'will never be called, which may cause the plugin to fail'); + console.error( + `Please update the ${part.plugin} plugin to not use the ${hook_name} hook`); return; } - + let hook_fn; try { - var hook_fn = loadFn(hook_fn_name, hook_name); + hook_fn = loadFn(hook_fn_name, hook_name); if (!hook_fn) { - throw 'Not a function'; + throw new Error('Not a function'); } } catch (exc) { - console.error(`Failed to load '${hook_fn_name}' for '${part.full_name}/${hook_set_name}/${hook_name}': ${exc.toString()}`); + console.error(`Failed to load '${hook_fn_name}' for ` + + `'${part.full_name}/${hook_set_name}/${hook_name}': ${exc.toString()}`); } if (hook_fn) { if (hooks[hook_name] == null) hooks[hook_name] = []; @@ -72,7 +76,7 @@ function extractHooks(parts, hook_set_name, normalizer) { }); }); return hooks; -} +}; exports.extractHooks = extractHooks; @@ -88,10 +92,10 @@ exports.extractHooks = extractHooks; * No plugins: [] * Some plugins: [ 'ep_adminpads', 'ep_add_buttons', 'ep_activepads' ] */ -exports.clientPluginNames = function () { +exports.clientPluginNames = () => { const client_plugin_names = _.uniq( defs.parts - .filter((part) => part.hasOwnProperty('client_hooks')) + .filter((part) => Object.prototype.hasOwnProperty.call(part, 'client_hooks')) .map((part) => `plugin-${part.plugin}`) ); From 5b701b97c3969486affef8b4a4e6ab9bc096c79b Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 1 Feb 2021 15:21:50 +0100 Subject: [PATCH 196/357] Localisation updates from https://translatewiki.net. --- src/locales/gl.json | 69 +++++++++++++++++++++++++++++++++++++-------- src/locales/pt.json | 44 ++++++++++++++++++++++++++--- 2 files changed, 97 insertions(+), 16 deletions(-) diff --git a/src/locales/gl.json b/src/locales/gl.json index 406c99521..02a04f563 100644 --- a/src/locales/gl.json +++ b/src/locales/gl.json @@ -2,12 +2,47 @@ "@metadata": { "authors": [ "Elisardojm", + "Ghose", "Toliño" ] }, + "admin.page-title": "Panel de administración - Etherpad", + "admin_plugins": "Xestor de complementos", + "admin_plugins.available": "Complementos dispoñibles", + "admin_plugins.available_not-found": "Non se atopan complementos.", + "admin_plugins.available_fetching": "Obtendo...", + "admin_plugins.available_install.value": "Instalar", + "admin_plugins.available_search.placeholder": "Buscar complementos para instalar", + "admin_plugins.description": "Descrición", + "admin_plugins.installed": "Complementos instalados", + "admin_plugins.installed_fetching": "Obtendo os complementos instalados...", + "admin_plugins.installed_nothing": "Aínda non instalaches ningún complemento.", + "admin_plugins.installed_uninstall.value": "Desinstalar", + "admin_plugins.last-update": "Última actualización", + "admin_plugins.name": "Nome", + "admin_plugins.page-title": "Xestos de complementos - Etherpad", + "admin_plugins.version": "Versión", + "admin_plugins_info": "Información para resolver problemas", + "admin_plugins_info.hooks": "Ganchos instalados", + "admin_plugins_info.hooks_client": "Ganchos do lado do cliente", + "admin_plugins_info.hooks_server": "Ganchos do lado do servidor", + "admin_plugins_info.parts": "Partes instaladas", + "admin_plugins_info.plugins": "Complementos instalados", + "admin_plugins_info.page-title": "Información do complemento - Etherpad", + "admin_plugins_info.version": "Versión de Etherpad", + "admin_plugins_info.version_latest": "Última versión dispoñible", + "admin_plugins_info.version_number": "Número da versión", + "admin_settings": "Axustes", + "admin_settings.current": "Configuración actual", + "admin_settings.current_example-devel": "Modelo de exemplo dos axustes de desenvolvemento", + "admin_settings.current_example-prod": "Modelo de exemplo dos axustes en produción", + "admin_settings.current_restart.value": "Reiniciar Etherpad", + "admin_settings.current_save.value": "Gardar axustes", + "admin_settings.page-title": "Axustes - Etherpad", "index.newPad": "Novo documento", - "index.createOpenPad": "ou cree/abra un documento co nome:", - "pad.toolbar.bold.title": "Negra (Ctrl-B)", + "index.createOpenPad": "ou crea/abre un documento co nome:", + "index.openPad": "abrir un Pad existente co nome:", + "pad.toolbar.bold.title": "Resaltado (Ctrl-B)", "pad.toolbar.italic.title": "Cursiva (Ctrl-I)", "pad.toolbar.underline.title": "Subliñar (Ctrl-U)", "pad.toolbar.strikethrough.title": "Riscar (Ctrl+5)", @@ -17,28 +52,30 @@ "pad.toolbar.unindent.title": "Sen sangría (Maiús.+TAB)", "pad.toolbar.undo.title": "Desfacer (Ctrl-Z)", "pad.toolbar.redo.title": "Refacer (Ctrl-Y)", - "pad.toolbar.clearAuthorship.title": "Limpar as cores de identificación dos autores (Ctrl+Shift+C)", + "pad.toolbar.clearAuthorship.title": "Eliminar as cores que identifican ás autoras (Ctrl+Shift+C)", "pad.toolbar.import_export.title": "Importar/Exportar desde/a diferentes formatos de ficheiro", "pad.toolbar.timeslider.title": "Liña do tempo", "pad.toolbar.savedRevision.title": "Gardar a revisión", - "pad.toolbar.settings.title": "Configuracións", + "pad.toolbar.settings.title": "Axustes", "pad.toolbar.embed.title": "Compartir e incorporar este documento", - "pad.toolbar.showusers.title": "Mostrar os usuarios deste documento", + "pad.toolbar.showusers.title": "Mostrar as usuarias deste documento", "pad.colorpicker.save": "Gardar", "pad.colorpicker.cancel": "Cancelar", "pad.loading": "Cargando...", - "pad.noCookie": "Non se puido atopar a cookie. Por favor, habilite as cookies no seu navegador!", - "pad.permissionDenied": "Non ten permiso para acceder a este documento", + "pad.noCookie": "Non se puido atopar a cookie. Por favor, habilita as cookies no teu navegador! A túa sesión e axustes non se gardarán entre visitas. Esto podería deberse a que Etherpad está incluído nalgún iFrame nalgúns navegadores. Asegúrate de que Etherpad está no mesmo subdominio/dominio que o iFrame pai", + "pad.permissionDenied": "Non tes permiso para acceder a este documento", "pad.settings.padSettings": "Configuracións do documento", "pad.settings.myView": "A miña vista", "pad.settings.stickychat": "Chat sempre visible", "pad.settings.chatandusers": "Mostrar o chat e os usuarios", "pad.settings.colorcheck": "Cores de identificación", "pad.settings.linenocheck": "Números de liña", - "pad.settings.rtlcheck": "Quere ler o contido da dereita á esquerda?", + "pad.settings.rtlcheck": "Queres ler o contido da dereita á esquerda?", "pad.settings.fontType": "Tipo de letra:", "pad.settings.fontType.normal": "Normal", "pad.settings.language": "Lingua:", + "pad.settings.about": "Acerca de", + "pad.settings.poweredBy": "Grazas a", "pad.importExport.import_export": "Importar/Exportar", "pad.importExport.import": "Cargar un ficheiro de texto ou documento", "pad.importExport.importSuccessful": "Correcto!", @@ -49,9 +86,9 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.abiword.innerHTML": "Só pode importar texto simple ou formatos HTML. Para obter máis información sobre as características de importación avanzadas instale AbiWord.", + "pad.importExport.abiword.innerHTML": "Só podes importar texto simple ou formatos HTML. Para obter máis información sobre as características de importación avanzadas instala AbiWord.", "pad.modals.connected": "Conectado.", - "pad.modals.reconnecting": "Reconectando co seu documento...", + "pad.modals.reconnecting": "Reconectando co teu documento...", "pad.modals.forcereconnect": "Forzar a reconexión", "pad.modals.reconnecttimer": "Intentarase reconectar en", "pad.modals.cancel": "Cancelar", @@ -73,6 +110,10 @@ "pad.modals.corruptPad.cause": "Isto pode deberse a unha cofiguración errónea do servidor ou algún outro comportamento inesperado. Póñase en contacto co administrador do servizo.", "pad.modals.deleted": "Borrado.", "pad.modals.deleted.explanation": "Este documento foi eliminado.", + "pad.modals.rateLimited": "Taxa limitada.", + "pad.modals.rateLimited.explanation": "Enviaches demasiadas mensaxes a este documento polo que te desconectamos.", + "pad.modals.rejected.explanation": "O servidor rexeitou unha mensaxe que o teu navegador enviou.", + "pad.modals.rejected.cause": "O servidor podería ter sido actualizado mentras ollabas o documento, ou pode que sexa un fallo de Etherpad. Intenta recargar a páxina.", "pad.modals.disconnected": "Foi desconectado.", "pad.modals.disconnected.explanation": "Perdeuse a conexión co servidor", "pad.modals.disconnected.cause": "O servidor non está dispoñible. Póñase en contacto co administrador do servizo se o problema continúa.", @@ -83,6 +124,9 @@ "pad.chat": "Chat", "pad.chat.title": "Abrir o chat deste documento.", "pad.chat.loadmessages": "Cargar máis mensaxes", + "pad.chat.stick.title": "Pegar a conversa á pantalla", + "pad.chat.writeMessage.placeholder": "Escribe aquí a túa mensaxe", + "timeslider.followContents": "Segue as actualizacións do contido", "timeslider.pageTitle": "Liña do tempo de {{appTitle}}", "timeslider.toolbar.returnbutton": "Volver ao documento", "timeslider.toolbar.authors": "Autores:", @@ -112,7 +156,7 @@ "pad.savedrevs.timeslider": "Pode consultar as revisións gardadas visitando a liña do tempo", "pad.userlist.entername": "Insira o seu nome", "pad.userlist.unnamed": "anónimo", - "pad.editbar.clearcolors": "Quere limpar as cores de identificación dos autores en todo o documento?", + "pad.editbar.clearcolors": "Eliminar as cores relativas aos autores en todo o documento? Non se poderán recuperar", "pad.impexp.importbutton": "Importar agora", "pad.impexp.importing": "Importando...", "pad.impexp.confirmimport": "A importación dun ficheiro ha sobrescribir o texto actual do documento. Está seguro de querer continuar?", @@ -121,5 +165,6 @@ "pad.impexp.uploadFailed": "Houbo un erro ao cargar o ficheiro; inténteo de novo", "pad.impexp.importfailed": "Fallou a importación", "pad.impexp.copypaste": "Copie e pegue", - "pad.impexp.exportdisabled": "A exportación en formato {{type}} está desactivada. Póñase en contacto co administrador do sistema se quere máis detalles." + "pad.impexp.exportdisabled": "A exportación en formato {{type}} está desactivada. Póñase en contacto co administrador do sistema se quere máis detalles.", + "pad.impexp.maxFileSize": "Ficheiro demasiado granda. Contacta coa administración para aumentar o tamaño permitido para importacións" } diff --git a/src/locales/pt.json b/src/locales/pt.json index d9faa3ba0..82972333e 100644 --- a/src/locales/pt.json +++ b/src/locales/pt.json @@ -4,6 +4,7 @@ "Athena in Wonderland", "Cainamarques", "GoEThe", + "Guilha", "Hamilton Abreu", "Imperadeiro98", "Luckas", @@ -16,9 +17,42 @@ "Waldyrious" ] }, + "admin.page-title": "Painel do administrador - Etherpad", + "admin_plugins": "Gestor de plugins", + "admin_plugins.available": "Plugins disponíveis", + "admin_plugins.available_not-found": "Não foram encontrados plugins.", + "admin_plugins.available_fetching": "A obter...", + "admin_plugins.available_install.value": "Instalar", + "admin_plugins.available_search.placeholder": "Procura plugins para instalar", + "admin_plugins.description": "Descrição", + "admin_plugins.installed": "Plugins instalados", + "admin_plugins.installed_fetching": "A obter plugins instalados...", + "admin_plugins.installed_nothing": "Não instalas-te nenhum plugin ainda.", + "admin_plugins.installed_uninstall.value": "Desinstalar", + "admin_plugins.last-update": "Ultima atualização", + "admin_plugins.name": "Nome", + "admin_plugins.page-title": "Gestor de plugins - Etherpad", + "admin_plugins.version": "Versão", + "admin_plugins_info": "Informação de resolução de problemas", + "admin_plugins_info.hooks": "Hooks instalados", + "admin_plugins_info.hooks_client": "Hooks do lado-do-cliente", + "admin_plugins_info.hooks_server": "Hooks do lado-do-servidor", + "admin_plugins_info.parts": "Partes instaladas", + "admin_plugins_info.plugins": "Plugins instalados", + "admin_plugins_info.page-title": "Informação do plugin - Etherpad", + "admin_plugins_info.version": "Versão do Etherpad", + "admin_plugins_info.version_latest": "Última versão disponível", + "admin_plugins_info.version_number": "Número de versão", + "admin_settings": "Definições", + "admin_settings.current": "Configuração atual", + "admin_settings.current_example-devel": "Exemplo do modo de Desenvolvedor", + "admin_settings.current_example-prod": "Exemplo do modo de Produção", + "admin_settings.current_restart.value": "Reiniciar Etherpad", + "admin_settings.current_save.value": "Guardar Definições", + "admin_settings.page-title": "Definições - Etherpad", "index.newPad": "Nova Nota", - "index.createOpenPad": "ou crie/abra uma nota com o nome:", - "index.openPad": "abrir uma «Nota» existente com o nome:", + "index.createOpenPad": "ou cria/abre uma nota com o nome:", + "index.openPad": "abrir uma Nota existente com o nome:", "pad.toolbar.bold.title": "Negrito (Ctrl+B)", "pad.toolbar.italic.title": "Itálico (Ctrl+I)", "pad.toolbar.underline.title": "Sublinhado (Ctrl+U)", @@ -26,7 +60,7 @@ "pad.toolbar.ol.title": "Lista ordenada (Ctrl+Shift+N)", "pad.toolbar.ul.title": "Lista desordenada (Ctrl+Shift+L)", "pad.toolbar.indent.title": "Indentar (TAB)", - "pad.toolbar.unindent.title": "Remover indentação (Shift+TAB)", + "pad.toolbar.unindent.title": "Indentação (Shift+TAB)", "pad.toolbar.undo.title": "Desfazer (Ctrl+Z)", "pad.toolbar.redo.title": "Refazer (Ctrl+Y)", "pad.toolbar.clearAuthorship.title": "Limpar cores de autoria (Ctrl+Shift+C)", @@ -65,7 +99,7 @@ "pad.importExport.exportopen": "ODF (Open Document Format)", "pad.importExport.abiword.innerHTML": "Só pode fazer importações de texto não formatado ou com formato HTML. Para funcionalidades de importação de texto mais avançadas, instale AbiWord ou LibreOffice, por favor.", "pad.modals.connected": "Ligado.", - "pad.modals.reconnecting": "A restabelecer ligação ao seu bloco…", + "pad.modals.reconnecting": "A restabelecer ligação à nota…", "pad.modals.forcereconnect": "Forçar restabelecimento de ligação", "pad.modals.reconnecttimer": "A tentar restabelecer ligação", "pad.modals.cancel": "Cancelar", @@ -89,6 +123,8 @@ "pad.modals.deleted.explanation": "Esta nota foi removida.", "pad.modals.rateLimited": "Limitado.", "pad.modals.rateLimited.explanation": "Enviou demasiadas mensagens para este pad, por isso foi desligado.", + "pad.modals.rejected.explanation": "O servidor rejeitou a mensagem que foi enviada pelo teu navegador.", + "pad.modals.rejected.cause": "O server foi atualizado enquanto estávas a ver esta nota, ou talvez seja apenas um bug do Etherpad. Tenta recarregar a página.", "pad.modals.disconnected": "Você foi desligado.", "pad.modals.disconnected.explanation": "A ligação ao servidor foi perdida", "pad.modals.disconnected.cause": "O servidor pode estar indisponível. Por favor, notifique o administrador de serviço se isto continuar a acontecer.", From 9987fab574d0e5b1291e5a3e88351b2ec35a4325 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 1 Feb 2021 09:47:42 +0000 Subject: [PATCH 197/357] lint: low hanging bin/doc/*.js --- bin/doc/generate.js | 28 +++++++++++---------- bin/doc/html.js | 42 +++++++++++++++---------------- bin/doc/json.js | 59 ++++++++++++++++++++++---------------------- bin/doc/package.json | 2 +- 4 files changed, 67 insertions(+), 64 deletions(-) diff --git a/bin/doc/generate.js b/bin/doc/generate.js index 803f5017e..d04468a8b 100644 --- a/bin/doc/generate.js +++ b/bin/doc/generate.js @@ -1,4 +1,7 @@ #!/usr/bin/env node + +'use strict'; + // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -20,7 +23,6 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -const marked = require('marked'); const fs = require('fs'); const path = require('path'); @@ -33,12 +35,12 @@ let template = null; let inputFile = null; args.forEach((arg) => { - if (!arg.match(/^\-\-/)) { + if (!arg.match(/^--/)) { inputFile = arg; - } else if (arg.match(/^\-\-format=/)) { - format = arg.replace(/^\-\-format=/, ''); - } else if (arg.match(/^\-\-template=/)) { - template = arg.replace(/^\-\-template=/, ''); + } else if (arg.match(/^--format=/)) { + format = arg.replace(/^--format=/, ''); + } else if (arg.match(/^--template=/)) { + template = arg.replace(/^--template=/, ''); } }); @@ -56,11 +58,11 @@ fs.readFile(inputFile, 'utf8', (er, input) => { }); -const includeExpr = /^@include\s+([A-Za-z0-9-_\/]+)(?:\.)?([a-zA-Z]*)$/gmi; +const includeExpr = /^@include\s+([A-Za-z0-9-_/]+)(?:\.)?([a-zA-Z]*)$/gmi; const includeData = {}; -function processIncludes(inputFile, input, cb) { +const processIncludes = (inputFile, input, cb) => { const includes = input.match(includeExpr); - if (includes === null) return cb(null, input); + if (includes == null) return cb(null, input); let errState = null; console.error(includes); let incCount = includes.length; @@ -70,7 +72,7 @@ function processIncludes(inputFile, input, cb) { let fname = include.replace(/^@include\s+/, ''); if (!fname.match(/\.md$/)) fname += '.md'; - if (includeData.hasOwnProperty(fname)) { + if (Object.prototype.hasOwnProperty.call(includeData, fname)) { input = input.split(include).join(includeData[fname]); incCount--; if (incCount === 0) { @@ -94,10 +96,10 @@ function processIncludes(inputFile, input, cb) { }); }); }); -} +}; -function next(er, input) { +const next = (er, input) => { if (er) throw er; switch (format) { case 'json': @@ -117,4 +119,4 @@ function next(er, input) { default: throw new Error(`Invalid format: ${format}`); } -} +}; diff --git a/bin/doc/html.js b/bin/doc/html.js index 26cf3f185..2c38aec23 100644 --- a/bin/doc/html.js +++ b/bin/doc/html.js @@ -1,3 +1,5 @@ +'use strict'; + // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -23,17 +25,17 @@ const fs = require('fs'); const marked = require('marked'); const path = require('path'); -module.exports = toHTML; -function toHTML(input, filename, template, cb) { +const toHTML = (input, filename, template, cb) => { const lexed = marked.lexer(input); fs.readFile(template, 'utf8', (er, template) => { if (er) return cb(er); render(lexed, filename, template, cb); }); -} +}; +module.exports = toHTML; -function render(lexed, filename, template, cb) { +const render = (lexed, filename, template, cb) => { // get the section const section = getSection(lexed); @@ -52,23 +54,23 @@ function render(lexed, filename, template, cb) { // content has to be the last thing we do with // the lexed tokens, because it's destructive. - content = marked.parser(lexed); + const content = marked.parser(lexed); template = template.replace(/__CONTENT__/g, content); cb(null, template); }); -} +}; // just update the list item text in-place. // lists that come right after a heading are what we're after. -function parseLists(input) { +const parseLists = (input) => { let state = null; let depth = 0; const output = []; output.links = input.links; input.forEach((tok) => { - if (state === null) { + if (state == null) { if (tok.type === 'heading') { state = 'AFTERHEADING'; } @@ -112,29 +114,27 @@ function parseLists(input) { }); return output; -} +}; -function parseListItem(text) { - text = text.replace(/\{([^\}]+)\}/, '$1'); +const parseListItem = (text) => { + text = text.replace(/\{([^}]+)\}/, '$1'); // XXX maybe put more stuff here? return text; -} +}; // section is just the first heading -function getSection(lexed) { - const section = ''; +const getSection = (lexed) => { for (let i = 0, l = lexed.length; i < l; i++) { const tok = lexed[i]; if (tok.type === 'heading') return tok.text; } return ''; -} +}; -function buildToc(lexed, filename, cb) { - const indent = 0; +const buildToc = (lexed, filename, cb) => { let toc = []; let depth = 0; lexed.forEach((tok) => { @@ -155,18 +155,18 @@ function buildToc(lexed, filename, cb) { toc = marked.parse(toc.join('\n')); cb(null, toc); -} +}; const idCounters = {}; -function getId(text) { +const getId = (text) => { text = text.toLowerCase(); text = text.replace(/[^a-z0-9]+/g, '_'); text = text.replace(/^_+|_+$/, ''); text = text.replace(/^([^a-z])/, '_$1'); - if (idCounters.hasOwnProperty(text)) { + if (Object.prototype.hasOwnProperty.call(idCounters, text)) { text += `_${++idCounters[text]}`; } else { idCounters[text] = 0; } return text; -} +}; diff --git a/bin/doc/json.js b/bin/doc/json.js index 3ce62a301..c71611e5f 100644 --- a/bin/doc/json.js +++ b/bin/doc/json.js @@ -1,3 +1,4 @@ +'use strict'; // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -26,7 +27,7 @@ module.exports = doJSON; const marked = require('marked'); -function doJSON(input, filename, cb) { +const doJSON = (input, filename, cb) => { const root = {source: filename}; const stack = [root]; let depth = 0; @@ -40,7 +41,7 @@ function doJSON(input, filename, cb) { // // This is for cases where the markdown semantic structure is lacking. if (type === 'paragraph' || type === 'html') { - const metaExpr = /\n*/g; + const metaExpr = /\n*/g; text = text.replace(metaExpr, (_0, k, v) => { current[k.trim()] = v.trim(); return ''; @@ -146,7 +147,7 @@ function doJSON(input, filename, cb) { } return cb(null, root); -} +}; // go from something like this: @@ -191,7 +192,7 @@ function doJSON(input, filename, cb) { // desc: 'whether or not to send output to parent\'s stdio.', // default: 'false' } ] } ] -function processList(section) { +const processList = (section) => { const list = section.list; const values = []; let current; @@ -203,13 +204,13 @@ function processList(section) { if (type === 'space') return; if (type === 'list_item_start') { if (!current) { - var n = {}; + const n = {}; values.push(n); current = n; } else { current.options = current.options || []; stack.push(current); - var n = {}; + const n = {}; current.options.push(n); current = n; } @@ -283,11 +284,11 @@ function processList(section) { // section.listParsed = values; delete section.list; -} +}; // textRaw = "someobject.someMethod(a, [b=100], [c])" -function parseSignature(text, sig) { +const parseSignature = (text, sig) => { let params = text.match(paramExpr); if (!params) return; params = params[1]; @@ -322,10 +323,10 @@ function parseSignature(text, sig) { if (optional) param.optional = true; if (def !== undefined) param.default = def; }); -} +}; -function parseListItem(item) { +const parseListItem = (item) => { if (item.options) item.options.forEach(parseListItem); if (!item.textRaw) return; @@ -341,7 +342,7 @@ function parseListItem(item) { item.name = 'return'; text = text.replace(retExpr, ''); } else { - const nameExpr = /^['`"]?([^'`": \{]+)['`"]?\s*:?\s*/; + const nameExpr = /^['`"]?([^'`": {]+)['`"]?\s*:?\s*/; const name = text.match(nameExpr); if (name) { item.name = name[1]; @@ -358,7 +359,7 @@ function parseListItem(item) { } text = text.trim(); - const typeExpr = /^\{([^\}]+)\}/; + const typeExpr = /^\{([^}]+)\}/; const type = text.match(typeExpr); if (type) { item.type = type[1]; @@ -376,10 +377,10 @@ function parseListItem(item) { text = text.replace(/^\s*-\s*/, ''); text = text.trim(); if (text) item.desc = text; -} +}; -function finishSection(section, parent) { +const finishSection = (section, parent) => { if (!section || !parent) { throw new Error(`Invalid finishSection call\n${ JSON.stringify(section)}\n${ @@ -479,50 +480,50 @@ function finishSection(section, parent) { parent[plur] = parent[plur] || []; parent[plur].push(section); -} +}; // Not a general purpose deep copy. // But sufficient for these basic things. -function deepCopy(src, dest) { +const deepCopy = (src, dest) => { Object.keys(src).filter((k) => !dest.hasOwnProperty(k)).forEach((k) => { dest[k] = deepCopy_(src[k]); }); -} +}; -function deepCopy_(src) { +const deepCopy_ = (src) => { if (!src) return src; if (Array.isArray(src)) { - var c = new Array(src.length); + const c = new Array(src.length); src.forEach((v, i) => { c[i] = deepCopy_(v); }); return c; } if (typeof src === 'object') { - var c = {}; + const c = {}; Object.keys(src).forEach((k) => { c[k] = deepCopy_(src[k]); }); return c; } return src; -} +}; // these parse out the contents of an H# tag const eventExpr = /^Event(?::|\s)+['"]?([^"']+).*$/i; const classExpr = /^Class:\s*([^ ]+).*?$/i; -const propExpr = /^(?:property:?\s*)?[^\.]+\.([^ \.\(\)]+)\s*?$/i; -const braceExpr = /^(?:property:?\s*)?[^\.\[]+(\[[^\]]+\])\s*?$/i; +const propExpr = /^(?:property:?\s*)?[^.]+\.([^ .()]+)\s*?$/i; +const braceExpr = /^(?:property:?\s*)?[^.[]+(\[[^\]]+\])\s*?$/i; const classMethExpr = - /^class\s*method\s*:?[^\.]+\.([^ \.\(\)]+)\([^\)]*\)\s*?$/i; + /^class\s*method\s*:?[^.]+\.([^ .()]+)\([^)]*\)\s*?$/i; const methExpr = - /^(?:method:?\s*)?(?:[^\.]+\.)?([^ \.\(\)]+)\([^\)]*\)\s*?$/i; -const newExpr = /^new ([A-Z][a-z]+)\([^\)]*\)\s*?$/; -var paramExpr = /\((.*)\);?$/; + /^(?:method:?\s*)?(?:[^.]+\.)?([^ .()]+)\([^)]*\)\s*?$/i; +const newExpr = /^new ([A-Z][a-z]+)\([^)]*\)\s*?$/; +const paramExpr = /\((.*)\);?$/; -function newSection(tok) { +const newSection = (tok) => { const section = {}; // infer the type from the text. const text = section.textRaw = tok.text; @@ -551,4 +552,4 @@ function newSection(tok) { section.name = text; } return section; -} +}; diff --git a/bin/doc/package.json b/bin/doc/package.json index 1a29f1b1c..2f027616c 100644 --- a/bin/doc/package.json +++ b/bin/doc/package.json @@ -4,7 +4,7 @@ "description": "Internal tool for generating Node.js API docs", "version": "0.0.0", "engines": { - "node": ">=0.6.10" + "node": ">=10.17.0" }, "dependencies": { "marked": "0.8.2" From 759e2aaec39a8b6f6e15409e6a9750c49f55627c Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 1 Feb 2021 14:43:09 -0500 Subject: [PATCH 198/357] lint: Use node config for tests/frontend/travis, tests/ratelimit The files in these directories contain test drivers, not tests. --- package.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 03cb677ba..de9298807 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,9 @@ "tests/**/*" ], "excludedFiles": [ - "**/.eslintrc.js" + "**/.eslintrc.js", + "tests/frontend/travis/**/*", + "tests/ratelimit/**/*" ], "extends": "etherpad/tests", "rules": { @@ -75,7 +77,8 @@ "tests/frontend/**/*" ], "excludedFiles": [ - "**/.eslintrc.js" + "**/.eslintrc.js", + "tests/frontend/travis/**/*" ], "extends": "etherpad/tests/frontend", "overrides": [ @@ -92,6 +95,12 @@ } } ] + }, + { + "files": [ + "tests/frontend/travis/**/*" + ], + "extends": "etherpad/node" } ], "root": true From 915849b3197158427c1a755e2d60e38822dc16a9 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 1 Feb 2021 20:23:14 +0000 Subject: [PATCH 199/357] Low hanging lint frontend tests (#4695) * lint: low hanging specs/alphabet.js * lint: low hanging specs/authorship_of_editions.js * lint: low hanging specs/bold.js * lint: low hanging specs/caret.js * lint: low hanging specs/change_user_color.js * lint: low hanging specs/change_user_name.js * lint: low hanging specs/chat.js * lint: low hanging specs/chat_load_messages.js * lint: low hanging specs/clear_authorship_colors.js * lint: low hanging specs/delete.js * lint: low hanging specs/drag_and_drop.js * lint: low hanging specs/embed_value.js * lint: low hanging specs/enter.js * lint: low hanging specs/font_type.js * lint: low hanging specs/helper.js * lint: low hanging specs/importexport.js * lint: low hanging specs/importindents.js * lint: low hanging specs/indentation.js * lint: low hanging specs/italic.js * lint: low hanging specs/language.js * lint: low hanging specs/multiple_authors_clear_authorship_colors.js * lint: low hanging specs/ordered_list.js * lint: low hanging specs/pad_modal.js * lint: low hanging specs/redo.js * lint: low hanging specs/responsiveness.js * lint: low hanging specs/select_formatting_buttons.js * lint: low hanging specs/strikethrough.js * lint: low hanging specs/timeslider.js * lint: low hanging specs/timeslider_labels.js * lint: low hanging specs/timeslider_numeric_padID.js * lint: low hanging specs/timeslider_revisions.js * lint: low hanging specs/undo.js * lint: low hanging specs/unordered_list.js * lint: low hanging specs/xxauto_reconnect.js * lint: attempt to do remote_runner.js * lint: helper linting * lint: rate limit linting * use constructor for Event to make eslint happier * for squash: lint fix refinements * for squash: lint fix refinements Co-authored-by: Richard Hansen --- tests/frontend/helper.js | 4 +- tests/frontend/helper/methods.js | 8 +- tests/frontend/helper/ui.js | 2 + tests/frontend/specs/alphabet.js | 3 +- .../frontend/specs/authorship_of_editions.js | 26 +- tests/frontend/specs/bold.js | 7 +- tests/frontend/specs/caret.js | 67 +++-- tests/frontend/specs/change_user_color.js | 16 +- tests/frontend/specs/change_user_name.js | 2 + tests/frontend/specs/chat.js | 19 +- tests/frontend/specs/chat_load_messages.js | 18 +- .../frontend/specs/clear_authorship_colors.js | 35 +-- tests/frontend/specs/delete.js | 8 +- tests/frontend/specs/drag_and_drop.js | 29 +- tests/frontend/specs/embed_value.js | 2 + tests/frontend/specs/enter.js | 9 +- tests/frontend/specs/font_type.js | 3 +- tests/frontend/specs/helper.js | 28 +- tests/frontend/specs/importexport.js | 271 ++++++++++++------ tests/frontend/specs/importindents.js | 82 ++++-- tests/frontend/specs/indentation.js | 39 +-- tests/frontend/specs/italic.js | 9 +- tests/frontend/specs/language.js | 18 +- ...ultiple_authors_clear_authorship_colors.js | 9 +- tests/frontend/specs/ordered_list.js | 27 +- tests/frontend/specs/pad_modal.js | 12 +- tests/frontend/specs/redo.js | 7 +- tests/frontend/specs/responsiveness.js | 37 ++- .../specs/select_formatting_buttons.js | 9 +- tests/frontend/specs/strikethrough.js | 4 +- tests/frontend/specs/timeslider.js | 3 +- tests/frontend/specs/timeslider_labels.js | 4 +- .../specs/timeslider_numeric_padID.js | 2 + tests/frontend/specs/timeslider_revisions.js | 48 ++-- tests/frontend/specs/undo.js | 5 +- tests/frontend/specs/unordered_list.js | 5 +- tests/frontend/specs/xxauto_reconnect.js | 2 + tests/frontend/travis/remote_runner.js | 63 ++-- tests/ratelimit/send_changesets.js | 10 +- 39 files changed, 595 insertions(+), 357 deletions(-) diff --git a/tests/frontend/helper.js b/tests/frontend/helper.js index 37c5af3b1..c38175fe1 100644 --- a/tests/frontend/helper.js +++ b/tests/frontend/helper.js @@ -1,5 +1,5 @@ 'use strict'; -const helper = {}; // eslint-disable-line +const helper = {}; // eslint-disable-line no-redeclare (function () { let $iframe; const @@ -181,7 +181,7 @@ const helper = {}; // eslint-disable-line }; helper.waitFor = function (conditionFunc, timeoutTime = 1900, intervalTime = 10) { - const deferred = $.Deferred(); // eslint-disable-line + const deferred = new $.Deferred(); const _fail = deferred.fail.bind(deferred); let listenForFail = false; diff --git a/tests/frontend/helper/methods.js b/tests/frontend/helper/methods.js index 4c7fe1204..157ba6aba 100644 --- a/tests/frontend/helper/methods.js +++ b/tests/frontend/helper/methods.js @@ -6,12 +6,12 @@ */ helper.spyOnSocketIO = function () { helper.contentWindow().pad.socket.on('message', (msg) => { - if (msg.type == 'COLLABROOM') { - if (msg.data.type == 'ACCEPT_COMMIT') { + if (msg.type === 'COLLABROOM') { + if (msg.data.type === 'ACCEPT_COMMIT') { helper.commits.push(msg); - } else if (msg.data.type == 'USER_NEWINFO') { + } else if (msg.data.type === 'USER_NEWINFO') { helper.userInfos.push(msg); - } else if (msg.data.type == 'CHAT_MESSAGE') { + } else if (msg.data.type === 'CHAT_MESSAGE') { helper.chatMessages.push(msg); } } diff --git a/tests/frontend/helper/ui.js b/tests/frontend/helper/ui.js index 0f3e64169..7ab8b990d 100644 --- a/tests/frontend/helper/ui.js +++ b/tests/frontend/helper/ui.js @@ -1,3 +1,5 @@ +'use strict'; + /** * the contentWindow is either the normal pad or timeslider * diff --git a/tests/frontend/specs/alphabet.js b/tests/frontend/specs/alphabet.js index a0ad61bdf..cc50e7d87 100644 --- a/tests/frontend/specs/alphabet.js +++ b/tests/frontend/specs/alphabet.js @@ -1,3 +1,5 @@ +'use strict'; + describe('All the alphabet works n stuff', function () { const expectedString = 'abcdefghijklmnopqrstuvwxyz'; @@ -9,7 +11,6 @@ describe('All the alphabet works n stuff', function () { it('when you enter any char it appears right', function (done) { const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; // get the first text element out of the inner iframe const firstTextElement = inner$('div').first(); diff --git a/tests/frontend/specs/authorship_of_editions.js b/tests/frontend/specs/authorship_of_editions.js index 6cf14b869..f6f29d491 100644 --- a/tests/frontend/specs/authorship_of_editions.js +++ b/tests/frontend/specs/authorship_of_editions.js @@ -1,3 +1,5 @@ +'use strict'; + describe('author of pad edition', function () { const REGULAR_LINE = 0; const LINE_WITH_ORDERED_LIST = 1; @@ -5,10 +7,11 @@ describe('author of pad edition', function () { // author 1 creates a new pad with some content (regular lines and lists) before(function (done) { - var padId = helper.newPad(() => { + const padId = helper.newPad(() => { // make sure pad has at least 3 lines const $firstLine = helper.padInner$('div').first(); - const threeLines = ['regular line', 'line with ordered list', 'line with unordered list'].join('
              '); + const threeLines = ['regular line', 'line with ordered list', 'line with unordered list'] + .join('
              '); $firstLine.html(threeLines); // wait for lines to be processed by Etherpad @@ -43,7 +46,8 @@ describe('author of pad edition', function () { setTimeout(() => { // Expire cookie, so author is changed after reloading the pad. // See https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#Example_4_Reset_the_previous_cookie - helper.padChrome$.document.cookie = 'token=foo;expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; + helper.padChrome$.document.cookie = + 'token=foo;expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; helper.newPad(done, padId); }, 1000); @@ -59,24 +63,22 @@ describe('author of pad edition', function () { changeLineAndCheckOnlyThatChangeIsFromThisAuthor(REGULAR_LINE, 'x', done); }); - it('marks only the new content as changes of the second user on a line with ordered list', function (done) { + it('marks only the new content as changes of the second user on a ' + + 'line with ordered list', function (done) { changeLineAndCheckOnlyThatChangeIsFromThisAuthor(LINE_WITH_ORDERED_LIST, 'y', done); }); - it('marks only the new content as changes of the second user on a line with unordered list', function (done) { + it('marks only the new content as changes of the second user on ' + + 'a line with unordered list', function (done) { changeLineAndCheckOnlyThatChangeIsFromThisAuthor(LINE_WITH_UNORDERED_LIST, 'z', done); }); /* ********************** Helper functions ************************ */ - var getLine = function (lineNumber) { - return helper.padInner$('div').eq(lineNumber); - }; + const getLine = (lineNumber) => helper.padInner$('div').eq(lineNumber); - const getAuthorFromClassList = function (classes) { - return classes.find((cls) => cls.startsWith('author')); - }; + const getAuthorFromClassList = (classes) => classes.find((cls) => cls.startsWith('author')); - var changeLineAndCheckOnlyThatChangeIsFromThisAuthor = function (lineNumber, textChange, done) { + const changeLineAndCheckOnlyThatChangeIsFromThisAuthor = (lineNumber, textChange, done) => { // get original author class const classes = getLine(lineNumber).find('span').first().attr('class').split(' '); const originalAuthor = getAuthorFromClassList(classes); diff --git a/tests/frontend/specs/bold.js b/tests/frontend/specs/bold.js index a7c46e1bc..613de4699 100644 --- a/tests/frontend/specs/bold.js +++ b/tests/frontend/specs/bold.js @@ -1,3 +1,5 @@ +'use strict'; + describe('bold button', function () { // create a new pad before each test run beforeEach(function (cb) { @@ -19,7 +21,6 @@ describe('bold button', function () { const $boldButton = chrome$('.buttonicon-bold'); $boldButton.click(); - // ace creates a new dom element when you press a button, so just get the first text element again const $newFirstTextElement = inner$('div').first(); // is there a element now? @@ -36,7 +37,6 @@ describe('bold button', function () { it('makes text bold on keypress', function (done) { const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; // get the first text element out of the inner iframe const $firstTextElement = inner$('div').first(); @@ -44,12 +44,11 @@ describe('bold button', function () { // select this text element $firstTextElement.sendkeys('{selectall}'); - const e = inner$.Event(helper.evtType); + const e = new inner$.Event(helper.evtType); e.ctrlKey = true; // Control key e.which = 66; // b inner$('#innerdocbody').trigger(e); - // ace creates a new dom element when you press a button, so just get the first text element again const $newFirstTextElement = inner$('div').first(); // is there a element now? diff --git a/tests/frontend/specs/caret.js b/tests/frontend/specs/caret.js index 1fb8d8aa7..e5ce255b8 100644 --- a/tests/frontend/specs/caret.js +++ b/tests/frontend/specs/caret.js @@ -1,7 +1,9 @@ +'use strict'; + describe('As the caret is moved is the UI properly updated?', function () { + /* let padName; const numberOfRows = 50; - /* //create a new pad before each test run beforeEach(function(cb){ @@ -16,7 +18,8 @@ describe('As the caret is moved is the UI properly updated?', function () { */ /* Tests to do - * Keystroke up (38), down (40), left (37), right (39) with and without special keys IE control / shift + * Keystroke up (38), down (40), left (37), right (39) + * with and without special keys IE control / shift * Page up (33) / down (34) with and without special keys * Page up on the first line shouldn't move the viewport * Down down on the last line shouldn't move the viewport @@ -25,7 +28,9 @@ describe('As the caret is moved is the UI properly updated?', function () { */ /* Challenges - * How do we keep the authors focus on a line if the lines above the author are modified? We should only redraw the user to a location if they are typing and make sure shift and arrow keys aren't redrawing the UI else highlight - copy/paste would get broken + * How do we keep the authors focus on a line if the lines above the author are modified? + * We should only redraw the user to a location if they are typing and make sure shift + * and arrow keys aren't redrawing the UI else highlight - copy/paste would get broken * How can we simulate an edit event in the test framework? */ /* @@ -200,7 +205,8 @@ console.log(inner$); var chrome$ = helper.padChrome$; var numberOfRows = 50; - //ace creates a new dom element when you press a keystroke, so just get the first text element again + // ace creates a new dom element when you press a keystroke, + // so just get the first text element again var $newFirstTextElement = inner$("div").first(); var originalDivHeight = inner$("div").first().css("height"); prepareDocument(numberOfRows, $newFirstTextElement); // N lines into the first div as a target @@ -208,28 +214,33 @@ console.log(inner$); helper.waitFor(function(){ // Wait for the DOM to register the new items return inner$("div").first().text().length == 6; }).done(function(){ // Once the DOM has registered the items - inner$("div").each(function(index){ // Randomize the item heights (replicates images / headings etc) + // Randomize the item heights (replicates images / headings etc) + inner$("div").each(function(index){ var random = Math.floor(Math.random() * (50)) + 20; $(this).css("height", random+"px"); }); console.log(caretPosition(inner$)); var newDivHeight = inner$("div").first().css("height"); - var heightHasChanged = originalDivHeight != newDivHeight; // has the new div height changed from the original div height + // has the new div height changed from the original div height + var heightHasChanged = originalDivHeight != newDivHeight; expect(heightHasChanged).to.be(true); // expect the first line to be blank }); // Is this Element now visible to the pad user? helper.waitFor(function(){ // Wait for the DOM to register the new items - return isScrolledIntoView(inner$("div:nth-child("+numberOfRows+")"), inner$); // Wait for the DOM to scroll into place + // Wait for the DOM to scroll into place + return isScrolledIntoView(inner$("div:nth-child("+numberOfRows+")"), inner$); }).done(function(){ // Once the DOM has registered the items - inner$("div").each(function(index){ // Randomize the item heights (replicates images / headings etc) + // Randomize the item heights (replicates images / headings etc) + inner$("div").each(function(index){ var random = Math.floor(Math.random() * (80 - 20 + 1)) + 20; $(this).css("height", random+"px"); }); var newDivHeight = inner$("div").first().css("height"); - var heightHasChanged = originalDivHeight != newDivHeight; // has the new div height changed from the original div height + // has the new div height changed from the original div height + var heightHasChanged = originalDivHeight != newDivHeight; expect(heightHasChanged).to.be(true); // expect the first line to be blank }); var i = 0; @@ -241,7 +252,8 @@ console.log(inner$); // Does scrolling back up the pad with the up arrow show the correct contents? helper.waitFor(function(){ // Wait for the new position to be in place try{ - return isScrolledIntoView(inner$("div:nth-child("+numberOfRows+")"), inner$); // Wait for the DOM to scroll into place + // Wait for the DOM to scroll into place + return isScrolledIntoView(inner$("div:nth-child("+numberOfRows+")"), inner$); }catch(e){ return false; } @@ -256,7 +268,8 @@ console.log(inner$); // Does scrolling back up the pad with the up arrow show the correct contents? helper.waitFor(function(){ // Wait for the new position to be in place try{ - return isScrolledIntoView(inner$("div:nth-child(0)"), inner$); // Wait for the DOM to scroll into place + // Wait for the DOM to scroll into place + return isScrolledIntoView(inner$("div:nth-child(0)"), inner$); }catch(e){ return false; } @@ -276,7 +289,8 @@ console.log(inner$); // Does scrolling back up the pad with the up arrow show the correct contents? helper.waitFor(function(){ // Wait for the new position to be in place - return isScrolledIntoView(inner$("div:nth-child(1)"), inner$); // Wait for the DOM to scroll into place + // Wait for the DOM to scroll into place + return isScrolledIntoView(inner$("div:nth-child(1)"), inner$); }).done(function(){ // Once the DOM has registered the items expect(true).to.be(true); done(); @@ -284,17 +298,19 @@ console.log(inner$); */ }); -function prepareDocument(n, target) { // generates a random document with random content on n lines +// generates a random document with random content on n lines +const prepareDocument = (n, target) => { let i = 0; while (i < n) { // for each line target.sendkeys(makeStr()); // generate a random string and send that to the editor target.sendkeys('{enter}'); // generator an enter keypress i++; // rinse n times } -} +}; -function keyEvent(target, charCode, ctrl, shift) { // sends a charCode to the window - const e = target.Event(helper.evtType); +// sends a charCode to the window +const keyEvent = (target, charCode, ctrl, shift) => { + const e = new target.Event(helper.evtType); if (ctrl) { e.ctrlKey = true; // Control key } @@ -304,30 +320,33 @@ function keyEvent(target, charCode, ctrl, shift) { // sends a charCode to the wi e.which = charCode; e.keyCode = charCode; target('#innerdocbody').trigger(e); -} +}; -function makeStr() { // from http://stackoverflow.com/questions/1349404/generate-a-string-of-5-random-characters-in-javascript +// from http://stackoverflow.com/questions/1349404/generate-a-string-of-5-random-characters-in-javascript +const makeStr = () => { let text = ''; const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; for (let i = 0; i < 5; i++) text += possible.charAt(Math.floor(Math.random() * possible.length)); return text; -} +}; -function isScrolledIntoView(elem, $) { // from http://stackoverflow.com/questions/487073/check-if-element-is-visible-after-scrolling +// from http://stackoverflow.com/questions/487073/check-if-element-is-visible-after-scrolling +const isScrolledIntoView = (elem, $) => { const docViewTop = $(window).scrollTop(); const docViewBottom = docViewTop + $(window).height(); const elemTop = $(elem).offset().top; // how far the element is from the top of it's container - let elemBottom = elemTop + $(elem).height(); // how far plus the height of the elem.. IE is it all in? + // how far plus the height of the elem.. IE is it all in? + let elemBottom = elemTop + $(elem).height(); elemBottom -= 16; // don't ask, sorry but this is needed.. return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)); -} +}; -function caretPosition($) { +const caretPosition = ($) => { const doc = $.window.document; const pos = doc.getSelection(); pos.y = pos.anchorNode.parentElement.offsetTop; pos.x = pos.anchorNode.parentElement.offsetLeft; return pos; -} +}; diff --git a/tests/frontend/specs/change_user_color.js b/tests/frontend/specs/change_user_color.js index e8c16db37..c8a3bf5b9 100644 --- a/tests/frontend/specs/change_user_color.js +++ b/tests/frontend/specs/change_user_color.js @@ -1,3 +1,5 @@ +'use strict'; + describe('change user color', function () { // create a new pad before each test run beforeEach(function (cb) { @@ -5,7 +7,8 @@ describe('change user color', function () { this.timeout(60000); }); - it('Color picker matches original color and remembers the user color after a refresh', function (done) { + it('Color picker matches original color and remembers the user color' + + ' after a refresh', function (done) { this.timeout(60000); const chrome$ = helper.padChrome$; @@ -60,7 +63,6 @@ describe('change user color', function () { }); it('Own user color is shown when you enter a chat', function (done) { - const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; const $colorOption = helper.padChrome$('#options-colorscheck'); @@ -90,13 +92,15 @@ describe('change user color', function () { $chatButton.click(); const $chatInput = chrome$('#chatinput'); $chatInput.sendkeys('O hi'); // simulate a keypress of typing user - $chatInput.sendkeys('{enter}'); // simulate a keypress of enter actually does evt.which = 10 not 13 + // simulate a keypress of enter actually does evt.which = 10 not 13 + $chatInput.sendkeys('{enter}'); - // check if chat shows up - helper.waitFor(() => chrome$('#chattext').children('p').length !== 0 // wait until the chat message shows up + // wait until the chat message shows up + helper.waitFor(() => chrome$('#chattext').children('p').length !== 0 ).done(() => { const $firstChatMessage = chrome$('#chattext').children('p'); - expect($firstChatMessage.css('background-color')).to.be(testColorRGB); // expect the first chat message to be of the user's color + // expect the first chat message to be of the user's color + expect($firstChatMessage.css('background-color')).to.be(testColorRGB); done(); }); }); diff --git a/tests/frontend/specs/change_user_name.js b/tests/frontend/specs/change_user_name.js index 0b9132f80..3c4b8b5bc 100644 --- a/tests/frontend/specs/change_user_name.js +++ b/tests/frontend/specs/change_user_name.js @@ -1,3 +1,5 @@ +'use strict'; + describe('change username value', function () { // create a new pad before each test run beforeEach(function (cb) { diff --git a/tests/frontend/specs/chat.js b/tests/frontend/specs/chat.js index d45988d60..fbc6ce788 100644 --- a/tests/frontend/specs/chat.js +++ b/tests/frontend/specs/chat.js @@ -1,10 +1,13 @@ +'use strict'; + describe('Chat messages and UI', function () { // create a new pad before each test run beforeEach(function (cb) { helper.newPad(cb); }); - it('opens chat, sends a message, makes sure it exists on the page and hides chat', async function () { + it('opens chat, sends a message, makes sure it exists ' + + 'on the page and hides chat', async function () { const chatValue = 'JohnMcLear'; await helper.showChat(); @@ -31,7 +34,8 @@ describe('Chat messages and UI', function () { await helper.showChat(); - await helper.sendChatMessage(`{enter}${chatValue}{enter}`); // simulate a keypress of typing enter, mluto and enter (to send 'mluto') + // simulate a keypress of typing enter, mluto and enter (to send 'mluto') + await helper.sendChatMessage(`{enter}${chatValue}{enter}`); const chat = helper.chatTextParagraphs(); @@ -44,7 +48,8 @@ describe('Chat messages and UI', function () { expect(chat.text()).to.be(`${username}${time} ${chatValue}`); }); - it('makes chat stick to right side of the screen via settings, remove sticky via settings, close it', async function () { + it('makes chat stick to right side of the screen via settings, ' + + 'remove sticky via settings, close it', async function () { await helper.showSettings(); await helper.enableStickyChatviaSettings(); @@ -60,7 +65,8 @@ describe('Chat messages and UI', function () { expect(helper.isChatboxShown()).to.be(false); }); - it('makes chat stick to right side of the screen via icon on the top right, remove sticky via icon, close it', async function () { + it('makes chat stick to right side of the screen via icon on the top' + + ' right, remove sticky via icon, close it', async function () { await helper.showChat(); await helper.enableStickyChatviaIcon(); @@ -76,10 +82,9 @@ describe('Chat messages and UI', function () { expect(helper.isChatboxShown()).to.be(false); }); - xit('Checks showChat=false URL Parameter hides chat then when removed it shows chat', function (done) { + xit('Checks showChat=false URL Parameter hides chat then' + + ' when removed it shows chat', function (done) { this.timeout(60000); - const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; setTimeout(() => { // give it a second to save the username on the server side helper.newPad({ // get a new pad, but don't clear the cookies diff --git a/tests/frontend/specs/chat_load_messages.js b/tests/frontend/specs/chat_load_messages.js index 29c1734ca..63d90fd63 100644 --- a/tests/frontend/specs/chat_load_messages.js +++ b/tests/frontend/specs/chat_load_messages.js @@ -1,3 +1,5 @@ +'use strict'; + describe('chat-load-messages', function () { let padName; @@ -7,7 +9,6 @@ describe('chat-load-messages', function () { }); it('adds a lot of messages', function (done) { - const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; const chatButton = chrome$('#chaticon'); chatButton.click(); @@ -19,12 +20,12 @@ describe('chat-load-messages', function () { const messages = 140; for (let i = 1; i <= messages; i++) { let num = `${i}`; - if (num.length == 1) num = `00${num}`; - if (num.length == 2) num = `0${num}`; + if (num.length === 1) num = `00${num}`; + if (num.length === 2) num = `0${num}`; chatInput.sendkeys(`msg${num}`); chatInput.sendkeys('{enter}'); } - helper.waitFor(() => chatText.children('p').length == messages, 60000).always(() => { + helper.waitFor(() => chatText.children('p').length === messages, 60000).always(() => { expect(chatText.children('p').length).to.be(messages); helper.newPad(done, padName); }); @@ -38,7 +39,7 @@ describe('chat-load-messages', function () { const chatButton = chrome$('#chaticon'); chatButton.click(); chatText = chrome$('#chattext'); - return chatText.children('p').length == expectedCount; + return chatText.children('p').length === expectedCount; }).always(() => { expect(chatText.children('p').length).to.be(expectedCount); done(); @@ -54,7 +55,7 @@ describe('chat-load-messages', function () { const loadMsgBtn = chrome$('#chatloadmessagesbutton'); loadMsgBtn.click(); - helper.waitFor(() => chatText.children('p').length == expectedCount).always(() => { + helper.waitFor(() => chatText.children('p').length === expectedCount).always(() => { expect(chatText.children('p').length).to.be(expectedCount); done(); }); @@ -65,13 +66,12 @@ describe('chat-load-messages', function () { const chrome$ = helper.padChrome$; const chatButton = chrome$('#chaticon'); chatButton.click(); - const chatText = chrome$('#chattext'); const loadMsgBtn = chrome$('#chatloadmessagesbutton'); const loadMsgBall = chrome$('#chatloadmessagesball'); loadMsgBtn.click(); - helper.waitFor(() => loadMsgBtn.css('display') == expectedDisplay && - loadMsgBall.css('display') == expectedDisplay).always(() => { + helper.waitFor(() => loadMsgBtn.css('display') === expectedDisplay && + loadMsgBall.css('display') === expectedDisplay).always(() => { expect(loadMsgBtn.css('display')).to.be(expectedDisplay); expect(loadMsgBall.css('display')).to.be(expectedDisplay); done(); diff --git a/tests/frontend/specs/clear_authorship_colors.js b/tests/frontend/specs/clear_authorship_colors.js index f622e912a..63d4c2f54 100644 --- a/tests/frontend/specs/clear_authorship_colors.js +++ b/tests/frontend/specs/clear_authorship_colors.js @@ -1,3 +1,5 @@ +'use strict'; + describe('clear authorship colors button', function () { // create a new pad before each test run beforeEach(function (cb) { @@ -17,9 +19,6 @@ describe('clear authorship colors button', function () { // get the first text element out of the inner iframe const $firstTextElement = inner$('div').first(); - // Get the original text - const originalText = inner$('div').first().text(); - // Set some new text const sentText = 'Hello'; @@ -28,7 +27,8 @@ describe('clear authorship colors button', function () { $firstTextElement.sendkeys(sentText); $firstTextElement.sendkeys('{rightarrow}'); - helper.waitFor(() => inner$('div span').first().attr('class').indexOf('author') !== -1 // wait until we have the full value available + // wait until we have the full value available + helper.waitFor(() => inner$('div span').first().attr('class').indexOf('author') !== -1 ).done(() => { // IE hates you if you don't give focus to the inner frame bevore you do a clearAuthorship inner$('div').first().focus(); @@ -37,16 +37,13 @@ describe('clear authorship colors button', function () { const $clearauthorshipcolorsButton = chrome$('.buttonicon-clearauthorship'); $clearauthorshipcolorsButton.click(); - // does the first divs span include an author class? - var hasAuthorClass = inner$('div span').first().attr('class').indexOf('author') !== -1; - // expect(hasAuthorClass).to.be(false); - // does the first div include an author class? - var hasAuthorClass = inner$('div').first().attr('class').indexOf('author') !== -1; + const hasAuthorClass = inner$('div').first().attr('class').indexOf('author') !== -1; expect(hasAuthorClass).to.be(false); helper.waitFor(() => { - const disconnectVisible = chrome$('div.disconnected').attr('class').indexOf('visible') === -1; + const disconnectVisible = + chrome$('div.disconnected').attr('class').indexOf('visible') === -1; return (disconnectVisible === true); }); @@ -69,9 +66,6 @@ describe('clear authorship colors button', function () { // get the first text element out of the inner iframe const $firstTextElement = inner$('div').first(); - // Get the original text - const originalText = inner$('div').first().text(); - // Set some new text const sentText = 'Hello'; @@ -80,7 +74,9 @@ describe('clear authorship colors button', function () { $firstTextElement.sendkeys(sentText); $firstTextElement.sendkeys('{rightarrow}'); - helper.waitFor(() => inner$('div span').first().attr('class').indexOf('author') !== -1 // wait until we have the full value available + // wait until we have the full value available + helper.waitFor( + () => inner$('div span').first().attr('class').indexOf('author') !== -1 ).done(() => { // IE hates you if you don't give focus to the inner frame bevore you do a clearAuthorship inner$('div').first().focus(); @@ -89,15 +85,11 @@ describe('clear authorship colors button', function () { const $clearauthorshipcolorsButton = chrome$('.buttonicon-clearauthorship'); $clearauthorshipcolorsButton.click(); - // does the first divs span include an author class? - var hasAuthorClass = inner$('div span').first().attr('class').indexOf('author') !== -1; - // expect(hasAuthorClass).to.be(false); - // does the first div include an author class? - var hasAuthorClass = inner$('div').first().attr('class').indexOf('author') !== -1; + let hasAuthorClass = inner$('div').first().attr('class').indexOf('author') !== -1; expect(hasAuthorClass).to.be(false); - const e = inner$.Event(helper.evtType); + const e = new inner$.Event(helper.evtType); e.ctrlKey = true; // Control key e.which = 90; // z inner$('#innerdocbody').trigger(e); // shouldn't od anything @@ -115,7 +107,8 @@ describe('clear authorship colors button', function () { expect(hasAuthorClass).to.be(false); helper.waitFor(() => { - const disconnectVisible = chrome$('div.disconnected').attr('class').indexOf('visible') === -1; + const disconnectVisible = + chrome$('div.disconnected').attr('class').indexOf('visible') === -1; return (disconnectVisible === true); }); diff --git a/tests/frontend/specs/delete.js b/tests/frontend/specs/delete.js index 4267aeec7..6cde43f47 100644 --- a/tests/frontend/specs/delete.js +++ b/tests/frontend/specs/delete.js @@ -1,3 +1,5 @@ +'use strict'; + describe('delete keystroke', function () { // create a new pad before each test run beforeEach(function (cb) { @@ -7,7 +9,6 @@ describe('delete keystroke', function () { it('makes text delete', function (done) { const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; // get the first text element out of the inner iframe const $firstTextElement = inner$('div').first(); @@ -15,15 +16,10 @@ describe('delete keystroke', function () { // get the original length of this element const elementLength = $firstTextElement.text().length; - // get the original string value minus the last char - const originalTextValue = $firstTextElement.text(); - const originalTextValueMinusFirstChar = originalTextValue.substring(1, originalTextValue.length); - // simulate key presses to delete content $firstTextElement.sendkeys('{leftarrow}'); // simulate a keypress of the left arrow key $firstTextElement.sendkeys('{del}'); // simulate a keypress of delete - // ace creates a new dom element when you press a keystroke, so just get the first text element again const $newFirstTextElement = inner$('div').first(); // get the new length of this element diff --git a/tests/frontend/specs/drag_and_drop.js b/tests/frontend/specs/drag_and_drop.js index a9726111c..2d1339beb 100644 --- a/tests/frontend/specs/drag_and_drop.js +++ b/tests/frontend/specs/drag_and_drop.js @@ -1,4 +1,6 @@ -// WARNING: drag and drop is only simulated on these tests, so manual testing might also be necessary +'use strict'; + +// WARNING: drag and drop is only simulated on these tests, manual testing might also be necessary describe('drag and drop', function () { before(function (done) { helper.newPad(() => { @@ -78,18 +80,19 @@ describe('drag and drop', function () { }); /* ********************* Helper functions/constants ********************* */ - var TARGET_LINE = 2; - var FIRST_SOURCE_LINE = 5; + const TARGET_LINE = 2; + const FIRST_SOURCE_LINE = 5; - var getLine = function (lineNumber) { + const getLine = (lineNumber) => { const $lines = helper.padInner$('div'); return $lines.slice(lineNumber, lineNumber + 1); }; - var createScriptWithSeveralLines = function (done) { + const createScriptWithSeveralLines = (done) => { // create some lines to be used on the tests const $firstLine = helper.padInner$('div').first(); - $firstLine.html('...
              ...
              Target line []
              ...
              ...
              Source line 1.
              Source line 2.
              '); + $firstLine.html('...
              ...
              Target line []
              ...
              ...
              ' + + 'Source line 1.
              Source line 2.
              '); // wait for lines to be split helper.waitFor(() => { @@ -98,7 +101,7 @@ describe('drag and drop', function () { }).done(done); }; - var selectPartOfSourceLine = function () { + const selectPartOfSourceLine = () => { const $sourceLine = getLine(FIRST_SOURCE_LINE); // select 'line 1' from 'Source line 1.' @@ -106,14 +109,14 @@ describe('drag and drop', function () { const end = start + 'line 1'.length; helper.selectLines($sourceLine, $sourceLine, start, end); }; - var selectMultipleSourceLines = function () { + const selectMultipleSourceLines = () => { const $firstSourceLine = getLine(FIRST_SOURCE_LINE); const $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1); helper.selectLines($firstSourceLine, $lastSourceLine); }; - var dragSelectedTextAndDropItIntoMiddleOfLine = function (targetLineNumber) { + const dragSelectedTextAndDropItIntoMiddleOfLine = (targetLineNumber) => { // dragstart: start dragging content triggerEvent('dragstart'); @@ -126,7 +129,7 @@ describe('drag and drop', function () { triggerEvent('dragend'); }; - var getHtmlFromSelectedText = function () { + const getHtmlFromSelectedText = () => { const innerDocument = helper.padInner$.document; const range = innerDocument.getSelection().getRangeAt(0); @@ -139,12 +142,12 @@ describe('drag and drop', function () { return draggedHtml; }; - var triggerEvent = function (eventName) { - const event = helper.padInner$.Event(eventName); + const triggerEvent = (eventName) => { + const event = new helper.padInner$.Event(eventName); helper.padInner$('#innerdocbody').trigger(event); }; - var moveSelectionIntoTarget = function (draggedHtml, targetLineNumber) { + const moveSelectionIntoTarget = (draggedHtml, targetLineNumber) => { const innerDocument = helper.padInner$.document; // delete original content diff --git a/tests/frontend/specs/embed_value.js b/tests/frontend/specs/embed_value.js index d6fb8c977..dac4c869d 100644 --- a/tests/frontend/specs/embed_value.js +++ b/tests/frontend/specs/embed_value.js @@ -1,3 +1,5 @@ +'use strict'; + describe('embed links', function () { const objectify = function (str) { const hash = {}; diff --git a/tests/frontend/specs/enter.js b/tests/frontend/specs/enter.js index 6108d7f82..2f5a90a69 100644 --- a/tests/frontend/specs/enter.js +++ b/tests/frontend/specs/enter.js @@ -1,3 +1,5 @@ +'use strict'; + describe('enter keystroke', function () { // create a new pad before each test run beforeEach(function (cb) { @@ -7,7 +9,6 @@ describe('enter keystroke', function () { it('creates a new line & puts cursor onto a new line', function (done) { const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; // get the first text element out of the inner iframe const $firstTextElement = inner$('div').first(); @@ -18,14 +19,12 @@ describe('enter keystroke', function () { // simulate key presses to enter content $firstTextElement.sendkeys('{enter}'); - // ace creates a new dom element when you press a keystroke, so just get the first text element again - const $newFirstTextElement = inner$('div').first(); - helper.waitFor(() => inner$('div').first().text() === '').done(() => { const $newSecondLine = inner$('div').first().next(); const newFirstTextElementValue = inner$('div').first().text(); expect(newFirstTextElementValue).to.be(''); // expect the first line to be blank - expect($newSecondLine.text()).to.be(originalTextValue); // expect the second line to be the same as the original first line. + // expect the second line to be the same as the original first line. + expect($newSecondLine.text()).to.be(originalTextValue); done(); }); }); diff --git a/tests/frontend/specs/font_type.js b/tests/frontend/specs/font_type.js index 51971da39..68df2f5e7 100644 --- a/tests/frontend/specs/font_type.js +++ b/tests/frontend/specs/font_type.js @@ -1,3 +1,5 @@ +'use strict'; + describe('font select', function () { // create a new pad before each test run beforeEach(function (cb) { @@ -15,7 +17,6 @@ describe('font select', function () { // get the font menu and RobotoMono option const $viewfontmenu = chrome$('#viewfontmenu'); - const $RobotoMonooption = $viewfontmenu.find('[value=RobotoMono]'); // select RobotoMono and fire change event // $RobotoMonooption.attr('selected','selected'); diff --git a/tests/frontend/specs/helper.js b/tests/frontend/specs/helper.js index 6bc6a3643..cb72fcf7a 100644 --- a/tests/frontend/specs/helper.js +++ b/tests/frontend/specs/helper.js @@ -1,3 +1,5 @@ +'use strict'; + describe('the test helper', function () { describe('the newPad method', function () { xit("doesn't leak memory if you creates iframes over and over again", function (done) { @@ -5,7 +7,7 @@ describe('the test helper', function () { let times = 10; - var loadPad = function () { + const loadPad = () => { helper.newPad(() => { times--; if (times > 0) { @@ -75,13 +77,14 @@ describe('the test helper', function () { // Before refreshing, make sure the name is there expect($usernameInput.val()).to.be('John McLear'); - // Now that we have a chrome, we can set a pad cookie, so we can confirm it gets wiped as well + // Now that we have a chrome, we can set a pad cookie + // so we can confirm it gets wiped as well chrome$.document.cookie = 'prefsHtml=baz;expires=Thu, 01 Jan 3030 00:00:00 GMT'; expect(chrome$.document.cookie).to.contain('prefsHtml=baz'); - // Cookies are weird. Because it's attached to chrome$ (as helper.setPadCookies does), AND we - // didn't put path=/, we shouldn't expect it to be visible on window.document.cookie. Let's just - // be sure. + // Cookies are weird. Because it's attached to chrome$ (as helper.setPadCookies does) + // AND we didn't put path=/, we shouldn't expect it to be visible on + // window.document.cookie. Let's just be sure. expect(window.document.cookie).to.not.contain('prefsHtml=baz'); setTimeout(() => { // give it a second to save the username on the server side @@ -266,7 +269,8 @@ describe('the test helper', function () { this.timeout(60000); }); - it('changes editor selection to be between startOffset of $startLine and endOffset of $endLine', function (done) { + it('changes editor selection to be between startOffset of $startLine ' + + 'and endOffset of $endLine', function (done) { const inner$ = helper.padInner$; const startOffset = 2; @@ -313,7 +317,8 @@ describe('the test helper', function () { * is not consistent between browsers but that's the situation so that's * how I'm covering it in this test. */ - expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm, ''))).to.be('ort lines to test'); + expect(cleanText( + selection.toString().replace(/(\r\n|\n|\r)/gm, ''))).to.be('ort lines to test'); done(); }); @@ -365,12 +370,14 @@ describe('the test helper', function () { * is not consistent between browsers but that's the situation so that's * how I'm covering it in this test. */ - expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm, ''))).to.be('ort lines to test'); + expect(cleanText( + selection.toString().replace(/(\r\n|\n|\r)/gm, ''))).to.be('ort lines to test'); done(); }); - it('selects all text between beginning of $startLine and end of $endLine when no offset is provided', function (done) { + it('selects all text between beginning of $startLine and end of $endLine ' + + 'when no offset is provided', function (done) { const inner$ = helper.padInner$; const $lines = inner$('div'); @@ -388,7 +395,8 @@ describe('the test helper', function () { * is not consistent between browsers but that's the situation so that's * how I'm covering it in this test. */ - expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm, ''))).to.be('short lines to test'); + expect(cleanText( + selection.toString().replace(/(\r\n|\n|\r)/gm, ''))).to.be('short lines to test'); done(); }); diff --git a/tests/frontend/specs/importexport.js b/tests/frontend/specs/importexport.js index 0be2a0744..4eb95eeb0 100644 --- a/tests/frontend/specs/importexport.js +++ b/tests/frontend/specs/importexport.js @@ -1,3 +1,5 @@ +'use strict'; + describe('import functionality', function () { beforeEach(function (cb) { helper.newPad(cb); // creates a new pad @@ -16,7 +18,6 @@ describe('import functionality', function () { return newtext; } function importrequest(data, importurl, type) { - let success; let error; const result = $.ajax({ url: importurl, @@ -27,7 +28,17 @@ describe('import functionality', function () { accepts: { text: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', }, - data: `Content-Type: multipart/form-data; boundary=--boundary\r\n\r\n--boundary\r\nContent-Disposition: form-data; name="file"; filename="import.${type}"\r\nContent-Type: text/plain\r\n\r\n${data}\r\n\r\n--boundary`, + data: [ + 'Content-Type: multipart/form-data; boundary=--boundary', + '', + '--boundary', + `Content-Disposition: form-data; name="file"; filename="import.${type}"`, + 'Content-Type: text/plain', + '', + data, + '', + '--boundary', + ].join('\r\n'), error(res) { error = res; }, @@ -56,7 +67,8 @@ describe('import functionality', function () { const importurl = `${helper.padChrome$.window.location.href}/import`; const textWithNewLines = 'imported text\nnewline'; importrequest(textWithNewLines, importurl, 'txt'); - helper.waitFor(() => expect(getinnertext()).to.be('imported text\nnewline\n
              \n')); + helper.waitFor(() => expect(getinnertext()) + .to.be('imported text\nnewline\n
              \n')); const results = exportfunc(helper.padChrome$.window.location.href); expect(results[0][1]).to.be('imported text
              newline

              '); expect(results[1][1]).to.be('imported text\nnewline\n\n'); @@ -66,7 +78,8 @@ describe('import functionality', function () { const importurl = `${helper.padChrome$.window.location.href}/import`; const htmlWithNewLines = 'htmltext
              newline'; importrequest(htmlWithNewLines, importurl, 'html'); - helper.waitFor(() => expect(getinnertext()).to.be('htmltext\nnewline\n
              \n')); + helper.waitFor(() => expect(getinnertext()) + .to.be('htmltext\nnewline\n
              \n')); const results = exportfunc(helper.padChrome$.window.location.href); expect(results[0][1]).to.be('htmltext
              newline

              '); expect(results[1][1]).to.be('htmltext\nnewline\n\n'); @@ -74,69 +87,109 @@ describe('import functionality', function () { }); xit('import a pad with attributes from html', function (done) { const importurl = `${helper.padChrome$.window.location.href}/import`; - const htmlWithNewLines = 'htmltext
              newline'; + const htmlWithNewLines = 'htmltext
              ' + + 'newline'; importrequest(htmlWithNewLines, importurl, 'html'); - helper.waitFor(() => expect(getinnertext()).to.be('htmltext\nnewline\n
              \n')); + helper.waitFor(() => expect(getinnertext()) + .to.be('htmltext\n' + + 'newline\n
              \n')); const results = exportfunc(helper.padChrome$.window.location.href); - expect(results[0][1]).to.be('htmltext
              newline

              '); + expect(results[0][1]) + .to.be('htmltext
              newline

              '); expect(results[1][1]).to.be('htmltext\nnewline\n\n'); done(); }); xit('import a pad with bullets from html', function (done) { const importurl = `${helper.padChrome$.window.location.href}/import`; - const htmlWithBullets = '
              • bullet line 1
              • bullet line 2
                • bullet2 line 1
                • bullet2 line 2
              '; + const htmlWithBullets = '
              • bullet line 1
              • ' + + '
              • bullet line 2
                • bullet2 line 1
                • ' + + '
                • bullet2 line 2
              '; importrequest(htmlWithBullets, importurl, 'html'); - helper.waitFor(() => expect(getinnertext()).to.be('\ -
              • bullet line 1
              \n\ -
              • bullet line 2
              \n\ -
              • bullet2 line 1
              \n\ -
              • bullet2 line 2
              \n\ -
              \n')); + helper.waitFor(() => expect(getinnertext()).to.be( + '
              • bullet line 1
              \n' + + '
              • bullet line 2
              \n' + + '
              • bullet2 line 1
              \n' + + '
              • bullet2 line 2
              \n' + + '
              \n')); const results = exportfunc(helper.padChrome$.window.location.href); - expect(results[0][1]).to.be('
              • bullet line 1
              • bullet line 2
                • bullet2 line 1
                • bullet2 line 2

              '); - expect(results[1][1]).to.be('\t* bullet line 1\n\t* bullet line 2\n\t\t* bullet2 line 1\n\t\t* bullet2 line 2\n\n'); + expect(results[0][1]).to.be( + '
              • bullet line 1
              • bullet line 2
              • ' + + '
                • bullet2 line 1
                • bullet2 line 2

              '); + expect(results[1][1]) + .to.be('\t* bullet line 1\n\t* bullet line 2\n' + + '\t\t* bullet2 line 1\n\t\t* bullet2 line 2\n\n'); done(); }); xit('import a pad with bullets and newlines from html', function (done) { const importurl = `${helper.padChrome$.window.location.href}/import`; - const htmlWithBullets = '
              • bullet line 1

              • bullet line 2
                • bullet2 line 1

                • bullet2 line 2
              '; + const htmlWithBullets = '
              • bullet line 1
              • ' + + '

              • bullet line 2
                • ' + + '
                • bullet2 line 1

                ' + + '
                • bullet2 line 2
              '; importrequest(htmlWithBullets, importurl, 'html'); - helper.waitFor(() => expect(getinnertext()).to.be('\ -
              • bullet line 1
              \n\ -
              \n\ -
              • bullet line 2
              \n\ -
              • bullet2 line 1
              \n\ -
              \n\ -
              • bullet2 line 2
              \n\ -
              \n')); + helper.waitFor(() => expect(getinnertext()).to.be( + '
              • bullet line 1
              \n' + + '
              \n' + + '
              • bullet line 2
              \n' + + '
              • bullet2 line 1
              \n' + + '
              \n' + + '
              • bullet2 line 2
              \n' + + '
              \n')); const results = exportfunc(helper.padChrome$.window.location.href); - expect(results[0][1]).to.be('
              • bullet line 1

              • bullet line 2
                • bullet2 line 1

                • bullet2 line 2

              '); - expect(results[1][1]).to.be('\t* bullet line 1\n\n\t* bullet line 2\n\t\t* bullet2 line 1\n\n\t\t* bullet2 line 2\n\n'); + expect(results[0][1]).to.be( + '
              • bullet line 1

                ' + + '
              • bullet line 2
                • bullet2 line 1
                ' + + '

                • bullet2 line 2

              '); + expect(results[1][1]).to.be( + '\t* bullet line 1\n\n\t* bullet line 2\n\t\t* bullet2 line 1\n\n\t\t* bullet2 line 2\n\n'); done(); }); xit('import a pad with bullets and newlines and attributes from html', function (done) { const importurl = `${helper.padChrome$.window.location.href}/import`; - const htmlWithBullets = '
              • bullet line 1

              • bullet line 2
                • bullet2 line 1

                    • bullet4 line 2 bisu
                    • bullet4 line 2 bs
                    • bullet4 line 2 uuis
              '; + const htmlWithBullets = '
              • bullet line 1
              • ' + + '

              • bullet line 2
              • ' + + '
                • bullet2 line 1
              ' + + '
                    ' + + '
                    • ' + + 'bullet4 line 2 bisu
                    • ' + + 'bullet4 line 2 bs
                    • ' + + '
                    • bullet4 line 2 u' + + 'uis
              '; importrequest(htmlWithBullets, importurl, 'html'); - helper.waitFor(() => expect(getinnertext()).to.be('\ -
              • bullet line 1
              \n\
              \n\ -
              • bullet line 2
              \n\ -
              • bullet2 line 1
              \n
              \n\ -
              • bullet4 line 2 bisu
              \n\ -
              • bullet4 line 2 bs
              \n\ -
              • bullet4 line 2 uuis
              \n\ -
              \n')); + helper.waitFor(() => expect(getinnertext()).to.be( + '
              • bullet line 1
              \n
              \n' + + '
              • bullet line 2
              \n' + + '
              • bullet2 line 1
              \n
              \n' + + '
              • ' + + 'bullet4 line 2 bisu
              \n' + + '
              • ' + + 'bullet4 line 2 bs
              \n' + + '
              • bullet4 line 2 u' + + 'uis
              \n' + + '
              \n')); const results = exportfunc(helper.padChrome$.window.location.href); - expect(results[0][1]).to.be('
              • bullet line 1

              • bullet line 2
                • bullet2 line 1

                    • bullet4 line 2 bisu
                    • bullet4 line 2 bs
                    • bullet4 line 2 uuis

              '); - expect(results[1][1]).to.be('\t* bullet line 1\n\n\t* bullet line 2\n\t\t* bullet2 line 1\n\n\t\t\t\t* bullet4 line 2 bisu\n\t\t\t\t* bullet4 line 2 bs\n\t\t\t\t* bullet4 line 2 uuis\n\n'); + expect(results[0][1]).to.be( + '
              • bullet line 1
              ' + + '
              • bullet line 2
                • bullet2 line 1
                • ' + + '

                    • bullet4 line 2 bisu' + + '
                    • bullet4 line 2 bs' + + '
                    • bullet4 line 2 uuis

              '); + expect(results[1][1]).to.be( + '\t* bullet line 1\n\n\t* bullet line 2\n\t\t* bullet2 line 1\n\n\t\t\t\t* bullet4 line 2' + + ' bisu\n\t\t\t\t* bullet4 line 2 bs\n\t\t\t\t* bullet4 line 2 uuis\n\n'); done(); }); xit('import a pad with nested bullets from html', function (done) { const importurl = `${helper.padChrome$.window.location.href}/import`; - const htmlWithBullets = '
              • bullet line 1
              • bullet line 2
                • bullet2 line 1
                    • bullet4 line 2
                    • bullet4 line 2
                    • bullet4 line 2
                  • bullet3 line 1
              • bullet2 line 1
              '; + const htmlWithBullets = '
              • bullet line 1
              • ' + + '
              • bullet line 2
                • ' + + '
                • bullet2 line 1
                  ' + + '
                    • bullet4 line 2
                    • ' + + '
                    • bullet4 line 2
                    • bullet4 line 2
                  • bullet3 line 1
                  ' + + '
              • bullet2 line 1
              '; importrequest(htmlWithBullets, importurl, 'html'); const oldtext = getinnertext(); - helper.waitFor(() => oldtext != getinnertext() + helper.waitFor(() => oldtext !== getinnertext() // return expect(getinnertext()).to.be('\ //
              • bullet line 1
              \n\ //
              • bullet line 2
              \n\ @@ -148,73 +201,127 @@ describe('import functionality', function () { ); const results = exportfunc(helper.padChrome$.window.location.href); - expect(results[0][1]).to.be('
              • bullet line 1
              • bullet line 2
                • bullet2 line 1
                    • bullet4 line 2
                    • bullet4 line 2
                    • bullet4 line 2
                  • bullet3 line 1
              • bullet2 line 1

              '); - expect(results[1][1]).to.be('\t* bullet line 1\n\t* bullet line 2\n\t\t* bullet2 line 1\n\t\t\t\t* bullet4 line 2\n\t\t\t\t* bullet4 line 2\n\t\t\t\t* bullet4 line 2\n\t\t\t* bullet3 line 1\n\t* bullet2 line 1\n\n'); + expect(results[0][1]).to.be( + '
              • bullet line 1
              • bullet line 2
              • ' + + '
                • bullet2 line 1
                    • bullet4 line 2
                    • ' + + '
                    • bullet4 line 2
                    • bullet4 line 2
                  • bullet3 line 1
                ' + + '
              • bullet2 line 1

              '); + expect(results[1][1]).to.be( + '\t* bullet line 1\n\t* bullet line 2\n\t\t* bullet2 line 1\n\t\t\t\t* bullet4 line 2' + + '\n\t\t\t\t* bullet4 line 2\n\t\t\t\t* bullet4 line 2\n\t\t\t* bullet3 line 1' + + '\n\t* bullet2 line 1\n\n'); done(); }); - xit('import a pad with 8 levels of bullets and newlines and attributes from html', function (done) { + xit('import with 8 levels of bullets and newlines and attributes from html', function (done) { const importurl = `${helper.padChrome$.window.location.href}/import`; - const htmlWithBullets = '
              • bullet line 1

              • bullet line 2
                • bullet2 line 1

                    • bullet4 line 2 bisu
                    • bullet4 line 2 bs
                    • bullet4 line 2 uuis
                            • foo
                            • foobar bs
                      • foobar
                '; + const htmlWithBullets = + '
                • bullet line 1
                • ' + + '

                • bullet line 2
                  • ' + + 'bullet2 line 1

                    ' + + '
                      • ' + + 'bullet4 line 2 bisu
                      • ' + + 'bullet4 line 2 bs
                      • bullet4 line 2 u' + + 'uis
                      • ' + + '
                              ' + + '
                              • foo
                              • ' + + 'foobar bs
                          ' + + '
                        • foobar
                  '; importrequest(htmlWithBullets, importurl, 'html'); - helper.waitFor(() => expect(getinnertext()).to.be('\ -
                  • bullet line 1
                  \n\
                  \n\ -
                  • bullet line 2
                  \n\ -
                  • bullet2 line 1
                  \n
                  \n\ -
                  • bullet4 line 2 bisu
                  \n\ -
                  • bullet4 line 2 bs
                  \n\ -
                  • bullet4 line 2 uuis
                  \n\ -
                  • foo
                  \n\ -
                  • foobar bs
                  \n\ -
                  • foobar
                  \n\ -
                  \n')); + helper.waitFor(() => expect(getinnertext()).to.be( + '
                  • bullet line 1
                  \n
                  \n' + + '
                  • bullet line 2
                  \n' + + '
                  • bullet2 line 1
                  \n
                  \n' + + '
                  • bullet4 line 2 bisu' + + '
                  \n' + + '
                  • bullet4 line 2 bs' + + '
                  \n' + + '
                  • bullet4 line 2 u' + + 'uis' + + '
                  \n' + + '
                  • foo
                  \n' + + '
                  • foobar bs' + + '
                  \n' + + '
                  • foobar
                  \n' + + '
                  \n')); const results = exportfunc(helper.padChrome$.window.location.href); - expect(results[0][1]).to.be('
                  • bullet line 1

                  • bullet line 2
                    • bullet2 line 1

                        • bullet4 line 2 bisu
                        • bullet4 line 2 bs
                        • bullet4 line 2 uuis
                                • foo
                                • foobar bs
                          • foobar

                  '); - expect(results[1][1]).to.be('\t* bullet line 1\n\n\t* bullet line 2\n\t\t* bullet2 line 1\n\n\t\t\t\t* bullet4 line 2 bisu\n\t\t\t\t* bullet4 line 2 bs\n\t\t\t\t* bullet4 line 2 uuis\n\t\t\t\t\t\t\t\t* foo\n\t\t\t\t\t\t\t\t* foobar bs\n\t\t\t\t\t* foobar\n\n'); + expect(results[0][1]).to.be( + '
                  • bullet line 1

                    ' + + '
                  • bullet line 2
                    • bullet2 line 1
                  ' + + '
                        • ' + + 'bullet4 line 2 bisu
                        • ' + + 'bullet4 line 2 bs
                        • bullet4 line 2 u' + + 'uis
                                • foo
                                • ' + + '
                                • foobar bs
                          • foobar
                          • ' + + '

                  '); + expect(results[1][1]).to.be( + '\t* bullet line 1\n\n\t* bullet line 2\n\t\t* ' + + 'bullet2 line 1\n\n\t\t\t\t* bullet4 line 2 bisu\n\t\t\t\t* bullet4 line 2 ' + + 'bs\n\t\t\t\t* bullet4 line 2 uuis\n\t\t\t\t\t\t\t\t* foo\n\t\t\t\t\t\t\t\t* ' + + 'foobar bs\n\t\t\t\t\t* foobar\n\n'); done(); }); xit('import a pad with ordered lists from html', function (done) { const importurl = `${helper.padChrome$.window.location.href}/import`; - const htmlWithBullets = '
                  1. number 1 line 1
                  1. number 2 line 2
                  '; + const htmlWithBullets = '
                    ' + + '
                  1. number 1 line 1
                    ' + + '
                  1. number 2 line 2
                  '; importrequest(htmlWithBullets, importurl, 'html'); console.error(getinnertext()); - expect(getinnertext()).to.be('\ -
                  1. number 1 line 1
                  \n\ -
                  1. number 2 line 2
                  \n\ -
                  \n'); + expect(getinnertext()).to.be( + '
                  1. number 1 line 1
                  \n' + + '
                  1. number 2 line 2
                  \n' + + '
                  \n'); const results = exportfunc(helper.padChrome$.window.location.href); - expect(results[0][1]).to.be('
                  1. number 1 line 1
                  1. number 2 line 2
                  '); + expect(results[0][1]).to.be( + '
                  1. number 1 line 1
                  2. ' + + '
                  1. number 2 line 2
                  '); expect(results[1][1]).to.be(''); done(); }); xit('import a pad with ordered lists and newlines from html', function (done) { const importurl = `${helper.padChrome$.window.location.href}/import`; - const htmlWithBullets = '
                  1. number 9 line 1

                  1. number 10 line 2
                    1. number 2 times line 1

                    1. number 2 times line 2
                  '; + const htmlWithBullets = '
                    ' + + '
                  1. number 9 line 1

                    ' + + '
                  1. number 10 line 2
                    1. ' + + '
                    2. number 2 times line 1

                    ' + + '
                    1. number 2 times line 2
                  '; importrequest(htmlWithBullets, importurl, 'html'); - expect(getinnertext()).to.be('\ -
                  1. number 9 line 1
                  \n\ -
                  \n\ -
                  1. number 10 line 2
                  \n\ -
                  1. number 2 times line 1
                  \n\ -
                  \n\ -
                  1. number 2 times line 2
                  \n\ -
                  \n'); + expect(getinnertext()).to.be( + '
                  1. number 9 line 1
                  \n' + + '
                  \n' + + '
                  1. number 10 line 2
                  2. ' + + '
                  \n' + + '
                  1. number 2 times line 1
                  \n' + + '
                  \n' + + '
                  1. number 2 times line 2
                  \n' + + '
                  \n'); const results = exportfunc(helper.padChrome$.window.location.href); console.error(results); done(); }); - xit('import a pad with nested ordered lists and attributes and newlines from html', function (done) { + xit('import with nested ordered lists and attributes and newlines from html', function (done) { const importurl = `${helper.padChrome$.window.location.href}/import`; - const htmlWithBullets = '
                  1. bold strikethrough italics underline line 1bold

                  1. number 10 line 2
                    1. number 2 times line 1

                    1. number 2 times line 2
                  '; + const htmlWithBullets = '
                  1. ' + + 'bold strikethrough italics underline' + + ' line 1bold
                  2. ' + + '

                    ' + + '
                  1. number 10 line 2
                    1. ' + + '
                    2. number 2 times line 1

                  ' + + '
                      ' + + '
                    1. number 2 times line 2
                  '; importrequest(htmlWithBullets, importurl, 'html'); - expect(getinnertext()).to.be('\ -
                  1. bold strikethrough italics underline line 1bold
                  \n\ -
                  \n\ -
                  1. number 10 line 2
                  \n\ -
                  1. number 2 times line 1
                  \n\ -
                  \n\ -
                  1. number 2 times line 2
                  \n\ -
                  \n'); + expect(getinnertext()).to.be( + '
                  1. ' + + 'bold strikethrough italics underline' + + ' line 1bold
                  \n' + + '
                  \n' + + '
                  1. number 10 line 2
                  \n' + + '
                  1. ' + + 'number 2 times line 1
                  \n' + + '
                  \n' + + '
                  1. number 2 times line 2
                  \n' + + '
                  \n'); const results = exportfunc(helper.padChrome$.window.location.href); console.error(results); done(); diff --git a/tests/frontend/specs/importindents.js b/tests/frontend/specs/importindents.js index 6209236df..eecbbce59 100644 --- a/tests/frontend/specs/importindents.js +++ b/tests/frontend/specs/importindents.js @@ -1,3 +1,5 @@ +'use strict'; + describe('import indents functionality', function () { beforeEach(function (cb) { helper.newPad(cb); // creates a new pad @@ -13,7 +15,6 @@ describe('import indents functionality', function () { return newtext; } function importrequest(data, importurl, type) { - let success; let error; const result = $.ajax({ url: importurl, @@ -24,7 +25,17 @@ describe('import indents functionality', function () { accepts: { text: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', }, - data: `Content-Type: multipart/form-data; boundary=--boundary\r\n\r\n--boundary\r\nContent-Disposition: form-data; name="file"; filename="import.${type}"\r\nContent-Type: text/plain\r\n\r\n${data}\r\n\r\n--boundary`, + data: [ + 'Content-Type: multipart/form-data; boundary=--boundary', + '', + '--boundary', + `Content-Disposition: form-data; name="file"; filename="import.${type}"`, + 'Content-Type: text/plain', + '', + data, + '', + '--boundary', + ].join('\r\n'), error(res) { error = res; }, @@ -51,54 +62,67 @@ describe('import indents functionality', function () { xit('import a pad with indents from html', function (done) { const importurl = `${helper.padChrome$.window.location.href}/import`; + /* eslint-disable-next-line max-len */ const htmlWithIndents = '
                  • indent line 1
                  • indent line 2
                    • indent2 line 1
                    • indent2 line 2
                  '; importrequest(htmlWithIndents, importurl, 'html'); - helper.waitFor(() => expect(getinnertext()).to.be('\ -
                  • indent line 1
                  \n\ -
                  • indent line 2
                  \n\ -
                  • indent2 line 1
                  \n\ -
                  • indent2 line 2
                  \n\ -
                  \n')); + helper.waitFor(() => expect(getinnertext()).to.be( + '
                  • indent line 1
                  \n' + + '
                  • indent line 2
                  \n' + + '
                  • indent2 line 1
                  \n' + + '
                  • indent2 line 2
                  \n' + + '
                  \n')); const results = exportfunc(helper.padChrome$.window.location.href); + /* eslint-disable-next-line max-len */ expect(results[0][1]).to.be('
                  • indent line 1
                  • indent line 2
                    • indent2 line 1
                    • indent2 line 2

                  '); - expect(results[1][1]).to.be('\tindent line 1\n\tindent line 2\n\t\tindent2 line 1\n\t\tindent2 line 2\n\n'); + expect(results[1][1]) + .to.be('\tindent line 1\n\tindent line 2\n\t\tindent2 line 1\n\t\tindent2 line 2\n\n'); done(); }); xit('import a pad with indented lists and newlines from html', function (done) { const importurl = `${helper.padChrome$.window.location.href}/import`; + /* eslint-disable-next-line max-len */ const htmlWithIndents = '
                  • indent line 1

                  • indent 1 line 2
                    • indent 2 times line 1

                    • indent 2 times line 2
                  '; importrequest(htmlWithIndents, importurl, 'html'); - helper.waitFor(() => expect(getinnertext()).to.be('\ -
                  • indent line 1
                  \n\ -
                  \n\ -
                  • indent 1 line 2
                  \n\ -
                  • indent 2 times line 1
                  \n\ -
                  \n\ -
                  • indent 2 times line 2
                  \n\ -
                  \n')); + helper.waitFor(() => expect(getinnertext()).to.be( + '
                  • indent line 1
                  \n' + + '
                  \n' + + '
                  • indent 1 line 2
                  \n' + + '
                  • indent 2 times line 1
                  \n' + + '
                  \n' + + '
                  • indent 2 times line 2
                  \n' + + '
                  \n')); const results = exportfunc(helper.padChrome$.window.location.href); + /* eslint-disable-next-line max-len */ expect(results[0][1]).to.be('
                  • indent line 1

                  • indent 1 line 2
                    • indent 2 times line 1

                    • indent 2 times line 2

                  '); + /* eslint-disable-next-line max-len */ expect(results[1][1]).to.be('\tindent line 1\n\n\tindent 1 line 2\n\t\tindent 2 times line 1\n\n\t\tindent 2 times line 2\n\n'); done(); }); - xit('import a pad with 8 levels of indents and newlines and attributes from html', function (done) { + xit('import with 8 levels of indents and newlines and attributes from html', function (done) { const importurl = `${helper.padChrome$.window.location.href}/import`; + /* eslint-disable-next-line max-len */ const htmlWithIndents = '
                  • indent line 1

                  • indent line 2
                    • indent2 line 1

                        • indent4 line 2 bisu
                        • indent4 line 2 bs
                        • indent4 line 2 uuis
                                • foo
                                • foobar bs
                          • foobar
                    '; importrequest(htmlWithIndents, importurl, 'html'); - helper.waitFor(() => expect(getinnertext()).to.be('\ -
                    • indent line 1
                    \n\
                    \n\ -
                    • indent line 2
                    \n\ -
                    • indent2 line 1
                    \n
                    \n\ -
                    • indent4 line 2 bisu
                    \n\ -
                    • indent4 line 2 bs
                    \n\ -
                    • indent4 line 2 uuis
                    \n\ -
                    • foo
                    \n\ -
                    • foobar bs
                    \n\ -
                    • foobar
                    \n\ -
                    \n')); + helper.waitFor(() => expect(getinnertext()).to.be( + '
                    • indent line 1
                    \n
                    \n' + + '
                    • indent line 2
                    \n' + + '
                    • indent2 line 1
                    \n
                    \n' + + '
                    • indent4 ' + + 'line 2 bisu
                    \n' + + '
                    • ' + + 'indent4 line 2 bs
                    \n' + + '
                    • indent4 line 2 u' + + 'uis
                    \n' + + '
                    • foo
                    \n' + + '
                    • foobar bs' + + '
                    \n' + + '
                    • foobar
                    \n' + + '
                    \n')); const results = exportfunc(helper.padChrome$.window.location.href); + /* eslint-disable-next-line max-len */ expect(results[0][1]).to.be('
                    • indent line 1

                    • indent line 2
                      • indent2 line 1

                          • indent4 line 2 bisu
                          • indent4 line 2 bs
                          • indent4 line 2 uuis
                                  • foo
                                  • foobar bs
                            • foobar

                    '); + /* eslint-disable-next-line max-len */ expect(results[1][1]).to.be('\tindent line 1\n\n\tindent line 2\n\t\tindent2 line 1\n\n\t\t\t\tindent4 line 2 bisu\n\t\t\t\tindent4 line 2 bs\n\t\t\t\tindent4 line 2 uuis\n\t\t\t\t\t\t\t\tfoo\n\t\t\t\t\t\t\t\tfoobar bs\n\t\t\t\t\tfoobar\n\n'); done(); }); diff --git a/tests/frontend/specs/indentation.js b/tests/frontend/specs/indentation.js index c52f5f406..006c52bb4 100644 --- a/tests/frontend/specs/indentation.js +++ b/tests/frontend/specs/indentation.js @@ -1,3 +1,5 @@ +'use strict'; + describe('indentation button', function () { // create a new pad before each test run beforeEach(function (cb) { @@ -7,7 +9,6 @@ describe('indentation button', function () { it('indent text with keypress', function (done) { const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; // get the first text element out of the inner iframe const $firstTextElement = inner$('div').first(); @@ -15,7 +16,7 @@ describe('indentation button', function () { // select this text element $firstTextElement.sendkeys('{selectall}'); - const e = inner$.Event(helper.evtType); + const e = new inner$.Event(helper.evtType); e.keyCode = 9; // tab :| inner$('#innerdocbody').trigger(e); @@ -56,9 +57,9 @@ describe('indentation button', function () { }); }); - it("indents text with spaces on enter if previous line ends with ':', '[', '(', or '{'", function (done) { + it('indents text with spaces on enter if previous line ends ' + + "with ':', '[', '(', or '{'", function (done) { const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; // type a bit, make a line break and type again const $firstTextElement = inner$('div').first(); @@ -77,7 +78,8 @@ describe('indentation button', function () { // curly braces const $lineWithCurlyBraces = inner$('div').first().next().next().next(); $lineWithCurlyBraces.sendkeys('{{}'); - pressEnter(); // cannot use sendkeys('{enter}') here, browser does not read the command properly + // cannot use sendkeys('{enter}') here, browser does not read the command properly + pressEnter(); const $lineAfterCurlyBraces = inner$('div').first().next().next().next().next(); expect($lineAfterCurlyBraces.text()).to.match(/\s{4}/); // tab === 4 spaces @@ -106,9 +108,9 @@ describe('indentation button', function () { }); }); - it("appends indentation to the indent of previous line if previous line ends with ':', '[', '(', or '{'", function (done) { + it('appends indentation to the indent of previous line if previous line ends ' + + "with ':', '[', '(', or '{'", function (done) { const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; // type a bit, make a line break and type again const $firstTextElement = inner$('div').first(); @@ -124,13 +126,15 @@ describe('indentation button', function () { $lineWithColon.sendkeys(':'); pressEnter(); const $lineAfterColon = inner$('div').first().next(); - expect($lineAfterColon.text()).to.match(/\s{6}/); // previous line indentation + regular tab (4 spaces) + // previous line indentation + regular tab (4 spaces) + expect($lineAfterColon.text()).to.match(/\s{6}/); done(); }); }); - it("issue #2772 shows '*' when multiple indented lines receive a style and are outdented", async function () { + it("issue #2772 shows '*' when multiple indented lines " + + ' receive a style and are outdented', async function () { const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; @@ -182,7 +186,6 @@ describe('indentation button', function () { var $indentButton = testHelper.$getPadChrome().find(".buttonicon-indent"); $indentButton.click(); - //ace creates a new dom element when you press a button, so just get the first text element again var newFirstTextElement = $inner.find("div").first(); // is there a list-indent class element now? @@ -220,7 +223,6 @@ describe('indentation button', function () { $outdentButton.click(); $outdentButton.click(); - //ace creates a new dom element when you press a button, so just get the first text element again var newFirstTextElement = $inner.find("div").first(); // is there a list-indent class element now? @@ -267,7 +269,9 @@ describe('indentation button', function () { //get the second text element out of the inner iframe setTimeout(function(){ // THIS IS REALLY BAD - var secondTextElement = $('iframe').contents().find('iframe').contents().find('iframe').contents().find('body > div').get(1); // THIS IS UGLY + var secondTextElement = $('iframe').contents() + .find('iframe').contents() + .find('iframe').contents().find('body > div').get(1); // THIS IS UGLY // is there a list-indent class element now? var firstChild = secondTextElement.children(":first"); @@ -282,7 +286,10 @@ describe('indentation button', function () { expect(isLI).to.be(true); //get the first text element out of the inner iframe - var thirdTextElement = $('iframe').contents().find('iframe').contents().find('iframe').contents().find('body > div').get(2); // THIS IS UGLY TOO + var thirdTextElement = $('iframe').contents() + .find('iframe').contents() + .find('iframe').contents() + .find('body > div').get(2); // THIS IS UGLY TOO // is there a list-indent class element now? var firstChild = thirdTextElement.children(":first"); @@ -300,9 +307,9 @@ describe('indentation button', function () { });*/ }); -function pressEnter() { +const pressEnter = () => { const inner$ = helper.padInner$; - const e = inner$.Event(helper.evtType); + const e = new inner$.Event(helper.evtType); e.keyCode = 13; // enter :| inner$('#innerdocbody').trigger(e); -} +}; diff --git a/tests/frontend/specs/italic.js b/tests/frontend/specs/italic.js index 3660f71f3..cbaf9e3da 100644 --- a/tests/frontend/specs/italic.js +++ b/tests/frontend/specs/italic.js @@ -1,3 +1,5 @@ +'use strict'; + describe('italic some text', function () { // create a new pad before each test run beforeEach(function (cb) { @@ -19,7 +21,7 @@ describe('italic some text', function () { const $boldButton = chrome$('.buttonicon-italic'); $boldButton.click(); - // ace creates a new dom element when you press a button, so just get the first text element again + // ace creates a new dom element when you press a button, just get the first text element again const $newFirstTextElement = inner$('div').first(); // is there a element now? @@ -36,7 +38,6 @@ describe('italic some text', function () { it('makes text italic using keypress', function (done) { const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; // get the first text element out of the inner iframe const $firstTextElement = inner$('div').first(); @@ -44,12 +45,12 @@ describe('italic some text', function () { // select this text element $firstTextElement.sendkeys('{selectall}'); - const e = inner$.Event(helper.evtType); + const e = new inner$.Event(helper.evtType); e.ctrlKey = true; // Control key e.which = 105; // i inner$('#innerdocbody').trigger(e); - // ace creates a new dom element when you press a button, so just get the first text element again + // ace creates a new dom element when you press a button, just get the first text element again const $newFirstTextElement = inner$('div').first(); // is there a element now? diff --git a/tests/frontend/specs/language.js b/tests/frontend/specs/language.js index d29b2407e..072c64e92 100644 --- a/tests/frontend/specs/language.js +++ b/tests/frontend/specs/language.js @@ -1,6 +1,8 @@ -function deletecookie(name) { +'use strict'; + +const deletecookie = (name) => { document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:01 GMT;`; -} +}; describe('Language select and change', function () { // Destroy language cookies @@ -14,7 +16,6 @@ describe('Language select and change', function () { // Destroy language cookies it('makes text german', function (done) { - const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; // click on the settings button to make settings visible @@ -29,7 +30,7 @@ describe('Language select and change', function () { $languageoption.attr('selected', 'selected'); $language.change(); - helper.waitFor(() => chrome$('.buttonicon-bold').parent()[0].title == 'Fett (Strg-B)') + helper.waitFor(() => chrome$('.buttonicon-bold').parent()[0].title === 'Fett (Strg-B)') .done(() => { // get the value of the bold button const $boldButton = chrome$('.buttonicon-bold').parent(); @@ -44,7 +45,6 @@ describe('Language select and change', function () { }); it('makes text English', function (done) { - const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; // click on the settings button to make settings visible @@ -60,7 +60,7 @@ describe('Language select and change', function () { // get the value of the bold button const $boldButton = chrome$('.buttonicon-bold').parent(); - helper.waitFor(() => $boldButton[0].title != 'Fett (Strg+B)') + helper.waitFor(() => $boldButton[0].title !== 'Fett (Strg+B)') .done(() => { // get the value of the bold button const $boldButton = chrome$('.buttonicon-bold').parent(); @@ -75,7 +75,6 @@ describe('Language select and change', function () { }); it('changes direction when picking an rtl lang', function (done) { - const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; // click on the settings button to make settings visible @@ -91,7 +90,7 @@ describe('Language select and change', function () { $language.val('ar'); $languageoption.change(); - helper.waitFor(() => chrome$('html')[0].dir != 'ltr') + helper.waitFor(() => chrome$('html')[0].dir !== 'ltr') .done(() => { // check if the document's direction was changed expect(chrome$('html')[0].dir).to.be('rtl'); @@ -100,7 +99,6 @@ describe('Language select and change', function () { }); it('changes direction when picking an ltr lang', function (done) { - const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; // click on the settings button to make settings visible @@ -117,7 +115,7 @@ describe('Language select and change', function () { $language.val('en'); $languageoption.change(); - helper.waitFor(() => chrome$('html')[0].dir != 'rtl') + helper.waitFor(() => chrome$('html')[0].dir !== 'rtl') .done(() => { // check if the document's direction was changed expect(chrome$('html')[0].dir).to.be('ltr'); diff --git a/tests/frontend/specs/multiple_authors_clear_authorship_colors.js b/tests/frontend/specs/multiple_authors_clear_authorship_colors.js index f532ea4be..19dfb44f9 100755 --- a/tests/frontend/specs/multiple_authors_clear_authorship_colors.js +++ b/tests/frontend/specs/multiple_authors_clear_authorship_colors.js @@ -1,7 +1,9 @@ +'use strict'; + describe('author of pad edition', function () { // author 1 creates a new pad with some content (regular lines and lists) before(function (done) { - var padId = helper.newPad(() => { + const padId = helper.newPad(() => { // make sure pad has at least 3 lines const $firstLine = helper.padInner$('div').first(); $firstLine.html('Hello World'); @@ -13,7 +15,8 @@ describe('author of pad edition', function () { setTimeout(() => { // Expire cookie, so author is changed after reloading the pad. // See https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#Example_4_Reset_the_previous_cookie - helper.padChrome$.document.cookie = 'token=foo;expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; + helper.padChrome$.document.cookie = + 'token=foo;expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; helper.newPad(done, padId); }, 1000); @@ -27,7 +30,7 @@ describe('author of pad edition', function () { clearAuthorship(done); }); - var clearAuthorship = function (done) { + const clearAuthorship = (done) => { const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; diff --git a/tests/frontend/specs/ordered_list.js b/tests/frontend/specs/ordered_list.js index a932335e8..d069a6487 100644 --- a/tests/frontend/specs/ordered_list.js +++ b/tests/frontend/specs/ordered_list.js @@ -1,3 +1,5 @@ +'use strict'; + describe('assign ordered list', function () { // create a new pad before each test run beforeEach(function (cb) { @@ -34,7 +36,8 @@ describe('assign ordered list', function () { }); it('does not insert unordered list', function (done) { - helper.waitFor(() => helper.padInner$('div').first().find('ol li').length === 1).done(() => { + helper.waitFor( + () => helper.padInner$('div').first().find('ol li').length === 1).done(() => { expect().fail(() => 'Unordered list inserted, should ignore shortcut'); }).fail(() => { done(); @@ -62,7 +65,8 @@ describe('assign ordered list', function () { }); it('does not insert unordered list', function (done) { - helper.waitFor(() => helper.padInner$('div').first().find('ol li').length === 1).done(() => { + helper.waitFor( + () => helper.padInner$('div').first().find('ol li').length === 1).done(() => { expect().fail(() => 'Unordered list inserted, should ignore shortcut'); }).fail(() => { done(); @@ -71,7 +75,8 @@ describe('assign ordered list', function () { }); }); - xit('issue #1125 keeps the numbered list on enter for the new line - EMULATES PASTING INTO A PAD', function (done) { + xit('issue #1125 keeps the numbered list on enter for the new line', function (done) { + // EMULATES PASTING INTO A PAD const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; @@ -91,24 +96,25 @@ describe('assign ordered list', function () { expect(hasOLElement).to.be(true); expect($newSecondLine.text()).to.be('line 2'); const hasLineNumber = $newSecondLine.find('ol').attr('start') === 2; - expect(hasLineNumber).to.be(true); // This doesn't work because pasting in content doesn't work + // This doesn't work because pasting in content doesn't work + expect(hasLineNumber).to.be(true); done(); }); }); - var triggerCtrlShiftShortcut = function (shortcutChar) { + const triggerCtrlShiftShortcut = (shortcutChar) => { const inner$ = helper.padInner$; - const e = inner$.Event(helper.evtType); + const e = new inner$.Event(helper.evtType); e.ctrlKey = true; e.shiftKey = true; e.which = shortcutChar.toString().charCodeAt(0); inner$('#innerdocbody').trigger(e); }; - var makeSureShortcutIsDisabled = function (shortcut) { + const makeSureShortcutIsDisabled = (shortcut) => { helper.padChrome$.window.clientVars.padShortcutEnabled[shortcut] = false; }; - var makeSureShortcutIsEnabled = function (shortcut) { + const makeSureShortcutIsEnabled = (shortcut) => { helper.padChrome$.window.clientVars.padShortcutEnabled[shortcut] = true; }; }); @@ -133,7 +139,7 @@ describe('Pressing Tab in an OL increases and decreases indentation', function ( const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist'); $insertorderedlistButton.click(); - const e = inner$.Event(helper.evtType); + const e = new inner$.Event(helper.evtType); e.keyCode = 9; // tab inner$('#innerdocbody').trigger(e); @@ -147,7 +153,8 @@ describe('Pressing Tab in an OL increases and decreases indentation', function ( }); -describe('Pressing indent/outdent button in an OL increases and decreases indentation and bullet / ol formatting', function () { +describe('Pressing indent/outdent button in an OL increases and ' + + 'decreases indentation and bullet / ol formatting', function () { // create a new pad before each test run beforeEach(function (cb) { helper.newPad(cb); diff --git a/tests/frontend/specs/pad_modal.js b/tests/frontend/specs/pad_modal.js index 1711e38b8..30277d5de 100644 --- a/tests/frontend/specs/pad_modal.js +++ b/tests/frontend/specs/pad_modal.js @@ -1,3 +1,5 @@ +'use strict'; + describe('Pad modal', function () { context('when modal is a "force reconnect" message', function () { const MODAL_SELECTOR = '#connectivity'; @@ -93,17 +95,17 @@ describe('Pad modal', function () { }); }); - var clickOnPadInner = function () { + const clickOnPadInner = () => { const $editor = helper.padInner$('#innerdocbody'); $editor.click(); }; - var clickOnPadOuter = function () { + const clickOnPadOuter = () => { const $lineNumbersColumn = helper.padOuter$('#sidedivinner'); $lineNumbersColumn.click(); }; - var openSettingsAndWaitForModalToBeVisible = function (done) { + const openSettingsAndWaitForModalToBeVisible = (done) => { helper.padChrome$('.buttonicon-settings').click(); // wait for modal to be displayed @@ -111,7 +113,7 @@ describe('Pad modal', function () { helper.waitFor(() => isModalOpened(modalSelector), 10000).done(done); }; - var isEditorDisabled = function () { + const isEditorDisabled = () => { const editorDocument = helper.padOuter$("iframe[name='ace_inner']").get(0).contentDocument; const editorBody = editorDocument.getElementById('innerdocbody'); @@ -121,7 +123,7 @@ describe('Pad modal', function () { return editorIsDisabled; }; - var isModalOpened = function (modalSelector) { + const isModalOpened = (modalSelector) => { const $modal = helper.padChrome$(modalSelector); return $modal.hasClass('popup-show'); diff --git a/tests/frontend/specs/redo.js b/tests/frontend/specs/redo.js index 58d5b6c12..3e8d3a168 100644 --- a/tests/frontend/specs/redo.js +++ b/tests/frontend/specs/redo.js @@ -1,3 +1,5 @@ +'use strict'; + describe('undo button then redo button', function () { beforeEach(function (cb) { helper.newPad(cb); // creates a new pad @@ -33,7 +35,6 @@ describe('undo button then redo button', function () { it('redo some typing with keypress', function (done) { const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; // get the first text element inside the editable space const $firstTextElement = inner$('div span').first(); @@ -44,12 +45,12 @@ describe('undo button then redo button', function () { const modifiedValue = $firstTextElement.text(); // get the modified value expect(modifiedValue).not.to.be(originalValue); // expect the value to change - var e = inner$.Event(helper.evtType); + let e = inner$.Event(helper.evtType); e.ctrlKey = true; // Control key e.which = 90; // z inner$('#innerdocbody').trigger(e); - var e = inner$.Event(helper.evtType); + e = inner$.Event(helper.evtType); e.ctrlKey = true; // Control key e.which = 121; // y inner$('#innerdocbody').trigger(e); diff --git a/tests/frontend/specs/responsiveness.js b/tests/frontend/specs/responsiveness.js index 63803f641..ec63faa10 100644 --- a/tests/frontend/specs/responsiveness.js +++ b/tests/frontend/specs/responsiveness.js @@ -1,14 +1,19 @@ +'use strict'; + // Test for https://github.com/ether/etherpad-lite/issues/1763 // This test fails in Opera, IE and Safari -// Opera fails due to a weird way of handling the order of execution, yet actual performance seems fine +// Opera fails due to a weird way of handling the order of execution, +// yet actual performance seems fine // Safari fails due the delay being too great yet the actual performance seems fine // Firefox might panic that the script is taking too long so will fail // IE will fail due to running out of memory as it can't fit 2M chars in memory. -// Just FYI Google Docs crashes on large docs whilst trying to Save, it's likely the limitations we are +// Just FYI Google Docs crashes on large docs whilst trying to Save, +// it's likely the limitations we are // experiencing are more to do with browser limitations than improper implementation. -// A ueber fix for this would be to have a separate lower cpu priority thread that handles operations that aren't +// A ueber fix for this would be to have a separate lower cpu priority +// thread that handles operations that aren't // visible to the user. // Adapted from John McLear's original test case. @@ -20,16 +25,18 @@ xdescribe('Responsiveness of Editor', function () { this.timeout(6000); }); // JM commented out on 8th Sep 2020 for a release, after release this needs uncommenting - // And the test needs to be fixed to work in Firefox 52 on Windows 7. I am not sure why it fails on this specific platform - // The errors show this.timeout... then crash the browser but I am sure something is actually causing the stack trace and + // And the test needs to be fixed to work in Firefox 52 on Windows 7. + // I am not sure why it fails on this specific platform + // The errors show this.timeout... then crash the browser but + // I am sure something is actually causing the stack trace and // I just need to narrow down what, offers to help accepted. it('Fast response to keypress in pad with large amount of contents', function (done) { // skip on Windows Firefox 52.0 - if (window.bowser && window.bowser.windows && window.bowser.firefox && window.bowser.version == '52.0') { + if (window.bowser && + window.bowser.windows && window.bowser.firefox && window.bowser.version === '52.0') { this.skip(); } const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; const chars = '0000000000'; // row of placeholder chars const amount = 200000; // number of blocks of chars we will insert const length = (amount * (chars.length) + 1); // include a counter for each space @@ -39,7 +46,7 @@ xdescribe('Responsiveness of Editor', function () { // get keys to send const keyMultiplier = 10; // multiplier * 10 == total number of key events let keysToSend = ''; - for (var i = 0; i <= keyMultiplier; i++) { + for (let i = 0; i <= keyMultiplier; i++) { keysToSend += chars; } @@ -47,23 +54,23 @@ xdescribe('Responsiveness of Editor', function () { textElement.sendkeys('{selectall}'); // select all textElement.sendkeys('{del}'); // clear the pad text - for (var i = 0; i <= amount; i++) { + for (let i = 0; i <= amount; i++) { text = `${text + chars} `; // add the chars and space to the text contents } inner$('div').first().text(text); // Put the text contents into the pad - helper.waitFor(() => // Wait for the new contents to be on the pad - inner$('div').text().length > length - ).done(() => { - expect(inner$('div').text().length).to.be.greaterThan(length); // has the text changed? + // Wait for the new contents to be on the pad + helper.waitFor(() => inner$('div').text().length > length).done(() => { + // has the text changed? + expect(inner$('div').text().length).to.be.greaterThan(length); const start = Date.now(); // get the start time // send some new text to the screen (ensure all 3 key events are sent) const el = inner$('div').first(); for (let i = 0; i < keysToSend.length; ++i) { - var x = keysToSend.charCodeAt(i); + const x = keysToSend.charCodeAt(i); ['keyup', 'keypress', 'keydown'].forEach((type) => { - const e = $.Event(type); + const e = new $.Event(type); e.keyCode = x; el.trigger(e); }); diff --git a/tests/frontend/specs/select_formatting_buttons.js b/tests/frontend/specs/select_formatting_buttons.js index 52595a044..358d9e5b7 100644 --- a/tests/frontend/specs/select_formatting_buttons.js +++ b/tests/frontend/specs/select_formatting_buttons.js @@ -1,3 +1,5 @@ +'use strict'; + describe('select formatting buttons when selection has style applied', function () { const STYLES = ['italic', 'bold', 'underline', 'strikethrough']; const SHORTCUT_KEYS = ['I', 'B', 'U', '5']; // italic, bold, underline, strikethrough @@ -21,7 +23,7 @@ describe('select formatting buttons when selection has style applied', function return $formattingButton.parent().hasClass('selected'); }; - var selectLine = function (lineNumber, offsetStart, offsetEnd) { + const selectLine = function (lineNumber, offsetStart, offsetEnd) { const inner$ = helper.padInner$; const $line = inner$('div').eq(lineNumber); helper.selectLines($line, $line, offsetStart, offsetEnd); @@ -58,7 +60,7 @@ describe('select formatting buttons when selection has style applied', function applyStyleOnLineOnFullLineAndRemoveSelection(line, style, placeCaretOnLine, cb); }; - var applyStyleOnLineOnFullLineAndRemoveSelection = function (line, style, selectTarget, cb) { + const applyStyleOnLineOnFullLineAndRemoveSelection = function (line, style, selectTarget, cb) { // see if line html has changed const inner$ = helper.padInner$; const oldLineHTML = inner$.find('div')[line]; @@ -80,7 +82,6 @@ describe('select formatting buttons when selection has style applied', function const pressFormattingShortcutOnSelection = function (key) { const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; // get the first text element out of the inner iframe const $firstTextElement = inner$('div').first(); @@ -88,7 +89,7 @@ describe('select formatting buttons when selection has style applied', function // select this text element $firstTextElement.sendkeys('{selectall}'); - const e = inner$.Event(helper.evtType); + const e = new inner$.Event(helper.evtType); e.ctrlKey = true; // Control key e.which = key.charCodeAt(0); // I, U, B, 5 inner$('#innerdocbody').trigger(e); diff --git a/tests/frontend/specs/strikethrough.js b/tests/frontend/specs/strikethrough.js index d8feae3be..9731ec75c 100644 --- a/tests/frontend/specs/strikethrough.js +++ b/tests/frontend/specs/strikethrough.js @@ -1,3 +1,5 @@ +'use strict'; + describe('strikethrough button', function () { // create a new pad before each test run beforeEach(function (cb) { @@ -19,7 +21,7 @@ describe('strikethrough button', function () { const $strikethroughButton = chrome$('.buttonicon-strikethrough'); $strikethroughButton.click(); - // ace creates a new dom element when you press a button, so just get the first text element again + // ace creates a new dom element when you press a button, just get the first text element again const $newFirstTextElement = inner$('div').first(); // is there a element now? diff --git a/tests/frontend/specs/timeslider.js b/tests/frontend/specs/timeslider.js index bea7932df..10f94b3cb 100644 --- a/tests/frontend/specs/timeslider.js +++ b/tests/frontend/specs/timeslider.js @@ -1,3 +1,5 @@ +'use strict'; + // deactivated, we need a nice way to get the timeslider, this is ugly xdescribe('timeslider button takes you to the timeslider of a pad', function () { beforeEach(function (cb) { @@ -12,7 +14,6 @@ xdescribe('timeslider button takes you to the timeslider of a pad', function () // get the first text element inside the editable space const $firstTextElement = inner$('div span').first(); const originalValue = $firstTextElement.text(); // get the original value - const newValue = `Testing${originalValue}`; $firstTextElement.sendkeys('Testing'); // send line 1 to the pad const modifiedValue = $firstTextElement.text(); // get the modified value diff --git a/tests/frontend/specs/timeslider_labels.js b/tests/frontend/specs/timeslider_labels.js index c7a4aca5a..dd418d976 100644 --- a/tests/frontend/specs/timeslider_labels.js +++ b/tests/frontend/specs/timeslider_labels.js @@ -1,3 +1,5 @@ +'use strict'; + describe('timeslider', function () { // create a new pad before each test run beforeEach(function (cb) { @@ -7,7 +9,7 @@ describe('timeslider', function () { /** * @todo test authorsList */ - it("Shows a date and time in the timeslider and make sure it doesn't include NaN", async function () { + it("Shows a date/time in the timeslider and make sure it doesn't include NaN", async function () { // make some changes to produce 3 revisions const revs = 3; diff --git a/tests/frontend/specs/timeslider_numeric_padID.js b/tests/frontend/specs/timeslider_numeric_padID.js index 53eb4a29c..4d05f95b3 100644 --- a/tests/frontend/specs/timeslider_numeric_padID.js +++ b/tests/frontend/specs/timeslider_numeric_padID.js @@ -1,3 +1,5 @@ +'use strict'; + describe('timeslider', function () { const padId = 735773577357 + (Math.round(Math.random() * 1000)); diff --git a/tests/frontend/specs/timeslider_revisions.js b/tests/frontend/specs/timeslider_revisions.js index fbfbb3615..2f5cc170a 100644 --- a/tests/frontend/specs/timeslider_revisions.js +++ b/tests/frontend/specs/timeslider_revisions.js @@ -1,3 +1,5 @@ +'use strict'; + describe('timeslider', function () { // create a new pad before each test run beforeEach(function (cb) { @@ -23,7 +25,8 @@ describe('timeslider', function () { setTimeout(() => { // go to timeslider - $('#iframe-container iframe').attr('src', `${$('#iframe-container iframe').attr('src')}/timeslider`); + $('#iframe-container iframe').attr('src', + `${$('#iframe-container iframe').attr('src')}/timeslider`); setTimeout(() => { const timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; @@ -66,7 +69,6 @@ describe('timeslider', function () { // Disabled as jquery trigger no longer works properly xit('changes the url when clicking on the timeslider', function (done) { const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; // make some changes to produce 7 revisions const timePerRev = 1000; @@ -81,13 +83,13 @@ describe('timeslider', function () { setTimeout(() => { // go to timeslider - $('#iframe-container iframe').attr('src', `${$('#iframe-container iframe').attr('src')}/timeslider`); + $('#iframe-container iframe').attr('src', + `${$('#iframe-container iframe').attr('src')}/timeslider`); setTimeout(() => { const timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; const $sliderBar = timeslider$('#ui-slider-bar'); - const latestContents = timeslider$('#innerdocbody').text(); const oldUrl = $('#iframe-container iframe')[0].contentWindow.location.hash; // Click somewhere on the timeslider @@ -96,20 +98,23 @@ describe('timeslider', function () { e.clientY = e.pageY = 60; $sliderBar.trigger(e); - helper.waitFor(() => $('#iframe-container iframe')[0].contentWindow.location.hash != oldUrl, 6000).always(() => { - expect($('#iframe-container iframe')[0].contentWindow.location.hash).not.to.eql(oldUrl); - done(); - }); + helper.waitFor( + () => $('#iframe-container iframe')[0].contentWindow.location.hash !== oldUrl, 6000) + .always(() => { + expect( + $('#iframe-container iframe')[0].contentWindow.location.hash + ).not.to.eql(oldUrl); + done(); + }); }, 6000); }, revs * timePerRev); }); it('jumps to a revision given in the url', function (done) { const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; this.timeout(40000); // wait for the text to be loaded - helper.waitFor(() => inner$('body').text().length != 0, 10000).always(() => { + helper.waitFor(() => inner$('body').text().length !== 0, 10000).always(() => { const newLines = inner$('body div').length; const oldLength = inner$('body').text().length + newLines / 2; expect(oldLength).to.not.eql(0); @@ -120,22 +125,25 @@ describe('timeslider', function () { helper.waitFor(() => { // newLines takes the new lines into account which are strippen when using // inner$('body').text(), one
                    is used for one line in ACE. - const lenOkay = inner$('body').text().length + newLines / 2 != oldLength; + const lenOkay = inner$('body').text().length + newLines / 2 !== oldLength; // this waits for the color to be added to our , which means that the revision // was accepted by the server. - const colorOkay = inner$('span').first().attr('class').indexOf('author-') == 0; + const colorOkay = inner$('span').first().attr('class').indexOf('author-') === 0; return lenOkay && colorOkay; }, 10000).always(() => { // go to timeslider with a specific revision set - $('#iframe-container iframe').attr('src', `${$('#iframe-container iframe').attr('src')}/timeslider#0`); + $('#iframe-container iframe').attr('src', + `${$('#iframe-container iframe').attr('src')}/timeslider#0`); // wait for the timeslider to be loaded helper.waitFor(() => { try { timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; - } catch (e) {} + } catch (e) { + // Empty catch block <3 + } if (timeslider$) { - return timeslider$('#innerdocbody').text().length == oldLength; + return timeslider$('#innerdocbody').text().length === oldLength; } }, 10000).always(() => { expect(timeslider$('#innerdocbody').text().length).to.eql(oldLength); @@ -147,24 +155,26 @@ describe('timeslider', function () { it('checks the export url', function (done) { const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; this.timeout(11000); inner$('div').first().sendkeys('a'); setTimeout(() => { // go to timeslider - $('#iframe-container iframe').attr('src', `${$('#iframe-container iframe').attr('src')}/timeslider#0`); + $('#iframe-container iframe').attr('src', + `${$('#iframe-container iframe').attr('src')}/timeslider#0`); let timeslider$; let exportLink; helper.waitFor(() => { try { timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; - } catch (e) {} + } catch (e) { + // Empty catch block <3 + } if (!timeslider$) return false; exportLink = timeslider$('#exportplaina').attr('href'); if (!exportLink) return false; - return exportLink.substr(exportLink.length - 12) == '0/export/txt'; + return exportLink.substr(exportLink.length - 12) === '0/export/txt'; }, 6000).always(() => { expect(exportLink.substr(exportLink.length - 12)).to.eql('0/export/txt'); done(); diff --git a/tests/frontend/specs/undo.js b/tests/frontend/specs/undo.js index 0c94f2230..0daa282fc 100644 --- a/tests/frontend/specs/undo.js +++ b/tests/frontend/specs/undo.js @@ -1,3 +1,5 @@ +'use strict'; + describe('undo button', function () { beforeEach(function (cb) { helper.newPad(cb); // creates a new pad @@ -30,7 +32,6 @@ describe('undo button', function () { it('undo some typing using a keypress', function (done) { const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; // get the first text element inside the editable space const $firstTextElement = inner$('div span').first(); @@ -40,7 +41,7 @@ describe('undo button', function () { const modifiedValue = $firstTextElement.text(); // get the modified value expect(modifiedValue).not.to.be(originalValue); // expect the value to change - const e = inner$.Event(helper.evtType); + const e = new inner$.Event(helper.evtType); e.ctrlKey = true; // Control key e.which = 90; // z inner$('#innerdocbody').trigger(e); diff --git a/tests/frontend/specs/unordered_list.js b/tests/frontend/specs/unordered_list.js index 4cbdabfac..22d1a6fe2 100644 --- a/tests/frontend/specs/unordered_list.js +++ b/tests/frontend/specs/unordered_list.js @@ -1,3 +1,5 @@ +'use strict'; + describe('assign unordered list', function () { // create a new pad before each test run beforeEach(function (cb) { @@ -130,7 +132,8 @@ describe('Pressing Tab in an UL increases and decreases indentation', function ( }); }); -describe('Pressing indent/outdent button in an UL increases and decreases indentation and bullet / ol formatting', function () { +describe('Pressing indent/outdent button in an UL increases and decreases indentation ' + + 'and bullet / ol formatting', function () { // create a new pad before each test run beforeEach(function (cb) { helper.newPad(cb); diff --git a/tests/frontend/specs/xxauto_reconnect.js b/tests/frontend/specs/xxauto_reconnect.js index 574616ce5..d92936563 100644 --- a/tests/frontend/specs/xxauto_reconnect.js +++ b/tests/frontend/specs/xxauto_reconnect.js @@ -1,3 +1,5 @@ +'use strict'; + describe('Automatic pad reload on Force Reconnect message', function () { let padId, $originalPadFrame; diff --git a/tests/frontend/travis/remote_runner.js b/tests/frontend/travis/remote_runner.js index 70c850ca8..ba0e7be0c 100644 --- a/tests/frontend/travis/remote_runner.js +++ b/tests/frontend/travis/remote_runner.js @@ -1,17 +1,19 @@ -var srcFolder = '../../../src/node_modules/'; -var wd = require(`${srcFolder}wd`); -var async = require(`${srcFolder}async`); +'use strict'; -var config = { +const wd = require('ep_etherpad-lite/node_modules/wd'); +const async = require('ep_etherpad-lite/node_modules/async'); + +const config = { host: 'ondemand.saucelabs.com', port: 80, username: process.env.SAUCE_USER, accessKey: process.env.SAUCE_ACCESS_KEY, }; -var allTestsPassed = true; +let allTestsPassed = true; // overwrite the default exit code -// in case not all worker can be run (due to saucelabs limits), `queue.drain` below will not be called +// in case not all worker can be run (due to saucelabs limits), +// `queue.drain` below will not be called // and the script would silently exit with error code 0 process.exitCode = 2; process.on('exit', (code) => { @@ -20,13 +22,18 @@ process.on('exit', (code) => { } }); -var sauceTestWorker = async.queue((testSettings, callback) => { - const browser = wd.promiseChainRemote(config.host, config.port, config.username, config.accessKey); - const name = `${process.env.GIT_HASH} - ${testSettings.browserName} ${testSettings.version}, ${testSettings.platform}`; +const sauceTestWorker = async.queue((testSettings, callback) => { + const browser = wd.promiseChainRemote( + config.host, config.port, config.username, config.accessKey); + const name = + `${process.env.GIT_HASH} - ${testSettings.browserName} ` + + `${testSettings.version}, ${testSettings.platform}`; testSettings.name = name; testSettings.public = true; testSettings.build = process.env.GIT_HASH; - testSettings.extendedDebugging = true; // console.json can be downloaded via saucelabs, don't know how to print them into output of the tests + // console.json can be downloaded via saucelabs, + // don't know how to print them into output of the tests + testSettings.extendedDebugging = true; testSettings.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER; browser.init(testSettings).get('http://localhost:9001/tests/frontend/', () => { @@ -34,7 +41,7 @@ var sauceTestWorker = async.queue((testSettings, callback) => { console.log(`Remote sauce test '${name}' started! ${url}`); // tear down the test excecution - const stopSauce = function (success, timesup) { + const stopSauce = (success, timesup) => { clearInterval(getStatusInterval); clearTimeout(timeout); @@ -43,12 +50,15 @@ var sauceTestWorker = async.queue((testSettings, callback) => { allTestsPassed = false; } - // if stopSauce is called via timeout (in contrast to via getStatusInterval) than the log of up to the last + // if stopSauce is called via timeout + // (in contrast to via getStatusInterval) than the log of up to the last // five seconds may not be available here. It's an error anyway, so don't care about it. printLog(logIndex); if (timesup) { - console.log(`[${testSettings.browserName} ${testSettings.platform}${testSettings.version === '' ? '' : (` ${testSettings.version}`)}] \x1B[31mFAILED\x1B[39m allowed test duration exceeded`); + console.log(`[${testSettings.browserName} ${testSettings.platform}` + + `${testSettings.version === '' ? '' : (` ${testSettings.version}`)}]` + + ' \x1B[31mFAILED\x1B[39m allowed test duration exceeded'); } console.log(`Remote sauce test '${name}' finished! ${url}`); @@ -58,17 +68,19 @@ var sauceTestWorker = async.queue((testSettings, callback) => { /** * timeout if a test hangs or the job exceeds 14.5 minutes - * It's necessary because if travis kills the saucelabs session due to inactivity, we don't get any output - * @todo this should be configured in testSettings, see https://wiki.saucelabs.com/display/DOCS/Test+Configuration+Options#TestConfigurationOptions-Timeouts + * It's necessary because if travis kills the saucelabs session due to inactivity, + * we don't get any output + * @todo this should be configured in testSettings, see + * https://wiki.saucelabs.com/display/DOCS/Test+Configuration+Options#TestConfigurationOptions-Timeouts */ - var timeout = setTimeout(() => { + const timeout = setTimeout(() => { stopSauce(false, true); }, 870000); // travis timeout is 15 minutes, set this to a slightly lower value let knownConsoleText = ''; // how many characters of the log have been sent to travis let logIndex = 0; - var getStatusInterval = setInterval(() => { + const getStatusInterval = setInterval(() => { browser.eval("$('#console').text()", (err, consoleText) => { if (!consoleText || err) { return; @@ -76,9 +88,10 @@ var sauceTestWorker = async.queue((testSettings, callback) => { knownConsoleText = consoleText; if (knownConsoleText.indexOf('FINISHED') > 0) { - const match = knownConsoleText.match(/FINISHED.*([0-9]+) tests passed, ([0-9]+) tests failed/); + const match = knownConsoleText.match( + /FINISHED.*([0-9]+) tests passed, ([0-9]+) tests failed/); // finished without failures - if (match[2] && match[2] == '0') { + if (match[2] && match[2] === '0') { stopSauce(true); // finished but some tests did not return or some tests failed @@ -99,13 +112,17 @@ var sauceTestWorker = async.queue((testSettings, callback) => { * * @param {number} index offset from where to start */ - function printLog(index) { - let testResult = knownConsoleText.substring(index).replace(/\[red\]/g, '\x1B[31m').replace(/\[yellow\]/g, '\x1B[33m') + const printLog = (index) => { + let testResult = knownConsoleText.substring(index) + .replace(/\[red\]/g, '\x1B[31m').replace(/\[yellow\]/g, '\x1B[33m') .replace(/\[green\]/g, '\x1B[32m').replace(/\[clear\]/g, '\x1B[39m'); - testResult = testResult.split('\\n').map((line) => `[${testSettings.browserName} ${testSettings.platform}${testSettings.version === '' ? '' : (` ${testSettings.version}`)}] ${line}`).join('\n'); + testResult = testResult.split('\\n').map((line) => `[${testSettings.browserName} ` + + `${testSettings.platform}` + + `${testSettings.version === '' ? '' : (` ${testSettings.version}`)}]` + + `${line}`).join('\n'); console.log(testResult); - } + }; }); }, 6); // run 6 tests in parrallel diff --git a/tests/ratelimit/send_changesets.js b/tests/ratelimit/send_changesets.js index b0d994c8c..92af23e18 100644 --- a/tests/ratelimit/send_changesets.js +++ b/tests/ratelimit/send_changesets.js @@ -1,8 +1,12 @@ +'use strict'; + +let etherpad; try { - var etherpad = require('../../src/node_modules/etherpad-cli-client'); + etherpad = require('ep_etherpad-lite/node_modules/etherpad-cli-client'); // ugly } catch { - var etherpad = require('etherpad-cli-client'); + /* eslint-disable-next-line node/no-missing-require */ + etherpad = require('etherpad-cli-client'); // uses global } const pad = etherpad.connect(process.argv[2]); pad.on('connected', () => { @@ -18,7 +22,7 @@ pad.on('connected', () => { }); // in case of disconnect exit code 1 pad.on('message', (message) => { - if (message.disconnect == 'rateLimited') { + if (message.disconnect === 'rateLimited') { process.exit(1); } }); From 3e910b990568eb7afe33e75511b0771c5a161340 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 1 Feb 2021 22:45:51 +0000 Subject: [PATCH 200/357] stale: remove convert.js as no one runs old Etherpad --- bin/convert.js | 391 ------------------------------------------------- 1 file changed, 391 deletions(-) delete mode 100644 bin/convert.js diff --git a/bin/convert.js b/bin/convert.js deleted file mode 100644 index 47f8b2d27..000000000 --- a/bin/convert.js +++ /dev/null @@ -1,391 +0,0 @@ -const startTime = Date.now(); -const fs = require('fs'); -const ueberDB = require('../src/node_modules/ueberdb2'); -const mysql = require('../src/node_modules/ueberdb2/node_modules/mysql'); -const async = require('../src/node_modules/async'); -const Changeset = require('ep_etherpad-lite/static/js/Changeset'); -const randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString; -const AttributePool = require('ep_etherpad-lite/static/js/AttributePool'); - -const settingsFile = process.argv[2]; -const sqlOutputFile = process.argv[3]; - -// stop if the settings file is not set -if (!settingsFile || !sqlOutputFile) { - console.error('Use: node convert.js $SETTINGSFILE $SQLOUTPUT'); - process.exit(1); -} - -log('read settings file...'); -// read the settings file and parse the json -const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8')); -log('done'); - -log('open output file...'); -const sqlOutput = fs.openSync(sqlOutputFile, 'w'); -const sql = 'SET CHARACTER SET UTF8;\n' + - 'CREATE TABLE IF NOT EXISTS `store` ( \n' + - '`key` VARCHAR( 100 ) NOT NULL , \n' + - '`value` LONGTEXT NOT NULL , \n' + - 'PRIMARY KEY ( `key` ) \n' + - ') ENGINE = INNODB;\n' + - 'START TRANSACTION;\n\n'; -fs.writeSync(sqlOutput, sql); -log('done'); - -const etherpadDB = mysql.createConnection({ - host: settings.etherpadDB.host, - user: settings.etherpadDB.user, - password: settings.etherpadDB.password, - database: settings.etherpadDB.database, - port: settings.etherpadDB.port, -}); - -// get the timestamp once -const timestamp = Date.now(); - -let padIDs; - -async.series([ - // get all padids out of the database... - function (callback) { - log('get all padIds out of the database...'); - - etherpadDB.query('SELECT ID FROM PAD_META', [], (err, _padIDs) => { - padIDs = _padIDs; - callback(err); - }); - }, - function (callback) { - log('done'); - - // create a queue with a concurrency 100 - const queue = async.queue((padId, callback) => { - convertPad(padId, (err) => { - incrementPadStats(); - callback(err); - }); - }, 100); - - // set the step callback as the queue callback - queue.drain = callback; - - // add the padids to the worker queue - for (let i = 0, length = padIDs.length; i < length; i++) { - queue.push(padIDs[i].ID); - } - }, -], (err) => { - if (err) throw err; - - // write the groups - let sql = ''; - for (const proID in proID2groupID) { - const groupID = proID2groupID[proID]; - const subdomain = proID2subdomain[proID]; - - sql += `REPLACE INTO store VALUES (${etherpadDB.escape(`group:${groupID}`)}, ${etherpadDB.escape(JSON.stringify(groups[groupID]))});\n`; - sql += `REPLACE INTO store VALUES (${etherpadDB.escape(`mapper2group:subdomain:${subdomain}`)}, ${etherpadDB.escape(groupID)});\n`; - } - - // close transaction - sql += 'COMMIT;'; - - // end the sql file - fs.writeSync(sqlOutput, sql, undefined, 'utf-8'); - fs.closeSync(sqlOutput); - - log('finished.'); - process.exit(0); -}); - -function log(str) { - console.log(`${(Date.now() - startTime) / 1000}\t${str}`); -} - -let padsDone = 0; - -function incrementPadStats() { - padsDone++; - - if (padsDone % 100 == 0) { - const averageTime = Math.round(padsDone / ((Date.now() - startTime) / 1000)); - log(`${padsDone}/${padIDs.length}\t${averageTime} pad/s`); - } -} - -var proID2groupID = {}; -var proID2subdomain = {}; -var groups = {}; - -function convertPad(padId, callback) { - const changesets = []; - const changesetsMeta = []; - const chatMessages = []; - const authors = []; - let apool; - let subdomain; - let padmeta; - - async.series([ - // get all needed db values - function (callback) { - async.parallel([ - // get the pad revisions - function (callback) { - const sql = 'SELECT * FROM `PAD_CHAT_TEXT` WHERE NUMID = (SELECT `NUMID` FROM `PAD_CHAT_META` WHERE ID=?)'; - - etherpadDB.query(sql, [padId], (err, results) => { - if (!err) { - try { - // parse the pages - for (let i = 0, length = results.length; i < length; i++) { - parsePage(chatMessages, results[i].PAGESTART, results[i].OFFSETS, results[i].DATA, true); - } - } catch (e) { err = e; } - } - - callback(err); - }); - }, - // get the chat entries - function (callback) { - const sql = 'SELECT * FROM `PAD_REVS_TEXT` WHERE NUMID = (SELECT `NUMID` FROM `PAD_REVS_META` WHERE ID=?)'; - - etherpadDB.query(sql, [padId], (err, results) => { - if (!err) { - try { - // parse the pages - for (let i = 0, length = results.length; i < length; i++) { - parsePage(changesets, results[i].PAGESTART, results[i].OFFSETS, results[i].DATA, false); - } - } catch (e) { err = e; } - } - - callback(err); - }); - }, - // get the pad revisions meta data - function (callback) { - const sql = 'SELECT * FROM `PAD_REVMETA_TEXT` WHERE NUMID = (SELECT `NUMID` FROM `PAD_REVMETA_META` WHERE ID=?)'; - - etherpadDB.query(sql, [padId], (err, results) => { - if (!err) { - try { - // parse the pages - for (let i = 0, length = results.length; i < length; i++) { - parsePage(changesetsMeta, results[i].PAGESTART, results[i].OFFSETS, results[i].DATA, true); - } - } catch (e) { err = e; } - } - - callback(err); - }); - }, - // get the attribute pool of this pad - function (callback) { - const sql = 'SELECT `JSON` FROM `PAD_APOOL` WHERE `ID` = ?'; - - etherpadDB.query(sql, [padId], (err, results) => { - if (!err) { - try { - apool = JSON.parse(results[0].JSON).x; - } catch (e) { err = e; } - } - - callback(err); - }); - }, - // get the authors informations - function (callback) { - const sql = 'SELECT * FROM `PAD_AUTHORS_TEXT` WHERE NUMID = (SELECT `NUMID` FROM `PAD_AUTHORS_META` WHERE ID=?)'; - - etherpadDB.query(sql, [padId], (err, results) => { - if (!err) { - try { - // parse the pages - for (let i = 0, length = results.length; i < length; i++) { - parsePage(authors, results[i].PAGESTART, results[i].OFFSETS, results[i].DATA, true); - } - } catch (e) { err = e; } - } - - callback(err); - }); - }, - // get the pad information - function (callback) { - const sql = 'SELECT JSON FROM `PAD_META` WHERE ID=?'; - - etherpadDB.query(sql, [padId], (err, results) => { - if (!err) { - try { - padmeta = JSON.parse(results[0].JSON).x; - } catch (e) { err = e; } - } - - callback(err); - }); - }, - // get the subdomain - function (callback) { - // skip if this is no proPad - if (padId.indexOf('$') == -1) { - callback(); - return; - } - - // get the proID out of this padID - const proID = padId.split('$')[0]; - - const sql = 'SELECT subDomain FROM pro_domains WHERE ID = ?'; - - etherpadDB.query(sql, [proID], (err, results) => { - if (!err) { - subdomain = results[0].subDomain; - } - - callback(err); - }); - }, - ], callback); - }, - function (callback) { - // saves all values that should be written to the database - const values = {}; - - // this is a pro pad, let's convert it to a group pad - if (padId.indexOf('$') != -1) { - const padIdParts = padId.split('$'); - const proID = padIdParts[0]; - const padName = padIdParts[1]; - - let groupID; - - // this proID is not converted so far, do it - if (proID2groupID[proID] == null) { - groupID = `g.${randomString(16)}`; - - // create the mappers for this new group - proID2groupID[proID] = groupID; - proID2subdomain[proID] = subdomain; - groups[groupID] = {pads: {}}; - } - - // use the generated groupID; - groupID = proID2groupID[proID]; - - // rename the pad - padId = `${groupID}$${padName}`; - - // set the value for this pad in the group - groups[groupID].pads[padId] = 1; - } - - try { - const newAuthorIDs = {}; - const oldName2newName = {}; - - // replace the authors with generated authors - // we need to do that cause where the original etherpad saves pad local authors, the new (lite) etherpad uses them global - for (var i in apool.numToAttrib) { - var key = apool.numToAttrib[i][0]; - const value = apool.numToAttrib[i][1]; - - // skip non authors and anonymous authors - if (key != 'author' || value == '') continue; - - // generate new author values - const authorID = `a.${randomString(16)}`; - const authorColorID = authors[i].colorId || Math.floor(Math.random() * (exports.getColorPalette().length)); - const authorName = authors[i].name || null; - - // overwrite the authorID of the attribute pool - apool.numToAttrib[i][1] = authorID; - - // write the author to the database - values[`globalAuthor:${authorID}`] = {colorId: authorColorID, name: authorName, timestamp}; - - // save in mappers - newAuthorIDs[i] = authorID; - oldName2newName[value] = authorID; - } - - // save all revisions - for (var i = 0; i < changesets.length; i++) { - values[`pad:${padId}:revs:${i}`] = {changeset: changesets[i], - meta: { - author: newAuthorIDs[changesetsMeta[i].a], - timestamp: changesetsMeta[i].t, - atext: changesetsMeta[i].atext || undefined, - }}; - } - - // save all chat messages - for (var i = 0; i < chatMessages.length; i++) { - values[`pad:${padId}:chat:${i}`] = {text: chatMessages[i].lineText, - userId: oldName2newName[chatMessages[i].userId], - time: chatMessages[i].time}; - } - - // generate the latest atext - const fullAPool = (new AttributePool()).fromJsonable(apool); - const keyRev = Math.floor(padmeta.head / padmeta.keyRevInterval) * padmeta.keyRevInterval; - let atext = changesetsMeta[keyRev].atext; - let curRev = keyRev; - while (curRev < padmeta.head) { - curRev++; - const changeset = changesets[curRev]; - atext = Changeset.applyToAText(changeset, atext, fullAPool); - } - - values[`pad:${padId}`] = {atext, - pool: apool, - head: padmeta.head, - chatHead: padmeta.numChatMessages}; - } catch (e) { - console.error(`Error while converting pad ${padId}, pad skipped`); - console.error(e.stack ? e.stack : JSON.stringify(e)); - callback(); - return; - } - - let sql = ''; - for (var key in values) { - sql += `REPLACE INTO store VALUES (${etherpadDB.escape(key)}, ${etherpadDB.escape(JSON.stringify(values[key]))});\n`; - } - - fs.writeSync(sqlOutput, sql, undefined, 'utf-8'); - callback(); - }, - ], callback); -} - -/** - * This parses a Page like Etherpad uses them in the databases - * The offsets describes the length of a unit in the page, the data are - * all values behind each other - */ -function parsePage(array, pageStart, offsets, data, json) { - let start = 0; - const lengths = offsets.split(','); - - for (let i = 0; i < lengths.length; i++) { - let unitLength = lengths[i]; - - // skip empty units - if (unitLength == '') continue; - - // parse the number - unitLength = Number(unitLength); - - // cut the unit out of data - const unit = data.substr(start, unitLength); - - // put it into the array - array[pageStart + i] = json ? JSON.parse(unit) : unit; - - // update start - start += unitLength; - } -} From 1bc52f49134ffffadde61264a6f2d3cbb001af37 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 30 Jan 2021 20:29:49 -0500 Subject: [PATCH 201/357] hooks: Remove unnecessary `callAllStr()` function --- src/static/js/linestylefilter.js | 7 ++----- src/static/js/pluginfw/hooks.js | 12 ------------ 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/src/static/js/linestylefilter.js b/src/static/js/linestylefilter.js index f70eefc23..254168990 100644 --- a/src/static/js/linestylefilter.js +++ b/src/static/js/linestylefilter.js @@ -93,11 +93,8 @@ linestylefilter.getLineStyleFilter = (lineLength, aline, textAndClassFunc, apool } else if (linestylefilter.ATTRIB_CLASSES[key]) { classes += ` ${linestylefilter.ATTRIB_CLASSES[key]}`; } else { - classes += hooks.callAllStr('aceAttribsToClasses', { - linestylefilter, - key, - value, - }, ' ', ' ', ''); + const results = hooks.callAll('aceAttribsToClasses', {linestylefilter, key, value}); + classes += ` ${results.join(' ')}`; } } } diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index e61079eb6..e0b733e5d 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -386,18 +386,6 @@ exports.aCallFirst = (hook_name, args, cb, predicate) => { } }; -exports.callAllStr = (hook_name, args, sep, pre, post) => { - if (sep === undefined) sep = ''; - if (pre === undefined) pre = ''; - if (post === undefined) post = ''; - const newCallhooks = []; - const callhooks = exports.callAll(hook_name, args); - for (let i = 0, ii = callhooks.length; i < ii; i++) { - newCallhooks[i] = pre + callhooks[i] + post; - } - return newCallhooks.join(sep || ''); -}; - exports.exportedForTestingOnly = { callHookFnAsync, callHookFnSync, From 47f0a7dacfa811c4c69605c207f4e353c96b1a0a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 31 Jan 2021 15:23:44 -0500 Subject: [PATCH 202/357] lint: Fix more ESLint errors --- src/static/js/pluginfw/hooks.js | 38 +++++++++++++++++--------------- tests/backend/specs/hooks.js | 17 +++++--------- tests/backend/specs/webaccess.js | 10 ++++----- 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index e0b733e5d..7ce71ae7b 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -1,6 +1,5 @@ 'use strict'; -const _ = require('underscore'); const pluginDefs = require('./plugin_defs'); // Maps the name of a server-side hook to a string explaining the deprecation @@ -24,9 +23,12 @@ const checkDeprecation = (hook) => { deprecationWarned[hook.hook_fn_name] = true; }; +// Flattens the array one level. +const flatten1 = (array) => array.reduce((a, b) => a.concat(b), []); + exports.bubbleExceptions = true; -const hookCallWrapper = (hook, hook_name, args, cb) => { +const hookCallWrapper = (hook, hookName, args, cb) => { if (cb === undefined) cb = (x) => x; checkDeprecation(hook); @@ -36,7 +38,7 @@ const hookCallWrapper = (hook, hook_name, args, cb) => { if (x === undefined) return []; return x; }; - const normalizedhook = () => normalize(hook.hook_fn(hook_name, args, (x) => cb(normalize(x)))); + const normalizedhook = () => normalize(hook.hook_fn(hookName, args, (x) => cb(normalize(x)))); if (exports.bubbleExceptions) { return normalizedhook(); @@ -44,7 +46,7 @@ const hookCallWrapper = (hook, hook_name, args, cb) => { try { return normalizedhook(); } catch (ex) { - console.error([hook_name, hook.part.full_name, ex.stack || ex]); + console.error([hookName, hook.part.full_name, ex.stack || ex]); } } }; @@ -204,12 +206,12 @@ const callHookFnSync = (hook, context) => { exports.callAll = (hookName, context) => { if (context == null) context = {}; const hooks = pluginDefs.hooks[hookName] || []; - return _.flatten(hooks.map((hook) => { + return flatten1(hooks.map((hook) => { const ret = callHookFnSync(hook, context); // `undefined` (but not `null`!) is treated the same as []. if (ret === undefined) return []; return ret; - }), 1); + })); }; // Calls the hook function asynchronously and returns a Promise that either resolves to the hook @@ -349,26 +351,26 @@ exports.aCallAll = async (hookName, context, cb) => { let resultsPromise = Promise.all(hooks.map((hook) => callHookFnAsync(hook, context) // `undefined` (but not `null`!) is treated the same as []. .then((result) => (result === undefined) ? [] : result))) - .then((results) => _.flatten(results, 1)); + .then(flatten1); if (cb != null) resultsPromise = resultsPromise.then((val) => cb(null, val), cb); return await resultsPromise; }; -exports.callFirst = (hook_name, args) => { +exports.callFirst = (hookName, args) => { if (!args) args = {}; - if (pluginDefs.hooks[hook_name] === undefined) return []; - return exports.syncMapFirst(pluginDefs.hooks[hook_name], - (hook) => hookCallWrapper(hook, hook_name, args)); + if (pluginDefs.hooks[hookName] === undefined) return []; + return exports.syncMapFirst(pluginDefs.hooks[hookName], + (hook) => hookCallWrapper(hook, hookName, args)); }; -const aCallFirst = (hook_name, args, cb, predicate) => { +const aCallFirst = (hookName, args, cb, predicate) => { if (!args) args = {}; if (!cb) cb = () => {}; - if (pluginDefs.hooks[hook_name] === undefined) return cb(null, []); + if (pluginDefs.hooks[hookName] === undefined) return cb(null, []); exports.mapFirst( - pluginDefs.hooks[hook_name], + pluginDefs.hooks[hookName], (hook, cb) => { - hookCallWrapper(hook, hook_name, args, (res) => { cb(null, res); }); + hookCallWrapper(hook, hookName, args, (res) => { cb(null, res); }); }, cb, predicate @@ -376,13 +378,13 @@ const aCallFirst = (hook_name, args, cb, predicate) => { }; /* return a Promise if cb is not supplied */ -exports.aCallFirst = (hook_name, args, cb, predicate) => { +exports.aCallFirst = (hookName, args, cb, predicate) => { if (cb === undefined) { return new Promise((resolve, reject) => { - aCallFirst(hook_name, args, (err, res) => err ? reject(err) : resolve(res), predicate); + aCallFirst(hookName, args, (err, res) => err ? reject(err) : resolve(res), predicate); }); } else { - return aCallFirst(hook_name, args, cb, predicate); + return aCallFirst(hookName, args, cb, predicate); } }; diff --git a/tests/backend/specs/hooks.js b/tests/backend/specs/hooks.js index 0e0f8075f..327b9bd1c 100644 --- a/tests/backend/specs/hooks.js +++ b/tests/backend/specs/hooks.js @@ -1,14 +1,9 @@ -/* global __dirname, __filename, afterEach, beforeEach, describe, it, process, require */ - -function m(mod) { return `${__dirname}/../../../src/${mod}`; } +'use strict'; const assert = require('assert').strict; -const common = require('../common'); -const hooks = require(m('static/js/pluginfw/hooks')); -const plugins = require(m('static/js/pluginfw/plugin_defs')); -const sinon = require(m('node_modules/sinon')); - -const logger = common.logger; +const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); +const plugins = require('ep_etherpad-lite/static/js/pluginfw/plugin_defs'); +const sinon = require('ep_etherpad-lite/node_modules/sinon'); describe(__filename, function () { const hookName = 'testHook'; @@ -203,7 +198,7 @@ describe(__filename, function () { // Test various ways a hook might attempt to settle twice. (Examples: call the callback a second // time, or call the callback and then return a value.) describe('bad hook function behavior (double settle)', function () { - beforeEach(function () { + beforeEach(async function () { sinon.stub(console, 'error'); }); @@ -558,7 +553,7 @@ describe(__filename, function () { // Test various ways a hook might attempt to settle twice. (Examples: call the callback a second // time, or call the callback and then return a value.) describe('bad hook function behavior (double settle)', function () { - beforeEach(function () { + beforeEach(async function () { sinon.stub(console, 'error'); }); diff --git a/tests/backend/specs/webaccess.js b/tests/backend/specs/webaccess.js index a21cc73a8..3b140c46c 100644 --- a/tests/backend/specs/webaccess.js +++ b/tests/backend/specs/webaccess.js @@ -1,11 +1,9 @@ -/* global __dirname, __filename, Buffer, afterEach, before, beforeEach, describe, it, require */ - -function m(mod) { return `${__dirname}/../../../src/${mod}`; } +'use strict'; const assert = require('assert').strict; const common = require('../common'); -const plugins = require(m('static/js/pluginfw/plugin_defs')); -const settings = require(m('node/utils/Settings')); +const plugins = require('ep_etherpad-lite/static/js/pluginfw/plugin_defs'); +const settings = require('ep_etherpad-lite/node/utils/Settings'); describe(__filename, function () { let agent; @@ -402,7 +400,7 @@ describe(__filename, function () { }; const handlers = {}; - beforeEach(function () { + beforeEach(async function () { failHookNames.forEach((hookName) => { const handler = new Handler(hookName); handlers[hookName] = handler; From ba02e700201f4d94c233c1b2211414ee978cdfa5 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 1 Feb 2021 23:55:16 -0500 Subject: [PATCH 203/357] tests: Make the fake webaccess hook registrations look more real The additional properties will be needed once `aCallAll()` is upgraded to use `callHookFnAsync()`. --- tests/backend/specs/webaccess.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/backend/specs/webaccess.js b/tests/backend/specs/webaccess.js index 3b140c46c..b82a5f017 100644 --- a/tests/backend/specs/webaccess.js +++ b/tests/backend/specs/webaccess.js @@ -10,6 +10,13 @@ describe(__filename, function () { const backups = {}; const authHookNames = ['preAuthorize', 'authenticate', 'authorize']; const failHookNames = ['preAuthzFailure', 'authnFailure', 'authzFailure', 'authFailure']; + const makeHook = (hookName, hookFn) => ({ + hook_fn: hookFn, + hook_fn_name: `fake_plugin/${hookName}`, + hook_name: hookName, + part: {plugin: 'fake_plugin'}, + }); + before(async function () { agent = await common.init(); }); beforeEach(async function () { backups.hooks = {}; @@ -139,7 +146,10 @@ describe(__filename, function () { const h0 = new Handler(hookName, '_0'); const h1 = new Handler(hookName, '_1'); handlers[hookName] = [h0, h1]; - plugins.hooks[hookName] = [{hook_fn: h0.handle.bind(h0)}, {hook_fn: h1.handle.bind(h1)}]; + plugins.hooks[hookName] = [ + makeHook(hookName, h0.handle.bind(h0)), + makeHook(hookName, h1.handle.bind(h1)), + ]; } }); @@ -195,7 +205,7 @@ describe(__filename, function () { it('runs preAuthzFailure hook when access is denied', async function () { handlers.preAuthorize[0].innerHandle = () => [false]; let called = false; - plugins.hooks.preAuthzFailure = [{hook_fn: (hookName, {req, res}, cb) => { + plugins.hooks.preAuthzFailure = [makeHook('preAuthzFailure', (hookName, {req, res}, cb) => { assert.equal(hookName, 'preAuthzFailure'); assert(req != null); assert(res != null); @@ -203,7 +213,7 @@ describe(__filename, function () { called = true; res.status(200).send('injected'); return cb([true]); - }}]; + })]; await agent.get('/admin/').auth('admin', 'admin-password').expect(200, 'injected'); assert(called); }); @@ -404,7 +414,7 @@ describe(__filename, function () { failHookNames.forEach((hookName) => { const handler = new Handler(hookName); handlers[hookName] = handler; - plugins.hooks[hookName] = [{hook_fn: handler.handle.bind(handler)}]; + plugins.hooks[hookName] = [makeHook(hookName, handler.handle.bind(handler))]; }); settings.requireAuthentication = true; settings.requireAuthorization = true; From 6b42dabf6c3e3e72dd4c5571c20cd2b418fba2c8 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 31 Jan 2021 15:28:06 -0500 Subject: [PATCH 204/357] hooks: Delete unused `bubbleExceptions` setting --- src/static/js/pluginfw/hooks.js | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 7ce71ae7b..489e6d6dc 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -26,8 +26,6 @@ const checkDeprecation = (hook) => { // Flattens the array one level. const flatten1 = (array) => array.reduce((a, b) => a.concat(b), []); -exports.bubbleExceptions = true; - const hookCallWrapper = (hook, hookName, args, cb) => { if (cb === undefined) cb = (x) => x; @@ -38,17 +36,7 @@ const hookCallWrapper = (hook, hookName, args, cb) => { if (x === undefined) return []; return x; }; - const normalizedhook = () => normalize(hook.hook_fn(hookName, args, (x) => cb(normalize(x)))); - - if (exports.bubbleExceptions) { - return normalizedhook(); - } else { - try { - return normalizedhook(); - } catch (ex) { - console.error([hookName, hook.part.full_name, ex.stack || ex]); - } - } + return () => normalize(hook.hook_fn(hookName, args, (x) => cb(normalize(x)))); }; exports.syncMapFirst = (lst, fn) => { From 7dba847f21f8969e52f39d336cba6254426054f5 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 31 Jan 2021 16:49:17 -0500 Subject: [PATCH 205/357] hooks: Don't export `syncMapFirst` or `mapFirst` Nobody uses these functions outside of this file. --- src/static/js/pluginfw/hooks.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 489e6d6dc..7d7e1dd43 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -39,7 +39,7 @@ const hookCallWrapper = (hook, hookName, args, cb) => { return () => normalize(hook.hook_fn(hookName, args, (x) => cb(normalize(x)))); }; -exports.syncMapFirst = (lst, fn) => { +const syncMapFirst = (lst, fn) => { let i; let result; for (i = 0; i < lst.length; i++) { @@ -49,7 +49,7 @@ exports.syncMapFirst = (lst, fn) => { return []; }; -exports.mapFirst = (lst, fn, cb, predicate) => { +const mapFirst = (lst, fn, cb, predicate) => { if (predicate == null) predicate = (x) => (x != null && x.length > 0); let i = 0; @@ -347,7 +347,7 @@ exports.aCallAll = async (hookName, context, cb) => { exports.callFirst = (hookName, args) => { if (!args) args = {}; if (pluginDefs.hooks[hookName] === undefined) return []; - return exports.syncMapFirst(pluginDefs.hooks[hookName], + return syncMapFirst(pluginDefs.hooks[hookName], (hook) => hookCallWrapper(hook, hookName, args)); }; @@ -355,7 +355,7 @@ const aCallFirst = (hookName, args, cb, predicate) => { if (!args) args = {}; if (!cb) cb = () => {}; if (pluginDefs.hooks[hookName] === undefined) return cb(null, []); - exports.mapFirst( + mapFirst( pluginDefs.hooks[hookName], (hook, cb) => { hookCallWrapper(hook, hookName, args, (res) => { cb(null, res); }); From f02f288e8012538061a3973b42f2d4b8d1113c32 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 31 Jan 2021 18:07:36 -0500 Subject: [PATCH 206/357] hooks: Rename `args` to `context` for consistency --- src/static/js/pluginfw/hooks.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 7d7e1dd43..8434906b5 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -26,7 +26,7 @@ const checkDeprecation = (hook) => { // Flattens the array one level. const flatten1 = (array) => array.reduce((a, b) => a.concat(b), []); -const hookCallWrapper = (hook, hookName, args, cb) => { +const hookCallWrapper = (hook, hookName, context, cb) => { if (cb === undefined) cb = (x) => x; checkDeprecation(hook); @@ -36,7 +36,7 @@ const hookCallWrapper = (hook, hookName, args, cb) => { if (x === undefined) return []; return x; }; - return () => normalize(hook.hook_fn(hookName, args, (x) => cb(normalize(x)))); + return () => normalize(hook.hook_fn(hookName, context, (x) => cb(normalize(x)))); }; const syncMapFirst = (lst, fn) => { @@ -344,21 +344,21 @@ exports.aCallAll = async (hookName, context, cb) => { return await resultsPromise; }; -exports.callFirst = (hookName, args) => { - if (!args) args = {}; +exports.callFirst = (hookName, context) => { + if (!context) context = {}; if (pluginDefs.hooks[hookName] === undefined) return []; return syncMapFirst(pluginDefs.hooks[hookName], - (hook) => hookCallWrapper(hook, hookName, args)); + (hook) => hookCallWrapper(hook, hookName, context)); }; -const aCallFirst = (hookName, args, cb, predicate) => { - if (!args) args = {}; +const aCallFirst = (hookName, context, cb, predicate) => { + if (!context) context = {}; if (!cb) cb = () => {}; if (pluginDefs.hooks[hookName] === undefined) return cb(null, []); mapFirst( pluginDefs.hooks[hookName], (hook, cb) => { - hookCallWrapper(hook, hookName, args, (res) => { cb(null, res); }); + hookCallWrapper(hook, hookName, context, (res) => { cb(null, res); }); }, cb, predicate @@ -366,13 +366,13 @@ const aCallFirst = (hookName, args, cb, predicate) => { }; /* return a Promise if cb is not supplied */ -exports.aCallFirst = (hookName, args, cb, predicate) => { +exports.aCallFirst = (hookName, context, cb, predicate) => { if (cb === undefined) { return new Promise((resolve, reject) => { - aCallFirst(hookName, args, (err, res) => err ? reject(err) : resolve(res), predicate); + aCallFirst(hookName, context, (err, res) => err ? reject(err) : resolve(res), predicate); }); } else { - return aCallFirst(hookName, args, cb, predicate); + return aCallFirst(hookName, context, cb, predicate); } }; From c89db33ff038d3744bc907b8ce4e944b1556be59 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 31 Jan 2021 16:15:52 -0500 Subject: [PATCH 207/357] hooks: Refine caveat comments about function parameter count --- src/static/js/pluginfw/hooks.js | 40 ++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 8434906b5..5e8d9e73b 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -165,14 +165,27 @@ const callHookFnSync = (hook, context) => { // The hook function is assumed to not have a callback parameter, so fall through and accept // `undefined` as the resolved value. // - // IMPORTANT: "Rest" parameters and default parameters are not counted in`Function.length`, so - // the assumption does not hold for wrappers like `(...args) => { real(...args); }`. Such - // functions will still work properly without any logged warnings or errors for now, but: + // IMPORTANT: "Rest" parameters and default parameters are not included in `Function.length`, + // so the assumption does not hold for wrappers such as: + // + // const wrapper = (...args) => real(...args); + // + // ECMAScript does not provide a way to determine whether a function has default or rest + // parameters, so there is no way to be certain that a hook function with `length` < 3 will + // not call the callback. Synchronous hook functions that call the callback even though + // `length` < 3 will still work properly without any logged warnings or errors, but: + // // * Once the hook is upgraded to support asynchronous hook functions, calling the callback - // will (eventually) cause a double settle error, and the function might prematurely + // asynchronously will cause a double settle error, and the hook function will prematurely // resolve to `undefined` instead of the desired value. + // // * The above "unsettled function" warning is not logged if the function fails to call the // callback like it is supposed to. + // + // Wrapper functions can avoid problems by setting the wrapper's `length` property to match + // the real function's `length` property: + // + // Object.defineProperty(wrapper, 'length', {value: real.length}); } } @@ -300,10 +313,21 @@ const callHookFnAsync = async (hook, context) => { // The hook function is assumed to not have a callback parameter, so fall through and accept // `undefined` as the resolved value. // - // IMPORTANT: "Rest" parameters and default parameters are not counted in `Function.length`, - // so the assumption does not hold for wrappers like `(...args) => { real(...args); }`. For - // such functions, calling the callback will (eventually) cause a double settle error, and - // the function might prematurely resolve to `undefined` instead of the desired value. + // IMPORTANT: "Rest" parameters and default parameters are not included in + // `Function.length`, so the assumption does not hold for wrappers such as: + // + // const wrapper = (...args) => real(...args); + // + // ECMAScript does not provide a way to determine whether a function has default or rest + // parameters, so there is no way to be certain that a hook function with `length` < 3 will + // not call the callback. Hook functions with `length` < 3 that call the callback + // asynchronously will cause a double settle error, and the hook function will prematurely + // resolve to `undefined` instead of the desired value. + // + // Wrapper functions can avoid problems by setting the wrapper's `length` property to match + // the real function's `length` property: + // + // Object.defineProperty(wrapper, 'length', {value: real.length}); } } From 0b83ff8ec286087aa9ee9f826d31d289be89e03a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 31 Jan 2021 16:30:55 -0500 Subject: [PATCH 208/357] hooks: Simplify `syncMapFirst` iteration --- src/static/js/pluginfw/hooks.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 5e8d9e73b..98b2af4c8 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -39,12 +39,11 @@ const hookCallWrapper = (hook, hookName, context, cb) => { return () => normalize(hook.hook_fn(hookName, context, (x) => cb(normalize(x)))); }; -const syncMapFirst = (lst, fn) => { - let i; - let result; - for (i = 0; i < lst.length; i++) { - result = fn(lst[i]); - if (result.length) return result; +const syncMapFirst = (hooks, fn) => { + const predicate = (val) => val.length; + for (const hook of hooks) { + const val = fn(hook); + if (predicate(val)) return val; } return []; }; From 53ccfa87030f795366df53050a483921900ffe5c Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 31 Jan 2021 16:51:37 -0500 Subject: [PATCH 209/357] hooks: Asyncify `mapFirst` --- src/static/js/pluginfw/hooks.js | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 98b2af4c8..de7ddc32c 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -1,6 +1,7 @@ 'use strict'; const pluginDefs = require('./plugin_defs'); +const util = require('util'); // Maps the name of a server-side hook to a string explaining the deprecation // (e.g., 'use the foo hook instead'). @@ -48,19 +49,13 @@ const syncMapFirst = (hooks, fn) => { return []; }; -const mapFirst = (lst, fn, cb, predicate) => { - if (predicate == null) predicate = (x) => (x != null && x.length > 0); - let i = 0; - - const next = () => { - if (i >= lst.length) return cb(null, []); - fn(lst[i++], (err, result) => { - if (err) return cb(err); - if (predicate(result)) return cb(null, result); - next(); - }); - }; - next(); +const mapFirst = async (hooks, fn, predicate = null) => { + if (predicate == null) predicate = (val) => val.length; + for (const hook of hooks) { + const val = await fn(hook); + if (predicate(val)) return val; + } + return []; }; // Calls the hook function synchronously and returns the value provided by the hook function (via @@ -377,15 +372,9 @@ exports.callFirst = (hookName, context) => { const aCallFirst = (hookName, context, cb, predicate) => { if (!context) context = {}; if (!cb) cb = () => {}; - if (pluginDefs.hooks[hookName] === undefined) return cb(null, []); - mapFirst( - pluginDefs.hooks[hookName], - (hook, cb) => { - hookCallWrapper(hook, hookName, context, (res) => { cb(null, res); }); - }, - cb, - predicate - ); + const hooks = pluginDefs.hooks[hookName] || []; + const fn = async (hook) => await util.promisify(hookCallWrapper)(hook, hookName, context); + util.callbackify(mapFirst)(hooks, fn, predicate, cb); }; /* return a Promise if cb is not supplied */ From 4ab7a99512a69b5438b45a7f37a2b824433f4eec Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 31 Jan 2021 18:35:17 -0500 Subject: [PATCH 210/357] hooks: Inline `syncMapFirst()` into `callFirst()` for readability There's only one caller of the function, and the function is simple, so there's no need for a separate function. --- src/static/js/pluginfw/hooks.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index de7ddc32c..4370b87f8 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -40,15 +40,6 @@ const hookCallWrapper = (hook, hookName, context, cb) => { return () => normalize(hook.hook_fn(hookName, context, (x) => cb(normalize(x)))); }; -const syncMapFirst = (hooks, fn) => { - const predicate = (val) => val.length; - for (const hook of hooks) { - const val = fn(hook); - if (predicate(val)) return val; - } - return []; -}; - const mapFirst = async (hooks, fn, predicate = null) => { if (predicate == null) predicate = (val) => val.length; for (const hook of hooks) { @@ -364,9 +355,13 @@ exports.aCallAll = async (hookName, context, cb) => { exports.callFirst = (hookName, context) => { if (!context) context = {}; - if (pluginDefs.hooks[hookName] === undefined) return []; - return syncMapFirst(pluginDefs.hooks[hookName], - (hook) => hookCallWrapper(hook, hookName, context)); + const predicate = (val) => val.length; + const hooks = pluginDefs.hooks[hookName] || []; + for (const hook of hooks) { + const val = hookCallWrapper(hook, hookName, context); + if (predicate(val)) return val; + } + return []; }; const aCallFirst = (hookName, context, cb, predicate) => { From 13e806ad7a21e18352880b16b43b1caa2784887f Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 31 Jan 2021 18:35:17 -0500 Subject: [PATCH 211/357] hooks: Inline `mapFirst()` into `aCallFirst()` for readability There's only one caller of the function, and the function is simple, so there's no need for a separate function. --- src/static/js/pluginfw/hooks.js | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 4370b87f8..2851e92a0 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -40,15 +40,6 @@ const hookCallWrapper = (hook, hookName, context, cb) => { return () => normalize(hook.hook_fn(hookName, context, (x) => cb(normalize(x)))); }; -const mapFirst = async (hooks, fn, predicate = null) => { - if (predicate == null) predicate = (val) => val.length; - for (const hook of hooks) { - const val = await fn(hook); - if (predicate(val)) return val; - } - return []; -}; - // Calls the hook function synchronously and returns the value provided by the hook function (via // callback or return value). // @@ -364,12 +355,18 @@ exports.callFirst = (hookName, context) => { return []; }; -const aCallFirst = (hookName, context, cb, predicate) => { +const aCallFirst = (hookName, context, cb, predicate = null) => { if (!context) context = {}; if (!cb) cb = () => {}; + if (predicate == null) predicate = (val) => val.length; const hooks = pluginDefs.hooks[hookName] || []; - const fn = async (hook) => await util.promisify(hookCallWrapper)(hook, hookName, context); - util.callbackify(mapFirst)(hooks, fn, predicate, cb); + util.callbackify(async () => { + for (const hook of hooks) { + const val = await util.promisify(hookCallWrapper)(hook, hookName, context); + if (predicate(val)) return val; + } + return []; + })(cb); }; /* return a Promise if cb is not supplied */ From 708206449a888ed46a0acdf52de68d996778696d Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 31 Jan 2021 19:11:48 -0500 Subject: [PATCH 212/357] hooks: Factor out callback attachment The separate function will be reused in a future commit. --- src/static/js/pluginfw/hooks.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 2851e92a0..2c0dd13b7 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -24,6 +24,11 @@ const checkDeprecation = (hook) => { deprecationWarned[hook.hook_fn_name] = true; }; +// Calls the node-style callback when the Promise settles. Unlike util.callbackify, this takes a +// Promise (rather than a function that returns a Promise), and it returns a Promise (rather than a +// function that returns undefined). +const attachCallback = (p, cb) => p.then((val) => cb(null, val), cb); + // Flattens the array one level. const flatten1 = (array) => array.reduce((a, b) => a.concat(b), []); @@ -333,15 +338,14 @@ const callHookFnAsync = async (hook, context) => { // 2. Convert each `undefined` entry into `[]`. // 3. Flatten one level. // If cb is non-null, this function resolves to the value returned by cb. -exports.aCallAll = async (hookName, context, cb) => { +exports.aCallAll = async (hookName, context, cb = null) => { + if (cb != null) return await attachCallback(exports.aCallAll(hookName, context), cb); if (context == null) context = {}; const hooks = pluginDefs.hooks[hookName] || []; - let resultsPromise = Promise.all(hooks.map((hook) => callHookFnAsync(hook, context) + const results = await Promise.all(hooks.map((hook) => callHookFnAsync(hook, context) // `undefined` (but not `null`!) is treated the same as []. - .then((result) => (result === undefined) ? [] : result))) - .then(flatten1); - if (cb != null) resultsPromise = resultsPromise.then((val) => cb(null, val), cb); - return await resultsPromise; + .then((result) => (result === undefined) ? [] : result))); + return flatten1(results); }; exports.callFirst = (hookName, context) => { From f316a3bacdc282de9cd60121013ea6421bfab9ca Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 31 Jan 2021 23:41:11 -0500 Subject: [PATCH 213/357] hooks: Never pass a falsy error to a callback --- src/static/js/pluginfw/hooks.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 2c0dd13b7..1b04a61c7 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -27,7 +27,11 @@ const checkDeprecation = (hook) => { // Calls the node-style callback when the Promise settles. Unlike util.callbackify, this takes a // Promise (rather than a function that returns a Promise), and it returns a Promise (rather than a // function that returns undefined). -const attachCallback = (p, cb) => p.then((val) => cb(null, val), cb); +const attachCallback = (p, cb) => p.then( + (val) => cb(null, val), + // Callbacks often only check the truthiness, not the nullness, of the first parameter. To avoid + // problems, always pass a truthy value as the first argument if the Promise is rejected. + (err) => cb(err || new Error(err))); // Flattens the array one level. const flatten1 = (array) => array.reduce((a, b) => a.concat(b), []); @@ -326,10 +330,11 @@ const callHookFnAsync = async (hook, context) => { // Arguments: // * hookName: Name of the hook to invoke. // * context: Passed unmodified to the hook functions, except nullish becomes {}. -// * cb: Deprecated callback. The following: +// * cb: Deprecated. Optional node-style callback. The following: // const p1 = hooks.aCallAll('myHook', context, cb); // is equivalent to: -// const p2 = hooks.aCallAll('myHook', context).then((val) => cb(null, val), cb); +// const p2 = hooks.aCallAll('myHook', context).then( +// (val) => cb(null, val), (err) => cb(err || new Error(err))); // // Return value: // If cb is nullish, this function resolves to a flattened array of hook results. Specifically, it From 22d02dbcbf3da3e8373f52cb99572144e1bd2e98 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 31 Jan 2021 18:56:06 -0500 Subject: [PATCH 214/357] hooks: Factor out value normalization --- src/static/js/pluginfw/hooks.js | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 1b04a61c7..5b3add2d1 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -33,20 +33,24 @@ const attachCallback = (p, cb) => p.then( // problems, always pass a truthy value as the first argument if the Promise is rejected. (err) => cb(err || new Error(err))); +// Normalizes the value provided by hook functions so that it is always an array. `undefined` (but +// not `null`!) becomes an empty array, array values are returned unmodified, and non-array values +// are wrapped in an array (so `null` becomes `[null]`). +const normalizeValue = (val) => { + // `undefined` is treated the same as `[]`. IMPORTANT: `null` is *not* treated the same as `[]` + // because some hooks use `null` as a special value. + if (val === undefined) return []; + if (Array.isArray(val)) return val; + return [val]; +}; + // Flattens the array one level. const flatten1 = (array) => array.reduce((a, b) => a.concat(b), []); const hookCallWrapper = (hook, hookName, context, cb) => { if (cb === undefined) cb = (x) => x; - checkDeprecation(hook); - - // Normalize output to list for both sync and async cases - const normalize = (x) => { - if (x === undefined) return []; - return x; - }; - return () => normalize(hook.hook_fn(hookName, context, (x) => cb(normalize(x)))); + return () => normalizeValue(hook.hook_fn(hookName, context, (x) => cb(normalizeValue(x)))); }; // Calls the hook function synchronously and returns the value provided by the hook function (via @@ -192,12 +196,7 @@ const callHookFnSync = (hook, context) => { exports.callAll = (hookName, context) => { if (context == null) context = {}; const hooks = pluginDefs.hooks[hookName] || []; - return flatten1(hooks.map((hook) => { - const ret = callHookFnSync(hook, context); - // `undefined` (but not `null`!) is treated the same as []. - if (ret === undefined) return []; - return ret; - })); + return flatten1(hooks.map((hook) => normalizeValue(callHookFnSync(hook, context)))); }; // Calls the hook function asynchronously and returns a Promise that either resolves to the hook @@ -347,9 +346,8 @@ exports.aCallAll = async (hookName, context, cb = null) => { if (cb != null) return await attachCallback(exports.aCallAll(hookName, context), cb); if (context == null) context = {}; const hooks = pluginDefs.hooks[hookName] || []; - const results = await Promise.all(hooks.map((hook) => callHookFnAsync(hook, context) - // `undefined` (but not `null`!) is treated the same as []. - .then((result) => (result === undefined) ? [] : result))); + const results = await Promise.all( + hooks.map(async (hook) => normalizeValue(await callHookFnAsync(hook, context)))); return flatten1(results); }; From 77f480d954bcefd99c176e87604d47aa9b8848ee Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 1 Feb 2021 00:03:06 -0500 Subject: [PATCH 215/357] hooks: Asyncify `aCallFirst` --- src/static/js/pluginfw/hooks.js | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 5b3add2d1..c570068cf 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -362,29 +362,21 @@ exports.callFirst = (hookName, context) => { return []; }; -const aCallFirst = (hookName, context, cb, predicate = null) => { +const aCallFirst = async (hookName, context, predicate = null) => { if (!context) context = {}; - if (!cb) cb = () => {}; if (predicate == null) predicate = (val) => val.length; const hooks = pluginDefs.hooks[hookName] || []; - util.callbackify(async () => { - for (const hook of hooks) { - const val = await util.promisify(hookCallWrapper)(hook, hookName, context); - if (predicate(val)) return val; - } - return []; - })(cb); + for (const hook of hooks) { + const val = await util.promisify(hookCallWrapper)(hook, hookName, context); + if (predicate(val)) return val; + } + return []; }; /* return a Promise if cb is not supplied */ exports.aCallFirst = (hookName, context, cb, predicate) => { - if (cb === undefined) { - return new Promise((resolve, reject) => { - aCallFirst(hookName, context, (err, res) => err ? reject(err) : resolve(res), predicate); - }); - } else { - return aCallFirst(hookName, context, cb, predicate); - } + if (cb == null) return aCallFirst(hookName, context, predicate); + util.callbackify(aCallFirst)(hookName, context, predicate, cb); }; exports.exportedForTestingOnly = { From fd5d3ce777c5cb93e3c49cccc77289099afa2b71 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 1 Feb 2021 00:34:49 -0500 Subject: [PATCH 216/357] hooks: Inline `aCallFirst()` into `exports.aCallFirst()` --- src/static/js/pluginfw/hooks.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index c570068cf..75e37315f 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -362,7 +362,10 @@ exports.callFirst = (hookName, context) => { return []; }; -const aCallFirst = async (hookName, context, predicate = null) => { +exports.aCallFirst = async (hookName, context, cb = null, predicate = null) => { + if (cb != null) { + return await attachCallback(exports.aCallFirst(hookName, context, null, predicate), cb); + } if (!context) context = {}; if (predicate == null) predicate = (val) => val.length; const hooks = pluginDefs.hooks[hookName] || []; @@ -373,12 +376,6 @@ const aCallFirst = async (hookName, context, predicate = null) => { return []; }; -/* return a Promise if cb is not supplied */ -exports.aCallFirst = (hookName, context, cb, predicate) => { - if (cb == null) return aCallFirst(hookName, context, predicate); - util.callbackify(aCallFirst)(hookName, context, predicate, cb); -}; - exports.exportedForTestingOnly = { callHookFnAsync, callHookFnSync, From c11d60c5f6f2e36f1643f57ed789a74ea4224175 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 1 Feb 2021 00:37:45 -0500 Subject: [PATCH 217/357] hooks: Check context nullness, not truthiness --- src/static/js/pluginfw/hooks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 75e37315f..14bd9d57e 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -352,7 +352,7 @@ exports.aCallAll = async (hookName, context, cb = null) => { }; exports.callFirst = (hookName, context) => { - if (!context) context = {}; + if (context == null) context = {}; const predicate = (val) => val.length; const hooks = pluginDefs.hooks[hookName] || []; for (const hook of hooks) { @@ -366,7 +366,7 @@ exports.aCallFirst = async (hookName, context, cb = null, predicate = null) => { if (cb != null) { return await attachCallback(exports.aCallFirst(hookName, context, null, predicate), cb); } - if (!context) context = {}; + if (context == null) context = {}; if (predicate == null) predicate = (val) => val.length; const hooks = pluginDefs.hooks[hookName] || []; for (const hook of hooks) { From 6f30ea7c38c81b0b334426fea87462d1e23c3369 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 1 Feb 2021 00:41:53 -0500 Subject: [PATCH 218/357] hooks: Use `callHookFn{Sync,Async}()` for `{call,aCall}First()` Benefits of `callHookFnSync()` and `callHookFnAsync()`: * They are a lot more forgiving than `hookCallWrapper()` was. * They perform useful sanity checks. * They have extensive unit test coverage. * They make the behavior of `callFirst()` and `aCallFirst()` match the behavior of `callAll()` and `aCallAll()`. --- src/static/js/pluginfw/hooks.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 14bd9d57e..65854730c 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -1,7 +1,6 @@ 'use strict'; const pluginDefs = require('./plugin_defs'); -const util = require('util'); // Maps the name of a server-side hook to a string explaining the deprecation // (e.g., 'use the foo hook instead'). @@ -47,12 +46,6 @@ const normalizeValue = (val) => { // Flattens the array one level. const flatten1 = (array) => array.reduce((a, b) => a.concat(b), []); -const hookCallWrapper = (hook, hookName, context, cb) => { - if (cb === undefined) cb = (x) => x; - checkDeprecation(hook); - return () => normalizeValue(hook.hook_fn(hookName, context, (x) => cb(normalizeValue(x)))); -}; - // Calls the hook function synchronously and returns the value provided by the hook function (via // callback or return value). // @@ -356,7 +349,7 @@ exports.callFirst = (hookName, context) => { const predicate = (val) => val.length; const hooks = pluginDefs.hooks[hookName] || []; for (const hook of hooks) { - const val = hookCallWrapper(hook, hookName, context); + const val = normalizeValue(callHookFnSync(hook, context)); if (predicate(val)) return val; } return []; @@ -370,7 +363,7 @@ exports.aCallFirst = async (hookName, context, cb = null, predicate = null) => { if (predicate == null) predicate = (val) => val.length; const hooks = pluginDefs.hooks[hookName] || []; for (const hook of hooks) { - const val = await util.promisify(hookCallWrapper)(hook, hookName, context); + const val = normalizeValue(await callHookFnAsync(hook, context)); if (predicate(val)) return val; } return []; From ba0544ea9e8b443326830adc69a14103a4eda980 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 1 Feb 2021 18:31:50 -0500 Subject: [PATCH 219/357] hooks: Add unit tests for `callFirst()`, `aCallFirst()` --- tests/backend/specs/hooks.js | 240 +++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) diff --git a/tests/backend/specs/hooks.js b/tests/backend/specs/hooks.js index 327b9bd1c..2ea7fac00 100644 --- a/tests/backend/specs/hooks.js +++ b/tests/backend/specs/hooks.js @@ -389,6 +389,90 @@ describe(__filename, function () { }); }); + describe('hooks.callFirst', function () { + it('no registered hooks (undefined) -> []', async function () { + delete plugins.hooks.testHook; + assert.deepEqual(hooks.callFirst(hookName), []); + }); + + it('no registered hooks (empty list) -> []', async function () { + testHooks.length = 0; + assert.deepEqual(hooks.callFirst(hookName), []); + }); + + it('passes hook name => {}', async function () { + hook.hook_fn = (hn) => { assert.equal(hn, hookName); }; + hooks.callFirst(hookName); + }); + + it('undefined context => {}', async function () { + hook.hook_fn = (hn, ctx) => { assert.deepEqual(ctx, {}); }; + hooks.callFirst(hookName); + }); + + it('null context => {}', async function () { + hook.hook_fn = (hn, ctx) => { assert.deepEqual(ctx, {}); }; + hooks.callFirst(hookName, null); + }); + + it('context unmodified', async function () { + const wantContext = {}; + hook.hook_fn = (hn, ctx) => { assert.equal(ctx, wantContext); }; + hooks.callFirst(hookName, wantContext); + }); + + it('predicate never satisfied -> calls all in order', async function () { + const gotCalls = []; + testHooks.length = 0; + for (let i = 0; i < 3; i++) { + const hook = makeHook(); + hook.hook_fn = () => { gotCalls.push(i); }; + testHooks.push(hook); + } + assert.deepEqual(hooks.callFirst(hookName), []); + assert.deepEqual(gotCalls, [0, 1, 2]); + }); + + it('stops when predicate is satisfied', async function () { + testHooks.length = 0; + testHooks.push(makeHook(), makeHook('val1'), makeHook('val2')); + assert.deepEqual(hooks.callFirst(hookName), ['val1']); + }); + + it('skips values that do not satisfy predicate (undefined)', async function () { + testHooks.length = 0; + testHooks.push(makeHook(), makeHook('val1')); + assert.deepEqual(hooks.callFirst(hookName), ['val1']); + }); + + it('skips values that do not satisfy predicate (empty list)', async function () { + testHooks.length = 0; + testHooks.push(makeHook([]), makeHook('val1')); + assert.deepEqual(hooks.callFirst(hookName), ['val1']); + }); + + it('null satisifes the predicate', async function () { + testHooks.length = 0; + testHooks.push(makeHook(null), makeHook('val1')); + assert.deepEqual(hooks.callFirst(hookName), [null]); + }); + + it('non-empty arrays are returned unmodified', async function () { + const want = ['val1']; + testHooks.length = 0; + testHooks.push(makeHook(want), makeHook(['val2'])); + assert.equal(hooks.callFirst(hookName), want); // Note: *NOT* deepEqual! + }); + + it('value can be passed via callback', async function () { + const want = {}; + hook.hook_fn = (hn, ctx, cb) => { cb(want); }; + const got = hooks.callFirst(hookName); + assert.deepEqual(got, [want]); + assert.equal(got[0], want); // Note: *NOT* deepEqual! + }); + }); + describe('callHookFnAsync', function () { const callHookFnAsync = hooks.exportedForTestingOnly.callHookFnAsync; // Convenience shorthand. @@ -883,4 +967,160 @@ describe(__filename, function () { }); }); }); + + describe('hooks.aCallFirst', function () { + it('no registered hooks (undefined) -> []', async function () { + delete plugins.hooks.testHook; + assert.deepEqual(await hooks.aCallFirst(hookName), []); + }); + + it('no registered hooks (empty list) -> []', async function () { + testHooks.length = 0; + assert.deepEqual(await hooks.aCallFirst(hookName), []); + }); + + it('passes hook name => {}', async function () { + hook.hook_fn = (hn) => { assert.equal(hn, hookName); }; + await hooks.aCallFirst(hookName); + }); + + it('undefined context => {}', async function () { + hook.hook_fn = (hn, ctx) => { assert.deepEqual(ctx, {}); }; + await hooks.aCallFirst(hookName); + }); + + it('null context => {}', async function () { + hook.hook_fn = (hn, ctx) => { assert.deepEqual(ctx, {}); }; + await hooks.aCallFirst(hookName, null); + }); + + it('context unmodified', async function () { + const wantContext = {}; + hook.hook_fn = (hn, ctx) => { assert.equal(ctx, wantContext); }; + await hooks.aCallFirst(hookName, wantContext); + }); + + it('default predicate: predicate never satisfied -> calls all in order', async function () { + const gotCalls = []; + testHooks.length = 0; + for (let i = 0; i < 3; i++) { + const hook = makeHook(); + hook.hook_fn = () => { gotCalls.push(i); }; + testHooks.push(hook); + } + assert.deepEqual(await hooks.aCallFirst(hookName), []); + assert.deepEqual(gotCalls, [0, 1, 2]); + }); + + it('calls hook functions serially', async function () { + const gotCalls = []; + testHooks.length = 0; + for (let i = 0; i < 3; i++) { + const hook = makeHook(); + hook.hook_fn = async () => { + gotCalls.push(i); + // Check gotCalls asynchronously to ensure that the next hook function does not start + // executing before this hook function has resolved. + return await new Promise((resolve) => { + setImmediate(() => { + assert.deepEqual(gotCalls, [...Array(i + 1).keys()]); + resolve(); + }); + }); + }; + testHooks.push(hook); + } + assert.deepEqual(await hooks.aCallFirst(hookName), []); + assert.deepEqual(gotCalls, [0, 1, 2]); + }); + + it('default predicate: stops when satisfied', async function () { + testHooks.length = 0; + testHooks.push(makeHook(), makeHook('val1'), makeHook('val2')); + assert.deepEqual(await hooks.aCallFirst(hookName), ['val1']); + }); + + it('default predicate: skips values that do not satisfy (undefined)', async function () { + testHooks.length = 0; + testHooks.push(makeHook(), makeHook('val1')); + assert.deepEqual(await hooks.aCallFirst(hookName), ['val1']); + }); + + it('default predicate: skips values that do not satisfy (empty list)', async function () { + testHooks.length = 0; + testHooks.push(makeHook([]), makeHook('val1')); + assert.deepEqual(await hooks.aCallFirst(hookName), ['val1']); + }); + + it('default predicate: null satisifes', async function () { + testHooks.length = 0; + testHooks.push(makeHook(null), makeHook('val1')); + assert.deepEqual(await hooks.aCallFirst(hookName), [null]); + }); + + it('custom predicate: called for each hook function', async function () { + testHooks.length = 0; + testHooks.push(makeHook(0), makeHook(1), makeHook(2)); + let got = 0; + await hooks.aCallFirst(hookName, null, null, (val) => { ++got; return false; }); + assert.equal(got, 3); + }); + + it('custom predicate: boolean false/true continues/stops iteration', async function () { + testHooks.length = 0; + testHooks.push(makeHook(1), makeHook(2), makeHook(3)); + let nCall = 0; + const predicate = (val) => { + assert.deepEqual(val, [++nCall]); + return nCall === 2; + }; + assert.deepEqual(await hooks.aCallFirst(hookName, null, null, predicate), [2]); + assert.equal(nCall, 2); + }); + + it('custom predicate: non-boolean falsy/truthy continues/stops iteration', async function () { + testHooks.length = 0; + testHooks.push(makeHook(1), makeHook(2), makeHook(3)); + let nCall = 0; + const predicate = (val) => { + assert.deepEqual(val, [++nCall]); + return nCall === 2 ? {} : null; + }; + assert.deepEqual(await hooks.aCallFirst(hookName, null, null, predicate), [2]); + assert.equal(nCall, 2); + }); + + it('custom predicate: array value passed unmodified to predicate', async function () { + const want = [0]; + hook.hook_fn = () => want; + const predicate = (got) => { assert.equal(got, want); }; // Note: *NOT* deepEqual! + await hooks.aCallFirst(hookName, null, null, predicate); + }); + + it('custom predicate: normalized value passed to predicate (undefined)', async function () { + const predicate = (got) => { assert.deepEqual(got, []); }; + await hooks.aCallFirst(hookName, null, null, predicate); + }); + + it('custom predicate: normalized value passed to predicate (null)', async function () { + hook.hook_fn = () => null; + const predicate = (got) => { assert.deepEqual(got, [null]); }; + await hooks.aCallFirst(hookName, null, null, predicate); + }); + + it('non-empty arrays are returned unmodified', async function () { + const want = ['val1']; + testHooks.length = 0; + testHooks.push(makeHook(want), makeHook(['val2'])); + assert.equal(await hooks.aCallFirst(hookName), want); // Note: *NOT* deepEqual! + }); + + it('value can be passed via callback', async function () { + const want = {}; + hook.hook_fn = (hn, ctx, cb) => { cb(want); }; + const got = await hooks.aCallFirst(hookName); + assert.deepEqual(got, [want]); + assert.equal(got[0], want); // Note: *NOT* deepEqual! + }); + }); }); From 763fe6fc2615962d54ccbf80d05df49b751a90c5 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 1 Feb 2021 01:43:04 -0500 Subject: [PATCH 220/357] hooks: Document `callFirst()` and `aCallFirst()` --- src/static/js/pluginfw/hooks.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 65854730c..459f84489 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -344,6 +344,9 @@ exports.aCallAll = async (hookName, context, cb = null) => { return flatten1(results); }; +// DEPRECATED: Use `aCallFirst()` instead. +// +// Like `aCallFirst()`, but synchronous. Hook functions must provide their values synchronously. exports.callFirst = (hookName, context) => { if (context == null) context = {}; const predicate = (val) => val.length; @@ -355,6 +358,27 @@ exports.callFirst = (hookName, context) => { return []; }; +// Invokes the registered hook functions one at a time until one provides a value that meets a +// customizable condition. +// +// Arguments: +// * hookName: Name of the hook to invoke. +// * context: Passed unmodified to the hook functions, except nullish becomes {}. +// * cb: Deprecated callback. The following: +// const p1 = hooks.aCallFirst('myHook', context, cb); +// is equivalent to: +// const p2 = hooks.aCallFirst('myHook', context).then( +// (val) => cb(null, val), (err) => cb(err || new Error(err))); +// * predicate: Optional predicate function that returns true if the hook function provided a +// value that satisfies a desired condition. If nullish, the predicate defaults to a non-empty +// array check. The predicate is invoked each time a hook function returns. It takes one +// argument: the normalized value provided by the hook function. If the predicate returns +// truthy, iteration over the hook functions stops (no more hook functions will be called). +// +// Return value: +// If cb is nullish, resolves to an array that is either the normalized value that satisfied the +// predicate or empty if the predicate was never satisfied. If cb is non-nullish, resolves to the +// value returned from cb(). exports.aCallFirst = async (hookName, context, cb = null, predicate = null) => { if (cb != null) { return await attachCallback(exports.aCallFirst(hookName, context, null, predicate), cb); From 05e0e8dbf7941cb2ff4b235a088034b2404fce46 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 1 Feb 2021 01:18:44 -0500 Subject: [PATCH 221/357] hooks: New `callAllSerial()` function This is necessary to migrate away from `callAll()` (which only supports synchronous hook functions). --- src/static/js/pluginfw/hooks.js | 20 +++++++- tests/backend/specs/hooks.js | 83 +++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 459f84489..72da63021 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -175,6 +175,8 @@ const callHookFnSync = (hook, context) => { return outcome.val; }; +// DEPRECATED: Use `callAllSerial()` or `aCallAll()` instead. +// // Invokes all registered hook functions synchronously. // // Arguments: @@ -317,7 +319,10 @@ const callHookFnAsync = async (hook, context) => { }); }; -// Invokes all registered hook functions asynchronously. +// Invokes all registered hook functions asynchronously and concurrently. This is NOT the async +// equivalent of `callAll()`: `callAll()` calls the hook functions serially (one at a time) but this +// function calls them concurrently. Use `callAllSerial()` if the hook functions must be called one +// at a time. // // Arguments: // * hookName: Name of the hook to invoke. @@ -344,6 +349,19 @@ exports.aCallAll = async (hookName, context, cb = null) => { return flatten1(results); }; +// Like `aCallAll()` except the hook functions are called one at a time instead of concurrently. +// Only use this function if the hook functions must be called one at a time, otherwise use +// `aCallAll()`. +exports.callAllSerial = async (hookName, context) => { + if (context == null) context = {}; + const hooks = pluginDefs.hooks[hookName] || []; + const results = []; + for (const hook of hooks) { + results.push(normalizeValue(await callHookFnAsync(hook, context))); + } + return flatten1(results); +}; + // DEPRECATED: Use `aCallFirst()` instead. // // Like `aCallFirst()`, but synchronous. Hook functions must provide their values synchronously. diff --git a/tests/backend/specs/hooks.js b/tests/backend/specs/hooks.js index 2ea7fac00..865cb132b 100644 --- a/tests/backend/specs/hooks.js +++ b/tests/backend/specs/hooks.js @@ -968,6 +968,89 @@ describe(__filename, function () { }); }); + describe('hooks.callAllSerial', function () { + describe('basic behavior', function () { + it('calls all asynchronously, serially, in order', async function () { + const gotCalls = []; + testHooks.length = 0; + for (let i = 0; i < 3; i++) { + const hook = makeHook(); + hook.hook_fn = async () => { + gotCalls.push(i); + // Check gotCalls asynchronously to ensure that the next hook function does not start + // executing before this hook function has resolved. + return await new Promise((resolve) => { + setImmediate(() => { + assert.deepEqual(gotCalls, [...Array(i + 1).keys()]); + resolve(i); + }); + }); + }; + testHooks.push(hook); + } + assert.deepEqual(await hooks.callAllSerial(hookName), [0, 1, 2]); + assert.deepEqual(gotCalls, [0, 1, 2]); + }); + + it('passes hook name', async function () { + hook.hook_fn = async (hn) => { assert.equal(hn, hookName); }; + await hooks.callAllSerial(hookName); + }); + + it('undefined context -> {}', async function () { + hook.hook_fn = async (hn, ctx) => { assert.deepEqual(ctx, {}); }; + await hooks.callAllSerial(hookName); + }); + + it('null context -> {}', async function () { + hook.hook_fn = async (hn, ctx) => { assert.deepEqual(ctx, {}); }; + await hooks.callAllSerial(hookName, null); + }); + + it('context unmodified', async function () { + const wantContext = {}; + hook.hook_fn = async (hn, ctx) => { assert.equal(ctx, wantContext); }; + await hooks.callAllSerial(hookName, wantContext); + }); + }); + + describe('result processing', function () { + it('no registered hooks (undefined) -> []', async function () { + delete plugins.hooks[hookName]; + assert.deepEqual(await hooks.callAllSerial(hookName), []); + }); + + it('no registered hooks (empty list) -> []', async function () { + testHooks.length = 0; + assert.deepEqual(await hooks.callAllSerial(hookName), []); + }); + + it('flattens one level', async function () { + testHooks.length = 0; + testHooks.push(makeHook(1), makeHook([2]), makeHook([[3]])); + assert.deepEqual(await hooks.callAllSerial(hookName), [1, 2, [3]]); + }); + + it('filters out undefined', async function () { + testHooks.length = 0; + testHooks.push(makeHook(), makeHook([2]), makeHook([[3]]), makeHook(Promise.resolve())); + assert.deepEqual(await hooks.callAllSerial(hookName), [2, [3]]); + }); + + it('preserves null', async function () { + testHooks.length = 0; + testHooks.push(makeHook(null), makeHook([2]), makeHook(Promise.resolve(null))); + assert.deepEqual(await hooks.callAllSerial(hookName), [null, 2, null]); + }); + + it('all undefined -> []', async function () { + testHooks.length = 0; + testHooks.push(makeHook(), makeHook(Promise.resolve())); + assert.deepEqual(await hooks.callAllSerial(hookName), []); + }); + }); + }); + describe('hooks.aCallFirst', function () { it('no registered hooks (undefined) -> []', async function () { delete plugins.hooks.testHook; From 65dec5bd2cfd3576fbfe6de5e5d652888a999e3f Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 1 Feb 2021 23:11:11 +0000 Subject: [PATCH 222/357] lint: json.js --- bin/doc/json.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/bin/doc/json.js b/bin/doc/json.js index c71611e5f..1a5ecb1d8 100644 --- a/bin/doc/json.js +++ b/bin/doc/json.js @@ -248,11 +248,11 @@ const processList = (section) => { switch (section.type) { case 'ctor': case 'classMethod': - case 'method': + case 'method': { // each item is an argument, unless the name is 'return', // in which case it's the return value. section.signatures = section.signatures || []; - var sig = {}; + const sig = {}; section.signatures.push(sig); sig.params = values.filter((v) => { if (v.name === 'return') { @@ -263,11 +263,11 @@ const processList = (section) => { }); parseSignature(section.textRaw, sig); break; - - case 'property': + } + case 'property': { // there should be only one item, which is the value. // copy the data up to the section. - var value = values[0] || {}; + const value = values[0] || {}; delete value.name; section.typeof = value.type; delete value.type; @@ -275,14 +275,15 @@ const processList = (section) => { section[k] = value[k]; }); break; + } - case 'event': + case 'event': { // event: each item is an argument. section.params = values; break; + } } - // section.listParsed = values; delete section.list; }; @@ -417,7 +418,7 @@ const finishSection = (section, parent) => { ctor.signatures.forEach((sig) => { sig.desc = ctor.desc; }); - sigs.push.apply(sigs, ctor.signatures); + sigs.push(...ctor.signatures); }); delete section.ctors; } @@ -486,7 +487,7 @@ const finishSection = (section, parent) => { // Not a general purpose deep copy. // But sufficient for these basic things. const deepCopy = (src, dest) => { - Object.keys(src).filter((k) => !dest.hasOwnProperty(k)).forEach((k) => { + Object.keys(src).filter((k) => !Object.prototype.hasOwnProperty.call(dest, k)).forEach((k) => { dest[k] = deepCopy_(src[k]); }); }; From ea202e41f69fcb88e8f3f34c8dba7de45cb78865 Mon Sep 17 00:00:00 2001 From: freddii Date: Wed, 3 Feb 2021 00:30:07 +0100 Subject: [PATCH 223/357] docs: fixed typos --- CHANGELOG.md | 16 ++++++++-------- bin/checkPadDeltas.js | 2 +- bin/cleanRun.sh | 2 +- bin/debugRun.sh | 2 +- bin/fastRun.sh | 2 +- bin/installDeps.sh | 2 +- bin/repairPad.js | 2 +- bin/run.sh | 2 +- doc/api/http_api.md | 2 +- doc/localization.md | 2 +- settings.json.docker | 2 +- settings.json.template | 2 +- src/node/db/DB.js | 4 ++-- src/node/db/PadManager.js | 2 +- src/node/handler/PadMessageHandler.js | 4 ++-- src/node/hooks/express/openapi.js | 2 +- src/node/hooks/express/socketio.js | 2 +- src/node/utils/Minify.js | 4 ++-- src/static/js/pad.js | 2 +- tests/backend/fuzzImportTest.js | 2 +- tests/backend/specs/api/importexport.js | 4 ++-- tests/backend/specs/api/pad.js | 4 ++-- tests/backend/specs/contentcollector.js | 4 ++-- tests/frontend/runner.js | 2 +- 24 files changed, 37 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42c2692b6..57613b826 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -122,7 +122,7 @@ * MINOR: Fix ?showChat URL param issue * MINOR: Issue where timeslider URI fails to be correct if padID is numeric * MINOR: Include prompt for clear authorship when entire document is selected -* MINOR: Include full document aText every 100 revisions to make pad restoration on database curruption achievable +* MINOR: Include full document aText every 100 revisions to make pad restoration on database corruption achievable * MINOR: Several Colibris CSS fixes * MINOR: Use mime library for mime types instead of hard-coded. * MINOR: Don't show "new pad button" if instance is read only @@ -372,7 +372,7 @@ finally put them back in their new location, uder `src/static/skins/no-skin`. # 1.5.3 * NEW: Accessibility support for Screen readers, includes new fonts and keyboard shortcuts * NEW: API endpoint for Append Chat Message and Chat Backend Tests - * NEW: Error messages displayed on load are included in Default Pad Text (can be supressed) + * NEW: Error messages displayed on load are included in Default Pad Text (can be suppressed) * NEW: Content Collector can handle key values * NEW: getAttributesOnPosition Method * FIX: Firefox keeps attributes (bold etc) on cut/copy -> paste @@ -441,7 +441,7 @@ finally put them back in their new location, uder `src/static/skins/no-skin`. * Fix: Timeslider UI Fix * Fix: Remove Dokuwiki * Fix: Remove long paths from windows build (stops error during extract) - * Fix: Various globals remvoed + * Fix: Various globals removed * Fix: Move all scripts into bin/ * Fix: Various CSS bugfixes for Mobile devices * Fix: Overflow Toolbar @@ -517,7 +517,7 @@ finally put them back in their new location, uder `src/static/skins/no-skin`. * FIX: HTML import (don't crash on malformed or blank HTML input; strip title out of html during import) * FIX: check if uploaded file only contains ascii chars when abiword disabled * FIX: Plugin search in /admin/plugins - * FIX: Don't create new pad if a non-existant read-only pad is accessed + * FIX: Don't create new pad if a non-existent read-only pad is accessed * FIX: Drop messages from unknown connections (would lead to a crash after a restart) * FIX: API: fix createGroupFor endpoint, if mapped group is deleted * FIX: Import form for other locales @@ -534,7 +534,7 @@ finally put them back in their new location, uder `src/static/skins/no-skin`. * NEW: Bump log4js for improved logging * Fix: Remove URL schemes which don't have RFC standard * Fix: Fix safeRun subsequent restarts issue - * Fix: Allow safeRun to pass arguements to run.sh + * Fix: Allow safeRun to pass arguments to run.sh * Fix: Include script for more efficient import * Fix: Fix sysv comptibile script * Fix: Fix client side changeset spamming @@ -573,7 +573,7 @@ finally put them back in their new location, uder `src/static/skins/no-skin`. * Fix: Support Node 0.10 * Fix: Log HTTP on DEBUG log level * Fix: Server wont crash on import fails on 0 file import. - * Fix: Import no longer fails consistantly + * Fix: Import no longer fails consistently * Fix: Language support for non existing languages * Fix: Mobile support for chat notifications are now usable * Fix: Re-Enable Editbar buttons on reconnect @@ -605,7 +605,7 @@ finally put them back in their new location, uder `src/static/skins/no-skin`. * NEW: Admin dashboard mobile device support and new hooks for Admin dashboard * NEW: Get current API version from API * NEW: CLI script to delete pads - * Fix: Automatic client reconnection on disonnect + * Fix: Automatic client reconnection on disconnect * Fix: Text Export indentation now supports multiple indentations * Fix: Bugfix getChatHistory API method * Fix: Stop Chrome losing caret after paste is texted @@ -625,7 +625,7 @@ finally put them back in their new location, uder `src/static/skins/no-skin`. * Fix: Stop Opera browser inserting two new lines on enter keypress * Fix: Stop timeslider from showing NaN on pads with only one revision * Other: Allow timeslider tests to run and provide & fix various other frontend-tests - * Other: Begin dropping referene to Lite. Etherpad Lite is now named "Etherpad" + * Other: Begin dropping reference to Lite. Etherpad Lite is now named "Etherpad" * Other: Update to latest jQuery * Other: Change loading message asking user to please wait on first build * Other: Allow etherpad to use global npm installation (Safe since node 6.3) diff --git a/bin/checkPadDeltas.js b/bin/checkPadDeltas.js index 45ee27d72..4900595c3 100644 --- a/bin/checkPadDeltas.js +++ b/bin/checkPadDeltas.js @@ -51,7 +51,7 @@ const util = require('util'); let atext = Changeset.makeAText('\n'); - // run trough all revisions + // run through all revisions for (const revNum of revisions) { // console.log('Fetching', revNum) const revision = await db.get(`pad:${padId}:revs:${revNum}`); diff --git a/bin/cleanRun.sh b/bin/cleanRun.sh index 57de27e5c..36dd1e384 100755 --- a/bin/cleanRun.sh +++ b/bin/cleanRun.sh @@ -3,7 +3,7 @@ # Move to the folder where ep-lite is installed cd "$(dirname "$0")"/.. -# Source constants and usefull functions +# Source constants and useful functions . bin/functions.sh #Was this script started in the bin folder? if yes move out diff --git a/bin/debugRun.sh b/bin/debugRun.sh index 9b2fff9bd..e04db88d0 100755 --- a/bin/debugRun.sh +++ b/bin/debugRun.sh @@ -3,7 +3,7 @@ # Move to the folder where ep-lite is installed cd "$(dirname "$0")"/.. -# Source constants and usefull functions +# Source constants and useful functions . bin/functions.sh # Prepare the environment diff --git a/bin/fastRun.sh b/bin/fastRun.sh index 90d83dc8e..63524e793 100755 --- a/bin/fastRun.sh +++ b/bin/fastRun.sh @@ -12,7 +12,7 @@ set -eu # source: https://stackoverflow.com/questions/59895/how-to-get-the-source-directory-of-a-bash-script-from-within-the-script-itself#246128 DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" -# Source constants and usefull functions +# Source constants and useful functions . ${DIR}/../bin/functions.sh echo "Running directly, without checking/installing dependencies" diff --git a/bin/installDeps.sh b/bin/installDeps.sh index bdce38fc7..be3f1fd8f 100755 --- a/bin/installDeps.sh +++ b/bin/installDeps.sh @@ -3,7 +3,7 @@ # Move to the folder where ep-lite is installed cd "$(dirname "$0")"/.. -# Source constants and usefull functions +# Source constants and useful functions . bin/functions.sh # Is node installed? diff --git a/bin/repairPad.js b/bin/repairPad.js index ff2da9776..ce0c6072e 100644 --- a/bin/repairPad.js +++ b/bin/repairPad.js @@ -23,7 +23,7 @@ const util = require('util'); (async () => { await util.promisify(npm.load)({}); - // intialize database + // initialize database require('ep_etherpad-lite/node/utils/Settings'); const db = require('ep_etherpad-lite/node/db/DB'); await db.init(); diff --git a/bin/run.sh b/bin/run.sh index 50bce4bdd..c10359904 100755 --- a/bin/run.sh +++ b/bin/run.sh @@ -3,7 +3,7 @@ # Move to the folder where ep-lite is installed cd "$(dirname "$0")"/.. -# Source constants and usefull functions +# Source constants and useful functions . bin/functions.sh ignoreRoot=0 diff --git a/doc/api/http_api.md b/doc/api/http_api.md index fb570a393..0cfc85a07 100644 --- a/doc/api/http_api.md +++ b/doc/api/http_api.md @@ -263,7 +263,7 @@ deletes a session #### getSessionInfo(sessionID) * API >= 1 -returns informations about a session +returns information about a session *Example returns:* * `{code: 0, message:"ok", data: {authorID: "a.s8oes9dhwrvt0zif", groupID: g.s8oes9dhwrvt0zif, validUntil: 1312201246}}` diff --git a/doc/localization.md b/doc/localization.md index 54675e2da..d047944ff 100644 --- a/doc/localization.md +++ b/doc/localization.md @@ -95,7 +95,7 @@ For example, if you want to replace `Chat` with `Notes`, simply add... ## Customization for Administrators -As an Etherpad administrator, it is possible to overwrite core mesages as well as messages in plugins. These include error messages, labels, and user instructions. Whereas the localization in the source code is in separate files separated by locale, an administrator's custom localizations are in `settings.json` under the `customLocaleStrings` key, with each locale separated by a sub-key underneath. +As an Etherpad administrator, it is possible to overwrite core messages as well as messages in plugins. These include error messages, labels, and user instructions. Whereas the localization in the source code is in separate files separated by locale, an administrator's custom localizations are in `settings.json` under the `customLocaleStrings` key, with each locale separated by a sub-key underneath. For example, let's say you want to change the text on the "New Pad" button on Etherpad's home page. If you look in `locales/en.json` (or `locales/en-gb.json`) you'll see the key for this text is `"index.newPad"`. You could add the following to `settings.json`: diff --git a/settings.json.docker b/settings.json.docker index 081af38b8..6938b2713 100644 --- a/settings.json.docker +++ b/settings.json.docker @@ -171,7 +171,7 @@ * * * Database specific settings are dependent on dbType, and go in dbSettings. - * Remember that since Etherpad 1.6.0 you can also store these informations in + * Remember that since Etherpad 1.6.0 you can also store this information in * credentials.json. * * For a complete list of the supported drivers, please refer to: diff --git a/settings.json.template b/settings.json.template index f4cfdea62..3c9c50837 100644 --- a/settings.json.template +++ b/settings.json.template @@ -162,7 +162,7 @@ * * * Database specific settings are dependent on dbType, and go in dbSettings. - * Remember that since Etherpad 1.6.0 you can also store these informations in + * Remember that since Etherpad 1.6.0 you can also store this information in * credentials.json. * * For a complete list of the supported drivers, please refer to: diff --git a/src/node/db/DB.js b/src/node/db/DB.js index 12d3d9f80..c0993e8ec 100644 --- a/src/node/db/DB.js +++ b/src/node/db/DB.js @@ -1,7 +1,7 @@ 'use strict'; /** - * The DB Module provides a database initalized with the settings + * The DB Module provides a database initialized with the settings * provided by the settings module */ @@ -36,7 +36,7 @@ const db = exports.db = null; /** - * Initalizes the database with the settings provided by the settings module + * Initializes the database with the settings provided by the settings module * @param {Function} callback */ exports.init = async () => await new Promise((resolve, reject) => { diff --git a/src/node/db/PadManager.js b/src/node/db/PadManager.js index 11e8cb1a5..48507949e 100644 --- a/src/node/db/PadManager.js +++ b/src/node/db/PadManager.js @@ -139,7 +139,7 @@ exports.getPad = async (id, text) => { // try to load pad pad = new Pad(id); - // initalize the pad + // initialize the pad await pad.init(text); globalPads.set(id, pad); padList.addPad(id); diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 01981d1f0..2a1f6385b 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -46,7 +46,7 @@ const rateLimiter = new RateLimiterMemory({ }); /** - * A associative array that saves informations about a session + * A associative array that saves information about a session * key = sessionId * values = padId, readonlyPadId, readonly, author, rev * padId = the real padId of the pad @@ -88,7 +88,7 @@ exports.setSocketIO = (socket_io) => { exports.handleConnect = (socket) => { stats.meter('connects').mark(); - // Initalize sessioninfos for this new session + // Initialize sessioninfos for this new session sessioninfos[socket.id] = {}; }; diff --git a/src/node/hooks/express/openapi.js b/src/node/hooks/express/openapi.js index 444077fda..950b56601 100644 --- a/src/node/hooks/express/openapi.js +++ b/src/node/hooks/express/openapi.js @@ -141,7 +141,7 @@ const resources = { // We need an operation that returns a SessionInfo so it can be picked up by the codegen :( info: { operationId: 'getSessionInfo', - summary: 'returns informations about a session', + summary: 'returns information about a session', responseSchema: {info: {$ref: '#/components/schemas/SessionInfo'}}, }, }, diff --git a/src/node/hooks/express/socketio.js b/src/node/hooks/express/socketio.js index 3d9e9debe..58d2f5a44 100644 --- a/src/node/hooks/express/socketio.js +++ b/src/node/hooks/express/socketio.js @@ -88,7 +88,7 @@ exports.expressCreateServer = (hookName, args, cb) => { // http://stackoverflow.com/questions/23981741/minify-socket-io-socket-io-js-with-1-0 // if(settings.minify) io.enable('browser client minification'); - // Initalize the Socket.IO Router + // Initialize the Socket.IO Router socketIORouter.setSocketIO(io); socketIORouter.addComponent('pad', padMessageHandler); diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 66a1926a9..49d26ba6b 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -311,7 +311,7 @@ const statFile = (filename, callback, dirStatLimit) => { const lastModifiedDateOfEverything = (callback) => { const folders2check = [`${ROOT_DIR}js/`, `${ROOT_DIR}css/`]; let latestModification = 0; - // go trough this two folders + // go through this two folders async.forEach(folders2check, (path, callback) => { // read the files in the folder fs.readdir(path, (err, files) => { @@ -320,7 +320,7 @@ const lastModifiedDateOfEverything = (callback) => { // we wanna check the directory itself for changes too files.push('.'); - // go trough all files in this folder + // go through all files in this folder async.forEach(files, (filename, callback) => { // get the stat data of this file fs.stat(`${path}/${filename}`, (err, stats) => { diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 1deee9f7e..753ab7d02 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -294,7 +294,7 @@ const handshake = () => { // set some client vars window.clientVars = obj.data; - // initalize the pad + // initialize the pad pad._afterHandshake(); if (clientVars.readonly) { diff --git a/tests/backend/fuzzImportTest.js b/tests/backend/fuzzImportTest.js index e2667e8f9..eb5a17d17 100644 --- a/tests/backend/fuzzImportTest.js +++ b/tests/backend/fuzzImportTest.js @@ -42,7 +42,7 @@ function runTest(number) { let fN = '/test.txt'; let cT = 'text/plain'; - // To be more agressive every other test we mess with Etherpad + // To be more aggressive every other test we mess with Etherpad // We provide a weird file name and also set a weird contentType if (number % 2 == 0) { fN = froth().toString(); diff --git a/tests/backend/specs/api/importexport.js b/tests/backend/specs/api/importexport.js index 1a9e94332..1e4461530 100644 --- a/tests/backend/specs/api/importexport.js +++ b/tests/backend/specs/api/importexport.js @@ -94,7 +94,7 @@ const testImports = { wantText: ' word1 word2 word3\n\n', }, 'nonBreakingSpacePreceededBySpaceBetweenWords': { - description: 'A non-breaking space preceeded by a normal space', + description: 'A non-breaking space preceded by a normal space', input: '  word1  word2  word3
                    ', wantHTML: ' word1  word2  word3

                    ', wantText: ' word1 word2 word3\n\n', @@ -191,7 +191,7 @@ const testImports = { wantText: 'а б в г ґ д е є ж з и і ї й к л м н о\nmultiple\n lines\n in\n pre\n\nп р с т у ф х ц ч ш щ ю я ь\n\n', }, 'preIntroducesASpace': { - description: 'pre should be on a new line not preceeded by a space', + description: 'pre should be on a new line not preceded by a space', input: `

                    1

                    preline
                    diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js
                    index 15ec4f0be..a0bbec62c 100644
                    --- a/tests/backend/specs/api/pad.js
                    +++ b/tests/backend/specs/api/pad.js
                    @@ -102,9 +102,9 @@ describe(__filename, function () {
                                              -> setText(padId, "hello world")
                                               -> getLastEdited(padID) -- Should be when pad was made
                                                -> getText(padId) -- Should be "hello world"
                    -                            -> movePad(padID, newPadId) -- Should provide consistant pad data
                    +                            -> movePad(padID, newPadId) -- Should provide consistent pad data
                                                  -> getText(newPadId) -- Should be "hello world"
                    -                              -> movePad(newPadID, originalPadId) -- Should provide consistant pad data
                    +                              -> movePad(newPadID, originalPadId) -- Should provide consistent pad data
                                                    -> getText(originalPadId) -- Should be "hello world"
                                                     -> getLastEdited(padID) -- Should not be 0
                                                     -> appendText(padID, "hello")
                    diff --git a/tests/backend/specs/contentcollector.js b/tests/backend/specs/contentcollector.js
                    index e6aff389a..62bdf5d5d 100644
                    --- a/tests/backend/specs/contentcollector.js
                    +++ b/tests/backend/specs/contentcollector.js
                    @@ -140,7 +140,7 @@ const tests = {
                         wantText: ['  word1  word2   word3'],
                       },
                       nonBreakingSpacePreceededBySpaceBetweenWords: {
                    -    description: 'A non-breaking space preceeded by a normal space',
                    +    description: 'A non-breaking space preceded by a normal space',
                         html: '  word1  word2  word3
                    ', wantLineAttribs: ['+l'], wantText: [' word1 word2 word3'], @@ -240,7 +240,7 @@ pre ], }, preIntroducesASpace: { - description: 'pre should be on a new line not preceeded by a space', + description: 'pre should be on a new line not preceded by a space', html: `

                    1

                    preline
                    diff --git a/tests/frontend/runner.js b/tests/frontend/runner.js
                    index 616c065b3..fcc56a7d6 100644
                    --- a/tests/frontend/runner.js
                    +++ b/tests/frontend/runner.js
                    @@ -170,7 +170,7 @@ $(() => {
                         }
                       });
                     
                    -  // initalize the test helper
                    +  // initialize the test helper
                       helper.init(() => {
                         // configure and start the test framework
                         const grep = getURLParameter('grep');
                    
                    From 1076783985a9d8274590a97c585a7277d19ee3cb Mon Sep 17 00:00:00 2001
                    From: John McLear 
                    Date: Wed, 3 Feb 2021 10:39:30 +0000
                    Subject: [PATCH 224/357] tests: backend test coverage for #3227 where a group
                     cannot be deleted if it has pads.
                    
                    ---
                     tests/backend/specs/api/sessionsAndGroups.js | 77 ++++++++++++++++++++
                     1 file changed, 77 insertions(+)
                    
                    diff --git a/tests/backend/specs/api/sessionsAndGroups.js b/tests/backend/specs/api/sessionsAndGroups.js
                    index 3b5e1a912..655324722 100644
                    --- a/tests/backend/specs/api/sessionsAndGroups.js
                    +++ b/tests/backend/specs/api/sessionsAndGroups.js
                    @@ -93,6 +93,83 @@ describe(__filename, function () {
                                 assert(res.body.data.groupID);
                               });
                         });
                    +
                    +    // Test coverage for https://github.com/ether/etherpad-lite/issues/4227
                    +    // Creates a group, creates 2 sessions, 2 pads and then deletes the group.
                    +    it('createGroup', async function () {
                    +      await api.get(endPoint('createGroup'))
                    +          .expect(200)
                    +          .expect('Content-Type', /json/)
                    +          .expect((res) => {
                    +            assert.equal(res.body.code, 0);
                    +            assert(res.body.data.groupID);
                    +            groupID = res.body.data.groupID;
                    +          });
                    +    });
                    +
                    +    it('createAuthor', async function () {
                    +      await api.get(endPoint('createAuthor'))
                    +          .expect(200)
                    +          .expect('Content-Type', /json/)
                    +          .expect((res) => {
                    +            assert.equal(res.body.code, 0);
                    +            assert(res.body.data.authorID);
                    +            authorID = res.body.data.authorID;
                    +          });
                    +    });
                    +
                    +    it('createSession', async function () {
                    +      await api.get(`${endPoint('createSession')
                    +      }&authorID=${authorID}&groupID=${groupID}&validUntil=999999999999`)
                    +          .expect(200)
                    +          .expect('Content-Type', /json/)
                    +          .expect((res) => {
                    +            assert.equal(res.body.code, 0);
                    +            assert(res.body.data.sessionID);
                    +            sessionID = res.body.data.sessionID;
                    +          });
                    +    });
                    +
                    +    it('createSession', async function () {
                    +      await api.get(`${endPoint('createSession')
                    +      }&authorID=${authorID}&groupID=${groupID}&validUntil=999999999999`)
                    +          .expect(200)
                    +          .expect('Content-Type', /json/)
                    +          .expect((res) => {
                    +            assert.equal(res.body.code, 0);
                    +            assert(res.body.data.sessionID);
                    +            sessionID = res.body.data.sessionID;
                    +          });
                    +    });
                    +
                    +    it('createGroupPad', async function () {
                    +      await api.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=x1234567`)
                    +          .expect(200)
                    +          .expect('Content-Type', /json/)
                    +          .expect((res) => {
                    +            assert.equal(res.body.code, 0);
                    +          });
                    +    });
                    +
                    +    it('createGroupPad', async function () {
                    +      await api.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=x12345678`)
                    +          .expect(200)
                    +          .expect('Content-Type', /json/)
                    +          .expect((res) => {
                    +            assert.equal(res.body.code, 0);
                    +          });
                    +    });
                    +
                    +    it('deleteGroup', async function () {
                    +      await api.get(`${endPoint('deleteGroup')}&groupID=${groupID}`)
                    +          .expect(200)
                    +          .expect('Content-Type', /json/)
                    +          .expect((res) => {
                    +            assert.equal(res.body.code, 0);
                    +          });
                    +    });
                    +    // End of coverage for https://github.com/ether/etherpad-lite/issues/4227
                    +
                       });
                     
                       describe('API: Author creation', function () {
                    
                    From a5bde7982a6a020a7b2f28958874a96158fc4cf6 Mon Sep 17 00:00:00 2001
                    From: "translatewiki.net" 
                    Date: Thu, 4 Feb 2021 08:58:26 +0100
                    Subject: [PATCH 225/357] Localisation updates from https://translatewiki.net.
                    
                    ---
                     src/locales/fy.json  | 5 +----
                     src/locales/gu.json  | 3 ---
                     src/locales/hi.json  | 1 -
                     src/locales/krc.json | 1 -
                     src/locales/pt.json  | 2 +-
                     src/locales/sl.json  | 2 +-
                     src/locales/tcy.json | 5 +----
                     7 files changed, 4 insertions(+), 15 deletions(-)
                    
                    diff --git a/src/locales/fy.json b/src/locales/fy.json
                    index ed4d605f7..093b40e32 100644
                    --- a/src/locales/fy.json
                    +++ b/src/locales/fy.json
                    @@ -35,8 +35,5 @@
                     	"timeslider.month.october": "oktober",
                     	"timeslider.month.november": "novimber",
                     	"timeslider.month.december": "desimber",
                    -	"pad.userlist.unnamed": "sûnder namme",
                    -	"pad.userlist.guest": "Gast",
                    -	"pad.userlist.deny": "Wegerje",
                    -	"pad.userlist.approve": "Goedkarre"
                    +	"pad.userlist.unnamed": "sûnder namme"
                     }
                    diff --git a/src/locales/gu.json b/src/locales/gu.json
                    index 894a5e3f8..b3a91d42d 100644
                    --- a/src/locales/gu.json
                    +++ b/src/locales/gu.json
                    @@ -45,9 +45,6 @@
                     	"timeslider.month.december": "ડિસેમ્બર",
                     	"pad.userlist.entername": "તમારું નામ દાખલ કરો",
                     	"pad.userlist.unnamed": "અનામી",
                    -	"pad.userlist.guest": "મહેમાન",
                    -	"pad.userlist.deny": "નકારો",
                    -	"pad.userlist.approve": "મંજૂર",
                     	"pad.impexp.importbutton": "આયાત કરો",
                     	"pad.impexp.importing": "આયાત કરે છે..."
                     }
                    diff --git a/src/locales/hi.json b/src/locales/hi.json
                    index c78d74d0d..b238694a5 100644
                    --- a/src/locales/hi.json
                    +++ b/src/locales/hi.json
                    @@ -31,7 +31,6 @@
                     	"timeslider.month.october": "अक्टूबर",
                     	"timeslider.month.november": "नवम्बर",
                     	"timeslider.month.december": "दिसम्बर",
                    -	"pad.userlist.guest": "अतिथि",
                     	"pad.impexp.importbutton": "अभी आयात करें",
                     	"pad.impexp.importing": "आयात कर रहा...",
                     	"pad.impexp.importfailed": "आयात विफल हुआ",
                    diff --git a/src/locales/krc.json b/src/locales/krc.json
                    index 11d3b6b44..408b78630 100644
                    --- a/src/locales/krc.json
                    +++ b/src/locales/krc.json
                    @@ -34,6 +34,5 @@
                     	"timeslider.month.october": "октябрь",
                     	"timeslider.month.november": "ноябрь",
                     	"timeslider.month.december": "декабрь",
                    -	"pad.userlist.guest": "Къонакъ",
                     	"pad.impexp.importing": "Импорт этиу…"
                     }
                    diff --git a/src/locales/pt.json b/src/locales/pt.json
                    index 82972333e..334c8bf37 100644
                    --- a/src/locales/pt.json
                    +++ b/src/locales/pt.json
                    @@ -60,7 +60,7 @@
                     	"pad.toolbar.ol.title": "Lista ordenada (Ctrl+Shift+N)",
                     	"pad.toolbar.ul.title": "Lista desordenada (Ctrl+Shift+L)",
                     	"pad.toolbar.indent.title": "Indentar (TAB)",
                    -	"pad.toolbar.unindent.title": "Indentação (Shift+TAB)",
                    +	"pad.toolbar.unindent.title": "Remover indentação (Shift+TAB)",
                     	"pad.toolbar.undo.title": "Desfazer (Ctrl+Z)",
                     	"pad.toolbar.redo.title": "Refazer (Ctrl+Y)",
                     	"pad.toolbar.clearAuthorship.title": "Limpar cores de autoria (Ctrl+Shift+C)",
                    diff --git a/src/locales/sl.json b/src/locales/sl.json
                    index 5ceb49d34..be11d4a76 100644
                    --- a/src/locales/sl.json
                    +++ b/src/locales/sl.json
                    @@ -81,7 +81,7 @@
                     	"pad.modals.corruptPad.explanation": "Dokument, do katerega želite dostopati, je poškodovan.",
                     	"pad.modals.corruptPad.cause": "Razlog za to je morda napačna konfiguracija strežnika ali neko drugo nepričakovano vedenje. Prosimo, stopite v stik s skrbnikom storitve.",
                     	"pad.modals.deleted": "Izbrisano.",
                    -	"pad.modals.deleted.explanation": "Dokument je bil odstranjen.",
                    +	"pad.modals.deleted.explanation": "Dokument je odstranjen.",
                     	"pad.modals.disconnected": "Vaša povezava je bila prekinjena.",
                     	"pad.modals.disconnected.explanation": "Povezava s strežnikom je bila izgubljena.",
                     	"pad.modals.disconnected.cause": "Strežnik morda ni na voljo. Prosimo, obvestite skrbnika storitve, če se to zgodi večkrat.",
                    diff --git a/src/locales/tcy.json b/src/locales/tcy.json
                    index 3cd815b6a..0e56ec596 100644
                    --- a/src/locales/tcy.json
                    +++ b/src/locales/tcy.json
                    @@ -18,7 +18,6 @@
                     	"pad.colorpicker.save": "ಒರಿಪಾಲೆ",
                     	"pad.colorpicker.cancel": "ವಜಾ ಮಲ್ಪುಲೆ",
                     	"pad.loading": "ದಿಂಜಾವೊಂದುಂಡು......",
                    -	"pad.wrongPassword": "ಇರೇನಾ ಪಾಸ್ ವರ್ಡ್ ತಪ್ಪತುಂಡ್",
                     	"pad.settings.padSettings": "ಪ್ಯಾಡ್ ಸಂಯೋಜನೆ",
                     	"pad.settings.language": "ಬಾಸೆ:",
                     	"pad.importExport.exportetherpad": "Etherpad",
                    @@ -42,7 +41,5 @@
                     	"timeslider.month.november": "ನವಂಬರೊ",
                     	"timeslider.month.december": "ದಸಂಬರೊ",
                     	"pad.userlist.entername": "ಈರೆನೆ ಪುದರ್ ಬರೆಲೆ",
                    -	"pad.userlist.unnamed": "ಪುದರ್ ಇಜ್ಜಂತಿನವು",
                    -	"pad.userlist.guest": "ಬಿನ್ನೆರ್",
                    -	"pad.userlist.approve": "ಒಪ್ಪಂದ ಅಂಡ್"
                    +	"pad.userlist.unnamed": "ಪುದರ್ ಇಜ್ಜಂತಿನವು"
                     }
                    
                    From c64ee6ff4cdfe20c4a79348ddcbd2396b82ff88c Mon Sep 17 00:00:00 2001
                    From: Richard Hansen 
                    Date: Thu, 4 Feb 2021 00:28:22 -0500
                    Subject: [PATCH 226/357] pluginfw: Call `npm.load()` before using `npm`
                    
                    This code is only used when testing `read-installed.js` by running it
                    directly (e.g., `node src/static/js/pluginfw/read-installed.js`).
                    ---
                     src/static/js/pluginfw/read-installed.js | 13 ++++++++-----
                     1 file changed, 8 insertions(+), 5 deletions(-)
                    
                    diff --git a/src/static/js/pluginfw/read-installed.js b/src/static/js/pluginfw/read-installed.js
                    index b035cb0ac..e679f030a 100644
                    --- a/src/static/js/pluginfw/read-installed.js
                    +++ b/src/static/js/pluginfw/read-installed.js
                    @@ -303,11 +303,14 @@ if (module === require.main) {
                       console.error('testing');
                     
                       let called = 0;
                    -  readInstalled(process.cwd(), (er, map) => {
                    -    console.error(called++);
                    -    if (er) return console.error(er.stack || er.message);
                    -    cleanup(map);
                    -    console.error(util.inspect(map, true, 10, true));
                    +  npm.load({}, (err) => {
                    +    if (err != null) throw err;
                    +    readInstalled(process.cwd(), (er, map) => {
                    +      console.error(called++);
                    +      if (er) return console.error(er.stack || er.message);
                    +      cleanup(map);
                    +      console.error(util.inspect(map, true, 10, true));
                    +    });
                       });
                     
                       const seen = [];
                    
                    From a06662fd008bddf3c3a735d0cdefea1692df4f18 Mon Sep 17 00:00:00 2001
                    From: Richard Hansen 
                    Date: Wed, 3 Feb 2021 19:26:43 -0500
                    Subject: [PATCH 227/357] pluginfw: Delete commented-out code
                    
                    ---
                     src/static/js/pluginfw/plugins.js        | 3 ---
                     src/static/js/pluginfw/read-installed.js | 9 ---------
                     2 files changed, 12 deletions(-)
                    
                    diff --git a/src/static/js/pluginfw/plugins.js b/src/static/js/pluginfw/plugins.js
                    index 4acdee7bd..d6ddcf520 100644
                    --- a/src/static/js/pluginfw/plugins.js
                    +++ b/src/static/js/pluginfw/plugins.js
                    @@ -97,9 +97,6 @@ exports.getPackages = async () => {
                             delete packages[name].dependencies;
                             delete packages[name].parent;
                           }
                    -
                    -      // I don't think we need recursion
                    -      // if (deps[name].dependencies !== undefined) flatten(deps[name].dependencies);
                         });
                       };
                     
                    diff --git a/src/static/js/pluginfw/read-installed.js b/src/static/js/pluginfw/read-installed.js
                    index e679f030a..dc331dae7 100644
                    --- a/src/static/js/pluginfw/read-installed.js
                    +++ b/src/static/js/pluginfw/read-installed.js
                    @@ -131,8 +131,6 @@ function readInstalled(folder, cb) {
                     
                     var rpSeen = {};
                     function readInstalled_(folder, parent, name, reqver, depth, maxDepth, cb) {
                    -  // console.error(folder, name)
                    -
                       let installed,
                         obj,
                         real,
                    @@ -161,7 +159,6 @@ function readInstalled_(folder, parent, name, reqver, depth, maxDepth, cb) {
                           return next(er);
                         }
                         fs.realpath(folder, (er, rp) => {
                    -      // console.error("realpath(%j) = %j", folder, rp)
                           real = rp;
                           if (st.isSymbolicLink()) link = rp;
                           next(er);
                    @@ -176,7 +173,6 @@ function readInstalled_(folder, parent, name, reqver, depth, maxDepth, cb) {
                           errState = er;
                           return cb(null, []);
                         }
                    -    // console.error('next', installed, obj && typeof obj, name, real)
                         if (!installed || !obj || !real || called) return;
                         called = true;
                         if (rpSeen[real]) return cb(null, rpSeen[real]);
                    @@ -256,13 +252,10 @@ const fuSeen = [];
                     function findUnmet(obj) {
                       if (fuSeen.indexOf(obj) !== -1) return;
                       fuSeen.push(obj);
                    -  // console.error("find unmet", obj.name, obj.parent && obj.parent.name)
                       const deps = obj.dependencies = obj.dependencies || {};
                    -  // console.error(deps)
                       Object.keys(deps)
                           .filter((d) => typeof deps[d] === 'string')
                           .forEach((d) => {
                    -      // console.error("find unmet", obj.name, d, deps[d])
                             let r = obj.parent;
                             let found = null;
                             while (r && !found && typeof deps[d] === 'string') {
                    @@ -328,9 +321,7 @@ if (module === require.main) {
                           }
                         }
                         const dep = map.dependencies;
                    -    //    delete map.dependencies
                         if (dep) {
                    -      //      map.dependencies = dep
                           for (var i in dep) {
                             if (typeof dep[i] === 'object') {
                               cleanup(dep[i]);
                    
                    From 895764e04755b46d30a79ce2d0ed7e9334bba991 Mon Sep 17 00:00:00 2001
                    From: Richard Hansen 
                    Date: Thu, 4 Feb 2021 02:04:59 -0500
                    Subject: [PATCH 228/357] pluginfw: Return from `findUnmet()` early if not
                     given an object
                    
                    For some reason strings are sometimes passed to `findUnmet()`, which
                    is obviously unexpected given the way the code is written. Rather than
                    figure out why strings are passed and how to safely avoid passing
                    strings, just return early. The net effect is the same, but returning
                    early avoids setting a property on a string, which is prohibited in
                    strict mode.
                    ---
                     src/static/js/pluginfw/read-installed.js | 1 +
                     1 file changed, 1 insertion(+)
                    
                    diff --git a/src/static/js/pluginfw/read-installed.js b/src/static/js/pluginfw/read-installed.js
                    index dc331dae7..4c2a00880 100644
                    --- a/src/static/js/pluginfw/read-installed.js
                    +++ b/src/static/js/pluginfw/read-installed.js
                    @@ -250,6 +250,7 @@ function resolveInheritance(obj) {
                     // No I/O
                     const fuSeen = [];
                     function findUnmet(obj) {
                    +  if (typeof obj !== 'object') return;
                       if (fuSeen.indexOf(obj) !== -1) return;
                       fuSeen.push(obj);
                       const deps = obj.dependencies = obj.dependencies || {};
                    
                    From 9a86ebec2a2a6a02cb1d33f86ede41454bdb14a3 Mon Sep 17 00:00:00 2001
                    From: Richard Hansen 
                    Date: Thu, 4 Feb 2021 02:10:55 -0500
                    Subject: [PATCH 229/357] pluginfw: Fix state reset logic
                    
                    ---
                     src/static/js/pluginfw/read-installed.js | 5 +++--
                     1 file changed, 3 insertions(+), 2 deletions(-)
                    
                    diff --git a/src/static/js/pluginfw/read-installed.js b/src/static/js/pluginfw/read-installed.js
                    index 4c2a00880..7564526ae 100644
                    --- a/src/static/js/pluginfw/read-installed.js
                    +++ b/src/static/js/pluginfw/read-installed.js
                    @@ -96,6 +96,8 @@ const asyncMap = require('slide').asyncMap;
                     const semver = require('semver');
                     const log = require('log4js').getLogger('pluginfw');
                     
                    +let fuSeen = [];
                    +
                     function readJson(file, callback) {
                       fs.readFile(file, (er, buf) => {
                         if (er) {
                    @@ -115,9 +117,9 @@ module.exports = readInstalled;
                     function readInstalled(folder, cb) {
                       /* This is where we clear the cache, these three lines are all the
                        * new code there is */
                    +  fuSeen = [];
                       rpSeen = {};
                       riSeen = [];
                    -  const fuSeen = [];
                     
                       const d = npm.config.get('depth');
                       readInstalled_(folder, null, null, null, 0, d, (er, obj) => {
                    @@ -248,7 +250,6 @@ function resolveInheritance(obj) {
                     
                     // find unmet deps by walking up the tree object.
                     // No I/O
                    -const fuSeen = [];
                     function findUnmet(obj) {
                       if (typeof obj !== 'object') return;
                       if (fuSeen.indexOf(obj) !== -1) return;
                    
                    From 2b32bc1840d76f51584ea835e42d149d1eaad532 Mon Sep 17 00:00:00 2001
                    From: Richard Hansen 
                    Date: Wed, 3 Feb 2021 18:31:42 -0500
                    Subject: [PATCH 230/357] lint: src/static/js/pluginfw/plugins.js
                    
                    ---
                     src/static/js/pluginfw/plugins.js | 92 ++++++++++++++-----------------
                     1 file changed, 42 insertions(+), 50 deletions(-)
                    
                    diff --git a/src/static/js/pluginfw/plugins.js b/src/static/js/pluginfw/plugins.js
                    index d6ddcf520..417fc1fe9 100644
                    --- a/src/static/js/pluginfw/plugins.js
                    +++ b/src/static/js/pluginfw/plugins.js
                    @@ -6,7 +6,6 @@ const readInstalled = require('./read-installed.js');
                     const path = require('path');
                     const tsort = require('./tsort');
                     const util = require('util');
                    -const _ = require('underscore');
                     const settings = require('../../../node/utils/Settings');
                     
                     const pluginUtils = require('./shared');
                    @@ -16,49 +15,42 @@ exports.prefix = 'ep_';
                     
                     exports.formatPlugins = () => Object.keys(defs.plugins).join(', ');
                     
                    -exports.formatPluginsWithVersion = () => {
                    -  const plugins = [];
                    -  _.forEach(defs.plugins, (plugin) => {
                    -    if (plugin.package.name !== 'ep_etherpad-lite') {
                    -      const pluginStr = `${plugin.package.name}@${plugin.package.version}`;
                    -      plugins.push(pluginStr);
                    -    }
                    -  });
                    -  return plugins.join(', ');
                    -};
                    +exports.formatPluginsWithVersion = () => Object.values(defs.plugins)
                    +    .filter((plugin) => plugin.package.name !== 'ep_etherpad-lite')
                    +    .map((plugin) => `${plugin.package.name}@${plugin.package.version}`)
                    +    .join(', ');
                     
                    -exports.formatParts = () => _.map(defs.parts, (part) => part.full_name).join('\n');
                    +exports.formatParts = () => defs.parts.map((part) => part.full_name).join('\n');
                     
                    -exports.formatHooks = (hook_set_name) => {
                    +exports.formatHooks = (hookSetName) => {
                       const res = [];
                    -  const hooks = pluginUtils.extractHooks(defs.parts, hook_set_name || 'hooks');
                    -
                    -  _.chain(hooks).keys().forEach((hook_name) => {
                    -    _.forEach(hooks[hook_name], (hook) => {
                    +  const hooks = pluginUtils.extractHooks(defs.parts, hookSetName || 'hooks');
                    +  for (const registeredHooks of Object.values(hooks)) {
                    +    for (const hook of registeredHooks) {
                           res.push(`
                    ${hook.hook_name}
                    ${hook.hook_fn_name} ` + `from ${hook.part.full_name}
                    `); - }); - }); + } + } return `
                    ${res.join('\n')}
                    `; }; const callInit = async () => { - await Promise.all(Object.keys(defs.plugins).map(async (plugin_name) => { - const plugin = defs.plugins[plugin_name]; - const ep_init = path.normalize(path.join(plugin.package.path, '.ep_initialized')); + await Promise.all(Object.keys(defs.plugins).map(async (pluginName) => { + const plugin = defs.plugins[pluginName]; + const epInit = path.normalize(path.join(plugin.package.path, '.ep_initialized')); try { - await fs.stat(ep_init); + await fs.stat(epInit); } catch (err) { - await fs.writeFile(ep_init, 'done'); - await hooks.aCallAll(`init_${plugin_name}`, {}); + await fs.writeFile(epInit, 'done'); + await hooks.aCallAll(`init_${pluginName}`, {}); } })); }; -exports.pathNormalization = (part, hook_fn_name, hook_name) => { - const tmp = hook_fn_name.split(':'); // hook_fn_name might be something like 'C:\\foo.js:myFunc'. +exports.pathNormalization = (part, hookFnName, hookName) => { + const tmp = hookFnName.split(':'); // hookFnName might be something like 'C:\\foo.js:myFunc'. // If there is a single colon assume it's 'filename:funcname' not 'C:\\filename'. - const functionName = (tmp.length > 1 ? tmp.pop() : null) || hook_name; + const functionName = (tmp.length > 1 ? tmp.pop() : null) || hookName; const moduleName = tmp.join(':') || part.plugin; const packageDir = path.dirname(defs.plugins[part.plugin].package.path); const fileName = path.normalize(path.join(packageDir, moduleName)); @@ -89,15 +81,15 @@ exports.getPackages = async () => { const packages = {}; const flatten = (deps) => { - _.chain(deps).keys().each((name) => { + for (const [name, dep] of Object.entries(deps)) { if (name.indexOf(exports.prefix) === 0) { - packages[name] = _.clone(deps[name]); + packages[name] = {...dep}; // Delete anything that creates loops so that the plugin // list can be sent as JSON to the web client delete packages[name].dependencies; delete packages[name].parent; } - }); + } }; const tmp = {}; @@ -106,40 +98,40 @@ exports.getPackages = async () => { return packages; }; -const loadPlugin = async (packages, plugin_name, plugins, parts) => { - const plugin_path = path.resolve(packages[plugin_name].path, 'ep.json'); +const loadPlugin = async (packages, pluginName, plugins, parts) => { + const pluginPath = path.resolve(packages[pluginName].path, 'ep.json'); try { - const data = await fs.readFile(plugin_path); + const data = await fs.readFile(pluginPath); try { const plugin = JSON.parse(data); - plugin.package = packages[plugin_name]; - plugins[plugin_name] = plugin; - _.each(plugin.parts, (part) => { - part.plugin = plugin_name; - part.full_name = `${plugin_name}/${part.name}`; + plugin.package = packages[pluginName]; + plugins[pluginName] = plugin; + for (const part of plugin.parts) { + part.plugin = pluginName; + part.full_name = `${pluginName}/${part.name}`; parts[part.full_name] = part; - }); + } } catch (ex) { - console.error(`Unable to parse plugin definition file ${plugin_path}: ${ex.toString()}`); + console.error(`Unable to parse plugin definition file ${pluginPath}: ${ex.toString()}`); } } catch (er) { - console.error(`Unable to load plugin definition file ${plugin_path}`); + console.error(`Unable to load plugin definition file ${pluginPath}`); } }; const partsToParentChildList = (parts) => { const res = []; - _.chain(parts).keys().forEach((name) => { - _.each(parts[name].post || [], (child_name) => { - res.push([name, child_name]); - }); - _.each(parts[name].pre || [], (parent_name) => { - res.push([parent_name, name]); - }); + for (const name of Object.keys(parts)) { + for (const childName of parts[name].post || []) { + res.push([name, childName]); + } + for (const parentName of parts[name].pre || []) { + res.push([parentName, name]); + } if (!parts[name].pre && !parts[name].post) { res.push([name, `:${name}`]); // Include apps with no dependency info } - }); + } return res; }; From 2c80c1f2dab5c7dc6daf1a375faedbd1acc9ae8a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 3 Feb 2021 18:31:42 -0500 Subject: [PATCH 231/357] lint: src/static/js/pluginfw/read-installed.js --- src/static/js/pluginfw/read-installed.js | 48 ++++++++++++------------ 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/static/js/pluginfw/read-installed.js b/src/static/js/pluginfw/read-installed.js index 7564526ae..6801b51aa 100644 --- a/src/static/js/pluginfw/read-installed.js +++ b/src/static/js/pluginfw/read-installed.js @@ -1,3 +1,5 @@ +'use strict'; + // A copy of npm/lib/utils/read-installed.js // that is hacked to not cache everything :) @@ -88,7 +90,6 @@ as far as the left-most node_modules folder. */ - const npm = require('npm/lib/npm.js'); const fs = require('graceful-fs'); const path = require('path'); @@ -97,8 +98,10 @@ const semver = require('semver'); const log = require('log4js').getLogger('pluginfw'); let fuSeen = []; +let riSeen = []; +let rpSeen = {}; -function readJson(file, callback) { +const readJson = (file, callback) => { fs.readFile(file, (er, buf) => { if (er) { callback(er); @@ -110,11 +113,9 @@ function readJson(file, callback) { callback(er); } }); -} +}; -module.exports = readInstalled; - -function readInstalled(folder, cb) { +const readInstalled = (folder, cb) => { /* This is where we clear the cache, these three lines are all the * new code there is */ fuSeen = []; @@ -129,10 +130,11 @@ function readInstalled(folder, cb) { resolveInheritance(obj); cb(null, obj); }); -} +}; -var rpSeen = {}; -function readInstalled_(folder, parent, name, reqver, depth, maxDepth, cb) { +module.exports = readInstalled; + +const readInstalled_ = (folder, parent, name, reqver, depth, maxDepth, cb) => { let installed, obj, real, @@ -229,11 +231,10 @@ function readInstalled_(folder, parent, name, reqver, depth, maxDepth, cb) { return cb(null, obj); }); } -} +}; // starting from a root object, call findUnmet on each layer of children -var riSeen = []; -function resolveInheritance(obj) { +const resolveInheritance = (obj) => { if (typeof obj !== 'object') return; if (riSeen.indexOf(obj) !== -1) return; riSeen.push(obj); @@ -246,11 +247,11 @@ function resolveInheritance(obj) { Object.keys(obj.dependencies).forEach((dep) => { resolveInheritance(obj.dependencies[dep]); }); -} +}; // find unmet deps by walking up the tree object. // No I/O -function findUnmet(obj) { +const findUnmet = (obj) => { if (typeof obj !== 'object') return; if (fuSeen.indexOf(obj) !== -1) return; fuSeen.push(obj); @@ -282,16 +283,16 @@ function findUnmet(obj) { } }); return obj; -} +}; -function copy(obj) { +const copy = (obj) => { if (!obj || typeof obj !== 'object') return obj; if (Array.isArray(obj)) return obj.map(copy); const o = {}; - for (const i in obj) o[i] = copy(obj[i]); + for (const [i, v] of Object.entries(obj)) o[i] = copy(v); return o; -} +}; if (module === require.main) { const util = require('util'); @@ -312,7 +313,7 @@ if (module === require.main) { function cleanup(map) { if (seen.indexOf(map) !== -1) return; seen.push(map); - for (var i in map) { + for (const i of Object.keys(map)) { switch (i) { case '_id': case 'path': @@ -322,12 +323,9 @@ if (module === require.main) { default: delete map[i]; } } - const dep = map.dependencies; - if (dep) { - for (var i in dep) { - if (typeof dep[i] === 'object') { - cleanup(dep[i]); - } + for (const dep of Object.values(map.dependencies || {})) { + if (typeof dep === 'object') { + cleanup(dep); } } return map; From 99ca57f3ab18c1c5cc41600dfdb74f9fdf90ff61 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 3 Feb 2021 18:31:42 -0500 Subject: [PATCH 232/357] lint: src/static/js/pluginfw/shared.js --- src/static/js/pluginfw/shared.js | 84 +++++++++++++++----------------- 1 file changed, 38 insertions(+), 46 deletions(-) diff --git a/src/static/js/pluginfw/shared.js b/src/static/js/pluginfw/shared.js index 981cd2558..a78697a2a 100644 --- a/src/static/js/pluginfw/shared.js +++ b/src/static/js/pluginfw/shared.js @@ -1,5 +1,5 @@ 'use strict'; -const _ = require('underscore'); + const defs = require('./plugin_defs'); const disabledHookReasons = { @@ -27,54 +27,49 @@ const loadFn = (path, hookName) => { let fn = require(path); functionName = functionName ? functionName : hookName; - _.each(functionName.split('.'), (name) => { + for (const name of functionName.split('.')) { fn = fn[name]; - }); + } return fn; }; -const extractHooks = (parts, hook_set_name, normalizer) => { +const extractHooks = (parts, hookSetName, normalizer) => { const hooks = {}; - _.each(parts, (part) => { - _.chain(part[hook_set_name] || {}) - .keys() - .each((hook_name) => { - let hook_fn_name = part[hook_set_name][hook_name]; - - /* On the server side, you can't just + for (const part of parts) { + for (const [hookName, regHookFnName] of Object.entries(part[hookSetName] || {})) { + /* On the server side, you can't just * require("pluginname/whatever") if the plugin is installed as * a dependency of another plugin! Bah, pesky little details of * npm... */ - if (normalizer) { - hook_fn_name = normalizer(part, hook_fn_name, hook_name); - } + const hookFnName = normalizer ? normalizer(part, regHookFnName, hookName) : regHookFnName; - const disabledReason = (disabledHookReasons[hook_set_name] || {})[hook_name]; - if (disabledReason) { - console.error( - `Hook ${hook_set_name}/${hook_name} is disabled. Reason: ${disabledReason}`); - console.error(`The hook function ${hook_fn_name} from plugin ${part.plugin} ` + - 'will never be called, which may cause the plugin to fail'); - console.error( - `Please update the ${part.plugin} plugin to not use the ${hook_name} hook`); - return; - } - let hook_fn; - try { - hook_fn = loadFn(hook_fn_name, hook_name); - if (!hook_fn) { - throw new Error('Not a function'); - } - } catch (exc) { - console.error(`Failed to load '${hook_fn_name}' for ` + - `'${part.full_name}/${hook_set_name}/${hook_name}': ${exc.toString()}`); - } - if (hook_fn) { - if (hooks[hook_name] == null) hooks[hook_name] = []; - hooks[hook_name].push({hook_name, hook_fn, hook_fn_name, part}); - } + const disabledReason = (disabledHookReasons[hookSetName] || {})[hookName]; + if (disabledReason) { + console.error(`Hook ${hookSetName}/${hookName} is disabled. Reason: ${disabledReason}`); + console.error(`The hook function ${hookFnName} from plugin ${part.plugin} ` + + 'will never be called, which may cause the plugin to fail'); + console.error(`Please update the ${part.plugin} plugin to not use the ${hookName} hook`); + return; + } + let hookFn; + try { + hookFn = loadFn(hookFnName, hookName); + if (!hookFn) throw new Error('Not a function'); + } catch (exc) { + console.error(`Failed to load '${hookFnName}' for ` + + `'${part.full_name}/${hookSetName}/${hookName}': ${exc.toString()}`); + } + if (hookFn) { + if (hooks[hookName] == null) hooks[hookName] = []; + hooks[hookName].push({ + hook_name: hookName, + hook_fn: hookFn, + hook_fn_name: hookFnName, + part, }); - }); + } + } + } return hooks; }; @@ -93,11 +88,8 @@ exports.extractHooks = extractHooks; * Some plugins: [ 'ep_adminpads', 'ep_add_buttons', 'ep_activepads' ] */ exports.clientPluginNames = () => { - const client_plugin_names = _.uniq( - defs.parts - .filter((part) => Object.prototype.hasOwnProperty.call(part, 'client_hooks')) - .map((part) => `plugin-${part.plugin}`) - ); - - return client_plugin_names; + const clientPluginNames = defs.parts + .filter((part) => Object.prototype.hasOwnProperty.call(part, 'client_hooks')) + .map((part) => `plugin-${part.plugin}`); + return [...new Set(clientPluginNames)]; }; From c5f0274116645458c3913da724d65159facfe744 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 4 Feb 2021 02:09:32 -0500 Subject: [PATCH 233/357] lint: Move functions up to fix more lint errors --- src/static/js/pluginfw/read-installed.js | 97 ++++++++++++------------ 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/src/static/js/pluginfw/read-installed.js b/src/static/js/pluginfw/read-installed.js index 6801b51aa..1f813b379 100644 --- a/src/static/js/pluginfw/read-installed.js +++ b/src/static/js/pluginfw/read-installed.js @@ -139,39 +139,10 @@ const readInstalled_ = (folder, parent, name, reqver, depth, maxDepth, cb) => { obj, real, link; - - fs.readdir(path.resolve(folder, 'node_modules'), (er, i) => { - // error indicates that nothing is installed here - if (er) i = []; - installed = i.filter((f) => f.charAt(0) !== '.'); - next(); - }); - - readJson(path.resolve(folder, 'package.json'), (er, data) => { - obj = copy(data); - - if (!parent) { - obj = obj || true; - er = null; - } - return next(er); - }); - - fs.lstat(folder, (er, st) => { - if (er) { - if (!parent) real = true; - return next(er); - } - fs.realpath(folder, (er, rp) => { - real = rp; - if (st.isSymbolicLink()) link = rp; - next(er); - }); - }); - let errState = null; let called = false; - function next(er) { + + const next = (er) => { if (errState) return; if (er) { errState = er; @@ -230,7 +201,36 @@ const readInstalled_ = (folder, parent, name, reqver, depth, maxDepth, cb) => { } return cb(null, obj); }); - } + }; + + fs.readdir(path.resolve(folder, 'node_modules'), (er, i) => { + // error indicates that nothing is installed here + if (er) i = []; + installed = i.filter((f) => f.charAt(0) !== '.'); + next(); + }); + + readJson(path.resolve(folder, 'package.json'), (er, data) => { + obj = copy(data); + + if (!parent) { + obj = obj || true; + er = null; + } + return next(er); + }); + + fs.lstat(folder, (er, st) => { + if (er) { + if (!parent) real = true; + return next(er); + } + fs.realpath(folder, (er, rp) => { + real = rp; + if (st.isSymbolicLink()) link = rp; + next(er); + }); + }); }; // starting from a root object, call findUnmet on each layer of children @@ -295,22 +295,9 @@ const copy = (obj) => { }; if (module === require.main) { - const util = require('util'); - console.error('testing'); - - let called = 0; - npm.load({}, (err) => { - if (err != null) throw err; - readInstalled(process.cwd(), (er, map) => { - console.error(called++); - if (er) return console.error(er.stack || er.message); - cleanup(map); - console.error(util.inspect(map, true, 10, true)); - }); - }); - const seen = []; - function cleanup(map) { + + const cleanup = (map) => { if (seen.indexOf(map) !== -1) return; seen.push(map); for (const i of Object.keys(map)) { @@ -329,5 +316,19 @@ if (module === require.main) { } } return map; - } + }; + + const util = require('util'); + console.error('testing'); + + let called = 0; + npm.load({}, (err) => { + if (err != null) throw err; + readInstalled(process.cwd(), (er, map) => { + console.error(called++); + if (er) return console.error(er.stack || er.message); + cleanup(map); + console.error(util.inspect(map, true, 10, true)); + }); + }); } From cd1d322af426baafa91f04aa699abc0e26057b07 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 3 Feb 2021 23:05:58 -0500 Subject: [PATCH 234/357] /admin/plugins/info: Move logic to `.js` file --- src/node/hooks/express/adminplugins.js | 17 +++++++++++------ src/templates/admin/plugins-info.html | 11 ++++------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index e2129c116..5963fb208 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -3,14 +3,15 @@ const eejs = require('../../eejs'); const settings = require('../../utils/Settings'); const installer = require('../../../static/js/pluginfw/installer'); -const plugins = require('../../../static/js/pluginfw/plugin_defs'); +const pluginDefs = require('../../../static/js/pluginfw/plugin_defs'); +const plugins = require('../../../static/js/pluginfw/plugins'); const semver = require('semver'); const UpdateCheck = require('../../utils/UpdateCheck'); exports.expressCreateServer = (hookName, args, cb) => { args.app.get('/admin/plugins', (req, res) => { res.send(eejs.require('ep_etherpad-lite/templates/admin/plugins.html', { - plugins: plugins.plugins, + plugins: pluginDefs.plugins, req, errors: [], })); @@ -23,6 +24,10 @@ exports.expressCreateServer = (hookName, args, cb) => { res.send(eejs.require('ep_etherpad-lite/templates/admin/plugins-info.html', { gitCommit, epVersion, + installedPlugins: `
                    ${plugins.formatPlugins().replace(/, /g, '\n')}
                    `, + installedParts: `
                    ${plugins.formatParts()}
                    `, + installedServerHooks: `
                    ${plugins.formatHooks()}
                    `, + installedClientHooks: `
                    ${plugins.formatHooks('client_hooks')}
                    `, latestVersion: UpdateCheck.getLatestVersion(), req, })); @@ -40,7 +45,7 @@ exports.socketio = (hookName, args, cb) => { socket.on('getInstalled', (query) => { // send currently installed plugins const installed = - Object.keys(plugins.plugins).map((plugin) => plugins.plugins[plugin].package); + Object.keys(pluginDefs.plugins).map((plugin) => pluginDefs.plugins[plugin].package); socket.emit('results:installed', {installed}); }); @@ -50,11 +55,11 @@ exports.socketio = (hookName, args, cb) => { try { const results = await installer.getAvailablePlugins(/* maxCacheAge:*/ 60 * 10); - const updatable = Object.keys(plugins.plugins).filter((plugin) => { + const updatable = Object.keys(pluginDefs.plugins).filter((plugin) => { if (!results[plugin]) return false; const latestVersion = results[plugin].version; - const currentVersion = plugins.plugins[plugin].package.version; + const currentVersion = pluginDefs.plugins[plugin].package.version; return semver.gt(latestVersion, currentVersion); }); @@ -82,7 +87,7 @@ exports.socketio = (hookName, args, cb) => { const results = await installer.search(query.searchTerm, /* maxCacheAge:*/ 60 * 10); let res = Object.keys(results) .map((pluginName) => results[pluginName]) - .filter((plugin) => !plugins.plugins[plugin.name]); + .filter((plugin) => !pluginDefs.plugins[plugin.name]); res = sortPluginList(res, query.sortBy, query.sortDir) .slice(query.offset, query.offset + query.limit); socket.emit('results:search', {results: res, query}); diff --git a/src/templates/admin/plugins-info.html b/src/templates/admin/plugins-info.html index b960ff32f..a737ea05b 100644 --- a/src/templates/admin/plugins-info.html +++ b/src/templates/admin/plugins-info.html @@ -1,6 +1,3 @@ -<% - var plugins = require("ep_etherpad-lite/static/js/pluginfw/plugins"); -%> @@ -31,17 +28,17 @@

                    Git sha: <%= gitCommit %>

                    Installed plugins

                    -
                    <%- plugins.formatPlugins().replace(/, /g,"\n") %>
                    + <%- installedPlugins %>

                    Installed parts

                    -
                    <%= plugins.formatParts() %>
                    + <%- installedParts %>

                    Installed hooks

                    Server-side hooks

                    -
                    <%- plugins.formatHooks() %>
                    + <%- installedServerHooks %>

                    Client-side hooks

                    -
                    <%- plugins.formatHooks("client_hooks") %>
                    + <%- installedClientHooks %>
                    From 746cc8cc3471c1d430f46d86c80c94b4c8bef408 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 3 Feb 2021 23:19:21 -0500 Subject: [PATCH 235/357] pluginfw: In-line `formatPluginsWithVersion()` There's only one caller of the function, so move the logic to where it is used. --- src/node/server.js | 7 ++++++- src/static/js/pluginfw/plugins.js | 5 ----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/node/server.js b/src/node/server.js index a0d9e2adc..affca7e1b 100755 --- a/src/node/server.js +++ b/src/node/server.js @@ -44,6 +44,7 @@ const db = require('./db/DB'); const express = require('./hooks/express'); const hooks = require('../static/js/pluginfw/hooks'); const npm = require('npm/lib/npm.js'); +const pluginDefs = require('../static/js/pluginfw/plugin_defs'); const plugins = require('../static/js/pluginfw/plugins'); const settings = require('./utils/Settings'); const util = require('util'); @@ -120,7 +121,11 @@ exports.start = async () => { await util.promisify(npm.load)(); await db.init(); await plugins.update(); - console.info(`Installed plugins: ${plugins.formatPluginsWithVersion()}`); + const installedPlugins = Object.values(pluginDefs.plugins) + .filter((plugin) => plugin.package.name !== 'ep_etherpad-lite') + .map((plugin) => `${plugin.package.name}@${plugin.package.version}`) + .join(', '); + console.info(`Installed plugins: ${installedPlugins}`); console.debug(`Installed parts:\n${plugins.formatParts()}`); console.debug(`Installed hooks:\n${plugins.formatHooks()}`); await hooks.aCallAll('loadSettings', {settings}); diff --git a/src/static/js/pluginfw/plugins.js b/src/static/js/pluginfw/plugins.js index 417fc1fe9..ceda3cc47 100644 --- a/src/static/js/pluginfw/plugins.js +++ b/src/static/js/pluginfw/plugins.js @@ -15,11 +15,6 @@ exports.prefix = 'ep_'; exports.formatPlugins = () => Object.keys(defs.plugins).join(', '); -exports.formatPluginsWithVersion = () => Object.values(defs.plugins) - .filter((plugin) => plugin.package.name !== 'ep_etherpad-lite') - .map((plugin) => `${plugin.package.name}@${plugin.package.version}`) - .join(', '); - exports.formatParts = () => defs.parts.map((part) => part.full_name).join('\n'); exports.formatHooks = (hookSetName) => { From fdaacc44c83191cd07769441982ecc4f88e92bb2 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 4 Feb 2021 01:06:51 -0500 Subject: [PATCH 236/357] pluginfw: Replace `slide.asyncMap()` with `Promise.all()` --- package-lock.json | 11 +++++------ src/package-lock.json | 5 ----- src/package.json | 1 - src/static/js/pluginfw/read-installed.js | 15 +++++++-------- 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 67ca85767..0f7986ace 100644 --- a/package-lock.json +++ b/package-lock.json @@ -862,7 +862,6 @@ "resolve": "1.1.7", "security": "1.0.0", "semver": "5.6.0", - "slide": "1.1.6", "socket.io": "^2.4.1", "terser": "^4.7.0", "threads": "^1.4.0", @@ -10544,6 +10543,11 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==" }, + "wtfnode": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/wtfnode/-/wtfnode-0.8.4.tgz", + "integrity": "sha512-64GEKtMt/MUBuAm+8kHqP74ojjafzu00aT0JKsmkIwYmjRQ/odO0yhbzKLm+Z9v1gMla+8dwITRKzTAlHsB+Og==" + }, "xml2js": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", @@ -10578,11 +10582,6 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, - "wtfnode": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/wtfnode/-/wtfnode-0.8.4.tgz", - "integrity": "sha512-64GEKtMt/MUBuAm+8kHqP74ojjafzu00aT0JKsmkIwYmjRQ/odO0yhbzKLm+Z9v1gMla+8dwITRKzTAlHsB+Og==" - }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/src/package-lock.json b/src/package-lock.json index 07441bde4..2227ac23e 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -7680,11 +7680,6 @@ } } }, - "slide": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", - "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=" - }, "socket.io": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.1.tgz", diff --git a/src/package.json b/src/package.json index 8399c19bd..583c924db 100644 --- a/src/package.json +++ b/src/package.json @@ -64,7 +64,6 @@ "resolve": "1.1.7", "security": "1.0.0", "semver": "5.6.0", - "slide": "1.1.6", "socket.io": "^2.4.1", "terser": "^4.7.0", "threads": "^1.4.0", diff --git a/src/static/js/pluginfw/read-installed.js b/src/static/js/pluginfw/read-installed.js index 1f813b379..8597d5289 100644 --- a/src/static/js/pluginfw/read-installed.js +++ b/src/static/js/pluginfw/read-installed.js @@ -93,9 +93,9 @@ as far as the left-most node_modules folder. const npm = require('npm/lib/npm.js'); const fs = require('graceful-fs'); const path = require('path'); -const asyncMap = require('slide').asyncMap; const semver = require('semver'); const log = require('log4js').getLogger('pluginfw'); +const util = require('util'); let fuSeen = []; let riSeen = []; @@ -178,14 +178,13 @@ const readInstalled_ = (folder, parent, name, reqver, depth, maxDepth, cb) => { rpSeen[real] = obj; obj.depth = depth; if (depth >= maxDepth) return cb(null, obj); - asyncMap(installed, (pkg, cb) => { + Promise.all(installed.map(async (pkg) => { let rv = obj.dependencies[pkg]; if (!rv && obj.devDependencies) rv = obj.devDependencies[pkg]; - readInstalled_(path.resolve(folder, `node_modules/${pkg}`) - , obj, pkg, obj.dependencies[pkg], depth + 1, maxDepth - , cb); - }, (er, installedData) => { - if (er) return cb(er); + const dir = path.resolve(folder, `node_modules/${pkg}`); + const deps = obj.dependencies[pkg]; + return await util.promisify(readInstalled_)(dir, obj, pkg, deps, depth + 1, maxDepth); + })).then((installedData) => { installedData.forEach((dep) => { obj.dependencies[dep.realName] = dep; }); @@ -200,7 +199,7 @@ const readInstalled_ = (folder, parent, name, reqver, depth, maxDepth, cb) => { }); } return cb(null, obj); - }); + }, (err) => cb(err || new Error(err))); }; fs.readdir(path.resolve(folder, 'node_modules'), (er, i) => { From a145b976829eaa671a00349c4e52b9d3aa715573 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 4 Feb 2021 01:09:58 -0500 Subject: [PATCH 237/357] pluginfw: Use `for` loops to improve readability --- src/static/js/pluginfw/read-installed.js | 66 ++++++++++++------------ 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/src/static/js/pluginfw/read-installed.js b/src/static/js/pluginfw/read-installed.js index 8597d5289..d0866ee79 100644 --- a/src/static/js/pluginfw/read-installed.js +++ b/src/static/js/pluginfw/read-installed.js @@ -153,7 +153,9 @@ const readInstalled_ = (folder, parent, name, reqver, depth, maxDepth, cb) => { if (rpSeen[real]) return cb(null, rpSeen[real]); if (obj === true) { obj = {dependencies: {}, path: folder}; - installed.forEach((i) => { obj.dependencies[i] = '*'; }); + for (const i of installed) { + obj.dependencies[i] = '*'; + } } if (name && obj.name !== name) obj.invalid = true; obj.realName = name || obj.name; @@ -185,18 +187,18 @@ const readInstalled_ = (folder, parent, name, reqver, depth, maxDepth, cb) => { const deps = obj.dependencies[pkg]; return await util.promisify(readInstalled_)(dir, obj, pkg, deps, depth + 1, maxDepth); })).then((installedData) => { - installedData.forEach((dep) => { + for (const dep of installedData) { obj.dependencies[dep.realName] = dep; - }); + } // any strings here are unmet things. however, if it's // optional, then that's fine, so just delete it. if (obj.optionalDependencies) { - Object.keys(obj.optionalDependencies).forEach((dep) => { + for (const dep of Object.keys(obj.optionalDependencies)) { if (typeof obj.dependencies[dep] === 'string') { delete obj.dependencies[dep]; } - }); + } } return cb(null, obj); }, (err) => cb(err || new Error(err))); @@ -240,12 +242,12 @@ const resolveInheritance = (obj) => { if (typeof obj.dependencies !== 'object') { obj.dependencies = {}; } - Object.keys(obj.dependencies).forEach((dep) => { + for (const dep of Object.keys(obj.dependencies)) { findUnmet(obj.dependencies[dep]); - }); - Object.keys(obj.dependencies).forEach((dep) => { + } + for (const dep of Object.keys(obj.dependencies)) { resolveInheritance(obj.dependencies[dep]); - }); + } }; // find unmet deps by walking up the tree object. @@ -255,32 +257,28 @@ const findUnmet = (obj) => { if (fuSeen.indexOf(obj) !== -1) return; fuSeen.push(obj); const deps = obj.dependencies = obj.dependencies || {}; - Object.keys(deps) - .filter((d) => typeof deps[d] === 'string') - .forEach((d) => { - let r = obj.parent; - let found = null; - while (r && !found && typeof deps[d] === 'string') { - // if r is a valid choice, then use that. - found = r.dependencies[d]; - if (!found && r.realName === d) found = r; + for (const d of Object.keys(deps).filter((d) => typeof deps[d] === 'string')) { + let r = obj.parent; + let found = null; + while (r && !found && typeof deps[d] === 'string') { + // if r is a valid choice, then use that. + found = r.dependencies[d]; + if (!found && r.realName === d) found = r; - if (!found) { - r = r.link ? null : r.parent; - continue; - } - if (typeof deps[d] === 'string' && - !semver.satisfies(found.version, deps[d])) { - // the bad thing will happen - log.warn(`${obj.path} requires ${d}@'${deps[d] - }' but will load\n${ - found.path},\nwhich is version ${found.version}` - , 'unmet dependency'); - found.invalid = true; - } - deps[d] = found; - } - }); + if (!found) { + r = r.link ? null : r.parent; + continue; + } + if (typeof deps[d] === 'string' && + !semver.satisfies(found.version, deps[d])) { + // the bad thing will happen + log.warn(`${obj.path} requires ${d}@'${deps[d]}' but will load\n${found.path},\n` + + `which is version ${found.version}`, 'unmet dependency'); + found.invalid = true; + } + deps[d] = found; + } + } return obj; }; From 5a865dfc7e852a64220cd4f0183515ef4432f7cd Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 4 Feb 2021 02:07:38 -0500 Subject: [PATCH 238/357] pluginfw: Delete unused return value --- src/static/js/pluginfw/read-installed.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/static/js/pluginfw/read-installed.js b/src/static/js/pluginfw/read-installed.js index d0866ee79..f24fe8dc8 100644 --- a/src/static/js/pluginfw/read-installed.js +++ b/src/static/js/pluginfw/read-installed.js @@ -279,7 +279,6 @@ const findUnmet = (obj) => { deps[d] = found; } } - return obj; }; const copy = (obj) => { From efde0b787a82829ba382c337839dc78fb4562555 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 4 Feb 2021 20:53:30 +0000 Subject: [PATCH 239/357] npm-load-remove: checkPad.js example (#4711) * npm-load-remove: checkPad.js example * npm-load-remove: checkPad.js example * npm-load-remove: checkPad.js example * npm-load-remove: checkPad.js example * npm-load-remove: checkPad.js example * npm-load-remove: checkPad.js example * npm-load-remove: checkPadDeltas * npm-load-remove: extractPadData * npm-load-remove: importSqlFile * npm-load-remove: rebuildPad * npm-load-remove: repairPad * npm-load-remove: checkAllPads * npm-load-remove: migrateDirtyDBtoRealDB * npm-load-remove: migrateDirtyDBtoRealDB path * npm-load-remove: migrateDirtyDBtoRealDB dbclose fix * npm-load-remove: migrateDirtyDBtoRealDB remove commented out line * npm-load-remove: migrateDirtyDBtoRealDB reintroduce util --- bin/checkAllPads.js | 5 ----- bin/checkPad.js | 5 ----- bin/checkPadDeltas.js | 4 ---- bin/extractPadData.js | 5 ----- bin/importSqlFile.js | 5 ----- bin/migrateDirtyDBtoRealDB.js | 10 ++-------- bin/rebuildPad.js | 5 ----- bin/repairPad.js | 5 ----- 8 files changed, 2 insertions(+), 42 deletions(-) diff --git a/bin/checkAllPads.js b/bin/checkAllPads.js index 356112e59..7f8a71401 100644 --- a/bin/checkAllPads.js +++ b/bin/checkAllPads.js @@ -7,14 +7,9 @@ // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); -const npm = require('ep_etherpad-lite/node_modules/npm'); -const util = require('util'); - if (process.argv.length !== 2) throw new Error('Use: node bin/checkAllPads.js'); (async () => { - await util.promisify(npm.load)({}); - // initialize the database require('ep_etherpad-lite/node/utils/Settings'); const db = require('ep_etherpad-lite/node/db/DB'); diff --git a/bin/checkPad.js b/bin/checkPad.js index 20b3fa226..53a3e10dc 100644 --- a/bin/checkPad.js +++ b/bin/checkPad.js @@ -7,9 +7,6 @@ // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); -const npm = require('ep_etherpad-lite/node_modules/npm'); -const util = require('util'); - if (process.argv.length !== 3) throw new Error('Use: node bin/checkPad.js $PADID'); // get the padID @@ -17,8 +14,6 @@ const padId = process.argv[2]; let checkRevisionCount = 0; (async () => { - await util.promisify(npm.load)({}); - // initialize database require('ep_etherpad-lite/node/utils/Settings'); const db = require('ep_etherpad-lite/node/db/DB'); diff --git a/bin/checkPadDeltas.js b/bin/checkPadDeltas.js index 4900595c3..67cc8f159 100644 --- a/bin/checkPadDeltas.js +++ b/bin/checkPadDeltas.js @@ -14,12 +14,8 @@ const padId = process.argv[2]; const expect = require('../tests/frontend/lib/expect'); const diff = require('ep_etherpad-lite/node_modules/diff'); -const npm = require('ep_etherpad-lite/node_modules/npm'); -const util = require('util'); (async () => { - await util.promisify(npm.load)({}); - // initialize database require('ep_etherpad-lite/node/utils/Settings'); const db = require('ep_etherpad-lite/node/db/DB'); diff --git a/bin/extractPadData.js b/bin/extractPadData.js index 181a8c6bf..dc413a9a4 100644 --- a/bin/extractPadData.js +++ b/bin/extractPadData.js @@ -15,12 +15,7 @@ if (process.argv.length !== 3) throw new Error('Use: node extractPadData.js $PAD // get the padID const padId = process.argv[2]; -const npm = require('ep_etherpad-lite/node_modules/npm'); -const util = require('util'); - (async () => { - await util.promisify(npm.load)({}); - // initialize database require('ep_etherpad-lite/node/utils/Settings'); const db = require('ep_etherpad-lite/node/db/DB'); diff --git a/bin/importSqlFile.js b/bin/importSqlFile.js index 5927d82ce..2ba450a7d 100644 --- a/bin/importSqlFile.js +++ b/bin/importSqlFile.js @@ -4,9 +4,6 @@ // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); -const npm = require('ep_etherpad-lite/node_modules/npm'); -const util = require('util'); - const startTime = Date.now(); const log = (str) => { @@ -47,8 +44,6 @@ const unescape = (val) => { }; (async () => { - await util.promisify(npm.load)({}); - const fs = require('fs'); const ueberDB = require('ep_etherpad-lite/node_modules/ueberdb2'); const settings = require('ep_etherpad-lite/node/utils/Settings'); diff --git a/bin/migrateDirtyDBtoRealDB.js b/bin/migrateDirtyDBtoRealDB.js index c79d9b747..bbf34cdd0 100644 --- a/bin/migrateDirtyDBtoRealDB.js +++ b/bin/migrateDirtyDBtoRealDB.js @@ -4,14 +4,7 @@ // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); -const npm = require('ep_etherpad-lite/node_modules/npm'); -const util = require('util'); - (async () => { - await util.promisify(npm.load)({}); - - process.chdir(`${npm.root}/..`); - // This script requires that you have modified your settings.json file // to work with a real database. Please make a backup of your dirty.db // file before using this script, just to be safe. @@ -20,6 +13,7 @@ const util = require('util'); // `node --max-old-space-size=4096 bin/migrateDirtyDBtoRealDB.js` + const util = require('util'); const settings = require('ep_etherpad-lite/node/utils/Settings'); const dirtyDb = require('ep_etherpad-lite/node_modules/dirty'); const ueberDB = require('ep_etherpad-lite/node_modules/ueberdb2'); @@ -36,7 +30,7 @@ const util = require('util'); await db.init(); console.log('Waiting for dirtyDB to parse its file.'); - const dirty = dirtyDb('var/dirty.db'); + const dirty = dirtyDb(`${__dirname}/../var/dirty.db`); const length = await new Promise((resolve) => { dirty.once('load', resolve); }); console.log(`Found ${length} records, processing now.`); diff --git a/bin/rebuildPad.js b/bin/rebuildPad.js index 1bc942fe2..a8daf7285 100644 --- a/bin/rebuildPad.js +++ b/bin/rebuildPad.js @@ -13,16 +13,11 @@ if (process.argv.length !== 4 && process.argv.length !== 5) { throw new Error('Use: node bin/repairPad.js $PADID $REV [$NEWPADID]'); } -const npm = require('ep_etherpad-lite/node_modules/npm'); -const util = require('util'); - const padId = process.argv[2]; const newRevHead = process.argv[3]; const newPadId = process.argv[4] || `${padId}-rebuilt`; (async () => { - await util.promisify(npm.load)({}); - const db = require('ep_etherpad-lite/node/db/DB'); await db.init(); diff --git a/bin/repairPad.js b/bin/repairPad.js index ce0c6072e..aef9102ae 100644 --- a/bin/repairPad.js +++ b/bin/repairPad.js @@ -17,12 +17,7 @@ const padId = process.argv[2]; let valueCount = 0; -const npm = require('ep_etherpad-lite/node_modules/npm'); -const util = require('util'); - (async () => { - await util.promisify(npm.load)({}); - // initialize database require('ep_etherpad-lite/node/utils/Settings'); const db = require('ep_etherpad-lite/node/db/DB'); From 2ea8ea12754c92ea06f22428f1e86341dcf00d99 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 3 Feb 2021 12:08:43 +0000 Subject: [PATCH 240/357] restructure: move bin/ and tests/ to src/ Also add symlinks from the old `bin/` and `tests/` locations to avoid breaking scripts and other tools. Motivations: * Scripts and tests no longer have to do dubious things like: require('ep_etherpad-lite/node_modules/foo') to access packages installed as dependencies in `src/package.json`. * Plugins can access the backend test helper library in a non-hacky way: require('ep_etherpad-lite/tests/backend/common') * We can delete the top-level `package.json` without breaking our ability to lint the files in `bin/` and `tests/`. Deleting the top-level `package.json` has downsides: It will cause `npm` to print warnings whenever plugins are installed, npm will no longer be able to enforce a plugin's peer dependency on ep_etherpad-lite, and npm will keep deleting the `node_modules/ep_etherpad-lite` symlink that points to `../src`. But there are significant upsides to deleting the top-level `package.json`: It will drastically speed up plugin installation because `npm` doesn't have to recursively walk the dependencies in `src/package.json`. Also, deleting the top-level `package.json` avoids npm's horrible dependency hoisting behavior (where it moves stuff from `src/node_modules/` to the top-level `node_modules/` directory). Dependency hoisting causes numerous mysterious problems such as silent failures in `npm outdated` and `npm update`. Dependency hoisting also breaks plugins that do: require('ep_etherpad-lite/node_modules/foo') --- CHANGELOG.md | 3 + bin | 1 + package-lock.json | 942 +----------------- package.json | 106 -- {bin => src/bin}/buildDebian.sh | 0 {bin => src/bin}/buildForWindows.sh | 0 {bin => src/bin}/checkAllPads.js | 8 +- {bin => src/bin}/checkPad.js | 8 +- {bin => src/bin}/checkPadDeltas.js | 10 +- {bin => src/bin}/cleanRun.sh | 0 .../bin}/convertSettings.json.template | 0 {bin => src/bin}/createRelease.sh | 0 {bin => src/bin}/createUserSession.js | 6 +- {bin => src/bin}/deb-src/DEBIAN/control | 0 {bin => src/bin}/deb-src/DEBIAN/postinst | 0 {bin => src/bin}/deb-src/DEBIAN/preinst | 0 {bin => src/bin}/deb-src/DEBIAN/prerm | 0 .../deb-src/sysroot/etc/init/etherpad.conf | 0 {bin => src/bin}/debugRun.sh | 0 {bin => src/bin}/deleteAllGroupSessions.js | 4 +- {bin => src/bin}/deletePad.js | 7 +- {bin => src/bin}/dirty-db-cleaner.py | 0 {bin => src/bin}/doc/LICENSE | 0 {bin => src/bin}/doc/README.md | 0 {bin => src/bin}/doc/generate.js | 0 {bin => src/bin}/doc/html.js | 0 {bin => src/bin}/doc/json.js | 0 {bin => src/bin}/doc/package-lock.json | 0 {bin => src/bin}/doc/package.json | 0 {bin => src/bin}/extractPadData.js | 8 +- {bin => src/bin}/fastRun.sh | 0 {bin => src/bin}/functions.sh | 0 {bin => src/bin}/importSqlFile.js | 6 +- {bin => src/bin}/installDeps.sh | 0 {bin => src/bin}/installOnWindows.bat | 0 {bin => src/bin}/migrateDirtyDBtoRealDB.js | 12 +- {bin => src/bin}/plugins/README.md | 0 {bin => src/bin}/plugins/checkPlugin.js | 2 +- {bin => src/bin}/plugins/getCorePlugins.sh | 0 {bin => src/bin}/plugins/lib/CONTRIBUTING.md | 0 {bin => src/bin}/plugins/lib/LICENSE.md | 0 {bin => src/bin}/plugins/lib/README.md | 0 .../bin}/plugins/lib/backend-tests.yml | 0 {bin => src/bin}/plugins/lib/gitignore | 0 {bin => src/bin}/plugins/lib/npmpublish.yml | 0 {bin => src/bin}/plugins/lib/travis.yml | 0 {bin => src/bin}/plugins/reTestAllPlugins.sh | 0 .../bin}/plugins/updateAllPluginsScript.sh | 0 {bin => src/bin}/plugins/updateCorePlugins.sh | 0 {bin => src/bin}/rebuildPad.js | 10 +- {bin => src/bin}/release.js | 2 +- {bin => src/bin}/repairPad.js | 6 +- {bin => src/bin}/run.sh | 0 {bin => src/bin}/safeRun.sh | 0 {bin => src/bin}/updatePlugins.sh | 0 src/package.json | 74 +- src/tests | 1 - {tests => src/tests}/README.md | 0 {tests => src/tests}/backend/common.js | 12 +- .../tests}/backend/fuzzImportTest.js | 2 +- {tests => src/tests}/backend/specs/api/api.js | 9 +- .../backend/specs/api/characterEncoding.js | 6 +- .../tests}/backend/specs/api/chat.js | 6 +- .../tests}/backend/specs/api/emojis.html | 0 .../backend/specs/api/fuzzImportTest.js | 7 +- .../tests}/backend/specs/api/image.png | Bin .../tests}/backend/specs/api/importexport.js | 2 +- .../backend/specs/api/importexportGetPost.js | 8 +- .../tests}/backend/specs/api/instance.js | 5 +- {tests => src/tests}/backend/specs/api/pad.js | 7 +- .../backend/specs/api/sessionsAndGroups.js | 5 +- .../tests}/backend/specs/api/test.doc | Bin .../tests}/backend/specs/api/test.docx | Bin .../tests}/backend/specs/api/test.etherpad | 0 .../tests}/backend/specs/api/test.odt | Bin .../tests}/backend/specs/api/test.pdf | Bin .../tests}/backend/specs/api/test.txt | 0 .../tests}/backend/specs/api/tidy.js | 8 +- .../backend/specs/caching_middleware.js | 2 +- .../tests}/backend/specs/contentcollector.js | 6 +- {tests => src/tests}/backend/specs/hooks.js | 6 +- .../tests}/backend/specs/promises.js | 4 +- .../tests}/backend/specs/socketio.js | 10 +- .../tests}/backend/specs/specialpages.js | 2 +- .../tests}/backend/specs/webaccess.js | 4 +- .../tests}/container/loadSettings.js | 4 +- .../tests}/container/specs/api/pad.js | 6 +- {tests => src/tests}/frontend/helper.js | 0 .../tests}/frontend/helper/methods.js | 0 {tests => src/tests}/frontend/helper/ui.js | 0 {tests => src/tests}/frontend/index.html | 0 {tests => src/tests}/frontend/lib/expect.js | 0 {tests => src/tests}/frontend/lib/mocha.js | 0 {tests => src/tests}/frontend/lib/sendkeys.js | 0 .../tests}/frontend/lib/underscore.js | 0 {tests => src/tests}/frontend/runner.css | 0 {tests => src/tests}/frontend/runner.js | 0 .../tests}/frontend/specs/alphabet.js | 0 .../frontend/specs/authorship_of_editions.js | 0 {tests => src/tests}/frontend/specs/bold.js | 0 {tests => src/tests}/frontend/specs/caret.js | 0 .../frontend/specs/change_user_color.js | 0 .../tests}/frontend/specs/change_user_name.js | 0 {tests => src/tests}/frontend/specs/chat.js | 0 .../frontend/specs/chat_load_messages.js | 0 .../frontend/specs/clear_authorship_colors.js | 0 {tests => src/tests}/frontend/specs/delete.js | 0 .../tests}/frontend/specs/drag_and_drop.js | 0 .../tests}/frontend/specs/embed_value.js | 0 {tests => src/tests}/frontend/specs/enter.js | 0 .../tests}/frontend/specs/font_type.js | 0 {tests => src/tests}/frontend/specs/helper.js | 0 .../tests}/frontend/specs/importexport.js | 0 .../tests}/frontend/specs/importindents.js | 0 .../tests}/frontend/specs/indentation.js | 0 {tests => src/tests}/frontend/specs/italic.js | 0 .../tests}/frontend/specs/language.js | 0 ...ultiple_authors_clear_authorship_colors.js | 0 .../tests}/frontend/specs/ordered_list.js | 0 .../tests}/frontend/specs/pad_modal.js | 0 {tests => src/tests}/frontend/specs/redo.js | 0 .../tests}/frontend/specs/responsiveness.js | 0 .../tests}/frontend/specs/scrollTo.js | 0 .../specs/select_formatting_buttons.js | 0 .../tests}/frontend/specs/strikethrough.js | 0 .../tests}/frontend/specs/timeslider.js | 0 .../frontend/specs/timeslider_follow.js | 0 .../frontend/specs/timeslider_labels.js | 0 .../specs/timeslider_numeric_padID.js | 0 .../frontend/specs/timeslider_revisions.js | 0 {tests => src/tests}/frontend/specs/undo.js | 0 .../tests}/frontend/specs/unordered_list.js | 0 .../frontend/specs/urls_become_clickable.js | 0 .../tests}/frontend/specs/xxauto_reconnect.js | 0 .../tests}/frontend/travis/.gitignore | 0 .../tests}/frontend/travis/remote_runner.js | 4 +- .../tests}/frontend/travis/runner.sh | 0 .../tests}/frontend/travis/runnerBackend.sh | 0 .../tests}/frontend/travis/runnerLoadTest.sh | 0 .../tests}/frontend/travis/sauce_tunnel.sh | 0 .../tests}/ratelimit/Dockerfile.anotherip | 0 .../tests}/ratelimit/Dockerfile.nginx | 0 {tests => src/tests}/ratelimit/nginx.conf | 0 .../tests}/ratelimit/send_changesets.js | 10 +- {tests => src/tests}/ratelimit/testlimits.sh | 0 tests | 1 + 146 files changed, 191 insertions(+), 1161 deletions(-) create mode 120000 bin rename {bin => src/bin}/buildDebian.sh (100%) rename {bin => src/bin}/buildForWindows.sh (100%) rename {bin => src/bin}/checkAllPads.js (91%) rename {bin => src/bin}/checkPad.js (90%) rename {bin => src/bin}/checkPadDeltas.js (90%) rename {bin => src/bin}/cleanRun.sh (100%) rename {bin => src/bin}/convertSettings.json.template (100%) rename {bin => src/bin}/createRelease.sh (100%) rename {bin => src/bin}/createUserSession.js (90%) rename {bin => src/bin}/deb-src/DEBIAN/control (100%) rename {bin => src/bin}/deb-src/DEBIAN/postinst (100%) rename {bin => src/bin}/deb-src/DEBIAN/preinst (100%) rename {bin => src/bin}/deb-src/DEBIAN/prerm (100%) rename {bin => src/bin}/deb-src/sysroot/etc/init/etherpad.conf (100%) rename {bin => src/bin}/debugRun.sh (100%) rename {bin => src/bin}/deleteAllGroupSessions.js (93%) rename {bin => src/bin}/deletePad.js (91%) rename {bin => src/bin}/dirty-db-cleaner.py (100%) rename {bin => src/bin}/doc/LICENSE (100%) rename {bin => src/bin}/doc/README.md (100%) rename {bin => src/bin}/doc/generate.js (100%) rename {bin => src/bin}/doc/html.js (100%) rename {bin => src/bin}/doc/json.js (100%) rename {bin => src/bin}/doc/package-lock.json (100%) rename {bin => src/bin}/doc/package.json (100%) rename {bin => src/bin}/extractPadData.js (87%) rename {bin => src/bin}/fastRun.sh (100%) rename {bin => src/bin}/functions.sh (100%) rename {bin => src/bin}/importSqlFile.js (92%) rename {bin => src/bin}/installDeps.sh (100%) rename {bin => src/bin}/installOnWindows.bat (100%) rename {bin => src/bin}/migrateDirtyDBtoRealDB.js (84%) rename {bin => src/bin}/plugins/README.md (100%) rename {bin => src/bin}/plugins/checkPlugin.js (99%) rename {bin => src/bin}/plugins/getCorePlugins.sh (100%) rename {bin => src/bin}/plugins/lib/CONTRIBUTING.md (100%) rename {bin => src/bin}/plugins/lib/LICENSE.md (100%) rename {bin => src/bin}/plugins/lib/README.md (100%) rename {bin => src/bin}/plugins/lib/backend-tests.yml (100%) rename {bin => src/bin}/plugins/lib/gitignore (100%) rename {bin => src/bin}/plugins/lib/npmpublish.yml (100%) rename {bin => src/bin}/plugins/lib/travis.yml (100%) rename {bin => src/bin}/plugins/reTestAllPlugins.sh (100%) rename {bin => src/bin}/plugins/updateAllPluginsScript.sh (100%) rename {bin => src/bin}/plugins/updateCorePlugins.sh (100%) rename {bin => src/bin}/rebuildPad.js (90%) rename {bin => src/bin}/release.js (97%) rename {bin => src/bin}/repairPad.js (90%) rename {bin => src/bin}/run.sh (100%) rename {bin => src/bin}/safeRun.sh (100%) rename {bin => src/bin}/updatePlugins.sh (100%) delete mode 120000 src/tests rename {tests => src/tests}/README.md (100%) rename {tests => src/tests}/backend/common.js (82%) rename {tests => src/tests}/backend/fuzzImportTest.js (96%) rename {tests => src/tests}/backend/specs/api/api.js (88%) rename {tests => src/tests}/backend/specs/api/characterEncoding.js (95%) rename {tests => src/tests}/backend/specs/api/chat.js (95%) rename {tests => src/tests}/backend/specs/api/emojis.html (100%) rename {tests => src/tests}/backend/specs/api/fuzzImportTest.js (86%) rename {tests => src/tests}/backend/specs/api/image.png (100%) rename {tests => src/tests}/backend/specs/api/importexport.js (99%) rename {tests => src/tests}/backend/specs/api/importexportGetPost.js (97%) rename {tests => src/tests}/backend/specs/api/instance.js (91%) rename {tests => src/tests}/backend/specs/api/pad.js (99%) rename {tests => src/tests}/backend/specs/api/sessionsAndGroups.js (98%) rename {tests => src/tests}/backend/specs/api/test.doc (100%) rename {tests => src/tests}/backend/specs/api/test.docx (100%) rename {tests => src/tests}/backend/specs/api/test.etherpad (100%) rename {tests => src/tests}/backend/specs/api/test.odt (100%) rename {tests => src/tests}/backend/specs/api/test.pdf (100%) rename {tests => src/tests}/backend/specs/api/test.txt (100%) rename {tests => src/tests}/backend/specs/api/tidy.js (87%) rename {tests => src/tests}/backend/specs/caching_middleware.js (98%) rename {tests => src/tests}/backend/specs/contentcollector.js (98%) rename {tests => src/tests}/backend/specs/hooks.js (99%) rename {tests => src/tests}/backend/specs/promises.js (95%) rename {tests => src/tests}/backend/specs/socketio.js (97%) rename {tests => src/tests}/backend/specs/specialpages.js (91%) rename {tests => src/tests}/backend/specs/webaccess.js (99%) rename {tests => src/tests}/container/loadSettings.js (86%) rename {tests => src/tests}/container/specs/api/pad.js (85%) rename {tests => src/tests}/frontend/helper.js (100%) rename {tests => src/tests}/frontend/helper/methods.js (100%) rename {tests => src/tests}/frontend/helper/ui.js (100%) rename {tests => src/tests}/frontend/index.html (100%) rename {tests => src/tests}/frontend/lib/expect.js (100%) rename {tests => src/tests}/frontend/lib/mocha.js (100%) rename {tests => src/tests}/frontend/lib/sendkeys.js (100%) rename {tests => src/tests}/frontend/lib/underscore.js (100%) rename {tests => src/tests}/frontend/runner.css (100%) rename {tests => src/tests}/frontend/runner.js (100%) rename {tests => src/tests}/frontend/specs/alphabet.js (100%) rename {tests => src/tests}/frontend/specs/authorship_of_editions.js (100%) rename {tests => src/tests}/frontend/specs/bold.js (100%) rename {tests => src/tests}/frontend/specs/caret.js (100%) rename {tests => src/tests}/frontend/specs/change_user_color.js (100%) rename {tests => src/tests}/frontend/specs/change_user_name.js (100%) rename {tests => src/tests}/frontend/specs/chat.js (100%) rename {tests => src/tests}/frontend/specs/chat_load_messages.js (100%) rename {tests => src/tests}/frontend/specs/clear_authorship_colors.js (100%) rename {tests => src/tests}/frontend/specs/delete.js (100%) rename {tests => src/tests}/frontend/specs/drag_and_drop.js (100%) rename {tests => src/tests}/frontend/specs/embed_value.js (100%) rename {tests => src/tests}/frontend/specs/enter.js (100%) rename {tests => src/tests}/frontend/specs/font_type.js (100%) rename {tests => src/tests}/frontend/specs/helper.js (100%) rename {tests => src/tests}/frontend/specs/importexport.js (100%) rename {tests => src/tests}/frontend/specs/importindents.js (100%) rename {tests => src/tests}/frontend/specs/indentation.js (100%) rename {tests => src/tests}/frontend/specs/italic.js (100%) rename {tests => src/tests}/frontend/specs/language.js (100%) rename {tests => src/tests}/frontend/specs/multiple_authors_clear_authorship_colors.js (100%) rename {tests => src/tests}/frontend/specs/ordered_list.js (100%) rename {tests => src/tests}/frontend/specs/pad_modal.js (100%) rename {tests => src/tests}/frontend/specs/redo.js (100%) rename {tests => src/tests}/frontend/specs/responsiveness.js (100%) rename {tests => src/tests}/frontend/specs/scrollTo.js (100%) rename {tests => src/tests}/frontend/specs/select_formatting_buttons.js (100%) rename {tests => src/tests}/frontend/specs/strikethrough.js (100%) rename {tests => src/tests}/frontend/specs/timeslider.js (100%) rename {tests => src/tests}/frontend/specs/timeslider_follow.js (100%) rename {tests => src/tests}/frontend/specs/timeslider_labels.js (100%) rename {tests => src/tests}/frontend/specs/timeslider_numeric_padID.js (100%) rename {tests => src/tests}/frontend/specs/timeslider_revisions.js (100%) rename {tests => src/tests}/frontend/specs/undo.js (100%) rename {tests => src/tests}/frontend/specs/unordered_list.js (100%) rename {tests => src/tests}/frontend/specs/urls_become_clickable.js (100%) rename {tests => src/tests}/frontend/specs/xxauto_reconnect.js (100%) rename {tests => src/tests}/frontend/travis/.gitignore (100%) rename {tests => src/tests}/frontend/travis/remote_runner.js (97%) rename {tests => src/tests}/frontend/travis/runner.sh (100%) rename {tests => src/tests}/frontend/travis/runnerBackend.sh (100%) rename {tests => src/tests}/frontend/travis/runnerLoadTest.sh (100%) rename {tests => src/tests}/frontend/travis/sauce_tunnel.sh (100%) rename {tests => src/tests}/ratelimit/Dockerfile.anotherip (100%) rename {tests => src/tests}/ratelimit/Dockerfile.nginx (100%) rename {tests => src/tests}/ratelimit/nginx.conf (100%) rename {tests => src/tests}/ratelimit/send_changesets.js (66%) rename {tests => src/tests}/ratelimit/testlimits.sh (100%) create mode 120000 tests diff --git a/CHANGELOG.md b/CHANGELOG.md index 57613b826..66f419735 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ### Compatibility changes * Node.js 10.17.0 or newer is now required. +* The `bin/` and `tests/` directories were moved under `src/`. Symlinks were + added at the old locations to hopefully avoid breaking user scripts and other + stuff. ### Notable new features * Database performance is significantly improved. diff --git a/bin b/bin new file mode 120000 index 000000000..70feaa890 --- /dev/null +++ b/bin @@ -0,0 +1 @@ +src/bin \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0f7986ace..118e4be8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,63 +34,6 @@ "adal-node": "^0.1.28" } }, - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } - } - }, - "@eslint/eslintrc": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", - "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - } - }, "@js-joda/core": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-3.2.0.tgz", @@ -192,18 +135,6 @@ "negotiator": "0.6.2" } }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true - }, "adal-node": { "version": "0.1.28", "resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.1.28.tgz", @@ -256,27 +187,6 @@ "uri-js": "^4.2.2" } }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -293,15 +203,6 @@ "readable-stream": "^2.0.6" } }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, "arraybuffer.slice": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", @@ -320,12 +221,6 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, "async": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", @@ -436,12 +331,6 @@ "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -458,57 +347,6 @@ "long": "^2.2.0" } }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "channels": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/channels/-/channels-0.0.4.tgz", @@ -536,19 +374,6 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "optional": true }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -593,17 +418,6 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -631,12 +445,6 @@ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "optional": true }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -669,15 +477,6 @@ "resolved": "https://registry.npmjs.org/dirty/-/dirty-1.1.0.tgz", "integrity": "sha1-cO3SuZlUHcmXT9Ooy9DGcP4jYHg=" }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -742,12 +541,6 @@ } } }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, "engine.io": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.5.0.tgz", @@ -816,15 +609,6 @@ "has-binary2": "~1.0.2" } }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, "ep_etherpad-lite": { "version": "file:src", "requires": { @@ -1096,10 +880,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==" }, "chalk": { "version": "2.4.2", @@ -1114,10 +895,7 @@ "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==" } } }, @@ -2073,12 +1851,10 @@ "esutils": "^2.0.2", "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", "globals": "^12.1.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -2843,10 +2619,7 @@ "is-core-module": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", - "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", - "requires": { - "has": "^1.0.3" - } + "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==" }, "is-decimal": { "version": "1.0.4", @@ -2942,7 +2715,6 @@ "istanbul-lib-coverage": "^3.0.0-alpha.1", "make-dir": "^3.0.0", "p-map": "^3.0.0", - "rimraf": "^3.0.0", "uuid": "^3.3.3" } }, @@ -3369,10 +3141,7 @@ "supports-color": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "requires": { - "has-flag": "^3.0.0" - } + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==" } } }, @@ -6993,7 +6762,6 @@ "p-map": "^3.0.0", "process-on-spawn": "^1.0.0", "resolve-from": "^5.0.0", - "rimraf": "^3.0.0", "signal-exit": "^3.0.2", "spawn-wrap": "^2.0.0", "test-exclude": "^6.0.0", @@ -7684,10 +7452,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==" }, "is-fullwidth-code-point": { "version": "2.0.0", @@ -7728,7 +7493,6 @@ "foreground-child": "^2.0.0", "is-windows": "^1.0.2", "make-dir": "^3.0.0", - "rimraf": "^3.0.0", "signal-exit": "^3.0.2", "which": "^2.0.1" }, @@ -8414,248 +8178,6 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, - "eslint": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", - "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.3.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.2.0", - "esutils": "^2.0.2", - "file-entry-cache": "^6.0.0", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.4", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - } - }, - "eslint-config-etherpad": { - "version": "1.0.24", - "resolved": "https://registry.npmjs.org/eslint-config-etherpad/-/eslint-config-etherpad-1.0.24.tgz", - "integrity": "sha512-zM92/lricP0ALURQWhSFKk8gwDUVkgNiup/Gv5lly6bHj9OIMpK86SlLv/NPkQZYM609pyQjKIeiObsiCSdQsw==", - "dev": true - }, - "eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", - "dev": true, - "requires": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" - } - }, - "eslint-plugin-eslint-comments": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz", - "integrity": "sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5", - "ignore": "^5.0.5" - }, - "dependencies": { - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - } - } - }, - "eslint-plugin-mocha": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-8.0.0.tgz", - "integrity": "sha512-n67etbWDz6NQM+HnTwZHyBwz/bLlYPOxUbw7bPuCyFujv7ZpaT/Vn6KTAbT02gf7nRljtYIjWcTxK/n8a57rQQ==", - "dev": true, - "requires": { - "eslint-utils": "^2.1.0", - "ramda": "^0.27.1" - } - }, - "eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", - "dev": true, - "requires": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" - }, - "dependencies": { - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "eslint-plugin-prefer-arrow": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.3.tgz", - "integrity": "sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==", - "dev": true - }, - "eslint-plugin-promise": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", - "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", - "dev": true - }, - "eslint-plugin-you-dont-need-lodash-underscore": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-you-dont-need-lodash-underscore/-/eslint-plugin-you-dont-need-lodash-underscore-6.10.0.tgz", - "integrity": "sha512-Zu1KbHiWKf+alVvT+kFX2M5HW1gmtnkfF1l2cjmFozMnG0gbGgXo8oqK7lwk+ygeOXDmVfOyijqBd7SUub9AEQ==", - "dev": true, - "requires": { - "kebab-case": "^1.0.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true - }, - "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -8681,37 +8203,6 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "file-entry-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.0.tgz", - "integrity": "sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -8764,17 +8255,6 @@ } } }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -8849,23 +8329,6 @@ "path-is-absolute": "^1.0.0" } }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", @@ -8886,14 +8349,6 @@ "har-schema": "^2.0.0" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, "has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", @@ -8929,11 +8384,6 @@ "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -8966,12 +8416,6 @@ "safer-buffer": ">= 2.1.2 < 3.0.0" } }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, "ignore-walk": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", @@ -8981,22 +8425,6 @@ "minimatch": "^3.0.4" } }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, "indexof": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", @@ -9027,33 +8455,11 @@ "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" }, - "is-core-module": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", - "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -9074,22 +8480,6 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, "jsbi": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.1.4.tgz", @@ -9110,12 +8500,6 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -9151,22 +8535,6 @@ "safe-buffer": "^5.0.1" } }, - "kebab-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/kebab-case/-/kebab-case-1.0.0.tgz", - "integrity": "sha1-P55JkK3K0MaGwOcB92RYaPdfkes=", - "dev": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, "lodash": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", @@ -9177,15 +8545,6 @@ "resolved": "https://registry.npmjs.org/long/-/long-2.4.0.tgz", "integrity": "sha1-n6GAux2VAM3CnEFWdmoZleH0Uk8=" }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, "memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", @@ -9316,12 +8675,6 @@ "resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz", "integrity": "sha1-eJkHjmS/PIo9cyYBs9QP8F21j6A=" }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, "needle": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/needle/-/needle-2.6.0.tgz", @@ -9553,20 +8906,6 @@ "wrappy": "1" } }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", @@ -9594,15 +8933,6 @@ "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, "parseqs": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", @@ -9618,18 +8948,6 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -9712,23 +9030,11 @@ "xtend": "^4.0.0" } }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -9744,12 +9050,6 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, - "ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", - "dev": true - }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -9820,12 +9120,6 @@ "redis-errors": "^1.0.0" } }, - "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true - }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -9874,12 +9168,6 @@ } } }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, "require_optional": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", @@ -9901,22 +9189,6 @@ } } }, - "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "dev": true, - "requires": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, "rethinkdb": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/rethinkdb/-/rethinkdb-2.4.2.tgz", @@ -9925,14 +9197,6 @@ "bluebird": ">= 2.3.2 < 3" } }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -9957,35 +9221,11 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, - "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -10002,49 +9242,6 @@ "debug": "^4.3.1" } }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - } - } - }, "socket.io": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.1.tgz", @@ -10180,12 +9377,6 @@ } } }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, "sqlite3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.1.tgz", @@ -10218,25 +9409,6 @@ "tweetnacl": "~0.14.0" } }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - } - } - }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -10252,62 +9424,6 @@ } } }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", - "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", - "dev": true, - "requires": { - "ajv": "^7.0.2", - "lodash": "^4.17.20", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" - }, - "dependencies": { - "ajv": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.0.3.tgz", - "integrity": "sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } - }, "tar": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", @@ -10367,12 +9483,6 @@ } } }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, "to-array": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", @@ -10411,21 +9521,6 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, "ueberdb2": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/ueberdb2/-/ueberdb2-1.2.5.tgz", @@ -10470,12 +9565,6 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, - "v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", - "dev": true - }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -10486,15 +9575,6 @@ "extsprintf": "^1.2.0" } }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -10527,12 +9607,6 @@ } } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -10582,12 +9656,6 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "yeast": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", diff --git a/package.json b/package.json index de9298807..772ec540b 100644 --- a/package.json +++ b/package.json @@ -2,112 +2,6 @@ "dependencies": { "ep_etherpad-lite": "file:src" }, - "devDependencies": { - "eslint": "^7.18.0", - "eslint-config-etherpad": "^1.0.24", - "eslint-plugin-eslint-comments": "^3.2.0", - "eslint-plugin-mocha": "^8.0.0", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prefer-arrow": "^1.2.3", - "eslint-plugin-promise": "^4.2.1", - "eslint-plugin-you-dont-need-lodash-underscore": "^6.10.0" - }, - "eslintConfig": { - "ignorePatterns": [ - "/src/", - "/tests/frontend/lib/" - ], - "overrides": [ - { - "files": [ - "**/.eslintrc.js" - ], - "extends": "etherpad/node" - }, - { - "files": [ - "**/*" - ], - "excludedFiles": [ - "**/.eslintrc.js", - "tests/frontend/**/*" - ], - "extends": "etherpad/node" - }, - { - "files": [ - "tests/**/*" - ], - "excludedFiles": [ - "**/.eslintrc.js", - "tests/frontend/travis/**/*", - "tests/ratelimit/**/*" - ], - "extends": "etherpad/tests", - "rules": { - "mocha/no-exports": "off", - "mocha/no-top-level-hooks": "off" - } - }, - { - "files": [ - "tests/backend/**/*" - ], - "excludedFiles": [ - "**/.eslintrc.js" - ], - "extends": "etherpad/tests/backend", - "overrides": [ - { - "files": [ - "tests/backend/**/*" - ], - "excludedFiles": [ - "tests/backend/specs/**/*" - ], - "rules": { - "mocha/no-exports": "off", - "mocha/no-top-level-hooks": "off" - } - } - ] - }, - { - "files": [ - "tests/frontend/**/*" - ], - "excludedFiles": [ - "**/.eslintrc.js", - "tests/frontend/travis/**/*" - ], - "extends": "etherpad/tests/frontend", - "overrides": [ - { - "files": [ - "tests/frontend/**/*" - ], - "excludedFiles": [ - "tests/frontend/specs/**/*" - ], - "rules": { - "mocha/no-exports": "off", - "mocha/no-top-level-hooks": "off" - } - } - ] - }, - { - "files": [ - "tests/frontend/travis/**/*" - ], - "extends": "etherpad/node" - } - ], - "root": true - }, - "scripts": { - "lint": "eslint ." - }, "engines": { "node": "^10.17.0 || >=11.14.0" } diff --git a/bin/buildDebian.sh b/src/bin/buildDebian.sh similarity index 100% rename from bin/buildDebian.sh rename to src/bin/buildDebian.sh diff --git a/bin/buildForWindows.sh b/src/bin/buildForWindows.sh similarity index 100% rename from bin/buildForWindows.sh rename to src/bin/buildForWindows.sh diff --git a/bin/checkAllPads.js b/src/bin/checkAllPads.js similarity index 91% rename from bin/checkAllPads.js rename to src/bin/checkAllPads.js index 7f8a71401..fe1719b88 100644 --- a/bin/checkAllPads.js +++ b/src/bin/checkAllPads.js @@ -11,13 +11,13 @@ if (process.argv.length !== 2) throw new Error('Use: node bin/checkAllPads.js'); (async () => { // initialize the database - require('ep_etherpad-lite/node/utils/Settings'); - const db = require('ep_etherpad-lite/node/db/DB'); + require('../node/utils/Settings'); + const db = require('../node/db/DB'); await db.init(); // load modules - const Changeset = require('ep_etherpad-lite/static/js/Changeset'); - const padManager = require('ep_etherpad-lite/node/db/PadManager'); + const Changeset = require('../static/js/Changeset'); + const padManager = require('../node/db/PadManager'); let revTestedCount = 0; diff --git a/bin/checkPad.js b/src/bin/checkPad.js similarity index 90% rename from bin/checkPad.js rename to src/bin/checkPad.js index 53a3e10dc..becca3a4a 100644 --- a/bin/checkPad.js +++ b/src/bin/checkPad.js @@ -15,13 +15,13 @@ let checkRevisionCount = 0; (async () => { // initialize database - require('ep_etherpad-lite/node/utils/Settings'); - const db = require('ep_etherpad-lite/node/db/DB'); + require('../node/utils/Settings'); + const db = require('../node/db/DB'); await db.init(); // load modules - const Changeset = require('ep_etherpad-lite/static/js/Changeset'); - const padManager = require('ep_etherpad-lite/node/db/PadManager'); + const Changeset = require('../static/js/Changeset'); + const padManager = require('../node/db/PadManager'); const exists = await padManager.doesPadExists(padId); if (!exists) throw new Error('Pad does not exist'); diff --git a/bin/checkPadDeltas.js b/src/bin/checkPadDeltas.js similarity index 90% rename from bin/checkPadDeltas.js rename to src/bin/checkPadDeltas.js index 67cc8f159..043187cef 100644 --- a/bin/checkPadDeltas.js +++ b/src/bin/checkPadDeltas.js @@ -13,17 +13,17 @@ if (process.argv.length !== 3) throw new Error('Use: node bin/checkPadDeltas.js const padId = process.argv[2]; const expect = require('../tests/frontend/lib/expect'); -const diff = require('ep_etherpad-lite/node_modules/diff'); +const diff = require('diff'); (async () => { // initialize database - require('ep_etherpad-lite/node/utils/Settings'); - const db = require('ep_etherpad-lite/node/db/DB'); + require('../node/utils/Settings'); + const db = require('../node/db/DB'); await db.init(); // load modules - const Changeset = require('ep_etherpad-lite/static/js/Changeset'); - const padManager = require('ep_etherpad-lite/node/db/PadManager'); + const Changeset = require('../static/js/Changeset'); + const padManager = require('../node/db/PadManager'); const exists = await padManager.doesPadExists(padId); if (!exists) throw new Error('Pad does not exist'); diff --git a/bin/cleanRun.sh b/src/bin/cleanRun.sh similarity index 100% rename from bin/cleanRun.sh rename to src/bin/cleanRun.sh diff --git a/bin/convertSettings.json.template b/src/bin/convertSettings.json.template similarity index 100% rename from bin/convertSettings.json.template rename to src/bin/convertSettings.json.template diff --git a/bin/createRelease.sh b/src/bin/createRelease.sh similarity index 100% rename from bin/createRelease.sh rename to src/bin/createRelease.sh diff --git a/bin/createUserSession.js b/src/bin/createUserSession.js similarity index 90% rename from bin/createUserSession.js rename to src/bin/createUserSession.js index 292fde8ba..33dcac18e 100644 --- a/bin/createUserSession.js +++ b/src/bin/createUserSession.js @@ -12,13 +12,13 @@ process.on('unhandledRejection', (err) => { throw err; }); const fs = require('fs'); const path = require('path'); const querystring = require('querystring'); -const settings = require('ep_etherpad-lite/node/utils/Settings'); -const supertest = require('ep_etherpad-lite/node_modules/supertest'); +const settings = require('../node/utils/Settings'); +const supertest = require('supertest'); (async () => { const api = supertest(`http://${settings.ip}:${settings.port}`); - const filePath = path.join(__dirname, '../APIKEY.txt'); + const filePath = path.join(__dirname, '../../APIKEY.txt'); const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'}); let res; diff --git a/bin/deb-src/DEBIAN/control b/src/bin/deb-src/DEBIAN/control similarity index 100% rename from bin/deb-src/DEBIAN/control rename to src/bin/deb-src/DEBIAN/control diff --git a/bin/deb-src/DEBIAN/postinst b/src/bin/deb-src/DEBIAN/postinst similarity index 100% rename from bin/deb-src/DEBIAN/postinst rename to src/bin/deb-src/DEBIAN/postinst diff --git a/bin/deb-src/DEBIAN/preinst b/src/bin/deb-src/DEBIAN/preinst similarity index 100% rename from bin/deb-src/DEBIAN/preinst rename to src/bin/deb-src/DEBIAN/preinst diff --git a/bin/deb-src/DEBIAN/prerm b/src/bin/deb-src/DEBIAN/prerm similarity index 100% rename from bin/deb-src/DEBIAN/prerm rename to src/bin/deb-src/DEBIAN/prerm diff --git a/bin/deb-src/sysroot/etc/init/etherpad.conf b/src/bin/deb-src/sysroot/etc/init/etherpad.conf similarity index 100% rename from bin/deb-src/sysroot/etc/init/etherpad.conf rename to src/bin/deb-src/sysroot/etc/init/etherpad.conf diff --git a/bin/debugRun.sh b/src/bin/debugRun.sh similarity index 100% rename from bin/debugRun.sh rename to src/bin/debugRun.sh diff --git a/bin/deleteAllGroupSessions.js b/src/bin/deleteAllGroupSessions.js similarity index 93% rename from bin/deleteAllGroupSessions.js rename to src/bin/deleteAllGroupSessions.js index fd8ba5341..c95bf1075 100644 --- a/bin/deleteAllGroupSessions.js +++ b/src/bin/deleteAllGroupSessions.js @@ -9,16 +9,16 @@ // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); -const supertest = require('ep_etherpad-lite/node_modules/supertest'); const path = require('path'); const fs = require('fs'); +const supertest = require('supertest'); // Set a delete counter which will increment on each delete attempt // TODO: Check delete is successful before incrementing let deleteCount = 0; // get the API Key -const filePath = path.join(__dirname, '../APIKEY.txt'); +const filePath = path.join(__dirname, '../../APIKEY.txt'); console.log('Deleting all group sessions, please be patient.'); (async () => { diff --git a/bin/deletePad.js b/src/bin/deletePad.js similarity index 91% rename from bin/deletePad.js rename to src/bin/deletePad.js index ea9aea7e0..51ea99639 100644 --- a/bin/deletePad.js +++ b/src/bin/deletePad.js @@ -10,10 +10,11 @@ process.on('unhandledRejection', (err) => { throw err; }); const settings = require('../tests/container/loadSettings').loadSettings(); -const supertest = require('ep_etherpad-lite/node_modules/supertest'); -const api = supertest(`http://${settings.ip}:${settings.port}`); const path = require('path'); const fs = require('fs'); +const supertest = require('supertest'); + +const api = supertest(`http://${settings.ip}:${settings.port}`); if (process.argv.length !== 3) throw new Error('Use: node deletePad.js $PADID'); @@ -21,7 +22,7 @@ if (process.argv.length !== 3) throw new Error('Use: node deletePad.js $PADID'); const padId = process.argv[2]; // get the API Key -const filePath = path.join(__dirname, '../APIKEY.txt'); +const filePath = path.join(__dirname, '../../APIKEY.txt'); const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'}); (async () => { diff --git a/bin/dirty-db-cleaner.py b/src/bin/dirty-db-cleaner.py similarity index 100% rename from bin/dirty-db-cleaner.py rename to src/bin/dirty-db-cleaner.py diff --git a/bin/doc/LICENSE b/src/bin/doc/LICENSE similarity index 100% rename from bin/doc/LICENSE rename to src/bin/doc/LICENSE diff --git a/bin/doc/README.md b/src/bin/doc/README.md similarity index 100% rename from bin/doc/README.md rename to src/bin/doc/README.md diff --git a/bin/doc/generate.js b/src/bin/doc/generate.js similarity index 100% rename from bin/doc/generate.js rename to src/bin/doc/generate.js diff --git a/bin/doc/html.js b/src/bin/doc/html.js similarity index 100% rename from bin/doc/html.js rename to src/bin/doc/html.js diff --git a/bin/doc/json.js b/src/bin/doc/json.js similarity index 100% rename from bin/doc/json.js rename to src/bin/doc/json.js diff --git a/bin/doc/package-lock.json b/src/bin/doc/package-lock.json similarity index 100% rename from bin/doc/package-lock.json rename to src/bin/doc/package-lock.json diff --git a/bin/doc/package.json b/src/bin/doc/package.json similarity index 100% rename from bin/doc/package.json rename to src/bin/doc/package.json diff --git a/bin/extractPadData.js b/src/bin/extractPadData.js similarity index 87% rename from bin/extractPadData.js rename to src/bin/extractPadData.js index dc413a9a4..0688245d4 100644 --- a/bin/extractPadData.js +++ b/src/bin/extractPadData.js @@ -17,13 +17,13 @@ const padId = process.argv[2]; (async () => { // initialize database - require('ep_etherpad-lite/node/utils/Settings'); - const db = require('ep_etherpad-lite/node/db/DB'); + require('../node/utils/Settings'); + const db = require('../node/db/DB'); await db.init(); // load extra modules - const dirtyDB = require('ep_etherpad-lite/node_modules/dirty'); - const padManager = require('ep_etherpad-lite/node/db/PadManager'); + const dirtyDB = require('dirty'); + const padManager = require('../node/db/PadManager'); // initialize output database const dirty = dirtyDB(`${padId}.db`); diff --git a/bin/fastRun.sh b/src/bin/fastRun.sh similarity index 100% rename from bin/fastRun.sh rename to src/bin/fastRun.sh diff --git a/bin/functions.sh b/src/bin/functions.sh similarity index 100% rename from bin/functions.sh rename to src/bin/functions.sh diff --git a/bin/importSqlFile.js b/src/bin/importSqlFile.js similarity index 92% rename from bin/importSqlFile.js rename to src/bin/importSqlFile.js index 2ba450a7d..5a0520885 100644 --- a/bin/importSqlFile.js +++ b/src/bin/importSqlFile.js @@ -45,9 +45,9 @@ const unescape = (val) => { (async () => { const fs = require('fs'); - const ueberDB = require('ep_etherpad-lite/node_modules/ueberdb2'); - const settings = require('ep_etherpad-lite/node/utils/Settings'); - const log4js = require('ep_etherpad-lite/node_modules/log4js'); + const log4js = require('log4js'); + const settings = require('../node/utils/Settings'); + const ueberDB = require('ueberdb2'); const dbWrapperSettings = { cache: 0, diff --git a/bin/installDeps.sh b/src/bin/installDeps.sh similarity index 100% rename from bin/installDeps.sh rename to src/bin/installDeps.sh diff --git a/bin/installOnWindows.bat b/src/bin/installOnWindows.bat similarity index 100% rename from bin/installOnWindows.bat rename to src/bin/installOnWindows.bat diff --git a/bin/migrateDirtyDBtoRealDB.js b/src/bin/migrateDirtyDBtoRealDB.js similarity index 84% rename from bin/migrateDirtyDBtoRealDB.js rename to src/bin/migrateDirtyDBtoRealDB.js index bbf34cdd0..fbeb0eb44 100644 --- a/bin/migrateDirtyDBtoRealDB.js +++ b/src/bin/migrateDirtyDBtoRealDB.js @@ -12,12 +12,12 @@ process.on('unhandledRejection', (err) => { throw err; }); // It might be necessary to run the script using more memory: // `node --max-old-space-size=4096 bin/migrateDirtyDBtoRealDB.js` - + const dirtyDb = require('dirty'); + const log4js = require('log4js'); + const settings = require('../node/utils/Settings'); + const ueberDB = require('ueberdb2'); const util = require('util'); - const settings = require('ep_etherpad-lite/node/utils/Settings'); - const dirtyDb = require('ep_etherpad-lite/node_modules/dirty'); - const ueberDB = require('ep_etherpad-lite/node_modules/ueberdb2'); - const log4js = require('ep_etherpad-lite/node_modules/log4js'); + const dbWrapperSettings = { cache: '0', // The cache slows things down when you're mostly writing. writeInterval: 0, // Write directly to the database, don't buffer @@ -30,7 +30,7 @@ process.on('unhandledRejection', (err) => { throw err; }); await db.init(); console.log('Waiting for dirtyDB to parse its file.'); - const dirty = dirtyDb(`${__dirname}/../var/dirty.db`); + const dirty = dirtyDb(`${__dirname}/../../var/dirty.db`); const length = await new Promise((resolve) => { dirty.once('load', resolve); }); console.log(`Found ${length} records, processing now.`); diff --git a/bin/plugins/README.md b/src/bin/plugins/README.md similarity index 100% rename from bin/plugins/README.md rename to src/bin/plugins/README.md diff --git a/bin/plugins/checkPlugin.js b/src/bin/plugins/checkPlugin.js similarity index 99% rename from bin/plugins/checkPlugin.js rename to src/bin/plugins/checkPlugin.js index 0b736af80..5bf35dd0e 100755 --- a/bin/plugins/checkPlugin.js +++ b/src/bin/plugins/checkPlugin.js @@ -429,7 +429,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { // The ep_etherpad-lite peer dep must be installed last otherwise `npm install` will nuke it. An // absolute path to etherpad-lite/src is used here so that pluginPath can be a symlink. execSync( - `${npmInstall} --no-save ep_etherpad-lite@file:${__dirname}/../../src`, {stdio: 'inherit'}); + `${npmInstall} --no-save ep_etherpad-lite@file:${__dirname}/../../`, {stdio: 'inherit'}); // linting begins try { diff --git a/bin/plugins/getCorePlugins.sh b/src/bin/plugins/getCorePlugins.sh similarity index 100% rename from bin/plugins/getCorePlugins.sh rename to src/bin/plugins/getCorePlugins.sh diff --git a/bin/plugins/lib/CONTRIBUTING.md b/src/bin/plugins/lib/CONTRIBUTING.md similarity index 100% rename from bin/plugins/lib/CONTRIBUTING.md rename to src/bin/plugins/lib/CONTRIBUTING.md diff --git a/bin/plugins/lib/LICENSE.md b/src/bin/plugins/lib/LICENSE.md similarity index 100% rename from bin/plugins/lib/LICENSE.md rename to src/bin/plugins/lib/LICENSE.md diff --git a/bin/plugins/lib/README.md b/src/bin/plugins/lib/README.md similarity index 100% rename from bin/plugins/lib/README.md rename to src/bin/plugins/lib/README.md diff --git a/bin/plugins/lib/backend-tests.yml b/src/bin/plugins/lib/backend-tests.yml similarity index 100% rename from bin/plugins/lib/backend-tests.yml rename to src/bin/plugins/lib/backend-tests.yml diff --git a/bin/plugins/lib/gitignore b/src/bin/plugins/lib/gitignore similarity index 100% rename from bin/plugins/lib/gitignore rename to src/bin/plugins/lib/gitignore diff --git a/bin/plugins/lib/npmpublish.yml b/src/bin/plugins/lib/npmpublish.yml similarity index 100% rename from bin/plugins/lib/npmpublish.yml rename to src/bin/plugins/lib/npmpublish.yml diff --git a/bin/plugins/lib/travis.yml b/src/bin/plugins/lib/travis.yml similarity index 100% rename from bin/plugins/lib/travis.yml rename to src/bin/plugins/lib/travis.yml diff --git a/bin/plugins/reTestAllPlugins.sh b/src/bin/plugins/reTestAllPlugins.sh similarity index 100% rename from bin/plugins/reTestAllPlugins.sh rename to src/bin/plugins/reTestAllPlugins.sh diff --git a/bin/plugins/updateAllPluginsScript.sh b/src/bin/plugins/updateAllPluginsScript.sh similarity index 100% rename from bin/plugins/updateAllPluginsScript.sh rename to src/bin/plugins/updateAllPluginsScript.sh diff --git a/bin/plugins/updateCorePlugins.sh b/src/bin/plugins/updateCorePlugins.sh similarity index 100% rename from bin/plugins/updateCorePlugins.sh rename to src/bin/plugins/updateCorePlugins.sh diff --git a/bin/rebuildPad.js b/src/bin/rebuildPad.js similarity index 90% rename from bin/rebuildPad.js rename to src/bin/rebuildPad.js index a8daf7285..bda611f79 100644 --- a/bin/rebuildPad.js +++ b/src/bin/rebuildPad.js @@ -18,11 +18,11 @@ const newRevHead = process.argv[3]; const newPadId = process.argv[4] || `${padId}-rebuilt`; (async () => { - const db = require('ep_etherpad-lite/node/db/DB'); + const db = require('../node/db/DB'); await db.init(); - const PadManager = require('ep_etherpad-lite/node/db/PadManager'); - const Pad = require('ep_etherpad-lite/node/db/Pad').Pad; + const PadManager = require('../node/db/PadManager'); + const Pad = require('../node/db/Pad').Pad; // Validate the newPadId if specified and that a pad with that ID does // not already exist to avoid overwriting it. if (!PadManager.isValidPadId(newPadId)) { @@ -43,8 +43,8 @@ const newPadId = process.argv[4] || `${padId}-rebuilt`; })); // Rebuild Pad from revisions up to and including the new revision head - const AuthorManager = require('ep_etherpad-lite/node/db/AuthorManager'); - const Changeset = require('ep_etherpad-lite/static/js/Changeset'); + const AuthorManager = require('../node/db/AuthorManager'); + const Changeset = require('../static/js/Changeset'); // Author attributes are derived from changesets, but there can also be // non-author attributes with specific mappings that changesets depend on // and, AFAICT, cannot be recreated any other way diff --git a/bin/release.js b/src/bin/release.js similarity index 97% rename from bin/release.js rename to src/bin/release.js index f06679e48..b12513d6d 100644 --- a/bin/release.js +++ b/src/bin/release.js @@ -6,7 +6,7 @@ process.on('unhandledRejection', (err) => { throw err; }); const fs = require('fs'); const childProcess = require('child_process'); -const semver = require('ep_etherpad-lite/node_modules/semver'); +const semver = require('semver'); /* diff --git a/bin/repairPad.js b/src/bin/repairPad.js similarity index 90% rename from bin/repairPad.js rename to src/bin/repairPad.js index aef9102ae..9aa200517 100644 --- a/bin/repairPad.js +++ b/src/bin/repairPad.js @@ -19,12 +19,12 @@ let valueCount = 0; (async () => { // initialize database - require('ep_etherpad-lite/node/utils/Settings'); - const db = require('ep_etherpad-lite/node/db/DB'); + require('../node/utils/Settings'); + const db = require('../node/db/DB'); await db.init(); // get the pad - const padManager = require('ep_etherpad-lite/node/db/PadManager'); + const padManager = require('../node/db/PadManager'); const pad = await padManager.getPad(padId); // accumulate the required keys diff --git a/bin/run.sh b/src/bin/run.sh similarity index 100% rename from bin/run.sh rename to src/bin/run.sh diff --git a/bin/safeRun.sh b/src/bin/safeRun.sh similarity index 100% rename from bin/safeRun.sh rename to src/bin/safeRun.sh diff --git a/bin/updatePlugins.sh b/src/bin/updatePlugins.sh similarity index 100% rename from bin/updatePlugins.sh rename to src/bin/updatePlugins.sh diff --git a/src/package.json b/src/package.json index 583c924db..bf61feaf7 100644 --- a/src/package.json +++ b/src/package.json @@ -104,7 +104,8 @@ "/static/js/gritter.js", "/static/js/html10n.js", "/static/js/jquery.js", - "/static/js/vendors/nice-select.js" + "/static/js/vendors/nice-select.js", + "/tests/frontend/lib/" ], "overrides": [ { @@ -118,7 +119,8 @@ "**/*" ], "excludedFiles": [ - "**/.eslintrc.js" + "**/.eslintrc.js", + "tests/frontend/**/*" ], "extends": "etherpad/node" }, @@ -133,6 +135,74 @@ "env": { "shared-node-browser": true } + }, + { + "files": [ + "tests/**/*" + ], + "excludedFiles": [ + "**/.eslintrc.js", + "tests/frontend/travis/**/*", + "tests/ratelimit/**/*" + ], + "extends": "etherpad/tests", + "rules": { + "mocha/no-exports": "off", + "mocha/no-top-level-hooks": "off" + } + }, + { + "files": [ + "tests/backend/**/*" + ], + "excludedFiles": [ + "**/.eslintrc.js" + ], + "extends": "etherpad/tests/backend", + "overrides": [ + { + "files": [ + "tests/backend/**/*" + ], + "excludedFiles": [ + "tests/backend/specs/**/*" + ], + "rules": { + "mocha/no-exports": "off", + "mocha/no-top-level-hooks": "off" + } + } + ] + }, + { + "files": [ + "tests/frontend/**/*" + ], + "excludedFiles": [ + "**/.eslintrc.js", + "tests/frontend/travis/**/*" + ], + "extends": "etherpad/tests/frontend", + "overrides": [ + { + "files": [ + "tests/frontend/**/*" + ], + "excludedFiles": [ + "tests/frontend/specs/**/*" + ], + "rules": { + "mocha/no-exports": "off", + "mocha/no-top-level-hooks": "off" + } + } + ] + }, + { + "files": [ + "tests/frontend/travis/**/*" + ], + "extends": "etherpad/node" } ], "root": true diff --git a/src/tests b/src/tests deleted file mode 120000 index 6dd24e02b..000000000 --- a/src/tests +++ /dev/null @@ -1 +0,0 @@ -../tests \ No newline at end of file diff --git a/tests/README.md b/src/tests/README.md similarity index 100% rename from tests/README.md rename to src/tests/README.md diff --git a/tests/backend/common.js b/src/tests/backend/common.js similarity index 82% rename from tests/backend/common.js rename to src/tests/backend/common.js index 642f83afe..3fe80de3b 100644 --- a/tests/backend/common.js +++ b/src/tests/backend/common.js @@ -1,12 +1,12 @@ 'use strict'; -const apiHandler = require('ep_etherpad-lite/node/handler/APIHandler'); -const log4js = require('ep_etherpad-lite/node_modules/log4js'); +const apiHandler = require('../../node/handler/APIHandler'); +const log4js = require('log4js'); const process = require('process'); -const server = require('ep_etherpad-lite/node/server'); -const settings = require('ep_etherpad-lite/node/utils/Settings'); -const supertest = require('ep_etherpad-lite/node_modules/supertest'); -const webaccess = require('ep_etherpad-lite/node/hooks/express/webaccess'); +const server = require('../../node/server'); +const settings = require('../../node/utils/Settings'); +const supertest = require('supertest'); +const webaccess = require('../../node/hooks/express/webaccess'); const backups = {}; let inited = false; diff --git a/tests/backend/fuzzImportTest.js b/src/tests/backend/fuzzImportTest.js similarity index 96% rename from tests/backend/fuzzImportTest.js rename to src/tests/backend/fuzzImportTest.js index eb5a17d17..bdcc92f11 100644 --- a/tests/backend/fuzzImportTest.js +++ b/src/tests/backend/fuzzImportTest.js @@ -3,10 +3,10 @@ * Usage: node fuzzImportTest.js */ const common = require('./common'); -const settings = require(`${__dirname}/loadSettings`).loadSettings(); const host = `http://${settings.ip}:${settings.port}`; const request = require('request'); const froth = require('mocha-froth'); +const settings = require('../container/loadSettings').loadSettings(); const apiKey = common.apiKey; const apiVersion = 1; diff --git a/tests/backend/specs/api/api.js b/src/tests/backend/specs/api/api.js similarity index 88% rename from tests/backend/specs/api/api.js rename to src/tests/backend/specs/api/api.js index de009c559..359bb3f6a 100644 --- a/tests/backend/specs/api/api.js +++ b/src/tests/backend/specs/api/api.js @@ -7,12 +7,11 @@ */ const common = require('../../common'); -const supertest = require(`${__dirname}/../../../../src/node_modules/supertest`); -const settings = require(`${__dirname}/../../../../src/node/utils/Settings`); +const settings = require('../../../../node/utils/Settings'); +const supertest = require('supertest'); +const validateOpenAPI = require('openapi-schema-validation').validate; + const api = supertest(`http://${settings.ip}:${settings.port}`); - -const validateOpenAPI = require(`${__dirname}/../../../../src/node_modules/openapi-schema-validation`).validate; - const apiKey = common.apiKey; let apiVersion = 1; diff --git a/tests/backend/specs/api/characterEncoding.js b/src/tests/backend/specs/api/characterEncoding.js similarity index 95% rename from tests/backend/specs/api/characterEncoding.js rename to src/tests/backend/specs/api/characterEncoding.js index 94c22307d..9c1e294e5 100644 --- a/tests/backend/specs/api/characterEncoding.js +++ b/src/tests/backend/specs/api/characterEncoding.js @@ -7,11 +7,11 @@ */ const common = require('../../common'); -const supertest = require(`${__dirname}/../../../../src/node_modules/supertest`); const fs = require('fs'); -const settings = require(`${__dirname}/../../../../src/node/utils/Settings`); -const api = supertest(`http://${settings.ip}:${settings.port}`); +const settings = require('../../../../node/utils/Settings'); +const supertest = require('supertest'); +const api = supertest(`http://${settings.ip}:${settings.port}`); const apiKey = common.apiKey; let apiVersion = 1; const testPadId = makeid(); diff --git a/tests/backend/specs/api/chat.js b/src/tests/backend/specs/api/chat.js similarity index 95% rename from tests/backend/specs/api/chat.js rename to src/tests/backend/specs/api/chat.js index d51dbfaee..e2ddf0540 100644 --- a/tests/backend/specs/api/chat.js +++ b/src/tests/backend/specs/api/chat.js @@ -1,8 +1,6 @@ -function m(mod) { return `${__dirname}/../../../../src/${mod}`; } - const common = require('../../common'); -const settings = require(m('node/utils/Settings')); -const supertest = require(m('node_modules/supertest')); +const settings = require('../../../../node/utils/Settings'); +const supertest = require('supertest'); const api = supertest(`http://${settings.ip}:${settings.port}`); const apiKey = common.apiKey; diff --git a/tests/backend/specs/api/emojis.html b/src/tests/backend/specs/api/emojis.html similarity index 100% rename from tests/backend/specs/api/emojis.html rename to src/tests/backend/specs/api/emojis.html diff --git a/tests/backend/specs/api/fuzzImportTest.js b/src/tests/backend/specs/api/fuzzImportTest.js similarity index 86% rename from tests/backend/specs/api/fuzzImportTest.js rename to src/tests/backend/specs/api/fuzzImportTest.js index 4c612101d..85f4c81f2 100644 --- a/tests/backend/specs/api/fuzzImportTest.js +++ b/src/tests/backend/specs/api/fuzzImportTest.js @@ -3,10 +3,11 @@ */ /* const common = require('../../common'); -const settings = require(__dirname+'/../../../../tests/container/loadSettings.js').loadSettings(); +const froth = require('mocha-froth'); +const request = require('request'); +const settings = require('../../../container/loadSettings.js').loadSettings(); + const host = "http://" + settings.ip + ":" + settings.port; -const request = require(__dirname+'/../../../../src/node_modules/request'); -const froth = require(__dirname+'/../../../../src/node_modules/mocha-froth'); const apiKey = common.apiKey; var apiVersion = 1; diff --git a/tests/backend/specs/api/image.png b/src/tests/backend/specs/api/image.png similarity index 100% rename from tests/backend/specs/api/image.png rename to src/tests/backend/specs/api/image.png diff --git a/tests/backend/specs/api/importexport.js b/src/tests/backend/specs/api/importexport.js similarity index 99% rename from tests/backend/specs/api/importexport.js rename to src/tests/backend/specs/api/importexport.js index 1e4461530..4cdb5b8fe 100644 --- a/tests/backend/specs/api/importexport.js +++ b/src/tests/backend/specs/api/importexport.js @@ -8,7 +8,7 @@ const common = require('../../common'); const settings = require('../../../container/loadSettings.js').loadSettings(); -const supertest = require('ep_etherpad-lite/node_modules/supertest'); +const supertest = require('supertest'); const api = supertest(`http://${settings.ip}:${settings.port}`); const apiKey = common.apiKey; diff --git a/tests/backend/specs/api/importexportGetPost.js b/src/tests/backend/specs/api/importexportGetPost.js similarity index 97% rename from tests/backend/specs/api/importexportGetPost.js rename to src/tests/backend/specs/api/importexportGetPost.js index 5fec10151..0a27f70ae 100644 --- a/tests/backend/specs/api/importexportGetPost.js +++ b/src/tests/backend/specs/api/importexportGetPost.js @@ -6,11 +6,11 @@ const assert = require('assert').strict; const common = require('../../common'); -const superagent = require('ep_etherpad-lite/node_modules/superagent'); const fs = require('fs'); -const settings = require('ep_etherpad-lite/node/utils/Settings'); -const padManager = require('ep_etherpad-lite/node/db/PadManager'); -const plugins = require('ep_etherpad-lite/static/js/pluginfw/plugin_defs'); +const settings = require('../../../../node/utils/Settings'); +const superagent = require('superagent'); +const padManager = require('../../../../node/db/PadManager'); +const plugins = require('../../../../static/js/pluginfw/plugin_defs'); const padText = fs.readFileSync('../tests/backend/specs/api/test.txt'); const etherpadDoc = fs.readFileSync('../tests/backend/specs/api/test.etherpad'); diff --git a/tests/backend/specs/api/instance.js b/src/tests/backend/specs/api/instance.js similarity index 91% rename from tests/backend/specs/api/instance.js rename to src/tests/backend/specs/api/instance.js index 7da967ed2..833c75d5a 100644 --- a/tests/backend/specs/api/instance.js +++ b/src/tests/backend/specs/api/instance.js @@ -4,8 +4,9 @@ * Section "GLOBAL FUNCTIONS" in src/node/db/API.js */ const common = require('../../common'); -const supertest = require(`${__dirname}/../../../../src/node_modules/supertest`); -const settings = require(`${__dirname}/../../../../src/node/utils/Settings`); +const settings = require('../../../../node/utils/Settings'); +const supertest = require('supertest'); + const api = supertest(`http://${settings.ip}:${settings.port}`); const apiKey = common.apiKey; diff --git a/tests/backend/specs/api/pad.js b/src/tests/backend/specs/api/pad.js similarity index 99% rename from tests/backend/specs/api/pad.js rename to src/tests/backend/specs/api/pad.js index a0bbec62c..d0280f789 100644 --- a/tests/backend/specs/api/pad.js +++ b/src/tests/backend/specs/api/pad.js @@ -5,11 +5,12 @@ * TODO: unify those two files, and merge in a single one. */ +const async = require('async'); const common = require('../../common'); -const supertest = require(`${__dirname}/../../../../src/node_modules/supertest`); -const settings = require(`${__dirname}/../../../../src/node/utils/Settings`); +const settings = require('../../../../node/utils/Settings'); +const supertest = require('supertest'); + const api = supertest(`http://${settings.ip}:${settings.port}`); -const async = require(`${__dirname}/../../../../src/node_modules/async`); const apiKey = common.apiKey; let apiVersion = 1; diff --git a/tests/backend/specs/api/sessionsAndGroups.js b/src/tests/backend/specs/api/sessionsAndGroups.js similarity index 98% rename from tests/backend/specs/api/sessionsAndGroups.js rename to src/tests/backend/specs/api/sessionsAndGroups.js index 655324722..16bbca000 100644 --- a/tests/backend/specs/api/sessionsAndGroups.js +++ b/src/tests/backend/specs/api/sessionsAndGroups.js @@ -1,7 +1,8 @@ const assert = require('assert').strict; const common = require('../../common'); -const supertest = require(`${__dirname}/../../../../src/node_modules/supertest`); -const settings = require(`${__dirname}/../../../../src/node/utils/Settings`); +const settings = require('../../../../node/utils/Settings'); +const supertest = require('supertest'); + const api = supertest(`http://${settings.ip}:${settings.port}`); const apiKey = common.apiKey; diff --git a/tests/backend/specs/api/test.doc b/src/tests/backend/specs/api/test.doc similarity index 100% rename from tests/backend/specs/api/test.doc rename to src/tests/backend/specs/api/test.doc diff --git a/tests/backend/specs/api/test.docx b/src/tests/backend/specs/api/test.docx similarity index 100% rename from tests/backend/specs/api/test.docx rename to src/tests/backend/specs/api/test.docx diff --git a/tests/backend/specs/api/test.etherpad b/src/tests/backend/specs/api/test.etherpad similarity index 100% rename from tests/backend/specs/api/test.etherpad rename to src/tests/backend/specs/api/test.etherpad diff --git a/tests/backend/specs/api/test.odt b/src/tests/backend/specs/api/test.odt similarity index 100% rename from tests/backend/specs/api/test.odt rename to src/tests/backend/specs/api/test.odt diff --git a/tests/backend/specs/api/test.pdf b/src/tests/backend/specs/api/test.pdf similarity index 100% rename from tests/backend/specs/api/test.pdf rename to src/tests/backend/specs/api/test.pdf diff --git a/tests/backend/specs/api/test.txt b/src/tests/backend/specs/api/test.txt similarity index 100% rename from tests/backend/specs/api/test.txt rename to src/tests/backend/specs/api/test.txt diff --git a/tests/backend/specs/api/tidy.js b/src/tests/backend/specs/api/tidy.js similarity index 87% rename from tests/backend/specs/api/tidy.js rename to src/tests/backend/specs/api/tidy.js index 98f726acd..aa07aa0be 100644 --- a/tests/backend/specs/api/tidy.js +++ b/src/tests/backend/specs/api/tidy.js @@ -6,16 +6,16 @@ const fs = require('fs'); const path = require('path'); let TidyHtml; let Settings; -const npm = require('ep_etherpad-lite/node_modules/npm/lib/npm.js'); -const nodeify = require('ep_etherpad-lite/node_modules/nodeify'); +const npm = require('npm/lib/npm.js'); +const nodeify = require('nodeify'); describe(__filename, function () { describe('tidyHtml', function () { before(function (done) { npm.load({}, (err) => { assert.ok(!err); - TidyHtml = require('ep_etherpad-lite/node/utils/TidyHtml'); - Settings = require('ep_etherpad-lite/node/utils/Settings'); + TidyHtml = require('../../../../node/utils/TidyHtml'); + Settings = require('../../../../node/utils/Settings'); return done(); }); }); diff --git a/tests/backend/specs/caching_middleware.js b/src/tests/backend/specs/caching_middleware.js similarity index 98% rename from tests/backend/specs/caching_middleware.js rename to src/tests/backend/specs/caching_middleware.js index e8e14a5cf..151bb6282 100644 --- a/tests/backend/specs/caching_middleware.js +++ b/src/tests/backend/specs/caching_middleware.js @@ -5,10 +5,10 @@ */ const common = require('../common'); -const settings = require('../../../src/node/utils/Settings'); const assert = require('assert').strict; const url = require('url'); const queryString = require('querystring'); +const settings = require('../../../node/utils/Settings'); let agent; diff --git a/tests/backend/specs/contentcollector.js b/src/tests/backend/specs/contentcollector.js similarity index 98% rename from tests/backend/specs/contentcollector.js rename to src/tests/backend/specs/contentcollector.js index 62bdf5d5d..f9b183f96 100644 --- a/tests/backend/specs/contentcollector.js +++ b/src/tests/backend/specs/contentcollector.js @@ -9,10 +9,10 @@ * If you add tests here, please also add them to importexport.js */ -const AttributePool = require('ep_etherpad-lite/static/js/AttributePool'); +const AttributePool = require('../../../static/js/AttributePool'); const assert = require('assert').strict; -const cheerio = require('ep_etherpad-lite/node_modules/cheerio'); -const contentcollector = require('ep_etherpad-lite/static/js/contentcollector'); +const cheerio = require('cheerio'); +const contentcollector = require('../../../static/js/contentcollector'); const tests = { nestedLi: { diff --git a/tests/backend/specs/hooks.js b/src/tests/backend/specs/hooks.js similarity index 99% rename from tests/backend/specs/hooks.js rename to src/tests/backend/specs/hooks.js index 865cb132b..47d45ae6c 100644 --- a/tests/backend/specs/hooks.js +++ b/src/tests/backend/specs/hooks.js @@ -1,9 +1,9 @@ 'use strict'; const assert = require('assert').strict; -const hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); -const plugins = require('ep_etherpad-lite/static/js/pluginfw/plugin_defs'); -const sinon = require('ep_etherpad-lite/node_modules/sinon'); +const hooks = require('../../../static/js/pluginfw/hooks'); +const plugins = require('../../../static/js/pluginfw/plugin_defs'); +const sinon = require('sinon'); describe(__filename, function () { const hookName = 'testHook'; diff --git a/tests/backend/specs/promises.js b/src/tests/backend/specs/promises.js similarity index 95% rename from tests/backend/specs/promises.js rename to src/tests/backend/specs/promises.js index 02c656d45..ad0c1ad92 100644 --- a/tests/backend/specs/promises.js +++ b/src/tests/backend/specs/promises.js @@ -1,7 +1,5 @@ -function m(mod) { return `${__dirname}/../../../src/${mod}`; } - const assert = require('assert').strict; -const promises = require(m('node/utils/promises')); +const promises = require('../../../node/utils/promises'); describe(__filename, function () { describe('promises.timesLimit', function () { diff --git a/tests/backend/specs/socketio.js b/src/tests/backend/specs/socketio.js similarity index 97% rename from tests/backend/specs/socketio.js rename to src/tests/backend/specs/socketio.js index 6f3af2ee0..c6d6db720 100644 --- a/tests/backend/specs/socketio.js +++ b/src/tests/backend/specs/socketio.js @@ -2,11 +2,11 @@ const assert = require('assert').strict; const common = require('../common'); -const io = require('ep_etherpad-lite/node_modules/socket.io-client'); -const padManager = require('ep_etherpad-lite/node/db/PadManager'); -const plugins = require('ep_etherpad-lite/static/js/pluginfw/plugin_defs'); -const setCookieParser = require('ep_etherpad-lite/node_modules/set-cookie-parser'); -const settings = require('ep_etherpad-lite/node/utils/Settings'); +const io = require('socket.io-client'); +const padManager = require('../../../node/db/PadManager'); +const plugins = require('../../../static/js/pluginfw/plugin_defs'); +const setCookieParser = require('set-cookie-parser'); +const settings = require('../../../node/utils/Settings'); const logger = common.logger; diff --git a/tests/backend/specs/specialpages.js b/src/tests/backend/specs/specialpages.js similarity index 91% rename from tests/backend/specs/specialpages.js rename to src/tests/backend/specs/specialpages.js index 3e385f6e9..571ca6d35 100644 --- a/tests/backend/specs/specialpages.js +++ b/src/tests/backend/specs/specialpages.js @@ -1,5 +1,5 @@ const common = require('../common'); -const settings = require('ep_etherpad-lite/node/utils/Settings'); +const settings = require('../../../node/utils/Settings'); describe(__filename, function () { let agent; diff --git a/tests/backend/specs/webaccess.js b/src/tests/backend/specs/webaccess.js similarity index 99% rename from tests/backend/specs/webaccess.js rename to src/tests/backend/specs/webaccess.js index b82a5f017..8079d6a22 100644 --- a/tests/backend/specs/webaccess.js +++ b/src/tests/backend/specs/webaccess.js @@ -2,8 +2,8 @@ const assert = require('assert').strict; const common = require('../common'); -const plugins = require('ep_etherpad-lite/static/js/pluginfw/plugin_defs'); -const settings = require('ep_etherpad-lite/node/utils/Settings'); +const plugins = require('../../../static/js/pluginfw/plugin_defs'); +const settings = require('../../../node/utils/Settings'); describe(__filename, function () { let agent; diff --git a/tests/container/loadSettings.js b/src/tests/container/loadSettings.js similarity index 86% rename from tests/container/loadSettings.js rename to src/tests/container/loadSettings.js index 7317f8022..b59ff0165 100644 --- a/tests/container/loadSettings.js +++ b/src/tests/container/loadSettings.js @@ -12,11 +12,11 @@ * back to a default) */ -const jsonminify = require(`${__dirname}/../../src/node_modules/jsonminify`); const fs = require('fs'); +const jsonminify = require('jsonminify'); function loadSettings() { - let settingsStr = fs.readFileSync(`${__dirname}/../../settings.json.docker`).toString(); + let settingsStr = fs.readFileSync(`${__dirname}/../../../settings.json.docker`).toString(); // try to parse the settings try { if (settingsStr) { diff --git a/tests/container/specs/api/pad.js b/src/tests/container/specs/api/pad.js similarity index 85% rename from tests/container/specs/api/pad.js rename to src/tests/container/specs/api/pad.js index 6aeb86708..04067f0e3 100644 --- a/tests/container/specs/api/pad.js +++ b/src/tests/container/specs/api/pad.js @@ -5,10 +5,10 @@ * TODO: unify those two files, and merge in a single one. */ -const supertest = require(`${__dirname}/../../../../src/node_modules/supertest`); -const settings = require(`${__dirname}/../../loadSettings`).loadSettings(); -const api = supertest(`http://${settings.ip}:${settings.port}`); +const settings = require('../../loadSettings').loadSettings(); +const supertest = require('supertest'); +const api = supertest(`http://${settings.ip}:${settings.port}`); const apiVersion = 1; describe('Connectivity', function () { diff --git a/tests/frontend/helper.js b/src/tests/frontend/helper.js similarity index 100% rename from tests/frontend/helper.js rename to src/tests/frontend/helper.js diff --git a/tests/frontend/helper/methods.js b/src/tests/frontend/helper/methods.js similarity index 100% rename from tests/frontend/helper/methods.js rename to src/tests/frontend/helper/methods.js diff --git a/tests/frontend/helper/ui.js b/src/tests/frontend/helper/ui.js similarity index 100% rename from tests/frontend/helper/ui.js rename to src/tests/frontend/helper/ui.js diff --git a/tests/frontend/index.html b/src/tests/frontend/index.html similarity index 100% rename from tests/frontend/index.html rename to src/tests/frontend/index.html diff --git a/tests/frontend/lib/expect.js b/src/tests/frontend/lib/expect.js similarity index 100% rename from tests/frontend/lib/expect.js rename to src/tests/frontend/lib/expect.js diff --git a/tests/frontend/lib/mocha.js b/src/tests/frontend/lib/mocha.js similarity index 100% rename from tests/frontend/lib/mocha.js rename to src/tests/frontend/lib/mocha.js diff --git a/tests/frontend/lib/sendkeys.js b/src/tests/frontend/lib/sendkeys.js similarity index 100% rename from tests/frontend/lib/sendkeys.js rename to src/tests/frontend/lib/sendkeys.js diff --git a/tests/frontend/lib/underscore.js b/src/tests/frontend/lib/underscore.js similarity index 100% rename from tests/frontend/lib/underscore.js rename to src/tests/frontend/lib/underscore.js diff --git a/tests/frontend/runner.css b/src/tests/frontend/runner.css similarity index 100% rename from tests/frontend/runner.css rename to src/tests/frontend/runner.css diff --git a/tests/frontend/runner.js b/src/tests/frontend/runner.js similarity index 100% rename from tests/frontend/runner.js rename to src/tests/frontend/runner.js diff --git a/tests/frontend/specs/alphabet.js b/src/tests/frontend/specs/alphabet.js similarity index 100% rename from tests/frontend/specs/alphabet.js rename to src/tests/frontend/specs/alphabet.js diff --git a/tests/frontend/specs/authorship_of_editions.js b/src/tests/frontend/specs/authorship_of_editions.js similarity index 100% rename from tests/frontend/specs/authorship_of_editions.js rename to src/tests/frontend/specs/authorship_of_editions.js diff --git a/tests/frontend/specs/bold.js b/src/tests/frontend/specs/bold.js similarity index 100% rename from tests/frontend/specs/bold.js rename to src/tests/frontend/specs/bold.js diff --git a/tests/frontend/specs/caret.js b/src/tests/frontend/specs/caret.js similarity index 100% rename from tests/frontend/specs/caret.js rename to src/tests/frontend/specs/caret.js diff --git a/tests/frontend/specs/change_user_color.js b/src/tests/frontend/specs/change_user_color.js similarity index 100% rename from tests/frontend/specs/change_user_color.js rename to src/tests/frontend/specs/change_user_color.js diff --git a/tests/frontend/specs/change_user_name.js b/src/tests/frontend/specs/change_user_name.js similarity index 100% rename from tests/frontend/specs/change_user_name.js rename to src/tests/frontend/specs/change_user_name.js diff --git a/tests/frontend/specs/chat.js b/src/tests/frontend/specs/chat.js similarity index 100% rename from tests/frontend/specs/chat.js rename to src/tests/frontend/specs/chat.js diff --git a/tests/frontend/specs/chat_load_messages.js b/src/tests/frontend/specs/chat_load_messages.js similarity index 100% rename from tests/frontend/specs/chat_load_messages.js rename to src/tests/frontend/specs/chat_load_messages.js diff --git a/tests/frontend/specs/clear_authorship_colors.js b/src/tests/frontend/specs/clear_authorship_colors.js similarity index 100% rename from tests/frontend/specs/clear_authorship_colors.js rename to src/tests/frontend/specs/clear_authorship_colors.js diff --git a/tests/frontend/specs/delete.js b/src/tests/frontend/specs/delete.js similarity index 100% rename from tests/frontend/specs/delete.js rename to src/tests/frontend/specs/delete.js diff --git a/tests/frontend/specs/drag_and_drop.js b/src/tests/frontend/specs/drag_and_drop.js similarity index 100% rename from tests/frontend/specs/drag_and_drop.js rename to src/tests/frontend/specs/drag_and_drop.js diff --git a/tests/frontend/specs/embed_value.js b/src/tests/frontend/specs/embed_value.js similarity index 100% rename from tests/frontend/specs/embed_value.js rename to src/tests/frontend/specs/embed_value.js diff --git a/tests/frontend/specs/enter.js b/src/tests/frontend/specs/enter.js similarity index 100% rename from tests/frontend/specs/enter.js rename to src/tests/frontend/specs/enter.js diff --git a/tests/frontend/specs/font_type.js b/src/tests/frontend/specs/font_type.js similarity index 100% rename from tests/frontend/specs/font_type.js rename to src/tests/frontend/specs/font_type.js diff --git a/tests/frontend/specs/helper.js b/src/tests/frontend/specs/helper.js similarity index 100% rename from tests/frontend/specs/helper.js rename to src/tests/frontend/specs/helper.js diff --git a/tests/frontend/specs/importexport.js b/src/tests/frontend/specs/importexport.js similarity index 100% rename from tests/frontend/specs/importexport.js rename to src/tests/frontend/specs/importexport.js diff --git a/tests/frontend/specs/importindents.js b/src/tests/frontend/specs/importindents.js similarity index 100% rename from tests/frontend/specs/importindents.js rename to src/tests/frontend/specs/importindents.js diff --git a/tests/frontend/specs/indentation.js b/src/tests/frontend/specs/indentation.js similarity index 100% rename from tests/frontend/specs/indentation.js rename to src/tests/frontend/specs/indentation.js diff --git a/tests/frontend/specs/italic.js b/src/tests/frontend/specs/italic.js similarity index 100% rename from tests/frontend/specs/italic.js rename to src/tests/frontend/specs/italic.js diff --git a/tests/frontend/specs/language.js b/src/tests/frontend/specs/language.js similarity index 100% rename from tests/frontend/specs/language.js rename to src/tests/frontend/specs/language.js diff --git a/tests/frontend/specs/multiple_authors_clear_authorship_colors.js b/src/tests/frontend/specs/multiple_authors_clear_authorship_colors.js similarity index 100% rename from tests/frontend/specs/multiple_authors_clear_authorship_colors.js rename to src/tests/frontend/specs/multiple_authors_clear_authorship_colors.js diff --git a/tests/frontend/specs/ordered_list.js b/src/tests/frontend/specs/ordered_list.js similarity index 100% rename from tests/frontend/specs/ordered_list.js rename to src/tests/frontend/specs/ordered_list.js diff --git a/tests/frontend/specs/pad_modal.js b/src/tests/frontend/specs/pad_modal.js similarity index 100% rename from tests/frontend/specs/pad_modal.js rename to src/tests/frontend/specs/pad_modal.js diff --git a/tests/frontend/specs/redo.js b/src/tests/frontend/specs/redo.js similarity index 100% rename from tests/frontend/specs/redo.js rename to src/tests/frontend/specs/redo.js diff --git a/tests/frontend/specs/responsiveness.js b/src/tests/frontend/specs/responsiveness.js similarity index 100% rename from tests/frontend/specs/responsiveness.js rename to src/tests/frontend/specs/responsiveness.js diff --git a/tests/frontend/specs/scrollTo.js b/src/tests/frontend/specs/scrollTo.js similarity index 100% rename from tests/frontend/specs/scrollTo.js rename to src/tests/frontend/specs/scrollTo.js diff --git a/tests/frontend/specs/select_formatting_buttons.js b/src/tests/frontend/specs/select_formatting_buttons.js similarity index 100% rename from tests/frontend/specs/select_formatting_buttons.js rename to src/tests/frontend/specs/select_formatting_buttons.js diff --git a/tests/frontend/specs/strikethrough.js b/src/tests/frontend/specs/strikethrough.js similarity index 100% rename from tests/frontend/specs/strikethrough.js rename to src/tests/frontend/specs/strikethrough.js diff --git a/tests/frontend/specs/timeslider.js b/src/tests/frontend/specs/timeslider.js similarity index 100% rename from tests/frontend/specs/timeslider.js rename to src/tests/frontend/specs/timeslider.js diff --git a/tests/frontend/specs/timeslider_follow.js b/src/tests/frontend/specs/timeslider_follow.js similarity index 100% rename from tests/frontend/specs/timeslider_follow.js rename to src/tests/frontend/specs/timeslider_follow.js diff --git a/tests/frontend/specs/timeslider_labels.js b/src/tests/frontend/specs/timeslider_labels.js similarity index 100% rename from tests/frontend/specs/timeslider_labels.js rename to src/tests/frontend/specs/timeslider_labels.js diff --git a/tests/frontend/specs/timeslider_numeric_padID.js b/src/tests/frontend/specs/timeslider_numeric_padID.js similarity index 100% rename from tests/frontend/specs/timeslider_numeric_padID.js rename to src/tests/frontend/specs/timeslider_numeric_padID.js diff --git a/tests/frontend/specs/timeslider_revisions.js b/src/tests/frontend/specs/timeslider_revisions.js similarity index 100% rename from tests/frontend/specs/timeslider_revisions.js rename to src/tests/frontend/specs/timeslider_revisions.js diff --git a/tests/frontend/specs/undo.js b/src/tests/frontend/specs/undo.js similarity index 100% rename from tests/frontend/specs/undo.js rename to src/tests/frontend/specs/undo.js diff --git a/tests/frontend/specs/unordered_list.js b/src/tests/frontend/specs/unordered_list.js similarity index 100% rename from tests/frontend/specs/unordered_list.js rename to src/tests/frontend/specs/unordered_list.js diff --git a/tests/frontend/specs/urls_become_clickable.js b/src/tests/frontend/specs/urls_become_clickable.js similarity index 100% rename from tests/frontend/specs/urls_become_clickable.js rename to src/tests/frontend/specs/urls_become_clickable.js diff --git a/tests/frontend/specs/xxauto_reconnect.js b/src/tests/frontend/specs/xxauto_reconnect.js similarity index 100% rename from tests/frontend/specs/xxauto_reconnect.js rename to src/tests/frontend/specs/xxauto_reconnect.js diff --git a/tests/frontend/travis/.gitignore b/src/tests/frontend/travis/.gitignore similarity index 100% rename from tests/frontend/travis/.gitignore rename to src/tests/frontend/travis/.gitignore diff --git a/tests/frontend/travis/remote_runner.js b/src/tests/frontend/travis/remote_runner.js similarity index 97% rename from tests/frontend/travis/remote_runner.js rename to src/tests/frontend/travis/remote_runner.js index ba0e7be0c..c5f3f1466 100644 --- a/tests/frontend/travis/remote_runner.js +++ b/src/tests/frontend/travis/remote_runner.js @@ -1,7 +1,7 @@ 'use strict'; -const wd = require('ep_etherpad-lite/node_modules/wd'); -const async = require('ep_etherpad-lite/node_modules/async'); +const async = require('async'); +const wd = require('wd'); const config = { host: 'ondemand.saucelabs.com', diff --git a/tests/frontend/travis/runner.sh b/src/tests/frontend/travis/runner.sh similarity index 100% rename from tests/frontend/travis/runner.sh rename to src/tests/frontend/travis/runner.sh diff --git a/tests/frontend/travis/runnerBackend.sh b/src/tests/frontend/travis/runnerBackend.sh similarity index 100% rename from tests/frontend/travis/runnerBackend.sh rename to src/tests/frontend/travis/runnerBackend.sh diff --git a/tests/frontend/travis/runnerLoadTest.sh b/src/tests/frontend/travis/runnerLoadTest.sh similarity index 100% rename from tests/frontend/travis/runnerLoadTest.sh rename to src/tests/frontend/travis/runnerLoadTest.sh diff --git a/tests/frontend/travis/sauce_tunnel.sh b/src/tests/frontend/travis/sauce_tunnel.sh similarity index 100% rename from tests/frontend/travis/sauce_tunnel.sh rename to src/tests/frontend/travis/sauce_tunnel.sh diff --git a/tests/ratelimit/Dockerfile.anotherip b/src/tests/ratelimit/Dockerfile.anotherip similarity index 100% rename from tests/ratelimit/Dockerfile.anotherip rename to src/tests/ratelimit/Dockerfile.anotherip diff --git a/tests/ratelimit/Dockerfile.nginx b/src/tests/ratelimit/Dockerfile.nginx similarity index 100% rename from tests/ratelimit/Dockerfile.nginx rename to src/tests/ratelimit/Dockerfile.nginx diff --git a/tests/ratelimit/nginx.conf b/src/tests/ratelimit/nginx.conf similarity index 100% rename from tests/ratelimit/nginx.conf rename to src/tests/ratelimit/nginx.conf diff --git a/tests/ratelimit/send_changesets.js b/src/tests/ratelimit/send_changesets.js similarity index 66% rename from tests/ratelimit/send_changesets.js rename to src/tests/ratelimit/send_changesets.js index 92af23e18..8f4f93d03 100644 --- a/tests/ratelimit/send_changesets.js +++ b/src/tests/ratelimit/send_changesets.js @@ -1,13 +1,7 @@ 'use strict'; -let etherpad; -try { - etherpad = require('ep_etherpad-lite/node_modules/etherpad-cli-client'); - // ugly -} catch { - /* eslint-disable-next-line node/no-missing-require */ - etherpad = require('etherpad-cli-client'); // uses global -} +const etherpad = require('etherpad-cli-client'); + const pad = etherpad.connect(process.argv[2]); pad.on('connected', () => { setTimeout(() => { diff --git a/tests/ratelimit/testlimits.sh b/src/tests/ratelimit/testlimits.sh similarity index 100% rename from tests/ratelimit/testlimits.sh rename to src/tests/ratelimit/testlimits.sh diff --git a/tests b/tests new file mode 120000 index 000000000..cce6a5cdf --- /dev/null +++ b/tests @@ -0,0 +1 @@ +src/tests \ No newline at end of file From 8b28e007842efefccf852a55f783761f86dc6090 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 4 Feb 2021 18:43:27 -0500 Subject: [PATCH 241/357] restructure: Prefix `bin/` and `tests/` with `src/` This is a follow-up to commit 2ea8ea12754c92ea06f22428f1e86341dcf00d99. --- .github/workflows/backend-tests.yml | 8 ++-- .github/workflows/dockerfile.yml | 2 +- .github/workflows/frontend-tests.yml | 14 +++---- .github/workflows/load-test.yml | 8 ++-- .github/workflows/rate-limit.yml | 8 ++-- .gitignore | 8 ++-- .travis.yml | 38 +++++++++---------- Dockerfile | 2 +- Makefile | 6 +-- README.md | 27 +++++++++---- doc/documentation.md | 2 +- src/bin/buildDebian.sh | 2 +- src/bin/buildForWindows.sh | 5 +-- src/bin/checkAllPads.js | 2 +- src/bin/checkPad.js | 2 +- src/bin/checkPadDeltas.js | 2 +- src/bin/cleanRun.sh | 14 +++---- src/bin/createRelease.sh | 2 +- .../deb-src/sysroot/etc/init/etherpad.conf | 2 +- src/bin/debugRun.sh | 9 +++-- src/bin/doc/README.md | 4 +- src/bin/fastRun.sh | 13 +++---- src/bin/installDeps.sh | 7 ++-- src/bin/migrateDirtyDBtoRealDB.js | 2 +- src/bin/plugins/README.md | 10 ++--- src/bin/plugins/checkPlugin.js | 31 ++++++++------- src/bin/plugins/lib/CONTRIBUTING.md | 4 +- src/bin/plugins/lib/backend-tests.yml | 6 +-- src/bin/plugins/lib/npmpublish.yml | 2 +- src/bin/plugins/lib/travis.yml | 14 +++---- src/bin/plugins/reTestAllPlugins.sh | 2 +- src/bin/plugins/updateAllPluginsScript.sh | 2 +- src/bin/plugins/updateCorePlugins.sh | 2 +- src/bin/rebuildPad.js | 2 +- src/bin/release.js | 5 ++- src/bin/repairPad.js | 2 +- src/bin/run.sh | 9 +++-- src/bin/safeRun.sh | 7 ++-- src/node/hooks/express/tests.js | 2 +- src/node/server.js | 2 +- src/package.json | 4 +- src/static/js/pluginfw/hooks.js | 6 ++- .../backend/specs/api/characterEncoding.js | 4 +- src/tests/backend/specs/api/importexport.js | 2 +- .../backend/specs/api/importexportGetPost.js | 12 +++--- src/tests/backend/specs/api/pad.js | 2 +- src/tests/frontend/travis/runner.sh | 9 ++--- src/tests/frontend/travis/runnerBackend.sh | 9 ++--- src/tests/frontend/travis/runnerLoadTest.sh | 9 ++--- src/tests/ratelimit/Dockerfile.anotherip | 2 +- src/tests/ratelimit/Dockerfile.nginx | 2 +- 51 files changed, 182 insertions(+), 170 deletions(-) diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index 8c9c45f28..a6a0d0139 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -24,11 +24,11 @@ jobs: sudo apt install -y --no-install-recommends libreoffice libreoffice-pdfimport - name: Install all dependencies and symlink for ep_etherpad-lite - run: bin/installDeps.sh + run: src/bin/installDeps.sh # configures some settings and runs npm run test - name: Run the backend tests - run: tests/frontend/travis/runnerBackend.sh + run: src/tests/frontend/travis/runnerBackend.sh withplugins: # run on pushes to any branch @@ -73,8 +73,8 @@ jobs: # if npm correctly hoists the dependencies, the hoisting seems to confuse # tools such as `npm outdated`, `npm update`, and some ESLint rules. - name: Install all dependencies and symlink for ep_etherpad-lite - run: bin/installDeps.sh + run: src/bin/installDeps.sh # configures some settings and runs npm run test - name: Run the backend tests - run: tests/frontend/travis/runnerBackend.sh + run: src/tests/frontend/travis/runnerBackend.sh diff --git a/.github/workflows/dockerfile.yml b/.github/workflows/dockerfile.yml index 8f6d5c3b0..5f8384705 100644 --- a/.github/workflows/dockerfile.yml +++ b/.github/workflows/dockerfile.yml @@ -21,6 +21,6 @@ jobs: run: | docker build -t etherpad:test . docker run -d -p 9001:9001 etherpad:test - ./bin/installDeps.sh + ./src/bin/installDeps.sh sleep 3 # delay for startup? cd src && npm run test-container diff --git a/.github/workflows/frontend-tests.yml b/.github/workflows/frontend-tests.yml index 0052cafe0..f8a0e76b4 100644 --- a/.github/workflows/frontend-tests.yml +++ b/.github/workflows/frontend-tests.yml @@ -17,10 +17,10 @@ jobs: SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} TRAVIS_JOB_NUMBER: ${{ github.run_id }}-${{ github.run_number }}-${{ github.job }} - run: tests/frontend/travis/sauce_tunnel.sh + run: src/tests/frontend/travis/sauce_tunnel.sh - name: Install all dependencies and symlink for ep_etherpad-lite - run: bin/installDeps.sh + run: src/bin/installDeps.sh - name: export GIT_HASH to env id: environment @@ -37,7 +37,7 @@ jobs: TRAVIS_JOB_NUMBER: ${{ github.run_id }}-${{ github.run_number }}-${{ github.job }} GIT_HASH: ${{ steps.environment.outputs.sha_short }} run: | - tests/frontend/travis/runner.sh + src/tests/frontend/travis/runner.sh withplugins: name: with plugins @@ -53,7 +53,7 @@ jobs: SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} TRAVIS_JOB_NUMBER: ${{ github.run_id }}-${{ github.run_number }}-${{ github.job }} - run: tests/frontend/travis/sauce_tunnel.sh + run: src/tests/frontend/travis/sauce_tunnel.sh - name: Install Etherpad plugins run: > @@ -79,7 +79,7 @@ jobs: # if npm correctly hoists the dependencies, the hoisting seems to confuse # tools such as `npm outdated`, `npm update`, and some ESLint rules. - name: Install all dependencies and symlink for ep_etherpad-lite - run: bin/installDeps.sh + run: src/bin/installDeps.sh - name: export GIT_HASH to env id: environment @@ -90,7 +90,7 @@ jobs: # XXX we should probably run all tests, because plugins could effect their results - name: Remove standard frontend test files, so only plugin tests are run - run: rm tests/frontend/specs/* + run: rm src/tests/frontend/specs/* - name: Run the frontend tests shell: bash @@ -100,4 +100,4 @@ jobs: TRAVIS_JOB_NUMBER: ${{ github.run_id }}-${{ github.run_number }}-${{ github.job }} GIT_HASH: ${{ steps.environment.outputs.sha_short }} run: | - tests/frontend/travis/runner.sh + src/tests/frontend/travis/runner.sh diff --git a/.github/workflows/load-test.yml b/.github/workflows/load-test.yml index ab0fb3bd0..056930ecb 100644 --- a/.github/workflows/load-test.yml +++ b/.github/workflows/load-test.yml @@ -18,13 +18,13 @@ jobs: uses: actions/checkout@v2 - name: Install all dependencies and symlink for ep_etherpad-lite - run: bin/installDeps.sh + run: src/bin/installDeps.sh - name: Install etherpad-load-test run: sudo npm install -g etherpad-load-test - name: Run load test - run: tests/frontend/travis/runnerLoadTest.sh + run: src/tests/frontend/travis/runnerLoadTest.sh withplugins: # run on pushes to any branch @@ -66,8 +66,8 @@ jobs: # if npm correctly hoists the dependencies, the hoisting seems to confuse # tools such as `npm outdated`, `npm update`, and some ESLint rules. - name: Install all dependencies and symlink for ep_etherpad-lite - run: bin/installDeps.sh + run: src/bin/installDeps.sh # configures some settings and runs npm run test - name: Run load test - run: tests/frontend/travis/runnerLoadTest.sh + run: src/tests/frontend/travis/runnerLoadTest.sh diff --git a/.github/workflows/rate-limit.yml b/.github/workflows/rate-limit.yml index 4bdfc2194..667558114 100644 --- a/.github/workflows/rate-limit.yml +++ b/.github/workflows/rate-limit.yml @@ -22,8 +22,8 @@ jobs: - name: build docker image run: | docker build -f Dockerfile -t epl-debian-slim . - docker build -f tests/ratelimit/Dockerfile.nginx -t nginx-latest . - docker build -f tests/ratelimit/Dockerfile.anotherip -t anotherip . + docker build -f src/tests/ratelimit/Dockerfile.nginx -t nginx-latest . + docker build -f src/tests/ratelimit/Dockerfile.anotherip -t anotherip . - name: run docker images run: | docker run --name etherpad-docker -p 9000:9001 --rm --network ep_net --ip 172.23.42.2 -e 'TRUST_PROXY=true' epl-debian-slim & @@ -31,9 +31,9 @@ jobs: docker run --rm --network ep_net --ip 172.23.42.3 --name anotherip -dt anotherip - name: install dependencies and create symlink for ep_etherpad-lite - run: bin/installDeps.sh + run: src/bin/installDeps.sh - name: run rate limit test run: | - cd tests/ratelimit + cd src/tests/ratelimit ./testlimits.sh diff --git a/.gitignore b/.gitignore index c75e5a61f..6dca890dc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,11 +3,8 @@ node_modules !settings.json.template APIKEY.txt SESSIONKEY.txt -bin/abiword.exe -bin/node.exe etherpad-lite-win.zip var/dirty.db -bin/convertSettings.json *~ *.patch npm-debug.log @@ -15,9 +12,12 @@ npm-debug.log .ep_initialized *.crt *.key -bin/etherpad-1.deb credentials.json out/ .nyc_output ./package-lock.json .idea +/src/bin/abiword.exe +/src/bin/convertSettings.json +/src/bin/etherpad-1.deb +/src/bin/node.exe diff --git a/.travis.yml b/.travis.yml index 3f8ad1cf1..f6cf4dba8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,19 +46,19 @@ jobs: name: "Test the Frontend without Plugins" install: - *set_loglevel_warn - - "tests/frontend/travis/sauce_tunnel.sh" - - "bin/installDeps.sh" + - "src/tests/frontend/travis/sauce_tunnel.sh" + - "src/bin/installDeps.sh" - "export GIT_HASH=$(git rev-parse --verify --short HEAD)" script: - - "./tests/frontend/travis/runner.sh" + - "./src/tests/frontend/travis/runner.sh" - name: "Run the Backend tests without Plugins" install: - *install_libreoffice - *set_loglevel_warn - - "bin/installDeps.sh" + - "src/bin/installDeps.sh" - "cd src && npm install && cd -" script: - - "tests/frontend/travis/runnerBackend.sh" + - "src/tests/frontend/travis/runnerBackend.sh" - name: "Test the Dockerfile" install: - "cd src && npm install && cd -" @@ -69,24 +69,24 @@ jobs: - name: "Load test Etherpad without Plugins" install: - *set_loglevel_warn - - "bin/installDeps.sh" + - "src/bin/installDeps.sh" - "cd src && npm install && cd -" - "npm install -g etherpad-load-test" script: - - "tests/frontend/travis/runnerLoadTest.sh" + - "src/tests/frontend/travis/runnerLoadTest.sh" # we can only frontend tests from the ether/ organization and not from forks. # To request tests to be run ask a maintainer to fork your repo to ether/ - if: fork = false name: "Test the Frontend Plugins only" install: - *set_loglevel_warn - - "tests/frontend/travis/sauce_tunnel.sh" - - "bin/installDeps.sh" - - "rm tests/frontend/specs/*" + - "src/tests/frontend/travis/sauce_tunnel.sh" + - "src/bin/installDeps.sh" + - "rm src/tests/frontend/specs/*" - *install_plugins - "export GIT_HASH=$(git rev-parse --verify --short HEAD)" script: - - "./tests/frontend/travis/runner.sh" + - "./src/tests/frontend/travis/runner.sh" - name: "Lint test package-lock.json" install: - "npm install lockfile-lint" @@ -96,11 +96,11 @@ jobs: install: - *install_libreoffice - *set_loglevel_warn - - "bin/installDeps.sh" + - "src/bin/installDeps.sh" - *install_plugins - "cd src && npm install && cd -" script: - - "tests/frontend/travis/runnerBackend.sh" + - "src/tests/frontend/travis/runnerBackend.sh" - name: "Test the Dockerfile" install: - "cd src && npm install && cd -" @@ -111,24 +111,24 @@ jobs: - name: "Load test Etherpad with Plugins" install: - *set_loglevel_warn - - "bin/installDeps.sh" + - "src/bin/installDeps.sh" - *install_plugins - "cd src && npm install && cd -" - "npm install -g etherpad-load-test" script: - - "tests/frontend/travis/runnerLoadTest.sh" + - "src/tests/frontend/travis/runnerLoadTest.sh" - name: "Test rate limit" install: - "docker network create --subnet=172.23.42.0/16 ep_net" - "docker build -f Dockerfile -t epl-debian-slim ." - - "docker build -f tests/ratelimit/Dockerfile.nginx -t nginx-latest ." - - "docker build -f tests/ratelimit/Dockerfile.anotherip -t anotherip ." + - "docker build -f src/tests/ratelimit/Dockerfile.nginx -t nginx-latest ." + - "docker build -f src/tests/ratelimit/Dockerfile.anotherip -t anotherip ." - "docker run -p 8081:80 --rm --network ep_net --ip 172.23.42.1 -d nginx-latest" - "docker run --name etherpad-docker -p 9000:9001 --rm --network ep_net --ip 172.23.42.2 -e 'TRUST_PROXY=true' epl-debian-slim &" - "docker run --rm --network ep_net --ip 172.23.42.3 --name anotherip -dt anotherip" - - "./bin/installDeps.sh" + - "./src/bin/installDeps.sh" script: - - "cd tests/ratelimit && bash testlimits.sh" + - "cd src/tests/ratelimit && bash testlimits.sh" notifications: irc: diff --git a/Dockerfile b/Dockerfile index aa6091a59..e16e58178 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,7 +35,7 @@ WORKDIR /opt/etherpad-lite COPY --chown=etherpad:0 ./ ./ # install node dependencies for Etherpad -RUN bin/installDeps.sh && \ +RUN src/bin/installDeps.sh && \ rm -rf ~/.npm/_cacache # Install the plugins, if ETHERPAD_PLUGINS is not empty. diff --git a/Makefile b/Makefile index 0c99d9ff4..bc79166b3 100644 --- a/Makefile +++ b/Makefile @@ -9,8 +9,8 @@ UNAME := $(shell uname -s) ensure_marked_is_installed: set -eu; \ hash npm; \ - if [ $(shell npm list --prefix bin/doc >/dev/null 2>/dev/null; echo $$?) -ne "0" ]; then \ - npm ci --prefix=bin/doc; \ + if [ $(shell npm list --prefix src/bin/doc >/dev/null 2>/dev/null; echo $$?) -ne "0" ]; then \ + npm ci --prefix=src/bin/doc; \ fi docs: ensure_marked_is_installed $(outdoc_files) $(docassets) @@ -21,7 +21,7 @@ out/doc/assets/%: doc/assets/% out/doc/%.html: doc/%.md mkdir -p $(@D) - node bin/doc/generate.js --format=html --template=doc/template.html $< > $@ + node src/bin/doc/generate.js --format=html --template=doc/template.html $< > $@ ifeq ($(UNAME),Darwin) sed -i '' 's/__VERSION__/${VERSION}/' $@ else diff --git a/README.md b/README.md index 5a66ddd78..3f1e6fb0f 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,9 @@ Etherpad is a real-time collaborative editor [scalable to thousands of simultane ``` curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - sudo apt install -y nodejs -git clone --branch master https://github.com/ether/etherpad-lite.git && cd etherpad-lite && bin/run.sh +git clone --branch master https://github.com/ether/etherpad-lite.git && +cd etherpad-lite && +src/bin/run.sh ``` ### Manual install @@ -31,9 +33,10 @@ You'll need git and [node.js](https://nodejs.org) installed (minimum required No 1. Move to a folder where you want to install Etherpad. Clone the git repository: `git clone --branch master git://github.com/ether/etherpad-lite.git` 2. Change into the new directory containing the cloned source code: `cd etherpad-lite` -3. run `bin/run.sh` and open in your browser. +3. run `src/bin/run.sh` and open in your browser. -To update to the latest released version, execute `git pull origin`. The next start with `bin/run.sh` will update the dependencies. +To update to the latest released version, execute `git pull origin`. The next +start with `src/bin/run.sh` will update the dependencies. [Next steps](#next-steps). @@ -53,11 +56,13 @@ You'll need [node.js](https://nodejs.org) and (optionally, though recommended) g 1. Grab the source, either - download - or `git clone --branch master https://github.com/ether/etherpad-lite.git` -2. With a "Run as administrator" command prompt execute `bin\installOnWindows.bat` +2. With a "Run as administrator" command prompt execute + `src\bin\installOnWindows.bat` Now, run `start.bat` and open in your browser. -Update to the latest version with `git pull origin`, then run `bin\installOnWindows.bat`, again. +Update to the latest version with `git pull origin`, then run +`src\bin\installOnWindows.bat`, again. If cloning to a subdirectory within another project, you may need to do the following: @@ -73,7 +78,9 @@ Find [here](doc/docker.md) information on running Etherpad in a container. ## Tweak the settings You can modify the settings in `settings.json`. -If you need to handle multiple settings files, you can pass the path to a settings file to `bin/run.sh` using the `-s|--settings` option: this allows you to run multiple Etherpad instances from the same installation. +If you need to handle multiple settings files, you can pass the path to a +settings file to `src/bin/run.sh` using the `-s|--settings` option: this allows +you to run multiple Etherpad instances from the same installation. Similarly, `--credentials` can be used to give a settings override file, `--apikey` to give a different APIKEY.txt file and `--sessionkey` to give a non-default SESSIONKEY.txt. **Each configuration parameter can also be set via an environment variable**, using the syntax `"${ENV_VAR}"` or `"${ENV_VAR:default_value}"`. For details, refer to `settings.json.template`. Once you have access to your `/admin` section settings can be modified through the web browser. @@ -115,9 +122,13 @@ Documentation can be found in `doc/`. # Development ## Things you should know -You can debug Etherpad using `bin/debugRun.sh`. -You can run Etherpad quickly launching `bin/fastRun.sh`. It's convenient for developers and advanced users. Be aware that it will skip the dependencies update, so remember to run `bin/installDeps.sh` after installing a new dependency or upgrading version. +You can debug Etherpad using `src/bin/debugRun.sh`. + +You can run Etherpad quickly launching `src/bin/fastRun.sh`. It's convenient for +developers and advanced users. Be aware that it will skip the dependencies +update, so remember to run `src/bin/installDeps.sh` after installing a new +dependency or upgrading version. If you want to find out how Etherpad's `Easysync` works (the library that makes it really realtime), start with this [PDF](https://github.com/ether/etherpad-lite/raw/master/doc/easysync/easysync-full-description.pdf) (complex, but worth reading). diff --git a/doc/documentation.md b/doc/documentation.md index 307c38af8..bc18fbc07 100644 --- a/doc/documentation.md +++ b/doc/documentation.md @@ -11,5 +11,5 @@ heading. Every `.html` file is generated based on the corresponding `.md` file in the `doc/api/` folder in the source tree. The -documentation is generated using the `bin/doc/generate.js` program. +documentation is generated using the `src/bin/doc/generate.js` program. The HTML template is located at `doc/template.html`. diff --git a/src/bin/buildDebian.sh b/src/bin/buildDebian.sh index f1f5675ec..241b3f751 100755 --- a/src/bin/buildDebian.sh +++ b/src/bin/buildDebian.sh @@ -14,7 +14,7 @@ rm -rf ${DIST} mkdir -p ${DIST}/ rm -rf ${SRC} -rsync -a bin/deb-src/ ${SRC}/ +rsync -a src/bin/deb-src/ ${SRC}/ mkdir -p ${SYSROOT}/opt/ rsync --exclude '.git' -a . ${SYSROOT}/opt/etherpad/ --delete diff --git a/src/bin/buildForWindows.sh b/src/bin/buildForWindows.sh index 818522ad0..d62ef810e 100755 --- a/src/bin/buildForWindows.sh +++ b/src/bin/buildForWindows.sh @@ -32,7 +32,7 @@ rm -f etherpad-lite-win.zip export NODE_ENV=production log "do a normal unix install first..." -bin/installDeps.sh || exit 1 +src/bin/installDeps.sh || exit 1 log "copy the windows settings template..." cp settings.json.template settings.json @@ -43,8 +43,7 @@ rm -rf node_modules mv node_modules_resolved node_modules log "download windows node..." -cd bin -wget "https://nodejs.org/dist/latest-erbium/win-x86/node.exe" -O ../node.exe +wget "https://nodejs.org/dist/latest-erbium/win-x86/node.exe" -O node.exe log "remove git history to reduce folder size" rm -rf .git/objects diff --git a/src/bin/checkAllPads.js b/src/bin/checkAllPads.js index fe1719b88..d15e5ec5b 100644 --- a/src/bin/checkAllPads.js +++ b/src/bin/checkAllPads.js @@ -7,7 +7,7 @@ // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); -if (process.argv.length !== 2) throw new Error('Use: node bin/checkAllPads.js'); +if (process.argv.length !== 2) throw new Error('Use: node src/bin/checkAllPads.js'); (async () => { // initialize the database diff --git a/src/bin/checkPad.js b/src/bin/checkPad.js index becca3a4a..5b17fa31a 100644 --- a/src/bin/checkPad.js +++ b/src/bin/checkPad.js @@ -7,7 +7,7 @@ // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); -if (process.argv.length !== 3) throw new Error('Use: node bin/checkPad.js $PADID'); +if (process.argv.length !== 3) throw new Error('Use: node src/bin/checkPad.js $PADID'); // get the padID const padId = process.argv[2]; diff --git a/src/bin/checkPadDeltas.js b/src/bin/checkPadDeltas.js index 043187cef..852c68332 100644 --- a/src/bin/checkPadDeltas.js +++ b/src/bin/checkPadDeltas.js @@ -7,7 +7,7 @@ // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); -if (process.argv.length !== 3) throw new Error('Use: node bin/checkPadDeltas.js $PADID'); +if (process.argv.length !== 3) throw new Error('Use: node src/bin/checkPadDeltas.js $PADID'); // get the padID const padId = process.argv[2]; diff --git a/src/bin/cleanRun.sh b/src/bin/cleanRun.sh index 36dd1e384..e8f4bd0d4 100755 --- a/src/bin/cleanRun.sh +++ b/src/bin/cleanRun.sh @@ -1,15 +1,11 @@ #!/bin/sh -# Move to the folder where ep-lite is installed -cd "$(dirname "$0")"/.. +# Move to the Etherpad base directory. +MY_DIR=$(cd "${0%/*}" && pwd -P) || exit 1 +cd "${MY_DIR}/../.." || exit 1 # Source constants and useful functions -. bin/functions.sh - -#Was this script started in the bin folder? if yes move out -if [ -d "../bin" ]; then - cd "../" -fi +. src/bin/functions.sh ignoreRoot=0 for ARG in "$@" @@ -35,7 +31,7 @@ fi rm -rf src/node_modules #Prepare the environment -bin/installDeps.sh "$@" || exit 1 +src/bin/installDeps.sh "$@" || exit 1 #Move to the node folder and start echo "Started Etherpad..." diff --git a/src/bin/createRelease.sh b/src/bin/createRelease.sh index 6768702f3..531a21502 100755 --- a/src/bin/createRelease.sh +++ b/src/bin/createRelease.sh @@ -134,7 +134,7 @@ function create_builds { git clone $ETHER_WEB_REPO echo "Creating windows build..." cd etherpad-lite - bin/buildForWindows.sh + src/bin/buildForWindows.sh [[ $? != 0 ]] && echo "Aborting: Error creating build for windows" && exit 1 echo "Creating docs..." make docs diff --git a/src/bin/deb-src/sysroot/etc/init/etherpad.conf b/src/bin/deb-src/sysroot/etc/init/etherpad.conf index cd6f4541c..aab40bca8 100644 --- a/src/bin/deb-src/sysroot/etc/init/etherpad.conf +++ b/src/bin/deb-src/sysroot/etc/init/etherpad.conf @@ -15,7 +15,7 @@ pre-start script chown $EPUSER $EPLOGS ||true chmod 0755 $EPLOGS ||true chown -R $EPUSER $EPHOME/var ||true - $EPHOME/bin/installDeps.sh >> $EPLOGS/error.log || { stop; exit 1; } + $EPHOME/src/bin/installDeps.sh >> $EPLOGS/error.log || { stop; exit 1; } end script script diff --git a/src/bin/debugRun.sh b/src/bin/debugRun.sh index e04db88d0..f418f4f64 100755 --- a/src/bin/debugRun.sh +++ b/src/bin/debugRun.sh @@ -1,13 +1,14 @@ #!/bin/sh -# Move to the folder where ep-lite is installed -cd "$(dirname "$0")"/.. +# Move to the Etherpad base directory. +MY_DIR=$(cd "${0%/*}" && pwd -P) || exit 1 +cd "${MY_DIR}/../.." || exit 1 # Source constants and useful functions -. bin/functions.sh +. src/bin/functions.sh # Prepare the environment -bin/installDeps.sh || exit 1 +src/bin/installDeps.sh || exit 1 echo "If you are new to debugging Node.js with Chrome DevTools, take a look at this page:" echo "https://medium.com/@paul_irish/debugging-node-js-nightlies-with-chrome-devtools-7c4a1b95ae27" diff --git a/src/bin/doc/README.md b/src/bin/doc/README.md index 4646c2004..19a137fd7 100644 --- a/src/bin/doc/README.md +++ b/src/bin/doc/README.md @@ -72,5 +72,5 @@ Each type of heading has a description block. Run the following from the etherpad-lite root directory: ```sh -$ node bin/doc/generate doc/index.md --format=html --template=doc/template.html > out.html -``` \ No newline at end of file +$ node src/bin/doc/generate doc/index.md --format=html --template=doc/template.html > out.html +``` diff --git a/src/bin/fastRun.sh b/src/bin/fastRun.sh index 63524e793..a782cafcb 100755 --- a/src/bin/fastRun.sh +++ b/src/bin/fastRun.sh @@ -9,17 +9,14 @@ set -eu -# source: https://stackoverflow.com/questions/59895/how-to-get-the-source-directory-of-a-bash-script-from-within-the-script-itself#246128 -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" +# Move to the Etherpad base directory. +MY_DIR=$(cd "${0%/*}" && pwd -P) || exit 1 +cd "${MY_DIR}/../.." || exit 1 # Source constants and useful functions -. ${DIR}/../bin/functions.sh +. src/bin/functions.sh echo "Running directly, without checking/installing dependencies" -# move to the base Etherpad directory. This will be necessary until Etherpad -# learns to run from arbitrary CWDs. -cd "${DIR}/.." - # run Etherpad main class -node $(compute_node_args) "${DIR}/../node_modules/ep_etherpad-lite/node/server.js" "$@" +node $(compute_node_args) "node_modules/ep_etherpad-lite/node/server.js" "$@" diff --git a/src/bin/installDeps.sh b/src/bin/installDeps.sh index be3f1fd8f..6ea555dbf 100755 --- a/src/bin/installDeps.sh +++ b/src/bin/installDeps.sh @@ -1,10 +1,11 @@ #!/bin/sh -# Move to the folder where ep-lite is installed -cd "$(dirname "$0")"/.. +# Move to the Etherpad base directory. +MY_DIR=$(cd "${0%/*}" && pwd -P) || exit 1 +cd "${MY_DIR}/../.." || exit 1 # Source constants and useful functions -. bin/functions.sh +. src/bin/functions.sh # Is node installed? # Not checking io.js, default installation creates a symbolic link to node diff --git a/src/bin/migrateDirtyDBtoRealDB.js b/src/bin/migrateDirtyDBtoRealDB.js index fbeb0eb44..75f6cc677 100644 --- a/src/bin/migrateDirtyDBtoRealDB.js +++ b/src/bin/migrateDirtyDBtoRealDB.js @@ -10,7 +10,7 @@ process.on('unhandledRejection', (err) => { throw err; }); // file before using this script, just to be safe. // It might be necessary to run the script using more memory: - // `node --max-old-space-size=4096 bin/migrateDirtyDBtoRealDB.js` + // `node --max-old-space-size=4096 src/bin/migrateDirtyDBtoRealDB.js` const dirtyDb = require('dirty'); const log4js = require('log4js'); diff --git a/src/bin/plugins/README.md b/src/bin/plugins/README.md index 5bb4786cd..b14065821 100755 --- a/src/bin/plugins/README.md +++ b/src/bin/plugins/README.md @@ -6,25 +6,25 @@ This code will check your plugin for known usual issues and some suggestions for improvements. No changes will be made to your project. ``` -node bin/plugins/checkPlugin.js $PLUGIN_NAME$ +node src/bin/plugins/checkPlugin.js $PLUGIN_NAME$ ``` # Basic Example: ``` -node bin/plugins/checkPlugin.js ep_webrtc +node src/bin/plugins/checkPlugin.js ep_webrtc ``` ## Autofixing - will autofix any issues it can ``` -node bin/plugins/checkPlugin.js ep_whatever autofix +node src/bin/plugins/checkPlugin.js ep_whatever autofix ``` ## Autocommitting, push, npm minor patch and npm publish (highly dangerous) ``` -node bin/plugins/checkPlugin.js ep_whatever autocommit +node src/bin/plugins/checkPlugin.js ep_whatever autocommit ``` # All the plugins @@ -41,7 +41,7 @@ cd .. for dir in node_modules/ep_*; do dir=${dir#node_modules/} [ "$dir" != ep_etherpad-lite ] || continue - node bin/plugins/checkPlugin.js "$dir" autocommit + node src/bin/plugins/checkPlugin.js "$dir" autocommit done ``` diff --git a/src/bin/plugins/checkPlugin.js b/src/bin/plugins/checkPlugin.js index 5bf35dd0e..6d1144bf2 100755 --- a/src/bin/plugins/checkPlugin.js +++ b/src/bin/plugins/checkPlugin.js @@ -3,10 +3,10 @@ /* * Usage -- see README.md * - * Normal usage: node bin/plugins/checkPlugin.js ep_whatever - * Auto fix the things it can: node bin/plugins/checkPlugin.js ep_whatever autofix + * Normal usage: node src/bin/plugins/checkPlugin.js ep_whatever + * Auto fix the things it can: node src/bin/plugins/checkPlugin.js ep_whatever autofix * Auto commit, push and publish to npm (highly dangerous): - * node bin/plugins/checkPlugin.js ep_whatever autocommit + * node src/bin/plugins/checkPlugin.js ep_whatever autocommit */ // As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an @@ -118,7 +118,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.log('create one and set npm secret to auto publish to npm on commit'); if (autoFix) { const npmpublish = - fs.readFileSync('bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'}); + fs.readFileSync('src/bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'}); fs.mkdirSync(`${pluginPath}/.github/workflows`, {recursive: true}); fs.writeFileSync(path, npmpublish); console.log("If you haven't already, setup autopublish for this plugin https://github.com/ether/etherpad-lite/wiki/Plugins:-Automatically-publishing-to-npm-on-commit-to-Github-Repo"); @@ -134,14 +134,14 @@ fs.readdir(pluginPath, (err, rootFiles) => { currVersionFile.substr(existingConfigLocation + 17, existingConfigLocation.length)); const reqVersionFile = - fs.readFileSync('bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'}); + fs.readFileSync('src/bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'}); const reqConfigLocation = reqVersionFile.indexOf('##ETHERPAD_NPM_V='); const reqValue = parseInt(reqVersionFile.substr(reqConfigLocation + 17, reqConfigLocation.length)); if (!existingValue || (reqValue > existingValue)) { const npmpublish = - fs.readFileSync('bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'}); + fs.readFileSync('src/bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'}); fs.mkdirSync(`${pluginPath}/.github/workflows`, {recursive: true}); fs.writeFileSync(path, npmpublish); } @@ -158,7 +158,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.log('create one and set npm secret to auto publish to npm on commit'); if (autoFix) { const backendTests = - fs.readFileSync('bin/plugins/lib/backend-tests.yml', {encoding: 'utf8', flag: 'r'}); + fs.readFileSync('src/bin/plugins/lib/backend-tests.yml', {encoding: 'utf8', flag: 'r'}); fs.mkdirSync(`${pluginPath}/.github/workflows`, {recursive: true}); fs.writeFileSync(path, backendTests); } @@ -171,14 +171,14 @@ fs.readdir(pluginPath, (err, rootFiles) => { currVersionFile.substr(existingConfigLocation + 17, existingConfigLocation.length)); const reqVersionFile = - fs.readFileSync('bin/plugins/lib/backend-tests.yml', {encoding: 'utf8', flag: 'r'}); + fs.readFileSync('src/bin/plugins/lib/backend-tests.yml', {encoding: 'utf8', flag: 'r'}); const reqConfigLocation = reqVersionFile.indexOf('##ETHERPAD_NPM_V='); const reqValue = parseInt(reqVersionFile.substr(reqConfigLocation + 17, reqConfigLocation.length)); if (!existingValue || (reqValue > existingValue)) { const backendTests = - fs.readFileSync('bin/plugins/lib/backend-tests.yml', {encoding: 'utf8', flag: 'r'}); + fs.readFileSync('src/bin/plugins/lib/backend-tests.yml', {encoding: 'utf8', flag: 'r'}); fs.mkdirSync(`${pluginPath}/.github/workflows`, {recursive: true}); fs.writeFileSync(path, backendTests); } @@ -283,7 +283,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { if (autoFix) { console.log('Autofixing missing README.md file'); console.log('please edit the README.md file further to include plugin specific details.'); - let readme = fs.readFileSync('bin/plugins/lib/README.md', {encoding: 'utf8', flag: 'r'}); + let readme = fs.readFileSync('src/bin/plugins/lib/README.md', {encoding: 'utf8', flag: 'r'}); readme = readme.replace(/\[plugin_name\]/g, pluginName); if (repository) { const org = repository.split('/')[3]; @@ -303,7 +303,7 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.log('Autofixing missing CONTRIBUTING.md file, please edit the CONTRIBUTING.md ' + 'file further to include plugin specific details.'); let contributing = - fs.readFileSync('bin/plugins/lib/CONTRIBUTING.md', {encoding: 'utf8', flag: 'r'}); + fs.readFileSync('src/bin/plugins/lib/CONTRIBUTING.md', {encoding: 'utf8', flag: 'r'}); contributing = contributing.replace(/\[plugin_name\]/g, pluginName); fs.writeFileSync(`${pluginPath}/CONTRIBUTING.md`, contributing); } @@ -325,14 +325,16 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.warn('LICENSE.md file not found, please create'); if (autoFix) { console.log('Autofixing missing LICENSE.md file, including Apache 2 license.'); - let license = fs.readFileSync('bin/plugins/lib/LICENSE.md', {encoding: 'utf8', flag: 'r'}); + let license = + fs.readFileSync('src/bin/plugins/lib/LICENSE.md', {encoding: 'utf8', flag: 'r'}); license = license.replace('[yyyy]', new Date().getFullYear()); license = license.replace('[name of copyright owner]', execSync('git config user.name')); fs.writeFileSync(`${pluginPath}/LICENSE.md`, license); } } - let travisConfig = fs.readFileSync('bin/plugins/lib/travis.yml', {encoding: 'utf8', flag: 'r'}); + let travisConfig = + fs.readFileSync('src/bin/plugins/lib/travis.yml', {encoding: 'utf8', flag: 'r'}); travisConfig = travisConfig.replace(/\[plugin_name\]/g, pluginName); if (files.indexOf('.travis.yml') === -1) { @@ -371,7 +373,8 @@ fs.readdir(pluginPath, (err, rootFiles) => { "ensure files aren't incorrectly commited to a repository."); if (autoFix) { console.log('Autofixing missing .gitignore file'); - const gitignore = fs.readFileSync('bin/plugins/lib/gitignore', {encoding: 'utf8', flag: 'r'}); + const gitignore = + fs.readFileSync('src/bin/plugins/lib/gitignore', {encoding: 'utf8', flag: 'r'}); fs.writeFileSync(`${pluginPath}/.gitignore`, gitignore); } } else { diff --git a/src/bin/plugins/lib/CONTRIBUTING.md b/src/bin/plugins/lib/CONTRIBUTING.md index 724e02ac0..347437b22 100644 --- a/src/bin/plugins/lib/CONTRIBUTING.md +++ b/src/bin/plugins/lib/CONTRIBUTING.md @@ -113,7 +113,9 @@ Documentation should be kept up-to-date. This means, whenever you add a new API You can build the docs e.g. produce html, using `make docs`. At some point in the future we will provide an online documentation. The current documentation in the github wiki should always reflect the state of `master` (!), since there are no docs in master, yet. ## Testing -Front-end tests are found in the `tests/frontend/` folder in the repository. Run them by pointing your browser to `/tests/frontend`. + +Front-end tests are found in the `src/tests/frontend/` folder in the repository. +Run them by pointing your browser to `/tests/frontend`. Back-end tests can be run from the `src` directory, via `npm test`. diff --git a/src/bin/plugins/lib/backend-tests.yml b/src/bin/plugins/lib/backend-tests.yml index 324cc4baf..f1d3a4af1 100644 --- a/src/bin/plugins/lib/backend-tests.yml +++ b/src/bin/plugins/lib/backend-tests.yml @@ -30,7 +30,7 @@ jobs: repository: ether/etherpad-lite - name: Install all dependencies and symlink for ep_etherpad-lite - run: bin/installDeps.sh + run: src/bin/installDeps.sh # clone this repository into node_modules/ep_plugin-name - name: Checkout plugin repository @@ -45,7 +45,7 @@ jobs: # configures some settings and runs npm run test - name: Run the backend tests - run: tests/frontend/travis/runnerBackend.sh + run: src/tests/frontend/travis/runnerBackend.sh ##ETHERPAD_NPM_V=1 -## NPM configuration automatically created using bin/plugins/updateAllPluginsScript.sh +## NPM configuration automatically created using src/bin/plugins/updateAllPluginsScript.sh diff --git a/src/bin/plugins/lib/npmpublish.yml b/src/bin/plugins/lib/npmpublish.yml index 2e0cf2405..4a930144e 100644 --- a/src/bin/plugins/lib/npmpublish.yml +++ b/src/bin/plugins/lib/npmpublish.yml @@ -80,4 +80,4 @@ jobs: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} ##ETHERPAD_NPM_V=2 -## NPM configuration automatically created using bin/plugins/updateAllPluginsScript.sh +## NPM configuration automatically created using src/bin/plugins/updateAllPluginsScript.sh diff --git a/src/bin/plugins/lib/travis.yml b/src/bin/plugins/lib/travis.yml index 099d7e445..1cf103167 100644 --- a/src/bin/plugins/lib/travis.yml +++ b/src/bin/plugins/lib/travis.yml @@ -12,7 +12,7 @@ install: - "export GIT_HASH=$(git rev-parse --verify --short HEAD)" #script: -# - "tests/frontend/travis/runner.sh" +# - "src/tests/frontend/travis/runner.sh" env: global: @@ -40,14 +40,14 @@ jobs: - "cd etherpad" - "mkdir -p node_modules" - "mv ../[plugin_name] node_modules" - - "bin/installDeps.sh" + - "src/bin/installDeps.sh" - "export GIT_HASH=$(git rev-parse --verify --short HEAD)" - "cd src && npm install && cd -" script: - - "tests/frontend/travis/runnerBackend.sh" + - "src/tests/frontend/travis/runnerBackend.sh" - name: "Test the Frontend" before_script: - - "tests/frontend/travis/sauce_tunnel.sh" + - "src/tests/frontend/travis/sauce_tunnel.sh" install: - "npm install" - "mkdir [plugin_name]" @@ -56,10 +56,10 @@ jobs: - "cd etherpad" - "mkdir -p node_modules" - "mv ../[plugin_name] node_modules" - - "bin/installDeps.sh" + - "src/bin/installDeps.sh" - "export GIT_HASH=$(git rev-parse --verify --short HEAD)" script: - - "tests/frontend/travis/runner.sh" + - "src/tests/frontend/travis/runner.sh" notifications: irc: @@ -67,4 +67,4 @@ notifications: - "irc.freenode.org#etherpad-lite-dev" ##ETHERPAD_TRAVIS_V=9 -## Travis configuration automatically created using bin/plugins/updateAllPluginsScript.sh +## Travis configuration automatically created using src/bin/plugins/updateAllPluginsScript.sh diff --git a/src/bin/plugins/reTestAllPlugins.sh b/src/bin/plugins/reTestAllPlugins.sh index 319d378d4..58628bdb0 100755 --- a/src/bin/plugins/reTestAllPlugins.sh +++ b/src/bin/plugins/reTestAllPlugins.sh @@ -4,7 +4,7 @@ do echo $dir if [[ $dir == *"ep_"* ]]; then if [[ $dir != "ep_etherpad-lite" ]]; then - # node bin/plugins/checkPlugin.js $dir autofix autocommit autoupdate + # node src/bin/plugins/checkPlugin.js $dir autofix autocommit autoupdate cd node_modules/$dir git commit -m "Automatic update: bump update to re-run latest Etherpad tests" --allow-empty git push origin master diff --git a/src/bin/plugins/updateAllPluginsScript.sh b/src/bin/plugins/updateAllPluginsScript.sh index 763724fca..bf5280ee0 100755 --- a/src/bin/plugins/updateAllPluginsScript.sh +++ b/src/bin/plugins/updateAllPluginsScript.sh @@ -10,7 +10,7 @@ do # echo $0 if [[ $dir == *"ep_"* ]]; then if [[ $dir != "ep_etherpad-lite" ]]; then - node bin/plugins/checkPlugin.js $dir autofix autocommit autoupdate + node src/bin/plugins/checkPlugin.js $dir autofix autocommit autoupdate fi fi # echo $dir diff --git a/src/bin/plugins/updateCorePlugins.sh b/src/bin/plugins/updateCorePlugins.sh index bf4e6b6d6..402a080ec 100755 --- a/src/bin/plugins/updateCorePlugins.sh +++ b/src/bin/plugins/updateCorePlugins.sh @@ -5,5 +5,5 @@ set -e for dir in node_modules/ep_*; do dir=${dir#node_modules/} [ "$dir" != ep_etherpad-lite ] || continue - node bin/plugins/checkPlugin.js "$dir" autofix autocommit autoupdate + node src/bin/plugins/checkPlugin.js "$dir" autofix autocommit autoupdate done diff --git a/src/bin/rebuildPad.js b/src/bin/rebuildPad.js index bda611f79..73f530889 100644 --- a/src/bin/rebuildPad.js +++ b/src/bin/rebuildPad.js @@ -10,7 +10,7 @@ process.on('unhandledRejection', (err) => { throw err; }); if (process.argv.length !== 4 && process.argv.length !== 5) { - throw new Error('Use: node bin/repairPad.js $PADID $REV [$NEWPADID]'); + throw new Error('Use: node src/bin/repairPad.js $PADID $REV [$NEWPADID]'); } const padId = process.argv[2]; diff --git a/src/bin/release.js b/src/bin/release.js index b12513d6d..21f1ddbc2 100644 --- a/src/bin/release.js +++ b/src/bin/release.js @@ -12,10 +12,11 @@ const semver = require('semver'); Usage -node bin/release.js patch +node src/bin/release.js patch */ -const usage = 'node bin/release.js [patch/minor/major] -- example: "node bin/release.js patch"'; +const usage = + 'node src/bin/release.js [patch/minor/major] -- example: "node src/bin/release.js patch"'; const release = process.argv[2]; diff --git a/src/bin/repairPad.js b/src/bin/repairPad.js index 9aa200517..7983fc88d 100644 --- a/src/bin/repairPad.js +++ b/src/bin/repairPad.js @@ -10,7 +10,7 @@ process.on('unhandledRejection', (err) => { throw err; }); console.warn('WARNING: This script must not be used while etherpad is running!'); -if (process.argv.length !== 3) throw new Error('Use: node bin/repairPad.js $PADID'); +if (process.argv.length !== 3) throw new Error('Use: node src/bin/repairPad.js $PADID'); // get the padID const padId = process.argv[2]; diff --git a/src/bin/run.sh b/src/bin/run.sh index c10359904..1a2aa36a9 100755 --- a/src/bin/run.sh +++ b/src/bin/run.sh @@ -1,10 +1,11 @@ #!/bin/sh -# Move to the folder where ep-lite is installed -cd "$(dirname "$0")"/.. +# Move to the Etherpad base directory. +MY_DIR=$(cd "${0%/*}" && pwd -P) || exit 1 +cd "${MY_DIR}/../.." || exit 1 # Source constants and useful functions -. bin/functions.sh +. src/bin/functions.sh ignoreRoot=0 for ARG in "$@"; do @@ -26,7 +27,7 @@ EOF fi # Prepare the environment -bin/installDeps.sh "$@" || exit 1 +src/bin/installDeps.sh "$@" || exit 1 # Move to the node folder and start log "Starting Etherpad..." diff --git a/src/bin/safeRun.sh b/src/bin/safeRun.sh index 6d43e3035..d9efa241a 100755 --- a/src/bin/safeRun.sh +++ b/src/bin/safeRun.sh @@ -23,8 +23,9 @@ fatal() { error "$@"; exit 1; } LAST_EMAIL_SEND=0 -# Move to the folder where ep-lite is installed -cd "$(dirname "$0")"/.. +# Move to the Etherpad base directory. +MY_DIR=$(try cd "${0%/*}" && try pwd -P) || exit 1 +try cd "${MY_DIR}/../.." # Check if a logfile parameter is set LOG="$1" @@ -39,7 +40,7 @@ while true; do [ -w "${LOG}" ] || fatal "Logfile '${LOG}' is not writeable" # Start the application - bin/run.sh "$@" >>${LOG} 2>>${LOG} + src/bin/run.sh "$@" >>${LOG} 2>>${LOG} TIME_FMT=$(date +%Y-%m-%dT%H:%M:%S%z) diff --git a/src/node/hooks/express/tests.js b/src/node/hooks/express/tests.js index 641ca819a..de868ba3b 100644 --- a/src/node/hooks/express/tests.js +++ b/src/node/hooks/express/tests.js @@ -91,4 +91,4 @@ exports.getPluginTests = async (callback) => { return Promise.all(promises).then(() => pluginSpecs); }; -exports.getCoreTests = () => readdir('tests/frontend/specs'); +exports.getCoreTests = () => readdir('src/tests/frontend/specs'); diff --git a/src/node/server.js b/src/node/server.js index affca7e1b..486975712 100755 --- a/src/node/server.js +++ b/src/node/server.js @@ -3,7 +3,7 @@ 'use strict'; /** - * This module is started with bin/run.sh. It sets up a Express HTTP and a Socket.IO Server. + * This module is started with src/bin/run.sh. It sets up a Express HTTP and a Socket.IO Server. * Static file Requests are answered directly from this module, Socket.IO messages are passed * to MessageHandler and minfied requests are passed to minified. */ diff --git a/src/package.json b/src/package.json index bf61feaf7..42e229751 100644 --- a/src/package.json +++ b/src/package.json @@ -217,8 +217,8 @@ }, "scripts": { "lint": "eslint .", - "test": "mocha --timeout 120000 --recursive ../tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs", - "test-container": "mocha --timeout 5000 ../tests/container/specs/api" + "test": "mocha --timeout 120000 --recursive tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs", + "test-container": "mocha --timeout 5000 tests/container/specs/api" }, "version": "1.8.7", "license": "Apache-2.0" diff --git a/src/static/js/pluginfw/hooks.js b/src/static/js/pluginfw/hooks.js index 72da63021..89f4267bb 100644 --- a/src/static/js/pluginfw/hooks.js +++ b/src/static/js/pluginfw/hooks.js @@ -72,7 +72,8 @@ const flatten1 = (array) => array.reduce((a, b) => a.concat(b), []); // there will be an unhandled promise rejection depending on whether the the subsequent attempt is a // duplicate (same value or error) or different, respectively. // -// See the tests in tests/backend/specs/hooks.js for examples of supported and prohibited behaviors. +// See the tests in src/tests/backend/specs/hooks.js for examples of supported and prohibited +// behaviors. // const callHookFnSync = (hook, context) => { checkDeprecation(hook); @@ -226,7 +227,8 @@ exports.callAll = (hookName, context) => { // effect except either an error message is logged or an Error object is thrown depending on whether // the the subsequent attempt is a duplicate (same value or error) or different, respectively. // -// See the tests in tests/backend/specs/hooks.js for examples of supported and prohibited behaviors. +// See the tests in src/tests/backend/specs/hooks.js for examples of supported and prohibited +// behaviors. // const callHookFnAsync = async (hook, context) => { checkDeprecation(hook); diff --git a/src/tests/backend/specs/api/characterEncoding.js b/src/tests/backend/specs/api/characterEncoding.js index 9c1e294e5..211885a28 100644 --- a/src/tests/backend/specs/api/characterEncoding.js +++ b/src/tests/backend/specs/api/characterEncoding.js @@ -1,7 +1,7 @@ 'use strict'; /* - * This file is copied & modified from /tests/backend/specs/api/pad.js + * This file is copied & modified from /src/tests/backend/specs/api/pad.js * * TODO: maybe unify those two files and merge in a single one. */ @@ -60,7 +60,7 @@ describe(__filename, function () { describe('setHTML', function () { it('Sets the HTML of a Pad attempting to weird utf8 encoded content', function (done) { - fs.readFile('../tests/backend/specs/api/emojis.html', 'utf8', (err, html) => { + fs.readFile('tests/backend/specs/api/emojis.html', 'utf8', (err, html) => { api.post(endPoint('setHTML')) .send({ padID: testPadId, diff --git a/src/tests/backend/specs/api/importexport.js b/src/tests/backend/specs/api/importexport.js index 4cdb5b8fe..1c93c11c4 100644 --- a/src/tests/backend/specs/api/importexport.js +++ b/src/tests/backend/specs/api/importexport.js @@ -1,7 +1,7 @@ 'use strict'; /* * ACHTUNG: there is a copied & modified version of this file in - * /tests/container/spacs/api/pad.js + * /src/tests/container/spacs/api/pad.js * * TODO: unify those two files, and merge in a single one. */ diff --git a/src/tests/backend/specs/api/importexportGetPost.js b/src/tests/backend/specs/api/importexportGetPost.js index 0a27f70ae..ce03c5768 100644 --- a/src/tests/backend/specs/api/importexportGetPost.js +++ b/src/tests/backend/specs/api/importexportGetPost.js @@ -12,12 +12,12 @@ const superagent = require('superagent'); const padManager = require('../../../../node/db/PadManager'); const plugins = require('../../../../static/js/pluginfw/plugin_defs'); -const padText = fs.readFileSync('../tests/backend/specs/api/test.txt'); -const etherpadDoc = fs.readFileSync('../tests/backend/specs/api/test.etherpad'); -const wordDoc = fs.readFileSync('../tests/backend/specs/api/test.doc'); -const wordXDoc = fs.readFileSync('../tests/backend/specs/api/test.docx'); -const odtDoc = fs.readFileSync('../tests/backend/specs/api/test.odt'); -const pdfDoc = fs.readFileSync('../tests/backend/specs/api/test.pdf'); +const padText = fs.readFileSync(`${__dirname}/test.txt`); +const etherpadDoc = fs.readFileSync(`${__dirname}/test.etherpad`); +const wordDoc = fs.readFileSync(`${__dirname}/test.doc`); +const wordXDoc = fs.readFileSync(`${__dirname}/test.docx`); +const odtDoc = fs.readFileSync(`${__dirname}/test.odt`); +const pdfDoc = fs.readFileSync(`${__dirname}/test.pdf`); let agent; const apiKey = common.apiKey; diff --git a/src/tests/backend/specs/api/pad.js b/src/tests/backend/specs/api/pad.js index d0280f789..92467cb1f 100644 --- a/src/tests/backend/specs/api/pad.js +++ b/src/tests/backend/specs/api/pad.js @@ -1,6 +1,6 @@ /* * ACHTUNG: there is a copied & modified version of this file in - * /tests/container/specs/api/pad.js + * /src/tests/container/specs/api/pad.js * * TODO: unify those two files, and merge in a single one. */ diff --git a/src/tests/frontend/travis/runner.sh b/src/tests/frontend/travis/runner.sh index da28ec1ef..b19c2873f 100755 --- a/src/tests/frontend/travis/runner.sh +++ b/src/tests/frontend/travis/runner.sh @@ -9,12 +9,11 @@ try() { "$@" || fatal "'$@' failed"; } [ -n "${SAUCE_USERNAME}" ] || fatal "SAUCE_USERNAME is unset - exiting" [ -n "${SAUCE_ACCESS_KEY}" ] || fatal "SAUCE_ACCESS_KEY is unset - exiting" -MY_DIR=$(try cd "${0%/*}" && try pwd) || exit 1 +# Move to the Etherpad base directory. +MY_DIR=$(try cd "${0%/*}" && try pwd -P) || exit 1 +try cd "${MY_DIR}/../../../.." -# reliably move to the etherpad base folder before running it -try cd "${MY_DIR}/../../../" - -log "Assuming bin/installDeps.sh has already been run" +log "Assuming src/bin/installDeps.sh has already been run" node node_modules/ep_etherpad-lite/node/server.js --experimental-worker "${@}" & ep_pid=$! diff --git a/src/tests/frontend/travis/runnerBackend.sh b/src/tests/frontend/travis/runnerBackend.sh index f829d0387..3bf2da1f0 100755 --- a/src/tests/frontend/travis/runnerBackend.sh +++ b/src/tests/frontend/travis/runnerBackend.sh @@ -6,10 +6,9 @@ error() { log "ERROR: $@" >&2; } fatal() { error "$@"; exit 1; } try() { "$@" || fatal "'$@' failed"; } -MY_DIR=$(try cd "${0%/*}" && try pwd) || fatal "failed to find script directory" - -# reliably move to the etherpad base folder before running it -try cd "${MY_DIR}/../../../" +# Move to the Etherpad base directory. +MY_DIR=$(try cd "${0%/*}" && try pwd -P) || exit 1 +try cd "${MY_DIR}/../../../.." try sed -e ' s!"soffice":[^,]*!"soffice": "/usr/bin/soffice"! @@ -20,7 +19,7 @@ s!"points":[^,]*!"points": 1000! s!"loglevel":[^,]*!"loglevel": "WARN"! ' settings.json.template >settings.json -log "Assuming bin/installDeps.sh has already been run" +log "Assuming src/bin/installDeps.sh has already been run" node node_modules/ep_etherpad-lite/node/server.js "${@}" & ep_pid=$! diff --git a/src/tests/frontend/travis/runnerLoadTest.sh b/src/tests/frontend/travis/runnerLoadTest.sh index 50ffcbb49..3e9d7d406 100755 --- a/src/tests/frontend/travis/runnerLoadTest.sh +++ b/src/tests/frontend/travis/runnerLoadTest.sh @@ -6,10 +6,9 @@ error() { log "ERROR: $@" >&2; } fatal() { error "$@"; exit 1; } try() { "$@" || fatal "'$@' failed"; } -MY_DIR=$(try cd "${0%/*}" && try pwd) || exit 1 - -# reliably move to the etherpad base folder before running it -try cd "${MY_DIR}/../../../" +# Move to the Etherpad base directory. +MY_DIR=$(try cd "${0%/*}" && try pwd -P) || exit 1 +try cd "${MY_DIR}/../../../.." try sed -e ' s!"loadTest":[^,]*!"loadTest": true! @@ -17,7 +16,7 @@ s!"loadTest":[^,]*!"loadTest": true! s!"points":[^,]*!"points": 1000! ' settings.json.template >settings.json -log "Assuming bin/installDeps.sh has already been run" +log "Assuming src/bin/installDeps.sh has already been run" node node_modules/ep_etherpad-lite/node/server.js "${@}" >/dev/null & ep_pid=$! diff --git a/src/tests/ratelimit/Dockerfile.anotherip b/src/tests/ratelimit/Dockerfile.anotherip index 5b9d1d21a..57f02f628 100644 --- a/src/tests/ratelimit/Dockerfile.anotherip +++ b/src/tests/ratelimit/Dockerfile.anotherip @@ -1,4 +1,4 @@ FROM node:alpine3.12 WORKDIR /tmp RUN npm i etherpad-cli-client -COPY ./tests/ratelimit/send_changesets.js /tmp/send_changesets.js +COPY ./src/tests/ratelimit/send_changesets.js /tmp/send_changesets.js diff --git a/src/tests/ratelimit/Dockerfile.nginx b/src/tests/ratelimit/Dockerfile.nginx index ba8dd358f..c2a1dae4e 100644 --- a/src/tests/ratelimit/Dockerfile.nginx +++ b/src/tests/ratelimit/Dockerfile.nginx @@ -1,2 +1,2 @@ FROM nginx -COPY ./tests/ratelimit/nginx.conf /etc/nginx/nginx.conf +COPY ./src/tests/ratelimit/nginx.conf /etc/nginx/nginx.conf From 5f58ce14d69937fc142888101c1f7fb78b8ae282 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 6 Feb 2021 09:58:10 +0000 Subject: [PATCH 242/357] editor: remove grayed logic and styles so background color is not lost on disconnect/reconnect. --- src/static/css/iframe_editor.css | 3 --- src/static/js/ace2_inner.js | 1 - src/static/js/pad_editor.js | 1 - 3 files changed, 5 deletions(-) diff --git a/src/static/css/iframe_editor.css b/src/static/css/iframe_editor.css index 42951f486..2a63c3802 100644 --- a/src/static/css/iframe_editor.css +++ b/src/static/css/iframe_editor.css @@ -151,6 +151,3 @@ body.mozilla, body.safari { #innerdocbody a { cursor: pointer !important; } -body.grayedout { - background-color: #eee !important -} diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 60b029f3b..194127c1d 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -696,7 +696,6 @@ function Ace2Inner() { sideDiv.parentNode.classList.toggle('line-numbers-hidden', !hasLineNumbers); fixView(); }, - grayedout: (val) => outerWin.document.body.classList.toggle('grayedout', !!val), dmesg: () => { dmesg = window.dmesg = value; }, userauthor: (value) => { thisAuthor = String(value); diff --git a/src/static/js/pad_editor.js b/src/static/js/pad_editor.js index 70afc0e09..a32af1644 100644 --- a/src/static/js/pad_editor.js +++ b/src/static/js/pad_editor.js @@ -161,7 +161,6 @@ const padeditor = (() => { }, disable: () => { if (self.ace) { - self.ace.setProperty('grayedOut', true); self.ace.setEditable(false); } }, From c969ae58c23938ef25e740cebf66fb60a1c91571 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 6 Feb 2021 19:53:52 +0000 Subject: [PATCH 243/357] stats: activePads & lastDisconnected stats --- src/node/handler/PadMessageHandler.js | 13 +++++++++++++ src/node/handler/SocketIORouter.js | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 2a1f6385b..8c106c615 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -90,6 +90,19 @@ exports.handleConnect = (socket) => { // Initialize sessioninfos for this new session sessioninfos[socket.id] = {}; + + stats.gauge('activePads', () => { + const padIds = []; + for (const session of Object.keys(sessioninfos)) { + if(sessioninfos[session].padId) { + if(padIds.indexOf(sessioninfos[session].padId) === -1) { + padIds.push(sessioninfos[session].padId); + } + } + }; + return padIds.length; + }); + }; /** diff --git a/src/node/handler/SocketIORouter.js b/src/node/handler/SocketIORouter.js index 42d710a39..35325c072 100644 --- a/src/node/handler/SocketIORouter.js +++ b/src/node/handler/SocketIORouter.js @@ -22,6 +22,7 @@ const log4js = require('log4js'); const messageLogger = log4js.getLogger('message'); +const stats = require('../stats'); /** * Saves all components @@ -77,6 +78,11 @@ exports.setSocketIO = (_socket) => { }); client.on('disconnect', () => { + // store the lastDisconnect as a timestamp, this is useful if you want to know + // when the last user disconnected. If your activePads is 0 and totalUsers is 0 + // you can say, if there has been no active pads or active users for 10 minutes + // this instance can be brought out of a scaling cluster. + stats.gauge('lastDisconnect', () => Date.now()); // tell all components about this disconnect for (const i in components) { components[i].handleDisconnect(client); From 4862d6fa9c49d93c315fa4d179db90f33cfd30d1 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 6 Feb 2021 19:56:59 +0000 Subject: [PATCH 244/357] editor: fix enter key keep line in view (#4639) --- src/static/js/ace2_inner.js | 2 +- src/static/js/scroll.js | 16 ++++++++++------ src/tests/frontend/specs/enter.js | 29 ++++++++++++++++++++++++++++- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 194127c1d..e5de72f9c 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -2179,7 +2179,7 @@ function Ace2Inner() { isScrollableEditEvent(currentCallStack.type); const innerHeight = getInnerHeight(); scroll.scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary( - rep, isScrollableEvent, innerHeight + rep, isScrollableEvent, innerHeight*2 ); } diff --git a/src/static/js/scroll.js b/src/static/js/scroll.js index 063e55513..86d6a3344 100644 --- a/src/static/js/scroll.js +++ b/src/static/js/scroll.js @@ -282,7 +282,7 @@ Scroll.prototype.scrollNodeVerticallyIntoView = function (rep, innerHeight) { const linePosition = caretPosition.getPosition(); if (linePosition) { const distanceOfTopOfViewport = linePosition.top - viewport.top; - const distanceOfBottomOfViewport = viewport.bottom - linePosition.bottom; + const distanceOfBottomOfViewport = viewport.bottom - linePosition.bottom - linePosition.height; const caretIsAboveOfViewport = distanceOfTopOfViewport < 0; const caretIsBelowOfViewport = distanceOfBottomOfViewport < 0; if (caretIsAboveOfViewport) { @@ -290,11 +290,15 @@ Scroll.prototype.scrollNodeVerticallyIntoView = function (rep, innerHeight) { distanceOfTopOfViewport - this._getPixelsRelativeToPercentageOfViewport(innerHeight, true); this._scrollYPage(pixelsToScroll); } else if (caretIsBelowOfViewport) { - const pixelsToScroll = -distanceOfBottomOfViewport + - this._getPixelsRelativeToPercentageOfViewport(innerHeight); - this._scrollYPage(pixelsToScroll); - } else { - this.scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary(rep, true, innerHeight); + // setTimeout is required here as line might not be fully rendered onto the pad + setTimeout(() => { + const outer = window.parent; + // scroll to the very end of the pad outer + outer.scrollTo(0, outer[0].innerHeight); + }, 150); + // if the above setTimeout and functionality is removed then hitting an enter + // key while on the last line wont be an optimal user experience + // Details at: https://github.com/ether/etherpad-lite/pull/4639/files } } }; diff --git a/src/tests/frontend/specs/enter.js b/src/tests/frontend/specs/enter.js index 2f5a90a69..ca71d2081 100644 --- a/src/tests/frontend/specs/enter.js +++ b/src/tests/frontend/specs/enter.js @@ -6,7 +6,6 @@ describe('enter keystroke', function () { helper.newPad(cb); this.timeout(60000); }); - it('creates a new line & puts cursor onto a new line', function (done) { const inner$ = helper.padInner$; @@ -28,4 +27,32 @@ describe('enter keystroke', function () { done(); }); }); + + it('enter is always visible after event', async function () { + const originalLength = helper.padInner$('div').length; + let $lastLine = helper.padInner$('div').last(); + + // simulate key presses to enter content + let i = 0; + const numberOfLines = 15; + let previousLineLength = originalLength; + while (i < numberOfLines) { + $lastLine = helper.padInner$('div').last(); + $lastLine.sendkeys('{enter}'); + await helper.waitFor(() => helper.padInner$('div').length > previousLineLength); + previousLineLength = helper.padInner$('div').length; + // check we can see the caret.. + + i++; + } + await helper.waitFor(() => helper.padInner$('div').length === numberOfLines + originalLength); + + // is edited line fully visible? + const lastLine = helper.padInner$('div').last(); + const bottomOfLastLine = lastLine.offset().top + lastLine.height(); + const scrolledWindow = helper.padChrome$('iframe')[0]; + const scrolledAmount = scrolledWindow.contentWindow.pageYOffset + + scrolledWindow.contentWindow.innerHeight; + await helper.waitFor(() => scrolledAmount >= bottomOfLastLine); + }); }); From e02246641e636407db3fd5142ad1c074517a7db6 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 6 Feb 2021 20:19:05 +0000 Subject: [PATCH 245/357] ordered lists bugfix: first line item can be 0.*, don't show 0 as undefined (#4600) --- src/node/utils/ExportTxt.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/node/utils/ExportTxt.js b/src/node/utils/ExportTxt.js index d97061364..ead3637ff 100644 --- a/src/node/utils/ExportTxt.js +++ b/src/node/utils/ExportTxt.js @@ -237,6 +237,8 @@ const getTXTFromAtext = (pad, atext, authorColors) => { if (line.listLevel > 1) { let x = 1; while (x <= line.listLevel - 1) { + // if it's undefined to avoid undefined.undefined.1 for 0.0.1 + if (!listNumbers[x]) listNumbers[x] = 0; pieces.push(`${listNumbers[x]}.`); x++; } From da10d42183f5d7707307bcef6f334a8ef9f9699a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 4 Feb 2021 19:47:57 -0500 Subject: [PATCH 246/357] Delete top-level `package.json` Advantages of a top-level `package.json`: * It prevents npm from printing benign warnings about missing `package.json` whenever a plugin is installed. * Semantically, it is "the right thing to do" if plugins are to be installed in the top-level directory. This avoids violating assumptions various tools make about `package.json`, which makes it more likely that we can easily switch to a new version of npm or to an npm alternative. Disadvantages of a top-level `package.json`: * Including a dependency of `ep_etherpad-lite@file:src` in the top-level `package.json`, which is required to keep npm from deleting the `node_modules/ep_etherpad-lite` symlink each time a package is installed, drastically slows down plugin installation because npm recursively walks the ep_etherpad-lite dependencies. * npm has a horrible dependency hoisting behavior: It moves dependencies from `src/node_modules/` to the top-level `node_modules/` directory when possible. This causes numerous mysterious problems, such as silent failures in `npm outdated` and `npm update`, and it breaks plugins that do `require('ep_etherpad-lite/node_modules/foo')`. Right now, with npm v6.x, eliminating the disadvantages is far more valuable than keeping the advantages. (This might change with npm v7.x.) For a long time there was no top-level `package.json` and it worked fairly well, although users were often confused by npm's benign warnings. The top-level `package.json` was added because we needed a place to put ESLint config for the stuff in `bin/` and `tests/`, and we wanted the advantages listed above. Unfortunately we were unaware of the disadvantages at the time. The `bin/` and `tests/` directories were moved under `src/` so we no longer need the top-level `package.json` for ESLint. An alternative to a top-level `package.json`: Create `plugins/package.json` and install all plugins in `plugins/`. If `plugins/package.json` has a dependency of `ep_etherpad-lite@file:../src` then plugin installation will still be slow (npm will still recursively walk the dependencies in `src/package.json`) but it should avoid npm's nasty dependency hoisting behavior. To avoid slow plugin installation we could create a lightweight `etherpad-pluginlib` package that Etherpad plugins would use to indirectly access Etherpad's internals. As an added bonus, this intermediate package could become an adaptor that provides a stable interface to plugins even when Etherpad core rapidly evolves. --- .gitignore | 2 +- package-lock.json | 9665 --------------------------------------------- package.json | 8 - 3 files changed, 1 insertion(+), 9674 deletions(-) delete mode 100644 package-lock.json delete mode 100644 package.json diff --git a/.gitignore b/.gitignore index 6dca890dc..09618cc83 100644 --- a/.gitignore +++ b/.gitignore @@ -15,8 +15,8 @@ npm-debug.log credentials.json out/ .nyc_output -./package-lock.json .idea +/package-lock.json /src/bin/abiword.exe /src/bin/convertSettings.json /src/bin/etherpad-1.deb diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 118e4be8a..000000000 --- a/package-lock.json +++ /dev/null @@ -1,9665 +0,0 @@ -{ - "requires": true, - "lockfileVersion": 1, - "dependencies": { - "@azure/ms-rest-azure-env": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@azure/ms-rest-azure-env/-/ms-rest-azure-env-2.0.0.tgz", - "integrity": "sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw==" - }, - "@azure/ms-rest-js": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.1.0.tgz", - "integrity": "sha512-4BXLVImYRt+jcUmEJ5LUWglI8RBNVQndY6IcyvQ4U8O4kIXdmlRz3cJdA/RpXf5rKT38KOoTO2T6Z1f6Z1HDBg==", - "requires": { - "@types/node-fetch": "^2.3.7", - "@types/tunnel": "0.0.1", - "abort-controller": "^3.0.0", - "form-data": "^2.5.0", - "node-fetch": "^2.6.0", - "tough-cookie": "^3.0.1", - "tslib": "^1.10.0", - "tunnel": "0.0.6", - "uuid": "^3.3.2", - "xml2js": "^0.4.19" - } - }, - "@azure/ms-rest-nodeauth": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@azure/ms-rest-nodeauth/-/ms-rest-nodeauth-3.0.6.tgz", - "integrity": "sha512-2twuzsXHdKMzEFI2+Sr82o6yS4ppNGZceYwil8PFo+rJxOZIoBm9e0//YC+dKilV/3F+6K/HuW8LdskDrJEQWA==", - "requires": { - "@azure/ms-rest-azure-env": "^2.0.0", - "@azure/ms-rest-js": "^2.0.4", - "adal-node": "^0.1.28" - } - }, - "@js-joda/core": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-3.2.0.tgz", - "integrity": "sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg==" - }, - "@kwsites/file-exists": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", - "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", - "requires": { - "debug": "^4.1.1" - } - }, - "@kwsites/promise-deferred": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", - "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" - }, - "@types/caseless": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", - "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==" - }, - "@types/long": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", - "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" - }, - "@types/node": { - "version": "14.14.22", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.22.tgz", - "integrity": "sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw==" - }, - "@types/node-fetch": { - "version": "2.5.8", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.8.tgz", - "integrity": "sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw==", - "requires": { - "@types/node": "*", - "form-data": "^3.0.0" - }, - "dependencies": { - "form-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", - "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } - } - }, - "@types/request": { - "version": "2.48.5", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz", - "integrity": "sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ==", - "requires": { - "@types/caseless": "*", - "@types/node": "*", - "@types/tough-cookie": "*", - "form-data": "^2.5.0" - } - }, - "@types/tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==" - }, - "@types/tunnel": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.1.tgz", - "integrity": "sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A==", - "requires": { - "@types/node": "*" - } - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "optional": true - }, - "abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "requires": { - "event-target-shim": "^5.0.0" - } - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "adal-node": { - "version": "0.1.28", - "resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.1.28.tgz", - "integrity": "sha1-RoxLs+u9lrEnBmn0ucuk4AZepIU=", - "requires": { - "@types/node": "^8.0.47", - "async": ">=0.6.0", - "date-utils": "*", - "jws": "3.x.x", - "request": ">= 2.52.0", - "underscore": ">= 1.3.1", - "uuid": "^3.1.0", - "xmldom": ">= 0.1.x", - "xpath.js": "~1.1.0" - }, - "dependencies": { - "@types/node": { - "version": "8.10.66", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", - "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==" - } - } - }, - "adm-zip": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", - "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==" - }, - "after": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" - }, - "agentkeepalive": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", - "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", - "requires": { - "humanize-ms": "^1.2.1" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "arraybuffer.slice": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "base64-arraybuffer": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", - "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=" - }, - "base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "bignumber.js": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", - "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" - }, - "bl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", - "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", - "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" - } - }, - "blob": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", - "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" - }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "optional": true, - "requires": { - "inherits": "~2.0.0" - } - }, - "bluebird": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "browser-request": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/browser-request/-/browser-request-0.3.3.tgz", - "integrity": "sha1-ns5bWsqJopkyJC4Yv5M975h2zBc=" - }, - "bson": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", - "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==" - }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" - }, - "buffer-writer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", - "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "cassandra-driver": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.6.1.tgz", - "integrity": "sha512-Vk0kUHlMV4vFXRPwRpKnCZEEMZkp9/RucBDB7gpaUmn9sCusKzzUzVkXeusTxKSoGuIgLJJ7YBiFJdXOctUS7A==", - "requires": { - "@types/long": "^4.0.0", - "@types/node": ">=8", - "adm-zip": "^0.4.13", - "long": "^2.2.0" - } - }, - "channels": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/channels/-/channels-0.0.4.tgz", - "integrity": "sha1-G+4yPt6hUrue8E9BvG5rD1lIqUE=" - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "optional": true - }, - "cloudant-follow": { - "version": "0.18.2", - "resolved": "https://registry.npmjs.org/cloudant-follow/-/cloudant-follow-0.18.2.tgz", - "integrity": "sha512-qu/AmKxDqJds+UmT77+0NbM7Yab2K3w0qSeJRzsq5dRWJTEJdWeb+XpG4OpKuTE9RKOa/Awn2gR3TTnvNr3TeA==", - "requires": { - "browser-request": "~0.3.0", - "debug": "^4.0.1", - "request": "^2.88.0" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "optional": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "component-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" - }, - "component-inherit": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "optional": true - }, - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "date-utils": { - "version": "1.2.21", - "resolved": "https://registry.npmjs.org/date-utils/-/date-utils-1.2.21.tgz", - "integrity": "sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q=" - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "optional": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "optional": true - }, - "denque": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz", - "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ==" - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "optional": true - }, - "dirty": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/dirty/-/dirty-1.1.0.tgz", - "integrity": "sha1-cO3SuZlUHcmXT9Ooy9DGcP4jYHg=" - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "elasticsearch": { - "version": "16.7.2", - "resolved": "https://registry.npmjs.org/elasticsearch/-/elasticsearch-16.7.2.tgz", - "integrity": "sha512-1ZLKZlG2ABfYVBX2d7/JgxOsKJrM5Yu62GvshWu7ZSvhxPomCN4Gas90DS51yYI56JolY0XGhyiRlUhLhIL05Q==", - "requires": { - "agentkeepalive": "^3.4.1", - "chalk": "^1.0.0", - "lodash": "^4.17.10" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" - } - } - }, - "engine.io": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.5.0.tgz", - "integrity": "sha512-21HlvPUKaitDGE4GXNtQ7PLP0Sz4aWLddMPw2VTyFz1FVZqu/kZsJUO8WNpKuE/OCL7nkfRaOui2ZCJloGznGA==", - "requires": { - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "debug": "~4.1.0", - "engine.io-parser": "~2.2.0", - "ws": "~7.4.2" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "engine.io-client": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.0.tgz", - "integrity": "sha512-12wPRfMrugVw/DNyJk34GQ5vIVArEcVMXWugQGGuw2XxUSztFNmJggZmv8IZlLyEdnpO1QB9LkcjeWewO2vxtA==", - "requires": { - "component-emitter": "~1.3.0", - "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.2.0", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parseqs": "0.0.6", - "parseuri": "0.0.6", - "ws": "~7.4.2", - "xmlhttprequest-ssl": "~1.5.4", - "yeast": "0.1.2" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "engine.io-parser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz", - "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==", - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", - "base64-arraybuffer": "0.1.4", - "blob": "0.0.5", - "has-binary2": "~1.0.2" - } - }, - "ep_etherpad-lite": { - "version": "file:src", - "requires": { - "async": "^3.2.0", - "async-stacktrace": "0.0.2", - "channels": "0.0.4", - "cheerio": "0.22.0", - "clean-css": "4.2.3", - "cookie-parser": "1.4.5", - "ejs": "2.6.1", - "etherpad-require-kernel": "1.0.9", - "etherpad-yajsml": "0.0.2", - "express": "4.17.1", - "express-rate-limit": "5.1.1", - "express-session": "1.17.1", - "find-root": "1.1.0", - "formidable": "1.2.1", - "graceful-fs": "4.2.4", - "http-errors": "1.8.0", - "js-cookie": "^2.2.1", - "jsonminify": "0.4.1", - "languages4translatewiki": "0.1.3", - "lodash.clonedeep": "4.5.0", - "log4js": "0.6.35", - "measured-core": "1.11.2", - "mime-types": "^2.1.27", - "nodeify": "1.0.1", - "npm": "6.14.8", - "openapi-backend": "2.4.1", - "proxy-addr": "^2.0.6", - "rate-limiter-flexible": "^2.1.4", - "rehype": "^10.0.0", - "rehype-minify-whitespace": "^4.0.5", - "request": "2.88.2", - "resolve": "1.1.7", - "security": "1.0.0", - "semver": "5.6.0", - "socket.io": "^2.4.1", - "terser": "^4.7.0", - "threads": "^1.4.0", - "tiny-worker": "^2.3.0", - "tinycon": "0.0.1", - "ueberdb2": "^1.2.5", - "underscore": "1.8.3", - "unorm": "1.4.1", - "wtfnode": "^0.8.4" - }, - "dependencies": { - "@apidevtools/json-schema-ref-parser": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-8.0.0.tgz", - "integrity": "sha512-n4YBtwQhdpLto1BaUCyAeflizmIbaloGShsPyRtFf5qdFJxfssj+GgLavczgKJFa3Bq+3St2CKcpRJdjtB4EBw==", - "requires": { - "@jsdevtools/ono": "^7.1.0", - "call-me-maybe": "^1.0.1", - "js-yaml": "^3.13.1" - } - }, - "@apidevtools/openapi-schemas": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.0.3.tgz", - "integrity": "sha512-QoPaxGXfgqgGpK1p21FJ400z56hV681a8DOcZt3J5z0WIHgFeaIZ4+6bX5ATqmOoCpRCsH4ITEwKaOyFMz7wOA==" - }, - "@apidevtools/swagger-methods": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.1.tgz", - "integrity": "sha512-1Vlm18XYW6Yg7uHunroXeunWz5FShPFAdxBbPy8H6niB2Elz9QQsCoYHMbcc11EL1pTxaIr9HXz2An/mHXlX1Q==" - }, - "@apidevtools/swagger-parser": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-9.0.1.tgz", - "integrity": "sha512-Irqybg4dQrcHhZcxJc/UM4vO7Ksoj1Id5e+K94XUOzllqX1n47HEA50EKiXTCQbykxuJ4cYGIivjx/MRSTC5OA==", - "requires": { - "@apidevtools/json-schema-ref-parser": "^8.0.0", - "@apidevtools/openapi-schemas": "^2.0.2", - "@apidevtools/swagger-methods": "^3.0.0", - "@jsdevtools/ono": "^7.1.0", - "call-me-maybe": "^1.0.1", - "openapi-types": "^1.3.5", - "z-schema": "^4.2.2" - } - }, - "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", - "requires": { - "@babel/highlight": "^7.8.3" - } - }, - "@babel/core": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.6.tgz", - "integrity": "sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg==", - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.6", - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helpers": "^7.9.6", - "@babel/parser": "^7.9.6", - "@babel/template": "^7.8.6", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "requires": { - "path-parse": "^1.0.6" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "@babel/generator": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.6.tgz", - "integrity": "sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ==", - "requires": { - "@babel/types": "^7.9.6", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, - "@babel/helper-function-name": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz", - "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==", - "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.9.5" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", - "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", - "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-module-imports": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", - "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-module-transforms": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", - "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", - "requires": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-simple-access": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/template": "^7.8.6", - "@babel/types": "^7.9.0", - "lodash": "^4.17.13" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", - "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-replace-supers": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz", - "integrity": "sha512-qX+chbxkbArLyCImk3bWV+jB5gTNU/rsze+JlcF6Nf8tVTigPJSI1o1oBow/9Resa1yehUO9lIipsmu9oG4RzA==", - "requires": { - "@babel/helper-member-expression-to-functions": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6" - } - }, - "@babel/helper-simple-access": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", - "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", - "requires": { - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", - "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.9.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", - "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==" - }, - "@babel/helpers": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.6.tgz", - "integrity": "sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw==", - "requires": { - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6" - } - }, - "@babel/highlight": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", - "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", - "requires": { - "@babel/helper-validator-identifier": "^7.9.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==" - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==" - } - } - }, - "@babel/parser": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.6.tgz", - "integrity": "sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==" - }, - "@babel/template": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", - "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6" - } - }, - "@babel/traverse": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.6.tgz", - "integrity": "sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg==", - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.6", - "@babel/helper-function-name": "^7.9.5", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.9.6", - "@babel/types": "^7.9.6", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "@babel/types": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.6.tgz", - "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==", - "requires": { - "@babel/helper-validator-identifier": "^7.9.5", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "@eslint/eslintrc": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz", - "integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==", - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "lodash": "^4.17.19", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "requires": { - "type-fest": "^0.8.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" - } - } - }, - "@istanbuljs/load-nyc-config": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", - "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==", - "requires": { - "find-up": "^4.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==" - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==" - }, - "@jsdevtools/ono": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.2.tgz", - "integrity": "sha512-qS/a24RA5FEoiJS9wiv6Pwg2c/kiUo3IVUQcfeM9JvsR6pM8Yx+yl/6xWYLckZCT5jpLNhslgjiA8p/XcGyMRQ==" - }, - "@sinonjs/commons": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", - "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==" - }, - "@sinonjs/fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", - "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@sinonjs/formatio": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz", - "integrity": "sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==", - "requires": { - "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^5.0.2" - } - }, - "@sinonjs/samsam": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.2.0.tgz", - "integrity": "sha512-CaIcyX5cDsjcW/ab7HposFWzV1kC++4HNsfnEdFJa7cP1QIuILAKV+BgfeqRXhcnSAc76r/Rh/O5C+300BwUIw==", - "requires": { - "@sinonjs/commons": "^1.6.0", - "lodash.get": "^4.4.2" - } - }, - "@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==" - }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" - }, - "@types/unist": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", - "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==" - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" - }, - "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==" - }, - "aggregate-error": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", - "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "6.12.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", - "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "append-transform": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", - "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", - "requires": { - "default-require-extensions": "^3.0.0" - } - }, - "archiver": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-3.1.1.tgz", - "integrity": "sha512-5Hxxcig7gw5Jod/8Gq0OneVgLYET+oNHcxgWItq4TbhOzRLKNAFUb9edAftiMKXvXfCB0vbGrJdZDNq0dWMsxg==", - "requires": { - "archiver-utils": "^2.1.0", - "async": "^2.6.3", - "buffer-crc32": "^0.2.1", - "glob": "^7.1.4", - "readable-stream": "^3.4.0", - "tar-stream": "^2.1.0", - "zip-stream": "^2.1.2" - }, - "dependencies": { - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "requires": { - "lodash": "^4.17.14" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", - "requires": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "readable-stream": "^2.0.0" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==" - }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "async-stacktrace": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/async-stacktrace/-/async-stacktrace-0.0.2.tgz", - "integrity": "sha1-i7uXh+OzjINscpp+nXwIYw210e8=" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", - "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" - }, - "bail": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", - "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==" - }, - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" - }, - "bath-es5": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/bath-es5/-/bath-es5-3.0.3.tgz", - "integrity": "sha512-PdCioDToH3t84lP40kUFCKWCOCH389Dl1kbC8FGoqOwamxsmqxxnJSXdkTOsPoNHXjem4+sJ+bbNoQm5zeCqxg==" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "binary-search": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", - "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==" - }, - "bl": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", - "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "dependencies": { - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - } - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" - }, - "buffer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "caching-transform": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", - "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", - "requires": { - "hasha": "^5.0.0", - "make-dir": "^3.0.0", - "package-hash": "^4.0.0", - "write-file-atomic": "^3.0.0" - } - }, - "call-me-maybe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", - "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=" - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "ccount": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.5.tgz", - "integrity": "sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw==" - }, - "channels": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/channels/-/channels-0.0.4.tgz", - "integrity": "sha1-G+4yPt6hUrue8E9BvG5rD1lIqUE=" - }, - "character-entities-html4": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz", - "integrity": "sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g==" - }, - "character-entities-legacy": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", - "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==" - }, - "cheerio": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", - "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", - "requires": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash.assignin": "^4.0.9", - "lodash.bind": "^4.1.4", - "lodash.defaults": "^4.0.1", - "lodash.filter": "^4.4.0", - "lodash.flatten": "^4.2.0", - "lodash.foreach": "^4.3.0", - "lodash.map": "^4.4.0", - "lodash.merge": "^4.4.0", - "lodash.pick": "^4.2.1", - "lodash.reduce": "^4.4.0", - "lodash.reject": "^4.4.0", - "lodash.some": "^4.4.0" - } - }, - "clean-css": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", - "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", - "requires": { - "source-map": "~0.6.0" - } - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "comma-separated-tokens": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", - "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==" - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" - }, - "compress-commons": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-2.1.1.tgz", - "integrity": "sha512-eVw6n7CnEMFzc3duyFVrQEuY1BlHR3rYsSztyG32ibGMW722i3C6IizEGMFmfMU+A+fALvBIwxN3czffTcdA+Q==", - "requires": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^3.0.1", - "readable-stream": "^2.3.6" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "requires": { - "safe-buffer": "~5.1.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "cookie-parser": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz", - "integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==", - "requires": { - "cookie": "0.4.0", - "cookie-signature": "1.0.6" - }, - "dependencies": { - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - } - } - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "crc": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", - "requires": { - "buffer": "^5.1.0" - } - }, - "crc32-stream": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-3.0.1.tgz", - "integrity": "sha512-mctvpXlbzsvK+6z8kJwSJ5crm7yBwrQMTybJzMw1O4lLGJqjlDCXY2Zw7KheiA6XBEcBmfLx1D88mjRGVJtY9w==", - "requires": { - "crc": "^3.4.4", - "readable-stream": "^3.4.0" - } - }, - "cross-spawn": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", - "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "dependencies": { - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" - }, - "default-require-extensions": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", - "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", - "requires": { - "strip-bom": "^4.0.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", - "requires": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" - } - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "ejs": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz", - "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "requires": { - "ansi-colors": "^4.1.1" - }, - "dependencies": { - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" - } - } - }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "eslint": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.14.0.tgz", - "integrity": "sha512-5YubdnPXrlrYAFCKybPuHIAH++PINe1pmKNc5wQRB9HSbqIK1ywAnntE3Wwua4giKu0bjligf1gLF6qxMGOYRA==", - "requires": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.2.1", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.0", - "esquery": "^1.2.0", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash": "^4.17.19", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "requires": { - "ms": "2.1.2" - } - }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "requires": { - "type-fest": "^0.8.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "eslint-config-etherpad": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/eslint-config-etherpad/-/eslint-config-etherpad-1.0.13.tgz", - "integrity": "sha512-wuykfSQyQj7EDN4Ok2SR5l59QOtozeANbIfew9MloUVh0P80+PIY45pXNblMrLEa9EUxYvKQQywzUWyQxHiweQ==" - }, - "eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", - "requires": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" - } - }, - "eslint-plugin-mocha": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-8.0.0.tgz", - "integrity": "sha512-n67etbWDz6NQM+HnTwZHyBwz/bLlYPOxUbw7bPuCyFujv7ZpaT/Vn6KTAbT02gf7nRljtYIjWcTxK/n8a57rQQ==", - "requires": { - "eslint-utils": "^2.1.0", - "ramda": "^0.27.1" - } - }, - "eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", - "requires": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" - }, - "dependencies": { - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" - }, - "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "requires": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "eslint-plugin-prefer-arrow": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.2.tgz", - "integrity": "sha512-C8YMhL+r8RMeMdYAw/rQtE6xNdMulj+zGWud/qIGnlmomiPRaLDGLMeskZ3alN6uMBojmooRimtdrXebLN4svQ==" - }, - "eslint-plugin-promise": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", - "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==" - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" - } - } - }, - "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==" - }, - "esm": { - "version": "3.2.25", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" - }, - "espree": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", - "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" - } - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "etherpad-cli-client": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/etherpad-cli-client/-/etherpad-cli-client-0.0.9.tgz", - "integrity": "sha1-A+5+fNzA4EZLTu/djn7gzwUaVDs=", - "requires": { - "async": "*", - "socket.io-client": "*" - } - }, - "etherpad-require-kernel": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/etherpad-require-kernel/-/etherpad-require-kernel-1.0.9.tgz", - "integrity": "sha1-7Y8E6f0szsOgBVu20t/p2ZkS5+I=" - }, - "etherpad-yajsml": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/etherpad-yajsml/-/etherpad-yajsml-0.0.2.tgz", - "integrity": "sha1-HCTSaLCUduY30EnN2xxt+McptG4=" - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "express-rate-limit": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.1.1.tgz", - "integrity": "sha512-puA1zcCx/quwWUOU6pT6daCt6t7SweD9wKChKhb+KSgFMKRwS81C224hiSAUANw/gnSHiwEhgozM/2ezEBZPeA==" - }, - "express-session": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.1.tgz", - "integrity": "sha512-UbHwgqjxQZJiWRTMyhvWGvjBQduGCSBDhhZXYenziMFjxst5rMV+aJZ6hKPHZnPyHGsrqRICxtX8jtEbm/z36Q==", - "requires": { - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-headers": "~1.0.2", - "parseurl": "~1.3.3", - "safe-buffer": "5.2.0", - "uid-safe": "~2.1.5" - }, - "dependencies": { - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, - "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "requires": { - "flat-cache": "^2.0.1" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - } - }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" - }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - }, - "dependencies": { - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==" - }, - "foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "formidable": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", - "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==" - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fromentries": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.2.0.tgz", - "integrity": "sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ==" - }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" - }, - "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==" - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, - "hasha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.0.tgz", - "integrity": "sha512-2W+jKdQbAdSIrggA8Q35Br8qKadTrqCTC8+XZvBWepKDK6m9XkX6Iz1a2yh2KP01kzAR/dpuMeUnocoLYDcskw==", - "requires": { - "is-stream": "^2.0.0", - "type-fest": "^0.8.0" - } - }, - "hast-util-embedded": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-1.0.6.tgz", - "integrity": "sha512-JQMW+TJe0UAIXZMjCJ4Wf6ayDV9Yv3PBDPsHD4ExBpAspJ6MOcCX+nzVF+UJVv7OqPcg852WEMSHQPoRA+FVSw==", - "requires": { - "hast-util-is-element": "^1.1.0" - }, - "dependencies": { - "hast-util-is-element": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.1.0.tgz", - "integrity": "sha512-oUmNua0bFbdrD/ELDSSEadRVtWZOf3iF6Lbv81naqsIV99RnSCieTbWuWCY8BAeEfKJTKl0gRdokv+dELutHGQ==" - } - } - }, - "hast-util-from-parse5": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz", - "integrity": "sha512-gOc8UB99F6eWVWFtM9jUikjN7QkWxB3nY0df5Z0Zq1/Nkwl5V4hAAsl0tmwlgWl/1shlTF8DnNYLO8X6wRV9pA==", - "requires": { - "ccount": "^1.0.3", - "hastscript": "^5.0.0", - "property-information": "^5.0.0", - "web-namespaces": "^1.1.2", - "xtend": "^4.0.1" - } - }, - "hast-util-is-element": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.0.4.tgz", - "integrity": "sha512-NFR6ljJRvDcyPP5SbV7MyPBgF47X3BsskLnmw1U34yL+X6YC0MoBx9EyMg8Jtx4FzGH95jw8+c1VPLHaRA0wDQ==" - }, - "hast-util-parse-selector": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.4.tgz", - "integrity": "sha512-gW3sxfynIvZApL4L07wryYF4+C9VvH3AUi7LAnVXV4MneGEgwOByXvFo18BgmTWnm7oHAe874jKbIB1YhHSIzA==" - }, - "hast-util-to-html": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-6.1.0.tgz", - "integrity": "sha512-IlC+LG2HGv0Y8js3wqdhg9O2sO4iVpRDbHOPwXd7qgeagpGsnY49i8yyazwqS35RA35WCzrBQE/n0M6GG/ewxA==", - "requires": { - "ccount": "^1.0.0", - "comma-separated-tokens": "^1.0.1", - "hast-util-is-element": "^1.0.0", - "hast-util-whitespace": "^1.0.0", - "html-void-elements": "^1.0.0", - "property-information": "^5.2.0", - "space-separated-tokens": "^1.0.0", - "stringify-entities": "^2.0.0", - "unist-util-is": "^3.0.0", - "xtend": "^4.0.1" - } - }, - "hast-util-whitespace": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-1.0.4.tgz", - "integrity": "sha512-I5GTdSfhYfAPNztx2xJRQpG8cuDSNt599/7YUn7Gx/WxNMsG+a835k97TDkFgk123cwjfwINaZknkKkphx/f2A==" - }, - "hastscript": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-5.1.2.tgz", - "integrity": "sha512-WlztFuK+Lrvi3EggsqOkQ52rKbxkXL3RwB6t5lwoa8QLMemoWfBuL43eDrwOamJyR7uKQKdmKYaBH1NZBiIRrQ==", - "requires": { - "comma-separated-tokens": "^1.0.0", - "hast-util-parse-selector": "^2.0.0", - "property-information": "^5.0.0", - "space-separated-tokens": "^1.0.0" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" - }, - "html-void-elements": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz", - "integrity": "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==" - }, - "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - } - }, - "http-errors": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz", - "integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "dependencies": { - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - } - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==" - }, - "import-fresh": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", - "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - } - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==" - }, - "is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "requires": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" - }, - "is-core-module": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", - "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==" - }, - "is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" - }, - "is-hexadecimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", - "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==" - }, - "is-observable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", - "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", - "requires": { - "symbol-observable": "^1.1.0" - } - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" - }, - "is-promise": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz", - "integrity": "sha1-MVc3YcBX4zwukaq56W2gjO++duU=" - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==" - }, - "istanbul-lib-hook": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", - "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", - "requires": { - "append-transform": "^2.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "istanbul-lib-processinfo": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", - "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", - "requires": { - "archy": "^1.0.0", - "cross-spawn": "^7.0.0", - "istanbul-lib-coverage": "^3.0.0-alpha.1", - "make-dir": "^3.0.0", - "p-map": "^3.0.0", - "uuid": "^3.3.3" - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "js-cookie": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", - "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "requires": { - "minimist": "^1.2.5" - } - }, - "jsonminify": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/jsonminify/-/jsonminify-0.4.1.tgz", - "integrity": "sha1-gF2vuzk5UYjO6atYLIHvlZ1+cQw=" - }, - "jsonschema": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.2.4.tgz", - "integrity": "sha512-lz1nOH69GbsVHeVgEdvyavc/33oymY1AZwtePMiMj4HZPMbP5OIKK3zT9INMWjwua/V4Z4yq7wSlBbSG+g4AEw==" - }, - "jsonschema-draft4": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/jsonschema-draft4/-/jsonschema-draft4-1.0.0.tgz", - "integrity": "sha1-8K8gBQVPDwrefqIRhhS2ncUS2GU=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "just-extend": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.1.1.tgz", - "integrity": "sha512-aWgeGFW67BP3e5181Ep1Fv2v8z//iBJfrvyTnq8wG86vEESwmonn1zPBJ0VfmT9CJq2FIT0VsETtrNFm2a+SHA==" - }, - "languages4translatewiki": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/languages4translatewiki/-/languages4translatewiki-0.1.3.tgz", - "integrity": "sha1-xDYgbgUtIUkLEQF6RNURj5Ih5ds=" - }, - "lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", - "requires": { - "readable-stream": "^2.0.5" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" - }, - "lodash.assignin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" - }, - "lodash.bind": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", - "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" - }, - "lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=" - }, - "lodash.filter": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=" - }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" - }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=" - }, - "lodash.foreach": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" - }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" - }, - "lodash.reduce": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" - }, - "lodash.reject": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", - "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=" - }, - "lodash.some": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" - }, - "lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=" - }, - "log4js": { - "version": "0.6.35", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.35.tgz", - "integrity": "sha1-OrHafLFII7dO04ZcSFk6zfEfG1k=", - "requires": { - "readable-stream": "~1.0.2", - "semver": "~4.3.3" - }, - "dependencies": { - "readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "semver": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=" - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } - } - }, - "measured-core": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/measured-core/-/measured-core-1.11.2.tgz", - "integrity": "sha1-nb6m0gdBtW9hq9hm5Jbri4Xmk0k=", - "requires": { - "binary-search": "^1.3.3", - "optional-js": "^2.0.0" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { - "mime-db": "1.44.0" - } - }, - "mocha": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz", - "integrity": "sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==", - "requires": { - "debug": "3.2.6", - "escape-string-regexp": "1.0.5", - "js-yaml": "3.13.1", - "minimatch": "3.0.4", - "mkdirp": "0.5.5", - "ms": "2.1.1", - "supports-color": "6.0.0", - "wide-align": "1.1.3" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==" - } - } - }, - "mocha-froth": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/mocha-froth/-/mocha-froth-0.2.10.tgz", - "integrity": "sha512-xyJqAYtm2zjrkG870hjeSVvGgS4Dc9tRokmN6R7XLgBKhdtAJ1ytU6zL045djblfHaPyTkSerQU4wqcjsv7Aew==" - }, - "mock-json-schema": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/mock-json-schema/-/mock-json-schema-1.0.8.tgz", - "integrity": "sha512-22yL+WggSo8HXqw0HkXgXXJjJMSBCfv54htfwN4BabaFdJ3808jL0CzE+VaBRlj8Nr0+pnSVE9YvsDG5Quu6hQ==", - "requires": { - "lodash": "^4.17.11", - "openapi-types": "^1.3.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "nise": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/nise/-/nise-4.0.4.tgz", - "integrity": "sha512-bTTRUNlemx6deJa+ZyoCUTRvH3liK5+N6VQZ4NIw90AgDXY6iPnsqplNFf6STcj+ePk0H/xqxnP75Lr0J0Fq3A==", - "requires": { - "@sinonjs/commons": "^1.7.0", - "@sinonjs/fake-timers": "^6.0.0", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" - }, - "dependencies": { - "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "requires": { - "isarray": "0.0.1" - } - } - } - }, - "node-preload": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", - "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", - "requires": { - "process-on-spawn": "^1.0.0" - } - }, - "nodeify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/nodeify/-/nodeify-1.0.1.tgz", - "integrity": "sha1-ZKtpp7268DzhB7TwM1yHwLnpGx0=", - "requires": { - "is-promise": "~1.0.0", - "promise": "~1.3.0" - } - }, - "npm": { - "version": "6.14.8", - "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.8.tgz", - "integrity": "sha512-HBZVBMYs5blsj94GTeQZel7s9odVuuSUHy1+AlZh7rPVux1os2ashvEGLy/STNK7vUjbrCg5Kq9/GXisJgdf6A==", - "requires": { - "JSONStream": "^1.3.5", - "abbrev": "~1.1.1", - "ansicolors": "~0.3.2", - "ansistyles": "~0.1.3", - "aproba": "^2.0.0", - "archy": "~1.0.0", - "bin-links": "^1.1.8", - "bluebird": "^3.5.5", - "byte-size": "^5.0.1", - "cacache": "^12.0.3", - "call-limit": "^1.1.1", - "chownr": "^1.1.4", - "ci-info": "^2.0.0", - "cli-columns": "^3.1.2", - "cli-table3": "^0.5.1", - "cmd-shim": "^3.0.3", - "columnify": "~1.5.4", - "config-chain": "^1.1.12", - "debuglog": "*", - "detect-indent": "~5.0.0", - "detect-newline": "^2.1.0", - "dezalgo": "~1.0.3", - "editor": "~1.0.0", - "figgy-pudding": "^3.5.1", - "find-npm-prefix": "^1.0.2", - "fs-vacuum": "~1.2.10", - "fs-write-stream-atomic": "~1.0.10", - "gentle-fs": "^2.3.1", - "glob": "^7.1.6", - "graceful-fs": "^4.2.4", - "has-unicode": "~2.0.1", - "hosted-git-info": "^2.8.8", - "iferr": "^1.0.2", - "imurmurhash": "*", - "infer-owner": "^1.0.4", - "inflight": "~1.0.6", - "inherits": "^2.0.4", - "ini": "^1.3.5", - "init-package-json": "^1.10.3", - "is-cidr": "^3.0.0", - "json-parse-better-errors": "^1.0.2", - "lazy-property": "~1.0.0", - "libcipm": "^4.0.8", - "libnpm": "^3.0.1", - "libnpmaccess": "^3.0.2", - "libnpmhook": "^5.0.3", - "libnpmorg": "^1.0.1", - "libnpmsearch": "^2.0.2", - "libnpmteam": "^1.0.2", - "libnpx": "^10.2.4", - "lock-verify": "^2.1.0", - "lockfile": "^1.0.4", - "lodash._baseindexof": "*", - "lodash._baseuniq": "~4.6.0", - "lodash._bindcallback": "*", - "lodash._cacheindexof": "*", - "lodash._createcache": "*", - "lodash._getnative": "*", - "lodash.clonedeep": "~4.5.0", - "lodash.restparam": "*", - "lodash.union": "~4.6.0", - "lodash.uniq": "~4.5.0", - "lodash.without": "~4.4.0", - "lru-cache": "^5.1.1", - "meant": "^1.0.2", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.5", - "move-concurrently": "^1.0.1", - "node-gyp": "^5.1.0", - "nopt": "^4.0.3", - "normalize-package-data": "^2.5.0", - "npm-audit-report": "^1.3.3", - "npm-cache-filename": "~1.0.2", - "npm-install-checks": "^3.0.2", - "npm-lifecycle": "^3.1.5", - "npm-package-arg": "^6.1.1", - "npm-packlist": "^1.4.8", - "npm-pick-manifest": "^3.0.2", - "npm-profile": "^4.0.4", - "npm-registry-fetch": "^4.0.7", - "npm-user-validate": "~1.0.0", - "npmlog": "~4.1.2", - "once": "~1.4.0", - "opener": "^1.5.1", - "osenv": "^0.1.5", - "pacote": "^9.5.12", - "path-is-inside": "~1.0.2", - "promise-inflight": "~1.0.1", - "qrcode-terminal": "^0.12.0", - "query-string": "^6.8.2", - "qw": "~1.0.1", - "read": "~1.0.7", - "read-cmd-shim": "^1.0.5", - "read-installed": "~4.0.3", - "read-package-json": "^2.1.1", - "read-package-tree": "^5.3.1", - "readable-stream": "^3.6.0", - "readdir-scoped-modules": "^1.1.0", - "request": "^2.88.0", - "retry": "^0.12.0", - "rimraf": "^2.7.1", - "safe-buffer": "^5.1.2", - "semver": "^5.7.1", - "sha": "^3.0.0", - "slide": "~1.1.6", - "sorted-object": "~2.0.1", - "sorted-union-stream": "~2.1.3", - "ssri": "^6.0.1", - "stringify-package": "^1.0.1", - "tar": "^4.4.13", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", - "uid-number": "0.0.6", - "umask": "~1.1.0", - "unique-filename": "^1.1.1", - "unpipe": "~1.0.0", - "update-notifier": "^2.5.0", - "uuid": "^3.3.3", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "~3.0.0", - "which": "^1.3.1", - "worker-farm": "^1.7.0", - "write-file-atomic": "^2.4.3" - }, - "dependencies": { - "JSONStream": { - "version": "1.3.5", - "resolved": false, - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "abbrev": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "agent-base": { - "version": "4.3.0", - "resolved": false, - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "agentkeepalive": { - "version": "3.5.2", - "resolved": false, - "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", - "requires": { - "humanize-ms": "^1.2.1" - } - }, - "ajv": { - "version": "5.5.2", - "resolved": false, - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "ansi-align": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", - "requires": { - "string-width": "^2.0.0" - } - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": false, - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "ansicolors": { - "version": "0.3.2", - "resolved": false, - "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=" - }, - "ansistyles": { - "version": "0.1.3", - "resolved": false, - "integrity": "sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk=" - }, - "aproba": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "archy": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" - }, - "are-we-there-yet": { - "version": "1.1.4", - "resolved": false, - "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "asap": { - "version": "2.0.6", - "resolved": false, - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" - }, - "asn1": { - "version": "0.2.4", - "resolved": false, - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "asynckit": { - "version": "0.4.0", - "resolved": false, - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": false, - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.8.0", - "resolved": false, - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "optional": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "bin-links": { - "version": "1.1.8", - "resolved": false, - "integrity": "sha512-KgmVfx+QqggqP9dA3iIc5pA4T1qEEEL+hOhOhNPaUm77OTrJoOXE/C05SJLNJe6m/2wUK7F1tDSou7n5TfCDzQ==", - "requires": { - "bluebird": "^3.5.3", - "cmd-shim": "^3.0.0", - "gentle-fs": "^2.3.0", - "graceful-fs": "^4.1.15", - "npm-normalize-package-bin": "^1.0.0", - "write-file-atomic": "^2.3.0" - } - }, - "bluebird": { - "version": "3.5.5", - "resolved": false, - "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==" - }, - "boxen": { - "version": "1.3.0", - "resolved": false, - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", - "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": false, - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "buffer-from": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==" - }, - "builtins": { - "version": "1.0.3", - "resolved": false, - "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=" - }, - "byline": { - "version": "5.0.0", - "resolved": false, - "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=" - }, - "byte-size": { - "version": "5.0.1", - "resolved": false, - "integrity": "sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw==" - }, - "cacache": { - "version": "12.0.3", - "resolved": false, - "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "call-limit": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-5twvci5b9eRBw2wCfPtN0GmlR2/gadZqyFpPhOK6CvMFoFgA+USnZ6Jpu1lhG9h85pQ3Ouil3PfXWRD4EUaRiQ==" - }, - "camelcase": { - "version": "4.1.0", - "resolved": false, - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" - }, - "capture-stack-trace": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=" - }, - "caseless": { - "version": "0.12.0", - "resolved": false, - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "chalk": { - "version": "2.4.1", - "resolved": false, - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": false, - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "ci-info": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, - "cidr-regex": { - "version": "2.0.10", - "resolved": false, - "integrity": "sha512-sB3ogMQXWvreNPbJUZMRApxuRYd+KoIo4RGQ81VatjmMW6WJPo+IJZ2846FGItr9VzKo5w7DXzijPLGtSd0N3Q==", - "requires": { - "ip-regex": "^2.1.0" - } - }, - "cli-boxes": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=" - }, - "cli-columns": { - "version": "3.1.2", - "resolved": false, - "integrity": "sha1-ZzLZcpee/CrkRKHwjgj6E5yWoY4=", - "requires": { - "string-width": "^2.0.0", - "strip-ansi": "^3.0.1" - } - }, - "cli-table3": { - "version": "0.5.1", - "resolved": false, - "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", - "requires": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", - "string-width": "^2.1.1" - } - }, - "cliui": { - "version": "5.0.0", - "resolved": false, - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": false, - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "3.1.0", - "resolved": false, - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": false, - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "clone": { - "version": "1.0.4", - "resolved": false, - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" - }, - "cmd-shim": { - "version": "3.0.3", - "resolved": false, - "integrity": "sha512-DtGg+0xiFhQIntSBRzL2fRQBnmtAVwXIDo4Qq46HPpObYquxMaZS4sb82U9nH91qJrlosC1wa9gwr0QyL/HypA==", - "requires": { - "graceful-fs": "^4.1.2", - "mkdirp": "~0.5.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": false, - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" - }, - "code-point-at": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "color-convert": { - "version": "1.9.1", - "resolved": false, - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "requires": { - "color-name": "^1.1.1" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": false, - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "colors": { - "version": "1.3.3", - "resolved": false, - "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", - "optional": true - }, - "columnify": { - "version": "1.5.4", - "resolved": false, - "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=", - "requires": { - "strip-ansi": "^3.0.0", - "wcwidth": "^1.0.0" - } - }, - "combined-stream": { - "version": "1.0.6", - "resolved": false, - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": false, - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "concat-stream": { - "version": "1.6.2", - "resolved": false, - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "config-chain": { - "version": "1.1.12", - "resolved": false, - "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", - "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "configstore": { - "version": "3.1.5", - "resolved": false, - "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==", - "requires": { - "dot-prop": "^4.2.1", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": false, - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "iferr": { - "version": "0.1.5", - "resolved": false, - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" - } - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "create-error-class": { - "version": "3.0.2", - "resolved": false, - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "requires": { - "capture-stack-trace": "^1.0.0" - } - }, - "cross-spawn": { - "version": "5.1.0", - "resolved": false, - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.5", - "resolved": false, - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": false, - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" - } - } - }, - "crypto-random-string": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=" - }, - "cyclist": { - "version": "0.2.2", - "resolved": false, - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=" - }, - "dashdash": { - "version": "1.14.1", - "resolved": false, - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "3.1.0", - "resolved": false, - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "debuglog": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=" - }, - "decamelize": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": false, - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" - }, - "deep-extend": { - "version": "0.6.0", - "resolved": false, - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" - }, - "defaults": { - "version": "1.0.3", - "resolved": false, - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "requires": { - "clone": "^1.0.2" - } - }, - "define-properties": { - "version": "1.1.3", - "resolved": false, - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "requires": { - "object-keys": "^1.0.12" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "delegates": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "detect-indent": { - "version": "5.0.0", - "resolved": false, - "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=" - }, - "detect-newline": { - "version": "2.1.0", - "resolved": false, - "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=" - }, - "dezalgo": { - "version": "1.0.3", - "resolved": false, - "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "dot-prop": { - "version": "4.2.1", - "resolved": false, - "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", - "requires": { - "is-obj": "^1.0.0" - } - }, - "dotenv": { - "version": "5.0.1", - "resolved": false, - "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==" - }, - "duplexer3": { - "version": "0.1.4", - "resolved": false, - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" - }, - "duplexify": { - "version": "3.6.0", - "resolved": false, - "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": false, - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "optional": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "editor": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-YMf4e9YrzGqJT6jM1q+3gjok90I=" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": false, - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "encoding": { - "version": "0.1.12", - "resolved": false, - "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", - "requires": { - "iconv-lite": "~0.4.13" - } - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": false, - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "requires": { - "once": "^1.4.0" - } - }, - "env-paths": { - "version": "2.2.0", - "resolved": false, - "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==" - }, - "err-code": { - "version": "1.1.2", - "resolved": false, - "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=" - }, - "errno": { - "version": "0.1.7", - "resolved": false, - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "requires": { - "prr": "~1.0.1" - } - }, - "es-abstract": { - "version": "1.12.0", - "resolved": false, - "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", - "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-promise": { - "version": "4.2.8", - "resolved": false, - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": false, - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "requires": { - "es6-promise": "^4.0.3" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": false, - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "execa": { - "version": "0.7.0", - "resolved": false, - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": false, - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": false, - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "figgy-pudding": { - "version": "3.5.1", - "resolved": false, - "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==" - }, - "find-npm-prefix": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA==" - }, - "flush-write-stream": { - "version": "1.0.3", - "resolved": false, - "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.4" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": false, - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.2", - "resolved": false, - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "1.0.6", - "mime-types": "^2.1.12" - } - }, - "from2": { - "version": "2.3.0", - "resolved": false, - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": false, - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "requires": { - "minipass": "^2.6.0" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "resolved": false, - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "fs-vacuum": { - "version": "1.2.10", - "resolved": false, - "integrity": "sha1-t2Kb7AekAxolSP35n17PHMizHjY=", - "requires": { - "graceful-fs": "^4.1.2", - "path-is-inside": "^1.0.1", - "rimraf": "^2.5.2" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": false, - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - }, - "dependencies": { - "iferr": { - "version": "0.1.5", - "resolved": false, - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" - }, - "readable-stream": { - "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "function-bind": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "gauge": { - "version": "2.7.4", - "resolved": false, - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "string-width": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "genfun": { - "version": "5.0.0", - "resolved": false, - "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==" - }, - "gentle-fs": { - "version": "2.3.1", - "resolved": false, - "integrity": "sha512-OlwBBwqCFPcjm33rF2BjW+Pr6/ll2741l+xooiwTCeaX2CA1ZuclavyMBe0/KlR21/XGsgY6hzEQZ15BdNa13Q==", - "requires": { - "aproba": "^1.1.2", - "chownr": "^1.1.2", - "cmd-shim": "^3.0.3", - "fs-vacuum": "^1.2.10", - "graceful-fs": "^4.1.11", - "iferr": "^0.1.5", - "infer-owner": "^1.0.4", - "mkdirp": "^0.5.1", - "path-is-inside": "^1.0.2", - "read-cmd-shim": "^1.0.1", - "slide": "^1.1.6" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - }, - "iferr": { - "version": "0.1.5", - "resolved": false, - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" - } - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": false, - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" - }, - "get-stream": { - "version": "4.1.0", - "resolved": false, - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": false, - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": false, - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "global-dirs": { - "version": "0.1.1", - "resolved": false, - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", - "requires": { - "ini": "^1.3.4" - } - }, - "got": { - "version": "6.7.1", - "resolved": false, - "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", - "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" - } - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": false, - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" - }, - "har-schema": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.0", - "resolved": false, - "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", - "requires": { - "ajv": "^5.3.0", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": false, - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "has-symbols": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" - }, - "has-unicode": { - "version": "2.0.1", - "resolved": false, - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" - }, - "hosted-git-info": { - "version": "2.8.8", - "resolved": false, - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==" - }, - "http-cache-semantics": { - "version": "3.8.1", - "resolved": false, - "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==" - }, - "http-proxy-agent": { - "version": "2.1.0", - "resolved": false, - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", - "requires": { - "agent-base": "4", - "debug": "3.1.0" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": false, - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - }, - "humanize-ms": { - "version": "1.2.1", - "resolved": false, - "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", - "requires": { - "ms": "^2.0.0" - } - }, - "iconv-lite": { - "version": "0.4.23", - "resolved": false, - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "iferr": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha512-9AfeLfji44r5TKInjhz3W9DyZI1zR1JAf2hVBMGhddAKPqBsupb89jGfbCTHIGZd6fGZl9WlHdn4AObygyMKwg==" - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": false, - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "requires": { - "minimatch": "^3.0.4" - } - }, - "import-lazy": { - "version": "2.1.0", - "resolved": false, - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": false, - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, - "infer-owner": { - "version": "1.0.4", - "resolved": false, - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" - }, - "inflight": { - "version": "1.0.6", - "resolved": false, - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": false, - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.5", - "resolved": false, - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, - "init-package-json": { - "version": "1.10.3", - "resolved": false, - "integrity": "sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw==", - "requires": { - "glob": "^7.1.1", - "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", - "promzard": "^0.3.0", - "read": "~1.0.1", - "read-package-json": "1 || 2", - "semver": "2.x || 3.x || 4 || 5", - "validate-npm-package-license": "^3.0.1", - "validate-npm-package-name": "^3.0.0" - } - }, - "ip": { - "version": "1.1.5", - "resolved": false, - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" - }, - "ip-regex": { - "version": "2.1.0", - "resolved": false, - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" - }, - "is-callable": { - "version": "1.1.4", - "resolved": false, - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" - }, - "is-ci": { - "version": "1.2.1", - "resolved": false, - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", - "requires": { - "ci-info": "^1.5.0" - }, - "dependencies": { - "ci-info": { - "version": "1.6.0", - "resolved": false, - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==" - } - } - }, - "is-cidr": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-8Xnnbjsb0x462VoYiGlhEi+drY8SFwrHiSYuzc/CEwco55vkehTaxAyIjEdpi3EMvLPPJAJi9FlzP+h+03gp0Q==", - "requires": { - "cidr-regex": "^2.0.10" - } - }, - "is-date-object": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-installed-globally": { - "version": "0.1.0", - "resolved": false, - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", - "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" - } - }, - "is-npm": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=" - }, - "is-obj": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-redirect": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=" - }, - "is-regex": { - "version": "1.0.4", - "resolved": false, - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "requires": { - "has": "^1.0.1" - } - }, - "is-retry-allowed": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" - }, - "is-stream": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-symbol": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "requires": { - "has-symbols": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isstream": { - "version": "0.1.2", - "resolved": false, - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "jsbn": { - "version": "0.1.1", - "resolved": false, - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "optional": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "json-schema": { - "version": "0.2.3", - "resolved": false, - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": false, - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": false, - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "jsonparse": { - "version": "1.3.1", - "resolved": false, - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": false, - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "latest-version": { - "version": "3.1.0", - "resolved": false, - "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", - "requires": { - "package-json": "^4.0.0" - } - }, - "lazy-property": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-hN3Es3Bnm6i9TNz6TAa0PVcREUc=" - }, - "libcipm": { - "version": "4.0.8", - "resolved": false, - "integrity": "sha512-IN3hh2yDJQtZZ5paSV4fbvJg4aHxCCg5tcZID/dSVlTuUiWktsgaldVljJv6Z5OUlYspx6xQkbR0efNodnIrOA==", - "requires": { - "bin-links": "^1.1.2", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.5.1", - "find-npm-prefix": "^1.0.2", - "graceful-fs": "^4.1.11", - "ini": "^1.3.5", - "lock-verify": "^2.1.0", - "mkdirp": "^0.5.1", - "npm-lifecycle": "^3.0.0", - "npm-logical-tree": "^1.2.1", - "npm-package-arg": "^6.1.0", - "pacote": "^9.1.0", - "read-package-json": "^2.0.13", - "rimraf": "^2.6.2", - "worker-farm": "^1.6.0" - } - }, - "libnpm": { - "version": "3.0.1", - "resolved": false, - "integrity": "sha512-d7jU5ZcMiTfBqTUJVZ3xid44fE5ERBm9vBnmhp2ECD2Ls+FNXWxHSkO7gtvrnbLO78gwPdNPz1HpsF3W4rjkBQ==", - "requires": { - "bin-links": "^1.1.2", - "bluebird": "^3.5.3", - "find-npm-prefix": "^1.0.2", - "libnpmaccess": "^3.0.2", - "libnpmconfig": "^1.2.1", - "libnpmhook": "^5.0.3", - "libnpmorg": "^1.0.1", - "libnpmpublish": "^1.1.2", - "libnpmsearch": "^2.0.2", - "libnpmteam": "^1.0.2", - "lock-verify": "^2.0.2", - "npm-lifecycle": "^3.0.0", - "npm-logical-tree": "^1.2.1", - "npm-package-arg": "^6.1.0", - "npm-profile": "^4.0.2", - "npm-registry-fetch": "^4.0.0", - "npmlog": "^4.1.2", - "pacote": "^9.5.3", - "read-package-json": "^2.0.13", - "stringify-package": "^1.0.0" - } - }, - "libnpmaccess": { - "version": "3.0.2", - "resolved": false, - "integrity": "sha512-01512AK7MqByrI2mfC7h5j8N9V4I7MHJuk9buo8Gv+5QgThpOgpjB7sQBDDkeZqRteFb1QM/6YNdHfG7cDvfAQ==", - "requires": { - "aproba": "^2.0.0", - "get-stream": "^4.0.0", - "npm-package-arg": "^6.1.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpmconfig": { - "version": "1.2.1", - "resolved": false, - "integrity": "sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA==", - "requires": { - "figgy-pudding": "^3.5.1", - "find-up": "^3.0.0", - "ini": "^1.3.5" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": false, - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": false, - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - } - } - }, - "libnpmhook": { - "version": "5.0.3", - "resolved": false, - "integrity": "sha512-UdNLMuefVZra/wbnBXECZPefHMGsVDTq5zaM/LgKNE9Keyl5YXQTnGAzEo+nFOpdRqTWI9LYi4ApqF9uVCCtuA==", - "requires": { - "aproba": "^2.0.0", - "figgy-pudding": "^3.4.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpmorg": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha512-0sRUXLh+PLBgZmARvthhYXQAWn0fOsa6T5l3JSe2n9vKG/lCVK4nuG7pDsa7uMq+uTt2epdPK+a2g6btcY11Ww==", - "requires": { - "aproba": "^2.0.0", - "figgy-pudding": "^3.4.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpmpublish": { - "version": "1.1.2", - "resolved": false, - "integrity": "sha512-2yIwaXrhTTcF7bkJKIKmaCV9wZOALf/gsTDxVSu/Gu/6wiG3fA8ce8YKstiWKTxSFNC0R7isPUb6tXTVFZHt2g==", - "requires": { - "aproba": "^2.0.0", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.0.0", - "lodash.clonedeep": "^4.5.0", - "normalize-package-data": "^2.4.0", - "npm-package-arg": "^6.1.0", - "npm-registry-fetch": "^4.0.0", - "semver": "^5.5.1", - "ssri": "^6.0.1" - } - }, - "libnpmsearch": { - "version": "2.0.2", - "resolved": false, - "integrity": "sha512-VTBbV55Q6fRzTdzziYCr64+f8AopQ1YZ+BdPOv16UegIEaE8C0Kch01wo4s3kRTFV64P121WZJwgmBwrq68zYg==", - "requires": { - "figgy-pudding": "^3.5.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpmteam": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha512-p420vM28Us04NAcg1rzgGW63LMM6rwe+6rtZpfDxCcXxM0zUTLl7nPFEnRF3JfFBF5skF/yuZDUthTsHgde8QA==", - "requires": { - "aproba": "^2.0.0", - "figgy-pudding": "^3.4.1", - "get-stream": "^4.0.0", - "npm-registry-fetch": "^4.0.0" - } - }, - "libnpx": { - "version": "10.2.4", - "resolved": false, - "integrity": "sha512-BPc0D1cOjBeS8VIBKUu5F80s6njm0wbVt7CsGMrIcJ+SI7pi7V0uVPGpEMH9H5L8csOcclTxAXFE2VAsJXUhfA==", - "requires": { - "dotenv": "^5.0.1", - "npm-package-arg": "^6.0.0", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.0", - "update-notifier": "^2.3.0", - "which": "^1.3.0", - "y18n": "^4.0.0", - "yargs": "^14.2.3" - } - }, - "lock-verify": { - "version": "2.1.0", - "resolved": false, - "integrity": "sha512-vcLpxnGvrqisKvLQ2C2v0/u7LVly17ak2YSgoK4PrdsYBXQIax19vhKiLfvKNFx7FRrpTnitrpzF/uuCMuorIg==", - "requires": { - "npm-package-arg": "^6.1.0", - "semver": "^5.4.1" - } - }, - "lockfile": { - "version": "1.0.4", - "resolved": false, - "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==", - "requires": { - "signal-exit": "^3.0.2" - } - }, - "lodash._baseindexof": { - "version": "3.1.0", - "resolved": false, - "integrity": "sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=" - }, - "lodash._baseuniq": { - "version": "4.6.0", - "resolved": false, - "integrity": "sha1-DrtE5FaBSveQXGIS+iybLVG4Qeg=", - "requires": { - "lodash._createset": "~4.0.0", - "lodash._root": "~3.0.0" - } - }, - "lodash._bindcallback": { - "version": "3.0.1", - "resolved": false, - "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=" - }, - "lodash._cacheindexof": { - "version": "3.0.2", - "resolved": false, - "integrity": "sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=" - }, - "lodash._createcache": { - "version": "3.1.2", - "resolved": false, - "integrity": "sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=", - "requires": { - "lodash._getnative": "^3.0.0" - } - }, - "lodash._createset": { - "version": "4.0.3", - "resolved": false, - "integrity": "sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=" - }, - "lodash._getnative": { - "version": "3.9.1", - "resolved": false, - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" - }, - "lodash._root": { - "version": "3.0.1", - "resolved": false, - "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=" - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": false, - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, - "lodash.restparam": { - "version": "3.6.1", - "resolved": false, - "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=" - }, - "lodash.union": { - "version": "4.6.0", - "resolved": false, - "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=" - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": false, - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" - }, - "lodash.without": { - "version": "4.4.0", - "resolved": false, - "integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=" - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" - }, - "lru-cache": { - "version": "5.1.1", - "resolved": false, - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "1.3.0", - "resolved": false, - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "requires": { - "pify": "^3.0.0" - } - }, - "make-fetch-happen": { - "version": "5.0.2", - "resolved": false, - "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==", - "requires": { - "agentkeepalive": "^3.4.1", - "cacache": "^12.0.0", - "http-cache-semantics": "^3.8.1", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "node-fetch-npm": "^2.0.2", - "promise-retry": "^1.1.1", - "socks-proxy-agent": "^4.0.0", - "ssri": "^6.0.0" - } - }, - "meant": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha512-KN+1uowN/NK+sT/Lzx7WSGIj2u+3xe5n2LbwObfjOhPZiA+cCfCm6idVl0RkEfjThkw5XJ96CyRcanq6GmKtUg==" - }, - "mime-db": { - "version": "1.35.0", - "resolved": false, - "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==" - }, - "mime-types": { - "version": "2.1.19", - "resolved": false, - "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", - "requires": { - "mime-db": "~1.35.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": false, - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": false, - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "minizlib": { - "version": "1.3.3", - "resolved": false, - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "requires": { - "minipass": "^2.9.0" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "resolved": false, - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "mississippi": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": false, - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": false, - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - } - } - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - } - } - }, - "ms": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "mute-stream": { - "version": "0.0.7", - "resolved": false, - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" - }, - "node-fetch-npm": { - "version": "2.0.2", - "resolved": false, - "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", - "requires": { - "encoding": "^0.1.11", - "json-parse-better-errors": "^1.0.0", - "safe-buffer": "^5.1.1" - } - }, - "node-gyp": { - "version": "5.1.0", - "resolved": false, - "integrity": "sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw==", - "requires": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.2", - "mkdirp": "^0.5.1", - "nopt": "^4.0.1", - "npmlog": "^4.1.2", - "request": "^2.88.0", - "rimraf": "^2.6.3", - "semver": "^5.7.1", - "tar": "^4.4.12", - "which": "^1.3.1" - } - }, - "nopt": { - "version": "4.0.3", - "resolved": false, - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": false, - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "resolve": { - "version": "1.10.0", - "resolved": false, - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", - "requires": { - "path-parse": "^1.0.6" - } - } - } - }, - "npm-audit-report": { - "version": "1.3.3", - "resolved": false, - "integrity": "sha512-8nH/JjsFfAWMvn474HB9mpmMjrnKb1Hx/oTAdjv4PT9iZBvBxiZ+wtDUapHCJwLqYGQVPaAfs+vL5+5k9QndXw==", - "requires": { - "cli-table3": "^0.5.0", - "console-control-strings": "^1.1.0" - } - }, - "npm-bundled": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-cache-filename": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-3tMGxbC/yHCp6fr4I7xfKD4FrhE=" - }, - "npm-install-checks": { - "version": "3.0.2", - "resolved": false, - "integrity": "sha512-E4kzkyZDIWoin6uT5howP8VDvkM+E8IQDcHAycaAxMbwkqhIg5eEYALnXOl3Hq9MrkdQB/2/g1xwBINXdKSRkg==", - "requires": { - "semver": "^2.3.0 || 3.x || 4 || 5" - } - }, - "npm-lifecycle": { - "version": "3.1.5", - "resolved": false, - "integrity": "sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g==", - "requires": { - "byline": "^5.0.0", - "graceful-fs": "^4.1.15", - "node-gyp": "^5.0.2", - "resolve-from": "^4.0.0", - "slide": "^1.1.6", - "uid-number": "0.0.6", - "umask": "^1.1.0", - "which": "^1.3.1" - } - }, - "npm-logical-tree": { - "version": "1.2.1", - "resolved": false, - "integrity": "sha512-AJI/qxDB2PWI4LG1CYN579AY1vCiNyWfkiquCsJWqntRu/WwimVrC8yXeILBFHDwxfOejxewlmnvW9XXjMlYIg==" - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "npm-package-arg": { - "version": "6.1.1", - "resolved": false, - "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", - "requires": { - "hosted-git-info": "^2.7.1", - "osenv": "^0.1.5", - "semver": "^5.6.0", - "validate-npm-package-name": "^3.0.0" - } - }, - "npm-packlist": { - "version": "1.4.8", - "resolved": false, - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-pick-manifest": { - "version": "3.0.2", - "resolved": false, - "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==", - "requires": { - "figgy-pudding": "^3.5.1", - "npm-package-arg": "^6.0.0", - "semver": "^5.4.1" - } - }, - "npm-profile": { - "version": "4.0.4", - "resolved": false, - "integrity": "sha512-Ta8xq8TLMpqssF0H60BXS1A90iMoM6GeKwsmravJ6wYjWwSzcYBTdyWa3DZCYqPutacBMEm7cxiOkiIeCUAHDQ==", - "requires": { - "aproba": "^1.1.2 || 2", - "figgy-pudding": "^3.4.1", - "npm-registry-fetch": "^4.0.0" - } - }, - "npm-registry-fetch": { - "version": "4.0.7", - "resolved": false, - "integrity": "sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ==", - "requires": { - "JSONStream": "^1.3.4", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.4.1", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^5.0.0", - "npm-package-arg": "^6.1.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": false, - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": false, - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "requires": { - "path-key": "^2.0.0" - } - }, - "npm-user-validate": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-jOyg9c6gTU6TUZ73LQVXp1Ei6VE=" - }, - "npmlog": { - "version": "4.1.2", - "resolved": false, - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": false, - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": false, - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-keys": { - "version": "1.0.12", - "resolved": false, - "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==" - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": false, - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": false, - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "opener": { - "version": "1.5.1", - "resolved": false, - "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==" - }, - "os-homedir": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": false, - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "package-json": { - "version": "4.0.1", - "resolved": false, - "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", - "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" - } - }, - "pacote": { - "version": "9.5.12", - "resolved": false, - "integrity": "sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ==", - "requires": { - "bluebird": "^3.5.3", - "cacache": "^12.0.2", - "chownr": "^1.1.2", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.1.0", - "glob": "^7.1.3", - "infer-owner": "^1.0.4", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^5.0.0", - "minimatch": "^3.0.4", - "minipass": "^2.3.5", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "normalize-package-data": "^2.4.0", - "npm-normalize-package-bin": "^1.0.0", - "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.1.12", - "npm-pick-manifest": "^3.0.0", - "npm-registry-fetch": "^4.0.0", - "osenv": "^0.1.5", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "protoduck": "^5.0.1", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.2", - "semver": "^5.6.0", - "ssri": "^6.0.1", - "tar": "^4.4.10", - "unique-filename": "^1.1.1", - "which": "^1.3.1" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "resolved": false, - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "parallel-transform": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", - "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" - }, - "path-key": { - "version": "2.0.1", - "resolved": false, - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "path-parse": { - "version": "1.0.6", - "resolved": false, - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" - }, - "performance-now": { - "version": "2.1.0", - "resolved": false, - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "pify": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - }, - "prepend-http": { - "version": "1.0.4", - "resolved": false, - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" - }, - "promise-retry": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", - "requires": { - "err-code": "^1.0.0", - "retry": "^0.10.0" - }, - "dependencies": { - "retry": { - "version": "0.10.1", - "resolved": false, - "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=" - } - } - }, - "promzard": { - "version": "0.3.0", - "resolved": false, - "integrity": "sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=", - "requires": { - "read": "1" - } - }, - "proto-list": { - "version": "1.2.4", - "resolved": false, - "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=" - }, - "protoduck": { - "version": "5.0.1", - "resolved": false, - "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", - "requires": { - "genfun": "^5.0.0" - } - }, - "prr": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" - }, - "pseudomap": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, - "psl": { - "version": "1.1.29", - "resolved": false, - "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" - }, - "pump": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": false, - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "resolved": false, - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "punycode": { - "version": "1.4.1", - "resolved": false, - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - }, - "qrcode-terminal": { - "version": "0.12.0", - "resolved": false, - "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==" - }, - "qs": { - "version": "6.5.2", - "resolved": false, - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "query-string": { - "version": "6.8.2", - "resolved": false, - "integrity": "sha512-J3Qi8XZJXh93t2FiKyd/7Ec6GNifsjKXUsVFkSBj/kjLsDylWhnCz4NT1bkPcKotttPW+QbKGqqPH8OoI2pdqw==", - "requires": { - "decode-uri-component": "^0.2.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "qw": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-77/cdA+a0FQwRCassYNBLMi5ltQ=" - }, - "rc": { - "version": "1.2.8", - "resolved": false, - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - } - }, - "read": { - "version": "1.0.7", - "resolved": false, - "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", - "requires": { - "mute-stream": "~0.0.4" - } - }, - "read-cmd-shim": { - "version": "1.0.5", - "resolved": false, - "integrity": "sha512-v5yCqQ/7okKoZZkBQUAfTsQ3sVJtXdNfbPnI5cceppoxEVLYA3k+VtV2omkeo8MS94JCy4fSiUwlRBAwCVRPUA==", - "requires": { - "graceful-fs": "^4.1.2" - } - }, - "read-installed": { - "version": "4.0.3", - "resolved": false, - "integrity": "sha1-/5uLZ/GH0eTCm5/rMfayI6zRkGc=", - "requires": { - "debuglog": "^1.0.1", - "graceful-fs": "^4.1.2", - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "slide": "~1.1.3", - "util-extend": "^1.0.1" - } - }, - "read-package-json": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==", - "requires": { - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "json-parse-better-errors": "^1.0.1", - "normalize-package-data": "^2.0.0", - "npm-normalize-package-bin": "^1.0.0" - } - }, - "read-package-tree": { - "version": "5.3.1", - "resolved": false, - "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", - "requires": { - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "util-promisify": "^2.1.0" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": false, - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdir-scoped-modules": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", - "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" - } - }, - "registry-auth-token": { - "version": "3.4.0", - "resolved": false, - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", - "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" - } - }, - "registry-url": { - "version": "3.1.0", - "resolved": false, - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", - "requires": { - "rc": "^1.0.1" - } - }, - "request": { - "version": "2.88.0", - "resolved": false, - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, - "resolve-from": { - "version": "4.0.0", - "resolved": false, - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - }, - "retry": { - "version": "0.12.0", - "resolved": false, - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" - }, - "rimraf": { - "version": "2.7.1", - "resolved": false, - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "requires": { - "glob": "^7.1.3" - } - }, - "run-queue": { - "version": "1.0.3", - "resolved": false, - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "requires": { - "aproba": "^1.1.1" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" - } - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": false, - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": false, - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "semver": { - "version": "5.7.1", - "resolved": false, - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "semver-diff": { - "version": "2.1.0", - "resolved": false, - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", - "requires": { - "semver": "^5.0.3" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "sha": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-DOYnM37cNsLNSGIG/zZWch5CKIRNoLdYUQTQlcgkRkoYIUwDYjqDyye16YcDZg/OPdcbUgTKMjc4SY6TB7ZAPw==", - "requires": { - "graceful-fs": "^4.1.2" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "signal-exit": { - "version": "3.0.2", - "resolved": false, - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, - "slide": { - "version": "1.1.6", - "resolved": false, - "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=" - }, - "smart-buffer": { - "version": "4.1.0", - "resolved": false, - "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==" - }, - "socks": { - "version": "2.3.3", - "resolved": false, - "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", - "requires": { - "ip": "1.1.5", - "smart-buffer": "^4.1.0" - } - }, - "socks-proxy-agent": { - "version": "4.0.2", - "resolved": false, - "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==", - "requires": { - "agent-base": "~4.2.1", - "socks": "~2.3.2" - }, - "dependencies": { - "agent-base": { - "version": "4.2.1", - "resolved": false, - "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", - "requires": { - "es6-promisify": "^5.0.0" - } - } - } - }, - "sorted-object": { - "version": "2.0.1", - "resolved": false, - "integrity": "sha1-fWMfS9OnmKJK8d/8+/6DM3pd9fw=" - }, - "sorted-union-stream": { - "version": "2.1.3", - "resolved": false, - "integrity": "sha1-x3lMfgd4gAUv9xqNSi27Sppjisc=", - "requires": { - "from2": "^1.3.0", - "stream-iterate": "^1.1.0" - }, - "dependencies": { - "from2": { - "version": "1.3.0", - "resolved": false, - "integrity": "sha1-iEE7qqX5pZfP3pIh2GmGzTwGHf0=", - "requires": { - "inherits": "~2.0.1", - "readable-stream": "~1.1.10" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": false, - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": false, - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": false, - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, - "spdx-correct": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.1.0", - "resolved": false, - "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==" - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.5", - "resolved": false, - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" - }, - "split-on-first": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" - }, - "sshpk": { - "version": "1.14.2", - "resolved": false, - "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "ssri": { - "version": "6.0.1", - "resolved": false, - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "stream-each": { - "version": "1.2.2", - "resolved": false, - "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-iterate": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha1-K9fHcpbBcCpGSIuK1B95hl7s1OE=", - "requires": { - "readable-stream": "^2.1.5", - "stream-shift": "^1.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" - }, - "strict-uri-encode": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" - }, - "string-width": { - "version": "2.1.1", - "resolved": false, - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": false, - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": false, - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.0", - "resolved": false, - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" - } - } - }, - "stringify-package": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": false, - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": false, - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "supports-color": { - "version": "5.4.0", - "resolved": false, - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "tar": { - "version": "4.4.13", - "resolved": false, - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - }, - "dependencies": { - "minipass": { - "version": "2.9.0", - "resolved": false, - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - } - } - }, - "term-size": { - "version": "1.2.0", - "resolved": false, - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", - "requires": { - "execa": "^0.7.0" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": false, - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" - }, - "through": { - "version": "2.3.8", - "resolved": false, - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "through2": { - "version": "2.0.3", - "resolved": false, - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", - "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "resolved": false, - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "timed-out": { - "version": "4.0.1", - "resolved": false, - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" - }, - "tiny-relative-date": { - "version": "1.3.0", - "resolved": false, - "integrity": "sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A==" - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": false, - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": false, - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": false, - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "optional": true - }, - "typedarray": { - "version": "0.0.6", - "resolved": false, - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "uid-number": { - "version": "0.0.6", - "resolved": false, - "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=" - }, - "umask": { - "version": "1.1.0", - "resolved": false, - "integrity": "sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=" - }, - "unique-filename": { - "version": "1.1.1", - "resolved": false, - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "unique-string": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", - "requires": { - "crypto-random-string": "^1.0.0" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "unzip-response": { - "version": "2.0.1", - "resolved": false, - "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=" - }, - "update-notifier": { - "version": "2.5.0", - "resolved": false, - "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", - "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": false, - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "requires": { - "prepend-http": "^1.0.1" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "util-extend": { - "version": "1.0.3", - "resolved": false, - "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8=" - }, - "util-promisify": { - "version": "2.1.0", - "resolved": false, - "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=", - "requires": { - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "uuid": { - "version": "3.3.3", - "resolved": false, - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": false, - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "validate-npm-package-name": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", - "requires": { - "builtins": "^1.0.3" - } - }, - "verror": { - "version": "1.10.0", - "resolved": false, - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "wcwidth": { - "version": "1.0.1", - "resolved": false, - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "requires": { - "defaults": "^1.0.3" - } - }, - "which": { - "version": "1.3.1", - "resolved": false, - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, - "wide-align": { - "version": "1.1.2", - "resolved": false, - "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", - "requires": { - "string-width": "^1.0.2" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "widest-line": { - "version": "2.0.1", - "resolved": false, - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", - "requires": { - "string-width": "^2.1.1" - } - }, - "worker-farm": { - "version": "1.7.0", - "resolved": false, - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", - "requires": { - "errno": "~0.1.7" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": false, - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": false, - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "3.1.0", - "resolved": false, - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": false, - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": false, - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "2.4.3", - "resolved": false, - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "xdg-basedir": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=" - }, - "xtend": { - "version": "4.0.1", - "resolved": false, - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" - }, - "y18n": { - "version": "4.0.0", - "resolved": false, - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" - }, - "yallist": { - "version": "3.0.3", - "resolved": false, - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" - }, - "yargs": { - "version": "14.2.3", - "resolved": false, - "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", - "requires": { - "cliui": "^5.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^15.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": false, - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "find-up": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": false, - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "locate-path": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": false, - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": false, - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": false, - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "string-width": { - "version": "3.1.0", - "resolved": false, - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": false, - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "yargs-parser": { - "version": "15.0.1", - "resolved": false, - "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": false, - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - } - } - } - } - }, - "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "requires": { - "boolbase": "~1.0.0" - } - }, - "nyc": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.0.1.tgz", - "integrity": "sha512-n0MBXYBYRqa67IVt62qW1r/d9UH/Qtr7SF1w/nQLJ9KxvWF6b2xCHImRAixHN9tnMMYHC2P14uo6KddNGwMgGg==", - "requires": { - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "caching-transform": "^4.0.0", - "convert-source-map": "^1.7.0", - "find-cache-dir": "^3.2.0", - "find-up": "^4.1.0", - "foreground-child": "^2.0.0", - "glob": "^7.1.6", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-hook": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-processinfo": "^2.0.2", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "make-dir": "^3.0.0", - "node-preload": "^0.2.1", - "p-map": "^3.0.0", - "process-on-spawn": "^1.0.0", - "resolve-from": "^5.0.0", - "signal-exit": "^3.0.2", - "spawn-wrap": "^2.0.0", - "test-exclude": "^6.0.0", - "yargs": "^15.0.2" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==" - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "yargs": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", - "requires": { - "cliui": "^6.0.0", - "find-up": "^4.1.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "yargs-parser": "^18.1.1" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==" - } - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "observable-fns": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/observable-fns/-/observable-fns-0.5.1.tgz", - "integrity": "sha512-wf7g4Jpo1Wt2KIqZKLGeiuLOEMqpaOZ5gJn7DmSdqXgTdxRwSdBhWegQQpPteQ2gZvzCKqNNpwb853wcpA0j7A==" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" - }, - "openapi-backend": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/openapi-backend/-/openapi-backend-2.4.1.tgz", - "integrity": "sha512-48j8QhDD9sfV6t7Zgn9JrfJtCpJ53bmoT2bzXYYig1HhG/Xn0Aa5fJhM0cQSZq9nq78/XbU7RDEa3e+IADNkmA==", - "requires": { - "ajv": "^6.10.0", - "bath-es5": "^3.0.3", - "cookie": "^0.4.0", - "lodash": "^4.17.15", - "mock-json-schema": "^1.0.5", - "openapi-schema-validation": "^0.4.2", - "openapi-types": "^1.3.4", - "qs": "^6.6.0", - "swagger-parser": "^9.0.1" - }, - "dependencies": { - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - } - } - }, - "openapi-schema-validation": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/openapi-schema-validation/-/openapi-schema-validation-0.4.2.tgz", - "integrity": "sha512-K8LqLpkUf2S04p2Nphq9L+3bGFh/kJypxIG2NVGKX0ffzT4NQI9HirhiY6Iurfej9lCu7y4Ndm4tv+lm86Ck7w==", - "requires": { - "jsonschema": "1.2.4", - "jsonschema-draft4": "^1.0.0", - "swagger-schema-official": "2.0.0-bab6bed" - } - }, - "openapi-types": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-1.3.5.tgz", - "integrity": "sha512-11oi4zYorsgvg5yBarZplAqbpev5HkuVNPlZaPTknPDzAynq+lnJdXAmruGWP0s+dNYZS7bjM+xrTpJw7184Fg==" - }, - "optional-js": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/optional-js/-/optional-js-2.1.1.tgz", - "integrity": "sha512-mUS4bDngcD5kKzzRUd1HVQkr9Lzzby3fSrrPR9wOHhQiyYo+hDS5NVli5YQzGjQRQ15k5Sno4xH9pfykJdeEUA==" - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "package-hash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", - "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", - "requires": { - "graceful-fs": "^4.1.15", - "hasha": "^5.0.0", - "lodash.flattendeep": "^4.4.0", - "release-zalgo": "^1.0.0" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "requires": { - "callsites": "^3.0.0" - } - }, - "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==" - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" - } - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" - }, - "process-on-spawn": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", - "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", - "requires": { - "fromentries": "^1.2.0" - } - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" - }, - "promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-1.3.0.tgz", - "integrity": "sha1-5cyaTIJ45GZP/twBx9qEhCsEAXU=", - "requires": { - "is-promise": "~1" - } - }, - "property-information": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.5.0.tgz", - "integrity": "sha512-RgEbCx2HLa1chNgvChcx+rrCWD0ctBmGSE0M7lVm1yyv4UbvbrWoXp/BkVLZefzjrRBGW8/Js6uh/BnlHXFyjA==", - "requires": { - "xtend": "^4.0.0" - } - }, - "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - } - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "ramda": { - "version": "0.27.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", - "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==" - }, - "random-bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "rate-limiter-flexible": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-2.1.4.tgz", - "integrity": "sha512-wtbWcqZbCqyAO1k63moagJlCZuPCEqbJJ6il1y2JVoiUyxlE36+cM7ETta9K6tTom9O5pNK+CxwHMgyyyJ31Gg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "dependencies": { - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - } - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==" - }, - "rehype": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/rehype/-/rehype-10.0.0.tgz", - "integrity": "sha512-0W8M4Y91b2QuzDSTjkZgBOJo79bP089YbSQNPMqebuUVrp6iveoi+Ra6/H7fJwUxq8FCHGCGzkLaq3fvO9XnVg==", - "requires": { - "rehype-parse": "^6.0.0", - "rehype-stringify": "^6.0.0", - "unified": "^9.0.0" - } - }, - "rehype-minify-whitespace": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/rehype-minify-whitespace/-/rehype-minify-whitespace-4.0.5.tgz", - "integrity": "sha512-QC3Z+bZ5wbv+jGYQewpAAYhXhzuH/TVRx7z08rurBmh9AbG8Nu8oJnvs9LWj43Fd/C7UIhXoQ7Wddgt+ThWK5g==", - "requires": { - "hast-util-embedded": "^1.0.0", - "hast-util-is-element": "^1.0.0", - "hast-util-whitespace": "^1.0.4", - "unist-util-is": "^4.0.0" - }, - "dependencies": { - "unist-util-is": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.4.tgz", - "integrity": "sha512-3dF39j/u423v4BBQrk1AQ2Ve1FxY5W3JKwXxVFzBODQ6WEvccguhgp802qQLKSnxPODE6WuRZtV+ohlUg4meBA==" - } - } - }, - "rehype-parse": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-6.0.2.tgz", - "integrity": "sha512-0S3CpvpTAgGmnz8kiCyFLGuW5yA4OQhyNTm/nwPopZ7+PI11WnGl1TTWTGv/2hPEe/g2jRLlhVVSsoDH8waRug==", - "requires": { - "hast-util-from-parse5": "^5.0.0", - "parse5": "^5.0.0", - "xtend": "^4.0.0" - } - }, - "rehype-stringify": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-6.0.1.tgz", - "integrity": "sha512-JfEPRDD4DiG7jet4md7sY07v6ACeb2x+9HWQtRPm2iA6/ic31hCv1SNBUtpolJASxQ/D8gicXiviW4TJKEMPKQ==", - "requires": { - "hast-util-to-html": "^6.0.0", - "xtend": "^4.0.0" - } - }, - "release-zalgo": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", - "requires": { - "es6-error": "^4.0.1" - } - }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - } - } - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" - }, - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "security": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/security/-/security-1.0.0.tgz", - "integrity": "sha1-gRwwAxNoYTPvAAcSXjsO1wCXiBU=" - }, - "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "set-cookie-parser": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.4.6.tgz", - "integrity": "sha512-mNCnTUF0OYPwYzSHbdRdCfNNHqrne+HS5tS5xNb6yJbdP9wInV0q5xPLE0EyfV/Q3tImo3y/OXpD8Jn0Jtnjrg==" - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" - }, - "sinon": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.0.tgz", - "integrity": "sha512-eSNXz1XMcGEMHw08NJXSyTHIu6qTCOiN8x9ODACmZpNQpr0aXTBXBnI4xTzQzR+TEpOmLiKowGf9flCuKIzsbw==", - "requires": { - "@sinonjs/commons": "^1.8.1", - "@sinonjs/fake-timers": "^6.0.1", - "@sinonjs/formatio": "^5.0.1", - "@sinonjs/samsam": "^5.2.0", - "diff": "^4.0.2", - "nise": "^4.0.4", - "supports-color": "^7.1.0" - }, - "dependencies": { - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - } - } - }, - "slide": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", - "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "space-separated-tokens": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", - "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==" - }, - "spawn-wrap": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", - "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", - "requires": { - "foreground-child": "^2.0.0", - "is-windows": "^1.0.2", - "make-dir": "^3.0.0", - "signal-exit": "^3.0.2", - "which": "^2.0.1" - }, - "dependencies": { - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "stringify-entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-2.0.0.tgz", - "integrity": "sha512-fqqhZzXyAM6pGD9lky/GOPq6V4X0SeTAFBl0iXb/BzOegl40gpf/bV3QQP7zULNYvjr6+Dx8SCaDULjVoOru0A==", - "requires": { - "character-entities-html4": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.2", - "is-hexadecimal": "^1.0.0" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" - }, - "superagent": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", - "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", - "requires": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.2.0", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.3.5" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "supertest": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-4.0.2.tgz", - "integrity": "sha512-1BAbvrOZsGA3YTCWqbmh14L0YEq0EGICX/nBnfkfVJn7SrxQV1I3pMYjSzG9y/7ZU2V9dWqyqk2POwxlb09duQ==", - "requires": { - "methods": "^1.1.2", - "superagent": "^3.8.3" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "superagent": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", - "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", - "requires": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.2.0", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.3.5" - } - } - } - }, - "swagger-parser": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-9.0.1.tgz", - "integrity": "sha512-oxOHUaeNetO9ChhTJm2fD+48DbGbLD09ZEOwPOWEqcW8J6zmjWxutXtSuOiXsoRgDWvORYlImbwM21Pn+EiuvQ==", - "requires": { - "@apidevtools/swagger-parser": "9.0.1" - } - }, - "swagger-schema-official": { - "version": "2.0.0-bab6bed", - "resolved": "https://registry.npmjs.org/swagger-schema-official/-/swagger-schema-official-2.0.0-bab6bed.tgz", - "integrity": "sha1-cAcEaNbSl3ylI3suUZyn0Gouo/0=" - }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" - }, - "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", - "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "tar-stream": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz", - "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==", - "requires": { - "bl": "^4.0.1", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, - "terser": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.7.0.tgz", - "integrity": "sha512-Lfb0RiZcjRDXCC3OSHJpEkxJ9Qeqs6mp2v4jf2MHfy8vGERmVDuvjXdd/EnP5Deme5F2yBRBymKmKHCBg2echw==", - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" - }, - "threads": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/threads/-/threads-1.4.1.tgz", - "integrity": "sha512-LSgGCu2lwdrfqjYWmeqO+7fgxAbUtjlsa7UA5J6r4x8fCoMd015h19rMwXqz4/q8l3svdloE36Of41rpZWiYFg==", - "requires": { - "callsites": "^3.1.0", - "debug": "^4.1.1", - "is-observable": "^1.1.0", - "observable-fns": "^0.5.1", - "tiny-worker": ">= 2" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "tiny-worker": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tiny-worker/-/tiny-worker-2.3.0.tgz", - "integrity": "sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g==", - "requires": { - "esm": "^3.2.25" - } - }, - "tinycon": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/tinycon/-/tinycon-0.0.1.tgz", - "integrity": "sha1-beEM1SGaHxIdmgokssEbP7JN/+0=" - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "trough": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", - "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "uid-safe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", - "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", - "requires": { - "random-bytes": "~1.0.0" - } - }, - "underscore": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", - "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" - }, - "unified": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/unified/-/unified-9.0.0.tgz", - "integrity": "sha512-ssFo33gljU3PdlWLjNp15Inqb77d6JnJSfyplGJPT/a+fNRNyCBeveBAYJdO5khKdF6WVHa/yYCC7Xl6BDwZUQ==", - "requires": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" - } - }, - "unist-util-is": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", - "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==" - }, - "unist-util-stringify-position": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", - "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", - "requires": { - "@types/unist": "^2.0.2" - } - }, - "unorm": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.4.1.tgz", - "integrity": "sha1-NkIA1fE2RsqLzURJAnEzVhR5IwA=" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==" - }, - "validator": { - "version": "12.2.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-12.2.0.tgz", - "integrity": "sha512-jJfE/DW6tIK1Ek8nCfNFqt8Wb3nzMoAbocBF6/Icgg1ZFSBpObdnwVY2jQj6qUqzhx5jc71fpvBWyLGO7Xl+nQ==" - }, - "vargs": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/vargs/-/vargs-0.1.0.tgz", - "integrity": "sha1-a2GE2mUgzDIEzhtAfKwm2SYJ6/8=" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "vfile": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.1.1.tgz", - "integrity": "sha512-lRjkpyDGjVlBA7cDQhQ+gNcvB1BGaTHYuSOcY3S7OhDmBtnzX95FhtZZDecSTDm6aajFymyve6S5DN4ZHGezdQ==", - "requires": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "replace-ext": "1.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-message": "^2.0.0" - } - }, - "vfile-message": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", - "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", - "requires": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - } - }, - "wd": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/wd/-/wd-1.12.1.tgz", - "integrity": "sha512-O99X8OnOgkqfmsPyLIRzG9LmZ+rjmdGFBCyhGpnsSL4MB4xzHoeWmSVcumDiQ5QqPZcwGkszTgeJvjk2VjtiNw==", - "requires": { - "archiver": "^3.0.0", - "async": "^2.0.0", - "lodash": "^4.0.0", - "mkdirp": "^0.5.1", - "q": "^1.5.1", - "request": "2.88.0", - "vargs": "^0.1.0" - }, - "dependencies": { - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "requires": { - "lodash": "^4.17.14" - } - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } - } - } - }, - "web-namespaces": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz", - "integrity": "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==" - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" - }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "requires": { - "mkdirp": "^0.5.1" - } - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "z-schema": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-4.2.3.tgz", - "integrity": "sha512-zkvK/9TC6p38IwcrbnT3ul9in1UX4cm1y/VZSs4GHKIiDCrlafc+YQBgQBUdDXLAoZHf2qvQ7gJJOo6yT1LH6A==", - "requires": { - "commander": "^2.7.1", - "lodash.get": "^4.4.2", - "lodash.isequal": "^4.5.0", - "validator": "^12.0.0" - } - }, - "zip-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-2.1.3.tgz", - "integrity": "sha512-EkXc2JGcKhO5N5aZ7TmuNo45budRaFGHOmz24wtJR7znbNqDPmdZtUauKX6et8KAVseAMBOyWJqEpXcHTBsh7Q==", - "requires": { - "archiver-utils": "^2.1.0", - "compress-commons": "^2.1.1", - "readable-stream": "^3.4.0" - } - } - } - }, - "errs": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/errs/-/errs-0.3.2.tgz", - "integrity": "sha1-eYCZstvTfKK8dJ5TinwTB9C1BJk=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "optional": true, - "requires": { - "minipass": "^2.6.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "optional": true, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "optional": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "optional": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" - } - } - }, - "has-binary2": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", - "requires": { - "isarray": "2.0.1" - }, - "dependencies": { - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" - } - } - }, - "has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "optional": true - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", - "requires": { - "ms": "^2.0.0" - } - }, - "iconv-lite": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", - "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "optional": true - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "jsbi": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.1.4.tgz", - "integrity": "sha512-52QRRFSsi9impURE8ZUbzAMCLjPm4THO7H2fcuIvaaeFTbSysvkodbQQXIVsNgq/ypDbq6dJiuGKL0vZ/i9hUg==" - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" - }, - "long": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/long/-/long-2.4.0.tgz", - "integrity": "sha1-n6GAux2VAM3CnEFWdmoZleH0Uk8=" - }, - "memory-pager": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", - "optional": true - }, - "mime-db": { - "version": "1.45.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", - "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==" - }, - "mime-types": { - "version": "2.1.28", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", - "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", - "requires": { - "mime-db": "1.45.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - }, - "dependencies": { - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "optional": true - } - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "optional": true, - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "mongodb": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.3.tgz", - "integrity": "sha512-rOZuR0QkodZiM+UbQE5kDsJykBqWi0CL4Ec2i1nrGrUI3KO11r6Fbxskqmq3JK2NH7aW4dcccBuUujAP0ERl5w==", - "requires": { - "bl": "^2.2.1", - "bson": "^1.1.4", - "denque": "^1.4.1", - "require_optional": "^1.0.1", - "safe-buffer": "^5.1.2", - "saslprep": "^1.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "mssql": { - "version": "7.0.0-beta.2", - "resolved": "https://registry.npmjs.org/mssql/-/mssql-7.0.0-beta.2.tgz", - "integrity": "sha512-7fOp+QzFf24ir/gGeSvyyGlQKfxZj6tx88vsk4UiQw/t/zpJ9PLjOBOoi6Ff+Tw/CZ1aJTa83MPm+CRYJ/UCQA==", - "requires": { - "debug": "^4", - "tarn": "^3.0.1", - "tedious": "^9.2.3" - } - }, - "mysql": { - "version": "2.18.1", - "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", - "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", - "requires": { - "bignumber.js": "9.0.0", - "readable-stream": "2.3.7", - "safe-buffer": "5.1.2", - "sqlstring": "2.3.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "nano": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/nano/-/nano-8.2.3.tgz", - "integrity": "sha512-nubyTQeZ/p+xf3ZFFMd7WrZwpcy9tUDrbaXw9HFBsM6zBY5gXspvOjvG2Zz3emT6nfJtP/h7F2/ESfsVVXnuMw==", - "requires": { - "@types/request": "^2.48.4", - "cloudant-follow": "^0.18.2", - "debug": "^4.1.1", - "errs": "^0.3.2", - "request": "^2.88.0" - } - }, - "native-duplexpair": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz", - "integrity": "sha1-eJkHjmS/PIo9cyYBs9QP8F21j6A=" - }, - "needle": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.6.0.tgz", - "integrity": "sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg==", - "optional": true, - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } - } - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "node-addon-api": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.1.0.tgz", - "integrity": "sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==", - "optional": true - }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - }, - "node-gyp": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", - "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", - "optional": true, - "requires": { - "fstream": "^1.0.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "nopt": "2 || 3", - "npmlog": "0 || 1 || 2 || 3 || 4", - "osenv": "0", - "request": "^2.87.0", - "rimraf": "2", - "semver": "~5.3.0", - "tar": "^2.0.0", - "which": "1" - }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "optional": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "optional": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "node-pre-gyp": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", - "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - }, - "dependencies": { - "nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "optional": true - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "optional": true - } - } - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "optional": true, - "requires": { - "abbrev": "1" - } - }, - "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "optional": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "optional": true - }, - "npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "optional": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "optional": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "packet-reader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", - "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" - }, - "parseqs": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", - "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" - }, - "parseuri": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", - "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "pg": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.5.1.tgz", - "integrity": "sha512-9wm3yX9lCfjvA98ybCyw2pADUivyNWT/yIP4ZcDVpMN0og70BUWYEGXPCTAQdGTAqnytfRADb7NERrY1qxhIqw==", - "requires": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.4.0", - "pg-pool": "^3.2.2", - "pg-protocol": "^1.4.0", - "pg-types": "^2.1.0", - "pgpass": "1.x" - } - }, - "pg-connection-string": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.4.0.tgz", - "integrity": "sha512-3iBXuv7XKvxeMrIgym7njT+HlZkwZqqGX4Bu9cci8xHZNT+Um1gWKqCsAzcC0d95rcKMU5WBg6YRUcHyV0HZKQ==" - }, - "pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" - }, - "pg-pool": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.2.tgz", - "integrity": "sha512-ORJoFxAlmmros8igi608iVEbQNNZlp89diFVx6yV5v+ehmpMY9sK6QgpmgoXbmkNaBAx8cOOZh9g80kJv1ooyA==" - }, - "pg-protocol": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.4.0.tgz", - "integrity": "sha512-El+aXWcwG/8wuFICMQjM5ZSAm6OWiJicFdNYo+VY3QP+8vI4SvLIWVe51PppTzMhikUJR+PsyIFKqfdXPz/yxA==" - }, - "pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "requires": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - } - }, - "pgpass": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz", - "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==", - "requires": { - "split2": "^3.1.1" - } - }, - "postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" - }, - "postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" - }, - "postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" - }, - "postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "requires": { - "xtend": "^4.0.0" - } - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "redis": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/redis/-/redis-3.0.2.tgz", - "integrity": "sha512-PNhLCrjU6vKVuMOyFu7oSP296mwBkcE6lrAjruBYG5LgdSqtRBoVQIylrMyVZD/lkF24RSNNatzvYag6HRBHjQ==", - "requires": { - "denque": "^1.4.1", - "redis-commands": "^1.5.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0" - } - }, - "redis-commands": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.6.0.tgz", - "integrity": "sha512-2jnZ0IkjZxvguITjFTrGiLyzQZcTvaw8DAaCXxZq/dsHXz7KfMQ3OUJy7Tz9vnRtZRVz6VRCPDvruvU8Ts44wQ==" - }, - "redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=" - }, - "redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=", - "requires": { - "redis-errors": "^1.0.0" - } - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - } - } - }, - "require_optional": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", - "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", - "requires": { - "resolve-from": "^2.0.0", - "semver": "^5.1.0" - }, - "dependencies": { - "resolve-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", - "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "rethinkdb": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/rethinkdb/-/rethinkdb-2.4.2.tgz", - "integrity": "sha512-6DzwqEpFc8cqesAdo07a845oBRxLiHvWzopTKBo/uY2ypGWIsJQFJk3wjRDtSEhczxJqLS0jnf37rwgzYAw8NQ==", - "requires": { - "bluebird": ">= 2.3.2 < 3" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "saslprep": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", - "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", - "optional": true, - "requires": { - "sparse-bitfield": "^3.0.3" - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "optional": true - }, - "simple-git": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-2.31.0.tgz", - "integrity": "sha512-/+rmE7dYZMbRAfEmn8EUIOwlM2G7UdzpkC60KF86YAfXGnmGtsPrKsym0hKvLBdFLLW019C+aZld1+6iIVy5xA==", - "requires": { - "@kwsites/file-exists": "^1.1.1", - "@kwsites/promise-deferred": "^1.1.1", - "debug": "^4.3.1" - } - }, - "socket.io": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.1.tgz", - "integrity": "sha512-Si18v0mMXGAqLqCVpTxBa8MGqriHGQh8ccEOhmsmNS3thNCGBwO8WGrwMibANsWtQQ5NStdZwHqZR3naJVFc3w==", - "requires": { - "debug": "~4.1.0", - "engine.io": "~3.5.0", - "has-binary2": "~1.0.2", - "socket.io-adapter": "~1.1.0", - "socket.io-client": "2.4.0", - "socket.io-parser": "~3.4.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "socket.io-adapter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", - "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==" - }, - "socket.io-client": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz", - "integrity": "sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==", - "requires": { - "backo2": "1.0.2", - "component-bind": "1.0.0", - "component-emitter": "~1.3.0", - "debug": "~3.1.0", - "engine.io-client": "~3.5.0", - "has-binary2": "~1.0.2", - "indexof": "0.0.1", - "parseqs": "0.0.6", - "parseuri": "0.0.6", - "socket.io-parser": "~3.3.0", - "to-array": "0.1.4" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "socket.io-parser": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.2.tgz", - "integrity": "sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==", - "requires": { - "component-emitter": "~1.3.0", - "debug": "~3.1.0", - "isarray": "2.0.1" - } - } - } - }, - "socket.io-parser": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz", - "integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==", - "requires": { - "component-emitter": "1.2.1", - "debug": "~4.1.0", - "isarray": "2.0.1" - }, - "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" - } - } - }, - "sparse-bitfield": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", - "optional": true, - "requires": { - "memory-pager": "^1.0.2" - } - }, - "split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "requires": { - "readable-stream": "^3.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "sqlite3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.1.tgz", - "integrity": "sha512-kh2lTIcYNfmVcvhVJihsYuPj9U0xzBbh6bmqILO2hkryWSC9RRhzYmkIDtJkJ+d8Kg4wZRJ0T1reyHUEspICfg==", - "optional": true, - "requires": { - "node-addon-api": "^3.0.0", - "node-gyp": "3.x", - "node-pre-gyp": "^0.11.0" - } - }, - "sqlstring": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", - "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "tar": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", - "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", - "optional": true, - "requires": { - "block-stream": "*", - "fstream": "^1.0.12", - "inherits": "2" - } - }, - "tarn": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.1.tgz", - "integrity": "sha512-6usSlV9KyHsspvwu2duKH+FMUhqJnAh6J5J/4MITl8s94iSUQTLkJggdiewKv4RyARQccnigV48Z+khiuVZDJw==" - }, - "tedious": { - "version": "9.2.3", - "resolved": "https://registry.npmjs.org/tedious/-/tedious-9.2.3.tgz", - "integrity": "sha512-+mI2r/5mqxpTHKBZ/SW+NNH2MK5i3Pwwkw0gF5ZrS2wf2uT/03bLSss8nm7xh604abJXyjx0sirhwH63H328qA==", - "requires": { - "@azure/ms-rest-nodeauth": "^3.0.6", - "@js-joda/core": "^3.1.0", - "adal-node": "^0.1.28", - "bl": "^3.0.0", - "depd": "^2.0.0", - "iconv-lite": "^0.6.2", - "jsbi": "^3.1.3", - "native-duplexpair": "^1.0.0", - "punycode": "^2.1.0", - "readable-stream": "^3.6.0", - "sprintf-js": "^1.1.2" - }, - "dependencies": { - "bl": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.1.tgz", - "integrity": "sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ==", - "requires": { - "readable-stream": "^3.0.1" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" - } - } - }, - "to-array": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" - }, - "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", - "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "ueberdb2": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/ueberdb2/-/ueberdb2-1.2.5.tgz", - "integrity": "sha512-Bts6kmVvhVDWiZjD1JAT1qYknHHK6t9L7kGIFIedGAZRNQ3lRw2XJdf9hKbFpN2HM0J3S/aJoLrZO5BLk3UiaA==", - "requires": { - "async": "^3.2.0", - "cassandra-driver": "^4.5.1", - "channels": "0.0.4", - "dirty": "^1.1.0", - "elasticsearch": "^16.7.1", - "mongodb": "^3.6.3", - "mssql": "^7.0.0-beta.2", - "mysql": "2.18.1", - "nano": "^8.2.2", - "pg": "^8.0.3", - "redis": "^3.0.2", - "rethinkdb": "^2.4.2", - "simple-git": "^2.4.0", - "sqlite3": "^5.0.1" - } - }, - "underscore": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.0.tgz", - "integrity": "sha512-21rQzss/XPMjolTiIezSu3JAjgagXKROtNrYFEOWK109qY1Uv2tVjPTZ1ci2HgvQDA16gHYSthQIJfB+XId/rQ==" - }, - "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "ws": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", - "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==" - }, - "wtfnode": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/wtfnode/-/wtfnode-0.8.4.tgz", - "integrity": "sha512-64GEKtMt/MUBuAm+8kHqP74ojjafzu00aT0JKsmkIwYmjRQ/odO0yhbzKLm+Z9v1gMla+8dwITRKzTAlHsB+Og==" - }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" - }, - "xmldom": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.4.0.tgz", - "integrity": "sha512-2E93k08T30Ugs+34HBSTQLVtpi6mCddaY8uO+pMNk1pqSjV5vElzn4mmh6KLxN3hki8rNcHSYzILoh3TEWORvA==" - }, - "xmlhttprequest-ssl": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", - "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" - }, - "xpath.js": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xpath.js/-/xpath.js-1.1.0.tgz", - "integrity": "sha512-jg+qkfS4K8E7965sqaUl8mRngXiKb3WZGfONgE18pr03FUQiuSV6G+Ej4tS55B+rIQSFEIw3phdVAQ4pPqNWfQ==" - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 772ec540b..000000000 --- a/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "dependencies": { - "ep_etherpad-lite": "file:src" - }, - "engines": { - "node": "^10.17.0 || >=11.14.0" - } -} From f8a19c4527cd1f4dd38ee45cf5769d47652421e0 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 7 Feb 2021 06:39:03 +0000 Subject: [PATCH 247/357] lint: lint and various fixes of frontend test specs --- src/tests/frontend/specs/caret.js | 3 +- src/tests/frontend/specs/drag_and_drop.js | 12 +- src/tests/frontend/specs/ordered_list.js | 134 ++++++----- src/tests/frontend/specs/pad_modal.js | 26 +- src/tests/frontend/specs/redo.js | 4 +- src/tests/frontend/specs/scrollTo.js | 48 ++-- .../specs/select_formatting_buttons.js | 38 +-- src/tests/frontend/specs/unordered_list.js | 226 +++++++++--------- src/tests/frontend/specs/xxauto_reconnect.js | 10 +- 9 files changed, 261 insertions(+), 240 deletions(-) diff --git a/src/tests/frontend/specs/caret.js b/src/tests/frontend/specs/caret.js index e5ce255b8..c4db08013 100644 --- a/src/tests/frontend/specs/caret.js +++ b/src/tests/frontend/specs/caret.js @@ -297,7 +297,7 @@ console.log(inner$); }); */ }); - +/* // generates a random document with random content on n lines const prepareDocument = (n, target) => { let i = 0; @@ -350,3 +350,4 @@ const caretPosition = ($) => { pos.x = pos.anchorNode.parentElement.offsetLeft; return pos; }; +*/ diff --git a/src/tests/frontend/specs/drag_and_drop.js b/src/tests/frontend/specs/drag_and_drop.js index 2d1339beb..1b08facd2 100644 --- a/src/tests/frontend/specs/drag_and_drop.js +++ b/src/tests/frontend/specs/drag_and_drop.js @@ -23,9 +23,13 @@ describe('drag and drop', function () { }); context('and user triggers UNDO', function () { - before(function () { + before(async function () { + const originalHTML = helper.padInner$('body').html(); const $undoButton = helper.padChrome$('.buttonicon-undo'); $undoButton.click(); + await helper.waitForPromise( + () => originalHTML !== helper.padInner$('body').html() + ); }); it('moves text back to its original place', function (done) { @@ -58,9 +62,13 @@ describe('drag and drop', function () { }); context('and user triggers UNDO', function () { - before(function () { + before(async function () { + const originalHTML = helper.padInner$('body').html(); const $undoButton = helper.padChrome$('.buttonicon-undo'); $undoButton.click(); + await helper.waitForPromise( + () => originalHTML !== helper.padInner$('body').html() + ); }); it('moves text back to its original place', function (done) { diff --git a/src/tests/frontend/specs/ordered_list.js b/src/tests/frontend/specs/ordered_list.js index d069a6487..3ad6f4ab1 100644 --- a/src/tests/frontend/specs/ordered_list.js +++ b/src/tests/frontend/specs/ordered_list.js @@ -19,9 +19,12 @@ describe('assign ordered list', function () { context('when user presses Ctrl+Shift+N', function () { context('and pad shortcut is enabled', function () { - beforeEach(function () { + beforeEach(async function () { + const originalHTML = helper.padInner$('body').html(); makeSureShortcutIsEnabled('cmdShiftN'); triggerCtrlShiftShortcut('N'); + await helper.waitForPromise( + () => helper.padInner$('body').html !== originalHTML); }); it('inserts unordered list', function (done) { @@ -30,9 +33,12 @@ describe('assign ordered list', function () { }); context('and pad shortcut is disabled', function () { - beforeEach(function () { + beforeEach(async function () { + const originalHTML = helper.padInner$('body').html(); makeSureShortcutIsDisabled('cmdShiftN'); triggerCtrlShiftShortcut('N'); + await helper.waitForPromise( + () => helper.padInner$('body').html !== originalHTML); }); it('does not insert unordered list', function (done) { @@ -48,9 +54,12 @@ describe('assign ordered list', function () { context('when user presses Ctrl+Shift+1', function () { context('and pad shortcut is enabled', function () { - beforeEach(function () { + beforeEach(async function () { + const originalHTML = helper.padInner$('body').html(); makeSureShortcutIsEnabled('cmdShift1'); triggerCtrlShiftShortcut('1'); + await helper.waitForPromise( + () => helper.padInner$('body').html !== originalHTML); }); it('inserts unordered list', function (done) { @@ -59,9 +68,12 @@ describe('assign ordered list', function () { }); context('and pad shortcut is disabled', function () { - beforeEach(function () { + beforeEach(async function () { + const originalHTML = helper.padInner$('body').html(); makeSureShortcutIsDisabled('cmdShift1'); triggerCtrlShiftShortcut('1'); + await helper.waitForPromise( + () => helper.padInner$('body').html !== originalHTML); }); it('does not insert unordered list', function (done) { @@ -117,71 +129,71 @@ describe('assign ordered list', function () { const makeSureShortcutIsEnabled = (shortcut) => { helper.padChrome$.window.clientVars.padShortcutEnabled[shortcut] = true; }; -}); -describe('Pressing Tab in an OL increases and decreases indentation', function () { - // create a new pad before each test run - beforeEach(function (cb) { - helper.newPad(cb); - this.timeout(60000); + describe('Pressing Tab in an OL increases and decreases indentation', function () { + // create a new pad before each test run + beforeEach(function (cb) { + helper.newPad(cb); + this.timeout(60000); + }); + + it('indent and de-indent list item with keypress', function (done) { + const inner$ = helper.padInner$; + const chrome$ = helper.padChrome$; + + // get the first text element out of the inner iframe + const $firstTextElement = inner$('div').first(); + + // select this text element + $firstTextElement.sendkeys('{selectall}'); + + const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist'); + $insertorderedlistButton.click(); + + const e = new inner$.Event(helper.evtType); + e.keyCode = 9; // tab + inner$('#innerdocbody').trigger(e); + + expect(inner$('div').first().find('.list-number2').length === 1).to.be(true); + e.shiftKey = true; // shift + e.keyCode = 9; // tab + inner$('#innerdocbody').trigger(e); + + helper.waitFor(() => inner$('div').first().find('.list-number1').length === 1).done(done); + }); }); - it('indent and de-indent list item with keypress', function (done) { - const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; - // get the first text element out of the inner iframe - const $firstTextElement = inner$('div').first(); + describe('Pressing indent/outdent button in an OL increases and ' + + 'decreases indentation and bullet / ol formatting', function () { + // create a new pad before each test run + beforeEach(function (cb) { + helper.newPad(cb); + this.timeout(60000); + }); - // select this text element - $firstTextElement.sendkeys('{selectall}'); + it('indent and de-indent list item with indent button', function (done) { + const inner$ = helper.padInner$; + const chrome$ = helper.padChrome$; - const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist'); - $insertorderedlistButton.click(); + // get the first text element out of the inner iframe + const $firstTextElement = inner$('div').first(); - const e = new inner$.Event(helper.evtType); - e.keyCode = 9; // tab - inner$('#innerdocbody').trigger(e); + // select this text element + $firstTextElement.sendkeys('{selectall}'); - expect(inner$('div').first().find('.list-number2').length === 1).to.be(true); - e.shiftKey = true; // shift - e.keyCode = 9; // tab - inner$('#innerdocbody').trigger(e); + const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist'); + $insertorderedlistButton.click(); - helper.waitFor(() => inner$('div').first().find('.list-number1').length === 1).done(done); - }); -}); - - -describe('Pressing indent/outdent button in an OL increases and ' + - 'decreases indentation and bullet / ol formatting', function () { - // create a new pad before each test run - beforeEach(function (cb) { - helper.newPad(cb); - this.timeout(60000); - }); - - it('indent and de-indent list item with indent button', function (done) { - const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; - - // get the first text element out of the inner iframe - const $firstTextElement = inner$('div').first(); - - // select this text element - $firstTextElement.sendkeys('{selectall}'); - - const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist'); - $insertorderedlistButton.click(); - - const $indentButton = chrome$('.buttonicon-indent'); - $indentButton.click(); // make it indented twice - - expect(inner$('div').first().find('.list-number2').length === 1).to.be(true); - - const $outdentButton = chrome$('.buttonicon-outdent'); - $outdentButton.click(); // make it deindented to 1 - - helper.waitFor(() => inner$('div').first().find('.list-number1').length === 1).done(done); + const $indentButton = chrome$('.buttonicon-indent'); + $indentButton.click(); // make it indented twice + + expect(inner$('div').first().find('.list-number2').length === 1).to.be(true); + + const $outdentButton = chrome$('.buttonicon-outdent'); + $outdentButton.click(); // make it deindented to 1 + + helper.waitFor(() => inner$('div').first().find('.list-number1').length === 1).done(done); + }); }); }); diff --git a/src/tests/frontend/specs/pad_modal.js b/src/tests/frontend/specs/pad_modal.js index 30277d5de..fe665df54 100644 --- a/src/tests/frontend/specs/pad_modal.js +++ b/src/tests/frontend/specs/pad_modal.js @@ -24,14 +24,11 @@ describe('Pad modal', function () { }); context('and user clicks on editor', function () { - beforeEach(function () { - clickOnPadInner(); - }); - it('does not close the modal', function (done) { + clickOnPadInner(); const $modal = helper.padChrome$(MODAL_SELECTOR); const modalIsVisible = $modal.hasClass('popup-show'); - + helper.waitForPromise(() => $modal.hasClass('popup-show') === true); expect(modalIsVisible).to.be(true); done(); @@ -39,14 +36,13 @@ describe('Pad modal', function () { }); context('and user clicks on pad outer', function () { - beforeEach(function () { - clickOnPadOuter(); - }); - it('does not close the modal', function (done) { const $modal = helper.padChrome$(MODAL_SELECTOR); const modalIsVisible = $modal.hasClass('popup-show'); + clickOnPadOuter(); + + helper.waitForPromise(() => $modal.hasClass('popup-show') === true); expect(modalIsVisible).to.be(true); done(); @@ -73,22 +69,18 @@ describe('Pad modal', function () { }); */ context('and user clicks on editor', function () { - beforeEach(function () { - clickOnPadInner(); - }); - it('closes the modal', function (done) { + clickOnPadInner(); + helper.waitForPromise(() => isModalOpened(MODAL_SELECTOR) === false); expect(isModalOpened(MODAL_SELECTOR)).to.be(false); done(); }); }); context('and user clicks on pad outer', function () { - beforeEach(function () { - clickOnPadOuter(); - }); - it('closes the modal', function (done) { + clickOnPadOuter(); + helper.waitForPromise(() => isModalOpened(MODAL_SELECTOR) === false); expect(isModalOpened(MODAL_SELECTOR)).to.be(false); done(); }); diff --git a/src/tests/frontend/specs/redo.js b/src/tests/frontend/specs/redo.js index 3e8d3a168..b0334ba82 100644 --- a/src/tests/frontend/specs/redo.js +++ b/src/tests/frontend/specs/redo.js @@ -45,12 +45,12 @@ describe('undo button then redo button', function () { const modifiedValue = $firstTextElement.text(); // get the modified value expect(modifiedValue).not.to.be(originalValue); // expect the value to change - let e = inner$.Event(helper.evtType); + let e = new inner$.Event(helper.evtType); e.ctrlKey = true; // Control key e.which = 90; // z inner$('#innerdocbody').trigger(e); - e = inner$.Event(helper.evtType); + e = new inner$.Event(helper.evtType); e.ctrlKey = true; // Control key e.which = 121; // y inner$('#innerdocbody').trigger(e); diff --git a/src/tests/frontend/specs/scrollTo.js b/src/tests/frontend/specs/scrollTo.js index 47fe1ca7e..ba96f8164 100755 --- a/src/tests/frontend/specs/scrollTo.js +++ b/src/tests/frontend/specs/scrollTo.js @@ -2,12 +2,15 @@ describe('scrolls to line', function () { // create a new pad with URL hash set before each test run - beforeEach(function (cb) { - helper.newPad({ + before(async function () { + this.timeout(60000); + await new Promise((resolve, reject) => helper.newPad({ + cb: (err) => { + if (err != null) return reject(err); + resolve(); + }, hash: 'L4', - cb, - }); - this.timeout(10000); + })); }); it('Scrolls down to Line 4', async function () { @@ -19,25 +22,28 @@ describe('scrolls to line', function () { return (topOffset >= 100); }); }); -}); -describe('doesnt break on weird hash input', function () { - // create a new pad with URL hash set before each test run - beforeEach(function (cb) { - helper.newPad({ - hash: '#DEEZ123123NUTS', - cb, + describe('doesnt break on weird hash input', function () { + // create a new pad with URL hash set before each test run + before(async function () { + this.timeout(60000); + await new Promise((resolve, reject) => helper.newPad({ + cb: (err) => { + if (err != null) return reject(err); + resolve(); + }, + hash: '#DEEZ123123NUTS', + })); }); - this.timeout(10000); - }); - it('Does NOT change scroll', async function () { - this.timeout(10000); - const chrome$ = helper.padChrome$; - await helper.waitForPromise(() => { - const topOffset = parseInt(chrome$('iframe').first('iframe') - .contents().find('#outerdocbody').css('top')); - return (!topOffset); // no css top should be set. + it('Does NOT change scroll', async function () { + this.timeout(10000); + const chrome$ = helper.padChrome$; + await helper.waitForPromise(() => { + const topOffset = parseInt(chrome$('iframe').first('iframe') + .contents().find('#outerdocbody').css('top')); + return (!topOffset); // no css top should be set. + }); }); }); }); diff --git a/src/tests/frontend/specs/select_formatting_buttons.js b/src/tests/frontend/specs/select_formatting_buttons.js index 358d9e5b7..67b78ed0a 100644 --- a/src/tests/frontend/specs/select_formatting_buttons.js +++ b/src/tests/frontend/specs/select_formatting_buttons.js @@ -35,9 +35,11 @@ describe('select formatting buttons when selection has style applied', function $line.sendkeys('{leftarrow}'); }; - const undo = function () { + const undo = async function () { + const originalHTML = helper.padInner$('body').html(); const $undoButton = helper.padChrome$('.buttonicon-undo'); $undoButton.click(); + await helper.waitFor(() => originalHTML !== helper.padInner$('body').html()); }; const testIfFormattingButtonIsDeselected = function (style) { @@ -80,9 +82,11 @@ describe('select formatting buttons when selection has style applied', function // }, 1000); }; - const pressFormattingShortcutOnSelection = function (key) { + const pressFormattingShortcutOnSelection = async function (key) { const inner$ = helper.padInner$; + const originalHTML = helper.padInner$('body').html(); + helper.waitFor(() => originalHTML !== helper.padInner$('body').html()); // get the first text element out of the inner iframe const $firstTextElement = inner$('div').first(); @@ -93,6 +97,8 @@ describe('select formatting buttons when selection has style applied', function e.ctrlKey = true; // Control key e.which = key.charCodeAt(0); // I, U, B, 5 inner$('#innerdocbody').trigger(e); + await helper.waitForPromise( + () => originalHTML !== helper.padInner$('body').html()); }; STYLES.forEach((style) => { @@ -102,8 +108,8 @@ describe('select formatting buttons when selection has style applied', function applyStyleOnLineAndSelectIt(FIRST_LINE, style, done); }); - after(function () { - undo(); + after(async function () { + await undo(); }); testIfFormattingButtonIsSelected(style); @@ -115,8 +121,8 @@ describe('select formatting buttons when selection has style applied', function applyStyleOnLineAndPlaceCaretOnit(FIRST_LINE, style, done); }); - after(function () { - undo(); + after(async function () { + await undo(); }); testIfFormattingButtonIsSelected(style); @@ -125,33 +131,27 @@ describe('select formatting buttons when selection has style applied', function context('when user applies a style and the selection does not change', function () { const style = STYLES[0]; // italic - before(function () { - applyStyleOnLine(style, FIRST_LINE); - }); - // clean the style applied - after(function () { + it('selects the style button', async function () { applyStyleOnLine(style, FIRST_LINE); - }); - - it('selects the style button', function (done) { + await helper.waitForPromise(() => isButtonSelected(style) === true); expect(isButtonSelected(style)).to.be(true); - done(); + applyStyleOnLine(style, FIRST_LINE); }); }); SHORTCUT_KEYS.forEach((key, index) => { const styleOfTheShortcut = STYLES[index]; // italic, bold, ... context(`when user presses CMD + ${key}`, function () { - before(function () { - pressFormattingShortcutOnSelection(key); + before(async function () { + await pressFormattingShortcutOnSelection(key); }); testIfFormattingButtonIsSelected(styleOfTheShortcut); context(`and user presses CMD + ${key} again`, function () { - before(function () { - pressFormattingShortcutOnSelection(key); + before(async function () { + await pressFormattingShortcutOnSelection(key); }); testIfFormattingButtonIsDeselected(styleOfTheShortcut); diff --git a/src/tests/frontend/specs/unordered_list.js b/src/tests/frontend/specs/unordered_list.js index 22d1a6fe2..cd3bebc76 100644 --- a/src/tests/frontend/specs/unordered_list.js +++ b/src/tests/frontend/specs/unordered_list.js @@ -31,135 +31,135 @@ describe('assign unordered list', function () { }); }); }); -}); -describe('unassign unordered list', function () { - // create a new pad before each test run - beforeEach(function (cb) { - helper.newPad(cb); - this.timeout(60000); + describe('unassign unordered list', function () { + // create a new pad before each test run + beforeEach(function (cb) { + helper.newPad(cb); + this.timeout(60000); + }); + + it('insert unordered list text then remove by clicking list again', function (done) { + const inner$ = helper.padInner$; + const chrome$ = helper.padChrome$; + const originalText = inner$('div').first().text(); + + const $insertunorderedlistButton = chrome$('.buttonicon-insertunorderedlist'); + $insertunorderedlistButton.click(); + + helper.waitFor(() => { + const newText = inner$('div').first().text(); + if (newText === originalText) { + return inner$('div').first().find('ul li').length === 1; + } + }).done(() => { + // remove indentation by bullet and ensure text string remains the same + $insertunorderedlistButton.click(); + helper.waitFor(() => { + const isList = inner$('div').find('ul').length === 1; + // sohuldn't be list + return (isList === false); + }).done(() => { + done(); + }); + }); + }); }); - it('insert unordered list text then remove by clicking list again', function (done) { - const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; - const originalText = inner$('div').first().text(); - const $insertunorderedlistButton = chrome$('.buttonicon-insertunorderedlist'); - $insertunorderedlistButton.click(); + describe('keep unordered list on enter key', function () { + // create a new pad before each test run + beforeEach(function (cb) { + helper.newPad(cb); + this.timeout(60000); + }); - helper.waitFor(() => { - const newText = inner$('div').first().text(); - if (newText === originalText) { - return inner$('div').first().find('ul li').length === 1; - } - }).done(() => { - // remove indentation by bullet and ensure text string remains the same - $insertunorderedlistButton.click(); - helper.waitFor(() => { - const isList = inner$('div').find('ul').length === 1; - // sohuldn't be list - return (isList === false); - }).done(() => { + it('Keeps the unordered list on enter for the new line', function (done) { + const inner$ = helper.padInner$; + const chrome$ = helper.padChrome$; + + const $insertorderedlistButton = chrome$('.buttonicon-insertunorderedlist'); + $insertorderedlistButton.click(); + + // type a bit, make a line break and type again + const $firstTextElement = inner$('div span').first(); + $firstTextElement.sendkeys('line 1'); + $firstTextElement.sendkeys('{enter}'); + $firstTextElement.sendkeys('line 2'); + $firstTextElement.sendkeys('{enter}'); + + helper.waitFor(() => inner$('div span').first().text().indexOf('line 2') === -1).done(() => { + const $newSecondLine = inner$('div').first().next(); + const hasULElement = $newSecondLine.find('ul li').length === 1; + expect(hasULElement).to.be(true); + expect($newSecondLine.text()).to.be('line 2'); done(); }); }); }); -}); + describe('Pressing Tab in an UL increases and decreases indentation', function () { + // create a new pad before each test run + beforeEach(function (cb) { + helper.newPad(cb); + this.timeout(60000); + }); -describe('keep unordered list on enter key', function () { - // create a new pad before each test run - beforeEach(function (cb) { - helper.newPad(cb); - this.timeout(60000); + it('indent and de-indent list item with keypress', function (done) { + const inner$ = helper.padInner$; + const chrome$ = helper.padChrome$; + + // get the first text element out of the inner iframe + const $firstTextElement = inner$('div').first(); + + // select this text element + $firstTextElement.sendkeys('{selectall}'); + + const $insertorderedlistButton = chrome$('.buttonicon-insertunorderedlist'); + $insertorderedlistButton.click(); + + const e = new inner$.Event(helper.evtType); + e.keyCode = 9; // tab + inner$('#innerdocbody').trigger(e); + + expect(inner$('div').first().find('.list-bullet2').length === 1).to.be(true); + e.shiftKey = true; // shift + e.keyCode = 9; // tab + inner$('#innerdocbody').trigger(e); + + helper.waitFor(() => inner$('div').first().find('.list-bullet1').length === 1).done(done); + }); }); - it('Keeps the unordered list on enter for the new line', function (done) { - const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; + describe('Pressing indent/outdent button in an UL increases and decreases indentation ' + + 'and bullet / ol formatting', function () { + // create a new pad before each test run + beforeEach(function (cb) { + helper.newPad(cb); + this.timeout(60000); + }); - const $insertorderedlistButton = chrome$('.buttonicon-insertunorderedlist'); - $insertorderedlistButton.click(); + it('indent and de-indent list item with indent button', function (done) { + const inner$ = helper.padInner$; + const chrome$ = helper.padChrome$; - // type a bit, make a line break and type again - const $firstTextElement = inner$('div span').first(); - $firstTextElement.sendkeys('line 1'); - $firstTextElement.sendkeys('{enter}'); - $firstTextElement.sendkeys('line 2'); - $firstTextElement.sendkeys('{enter}'); + // get the first text element out of the inner iframe + const $firstTextElement = inner$('div').first(); - helper.waitFor(() => inner$('div span').first().text().indexOf('line 2') === -1).done(() => { - const $newSecondLine = inner$('div').first().next(); - const hasULElement = $newSecondLine.find('ul li').length === 1; - expect(hasULElement).to.be(true); - expect($newSecondLine.text()).to.be('line 2'); - done(); + // select this text element + $firstTextElement.sendkeys('{selectall}'); + + const $insertunorderedlistButton = chrome$('.buttonicon-insertunorderedlist'); + $insertunorderedlistButton.click(); + + const $indentButton = chrome$('.buttonicon-indent'); + $indentButton.click(); // make it indented twice + + expect(inner$('div').first().find('.list-bullet2').length === 1).to.be(true); + const $outdentButton = chrome$('.buttonicon-outdent'); + $outdentButton.click(); // make it deindented to 1 + + helper.waitFor(() => inner$('div').first().find('.list-bullet1').length === 1).done(done); }); }); }); - -describe('Pressing Tab in an UL increases and decreases indentation', function () { - // create a new pad before each test run - beforeEach(function (cb) { - helper.newPad(cb); - this.timeout(60000); - }); - - it('indent and de-indent list item with keypress', function (done) { - const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; - - // get the first text element out of the inner iframe - const $firstTextElement = inner$('div').first(); - - // select this text element - $firstTextElement.sendkeys('{selectall}'); - - const $insertorderedlistButton = chrome$('.buttonicon-insertunorderedlist'); - $insertorderedlistButton.click(); - - const e = inner$.Event(helper.evtType); - e.keyCode = 9; // tab - inner$('#innerdocbody').trigger(e); - - expect(inner$('div').first().find('.list-bullet2').length === 1).to.be(true); - e.shiftKey = true; // shift - e.keyCode = 9; // tab - inner$('#innerdocbody').trigger(e); - - helper.waitFor(() => inner$('div').first().find('.list-bullet1').length === 1).done(done); - }); -}); - -describe('Pressing indent/outdent button in an UL increases and decreases indentation ' + - 'and bullet / ol formatting', function () { - // create a new pad before each test run - beforeEach(function (cb) { - helper.newPad(cb); - this.timeout(60000); - }); - - it('indent and de-indent list item with indent button', function (done) { - const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; - - // get the first text element out of the inner iframe - const $firstTextElement = inner$('div').first(); - - // select this text element - $firstTextElement.sendkeys('{selectall}'); - - const $insertunorderedlistButton = chrome$('.buttonicon-insertunorderedlist'); - $insertunorderedlistButton.click(); - - const $indentButton = chrome$('.buttonicon-indent'); - $indentButton.click(); // make it indented twice - - expect(inner$('div').first().find('.list-bullet2').length === 1).to.be(true); - const $outdentButton = chrome$('.buttonicon-outdent'); - $outdentButton.click(); // make it deindented to 1 - - helper.waitFor(() => inner$('div').first().find('.list-bullet1').length === 1).done(done); - }); -}); diff --git a/src/tests/frontend/specs/xxauto_reconnect.js b/src/tests/frontend/specs/xxauto_reconnect.js index d92936563..af35e528e 100644 --- a/src/tests/frontend/specs/xxauto_reconnect.js +++ b/src/tests/frontend/specs/xxauto_reconnect.js @@ -34,9 +34,11 @@ describe('Automatic pad reload on Force Reconnect message', function () { }); context('and user clicks on Cancel', function () { - beforeEach(function () { + beforeEach(async function () { const $errorMessageModal = helper.padChrome$('#connectivity .userdup'); $errorMessageModal.find('#cancelreconnect').click(); + await helper.waitForPromise( + () => helper.padChrome$('#connectivity .userdup').is(':visible') === true); }); it('does not show Cancel button nor timer anymore', function (done) { @@ -54,16 +56,16 @@ describe('Automatic pad reload on Force Reconnect message', function () { context('and user does not click on Cancel until timer expires', function () { let padWasReloaded = false; - beforeEach(function () { + beforeEach(async function () { $originalPadFrame.one('load', () => { padWasReloaded = true; }); }); it('reloads the pad', function (done) { - helper.waitFor(() => padWasReloaded, 5000).done(done); + helper.waitFor(() => padWasReloaded, 10000).done(done); - this.timeout(5000); + this.timeout(10000); }); }); }); From 1f0cb01110fa07febd81b0857ad32749c1de98d2 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 7 Feb 2021 06:59:32 +0000 Subject: [PATCH 248/357] tests: fix enter test --- src/tests/frontend/specs/enter.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tests/frontend/specs/enter.js b/src/tests/frontend/specs/enter.js index ca71d2081..d0be72954 100644 --- a/src/tests/frontend/specs/enter.js +++ b/src/tests/frontend/specs/enter.js @@ -51,8 +51,10 @@ describe('enter keystroke', function () { const lastLine = helper.padInner$('div').last(); const bottomOfLastLine = lastLine.offset().top + lastLine.height(); const scrolledWindow = helper.padChrome$('iframe')[0]; - const scrolledAmount = scrolledWindow.contentWindow.pageYOffset + + await helper.waitFor(() => { + const scrolledAmount = scrolledWindow.contentWindow.pageYOffset + scrolledWindow.contentWindow.innerHeight; - await helper.waitFor(() => scrolledAmount >= bottomOfLastLine); + return scrolledAmount >= bottomOfLastLine; + }); }); }); From c625c611d263f18becbcc36d85cdf8e1175c9006 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 01:23:36 -0500 Subject: [PATCH 249/357] tests: delete `src/tests/frontend/specs/caret.js` All of the tests in this file are commented out so this file does nothing. We can uncomment the code and clean it up, but the approach taken in these tests will never work: For security reasons, browsers do not allow synthetic key events to perform the default behavior (such as moving the carent when an arrow key is pressed). There are two ways to test responses to navigation keys: * Use WebDriver to create "genuine" keyboard events. * Suppress the default behavior and implement caret movement ourselves. This is tremendously complicated, especially arrow up/down. --- src/tests/frontend/specs/caret.js | 353 ------------------------------ 1 file changed, 353 deletions(-) delete mode 100644 src/tests/frontend/specs/caret.js diff --git a/src/tests/frontend/specs/caret.js b/src/tests/frontend/specs/caret.js deleted file mode 100644 index c4db08013..000000000 --- a/src/tests/frontend/specs/caret.js +++ /dev/null @@ -1,353 +0,0 @@ -'use strict'; - -describe('As the caret is moved is the UI properly updated?', function () { - /* - let padName; - const numberOfRows = 50; - - //create a new pad before each test run - beforeEach(function(cb){ - helper.newPad(cb); - this.timeout(60000); - }); - - xit("creates a pad", function(done) { - padName = helper.newPad(done); - this.timeout(60000); - }); -*/ - - /* Tests to do - * Keystroke up (38), down (40), left (37), right (39) - * with and without special keys IE control / shift - * Page up (33) / down (34) with and without special keys - * Page up on the first line shouldn't move the viewport - * Down down on the last line shouldn't move the viewport - * Down arrow on any other line except the last lines shouldn't move the viewport - * Do all of the above tests after a copy/paste event - */ - - /* Challenges - * How do we keep the authors focus on a line if the lines above the author are modified? - * We should only redraw the user to a location if they are typing and make sure shift - * and arrow keys aren't redrawing the UI else highlight - copy/paste would get broken - * How can we simulate an edit event in the test framework? - */ - /* - // THIS DOESNT WORK IN CHROME AS IT DOESNT MOVE THE CURSOR! - it("down arrow", function(done){ - var inner$ = helper.padInner$; - var chrome$ = helper.padChrome$; - - var $newFirstTextElement = inner$("div").first(); - $newFirstTextElement.focus(); - keyEvent(inner$, 37, false, false); // arrow down - keyEvent(inner$, 37, false, false); // arrow down - - done(); - }); - - it("Creates N lines", function(done){ - var inner$ = helper.padInner$; -console.log(inner$); - var chrome$ = helper.padChrome$; - var $newFirstTextElement = inner$("div").first(); - - prepareDocument(numberOfRows, $newFirstTextElement); // N lines into the first div as a target - helper.waitFor(function(){ // Wait for the DOM to register the new items - return inner$("div").first().text().length == 6; - }).done(function(){ // Once the DOM has registered the items - done(); - }); - }); - - it("Moves caret up a line", function(done){ - var inner$ = helper.padInner$; - var $newFirstTextElement = inner$("div").first(); - var originalCaretPosition = caretPosition(inner$); - var originalPos = originalCaretPosition.y; - var newCaretPos; - keyEvent(inner$, 38, false, false); // arrow up - - helper.waitFor(function(){ // Wait for the DOM to register the new items - var newCaretPosition = caretPosition(inner$); - newCaretPos = newCaretPosition.y; - return (newCaretPos < originalPos); - }).done(function(){ - expect(newCaretPos).to.be.lessThan(originalPos); - done(); - }); - }); - - it("Moves caret down a line", function(done){ - var inner$ = helper.padInner$; - var $newFirstTextElement = inner$("div").first(); - var originalCaretPosition = caretPosition(inner$); - var originalPos = originalCaretPosition.y; - var newCaretPos; - keyEvent(inner$, 40, false, false); // arrow down - - helper.waitFor(function(){ // Wait for the DOM to register the new items - var newCaretPosition = caretPosition(inner$); - newCaretPos = newCaretPosition.y; - return (newCaretPos > originalPos); - }).done(function(){ - expect(newCaretPos).to.be.moreThan(originalPos); - done(); - }); - }); - - it("Moves caret to top of doc", function(done){ - var inner$ = helper.padInner$; - var $newFirstTextElement = inner$("div").first(); - var originalCaretPosition = caretPosition(inner$); - var originalPos = originalCaretPosition.y; - var newCaretPos; - - var i = 0; - while(i < numberOfRows){ // press pageup key N times - keyEvent(inner$, 33, false, false); - i++; - } - - helper.waitFor(function(){ // Wait for the DOM to register the new items - var newCaretPosition = caretPosition(inner$); - newCaretPos = newCaretPosition.y; - return (newCaretPos < originalPos); - }).done(function(){ - expect(newCaretPos).to.be.lessThan(originalPos); - done(); - }); - }); - - it("Moves caret right a position", function(done){ - var inner$ = helper.padInner$; - var $newFirstTextElement = inner$("div").first(); - var originalCaretPosition = caretPosition(inner$); - var originalPos = originalCaretPosition.x; - var newCaretPos; - keyEvent(inner$, 39, false, false); // arrow right - - helper.waitFor(function(){ // Wait for the DOM to register the new items - var newCaretPosition = caretPosition(inner$); - newCaretPos = newCaretPosition.x; - return (newCaretPos > originalPos); - }).done(function(){ - expect(newCaretPos).to.be.moreThan(originalPos); - done(); - }); - }); - - it("Moves caret left a position", function(done){ - var inner$ = helper.padInner$; - var $newFirstTextElement = inner$("div").first(); - var originalCaretPosition = caretPosition(inner$); - var originalPos = originalCaretPosition.x; - var newCaretPos; - keyEvent(inner$, 33, false, false); // arrow left - - helper.waitFor(function(){ // Wait for the DOM to register the new items - var newCaretPosition = caretPosition(inner$); - newCaretPos = newCaretPosition.x; - return (newCaretPos < originalPos); - }).done(function(){ - expect(newCaretPos).to.be.lessThan(originalPos); - done(); - }); - }); - - it("Moves caret to the next line using right arrow", function(done){ - var inner$ = helper.padInner$; - var $newFirstTextElement = inner$("div").first(); - var originalCaretPosition = caretPosition(inner$); - var originalPos = originalCaretPosition.y; - var newCaretPos; - keyEvent(inner$, 39, false, false); // arrow right - keyEvent(inner$, 39, false, false); // arrow right - keyEvent(inner$, 39, false, false); // arrow right - keyEvent(inner$, 39, false, false); // arrow right - keyEvent(inner$, 39, false, false); // arrow right - keyEvent(inner$, 39, false, false); // arrow right - keyEvent(inner$, 39, false, false); // arrow right - - helper.waitFor(function(){ // Wait for the DOM to register the new items - var newCaretPosition = caretPosition(inner$); - newCaretPos = newCaretPosition.y; - return (newCaretPos > originalPos); - }).done(function(){ - expect(newCaretPos).to.be.moreThan(originalPos); - done(); - }); - }); - - it("Moves caret to the previous line using left arrow", function(done){ - var inner$ = helper.padInner$; - var $newFirstTextElement = inner$("div").first(); - var originalCaretPosition = caretPosition(inner$); - var originalPos = originalCaretPosition.y; - var newCaretPos; - keyEvent(inner$, 33, false, false); // arrow left - - helper.waitFor(function(){ // Wait for the DOM to register the new items - var newCaretPosition = caretPosition(inner$); - newCaretPos = newCaretPosition.y; - return (newCaretPos < originalPos); - }).done(function(){ - expect(newCaretPos).to.be.lessThan(originalPos); - done(); - }); - }); - - -/* - it("Creates N rows, changes height of rows, updates UI by caret key events", function(done){ - var inner$ = helper.padInner$; - var chrome$ = helper.padChrome$; - var numberOfRows = 50; - - // ace creates a new dom element when you press a keystroke, - // so just get the first text element again - var $newFirstTextElement = inner$("div").first(); - var originalDivHeight = inner$("div").first().css("height"); - prepareDocument(numberOfRows, $newFirstTextElement); // N lines into the first div as a target - - helper.waitFor(function(){ // Wait for the DOM to register the new items - return inner$("div").first().text().length == 6; - }).done(function(){ // Once the DOM has registered the items - // Randomize the item heights (replicates images / headings etc) - inner$("div").each(function(index){ - var random = Math.floor(Math.random() * (50)) + 20; - $(this).css("height", random+"px"); - }); - - console.log(caretPosition(inner$)); - var newDivHeight = inner$("div").first().css("height"); - // has the new div height changed from the original div height - var heightHasChanged = originalDivHeight != newDivHeight; - expect(heightHasChanged).to.be(true); // expect the first line to be blank - }); - - // Is this Element now visible to the pad user? - helper.waitFor(function(){ // Wait for the DOM to register the new items - // Wait for the DOM to scroll into place - return isScrolledIntoView(inner$("div:nth-child("+numberOfRows+")"), inner$); - }).done(function(){ // Once the DOM has registered the items - // Randomize the item heights (replicates images / headings etc) - inner$("div").each(function(index){ - var random = Math.floor(Math.random() * (80 - 20 + 1)) + 20; - $(this).css("height", random+"px"); - }); - - var newDivHeight = inner$("div").first().css("height"); - // has the new div height changed from the original div height - var heightHasChanged = originalDivHeight != newDivHeight; - expect(heightHasChanged).to.be(true); // expect the first line to be blank - }); - var i = 0; - while(i < numberOfRows){ // press down arrow - keyEvent(inner$, 40, false, false); - i++; - } - - // Does scrolling back up the pad with the up arrow show the correct contents? - helper.waitFor(function(){ // Wait for the new position to be in place - try{ - // Wait for the DOM to scroll into place - return isScrolledIntoView(inner$("div:nth-child("+numberOfRows+")"), inner$); - }catch(e){ - return false; - } - }).done(function(){ // Once the DOM has registered the items - - var i = 0; - while(i < numberOfRows){ // press down arrow - keyEvent(inner$, 33, false, false); // doesn't work - i++; - } - - // Does scrolling back up the pad with the up arrow show the correct contents? - helper.waitFor(function(){ // Wait for the new position to be in place - try{ - // Wait for the DOM to scroll into place - return isScrolledIntoView(inner$("div:nth-child(0)"), inner$); - }catch(e){ - return false; - } - }).done(function(){ // Once the DOM has registered the items - - - }); - }); - - - var i = 0; - while(i < numberOfRows){ // press down arrow - keyEvent(inner$, 33, false, false); // doesn't work - i++; - } - - - // Does scrolling back up the pad with the up arrow show the correct contents? - helper.waitFor(function(){ // Wait for the new position to be in place - // Wait for the DOM to scroll into place - return isScrolledIntoView(inner$("div:nth-child(1)"), inner$); - }).done(function(){ // Once the DOM has registered the items - expect(true).to.be(true); - done(); - }); -*/ -}); -/* -// generates a random document with random content on n lines -const prepareDocument = (n, target) => { - let i = 0; - while (i < n) { // for each line - target.sendkeys(makeStr()); // generate a random string and send that to the editor - target.sendkeys('{enter}'); // generator an enter keypress - i++; // rinse n times - } -}; - -// sends a charCode to the window -const keyEvent = (target, charCode, ctrl, shift) => { - const e = new target.Event(helper.evtType); - if (ctrl) { - e.ctrlKey = true; // Control key - } - if (shift) { - e.shiftKey = true; // Shift Key - } - e.which = charCode; - e.keyCode = charCode; - target('#innerdocbody').trigger(e); -}; - - -// from http://stackoverflow.com/questions/1349404/generate-a-string-of-5-random-characters-in-javascript -const makeStr = () => { - let text = ''; - const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - - for (let i = 0; i < 5; i++) text += possible.charAt(Math.floor(Math.random() * possible.length)); - return text; -}; - -// from http://stackoverflow.com/questions/487073/check-if-element-is-visible-after-scrolling -const isScrolledIntoView = (elem, $) => { - const docViewTop = $(window).scrollTop(); - const docViewBottom = docViewTop + $(window).height(); - const elemTop = $(elem).offset().top; // how far the element is from the top of it's container - // how far plus the height of the elem.. IE is it all in? - let elemBottom = elemTop + $(elem).height(); - elemBottom -= 16; // don't ask, sorry but this is needed.. - return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)); -}; - -const caretPosition = ($) => { - const doc = $.window.document; - const pos = doc.getSelection(); - pos.y = pos.anchorNode.parentElement.offsetTop; - pos.x = pos.anchorNode.parentElement.offsetLeft; - return pos; -}; -*/ From 0ff8274d2e22edb5f9873a19b88599dc95c81442 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 02:11:28 -0500 Subject: [PATCH 250/357] tests: Fix `waitForPromise()` in `enter.js` --- src/tests/frontend/specs/enter.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tests/frontend/specs/enter.js b/src/tests/frontend/specs/enter.js index d0be72954..85ab43a2e 100644 --- a/src/tests/frontend/specs/enter.js +++ b/src/tests/frontend/specs/enter.js @@ -39,21 +39,22 @@ describe('enter keystroke', function () { while (i < numberOfLines) { $lastLine = helper.padInner$('div').last(); $lastLine.sendkeys('{enter}'); - await helper.waitFor(() => helper.padInner$('div').length > previousLineLength); + await helper.waitForPromise(() => helper.padInner$('div').length > previousLineLength); previousLineLength = helper.padInner$('div').length; // check we can see the caret.. i++; } - await helper.waitFor(() => helper.padInner$('div').length === numberOfLines + originalLength); + await helper.waitForPromise( + () => helper.padInner$('div').length === numberOfLines + originalLength); // is edited line fully visible? const lastLine = helper.padInner$('div').last(); const bottomOfLastLine = lastLine.offset().top + lastLine.height(); const scrolledWindow = helper.padChrome$('iframe')[0]; - await helper.waitFor(() => { - const scrolledAmount = scrolledWindow.contentWindow.pageYOffset + - scrolledWindow.contentWindow.innerHeight; + await helper.waitForPromise(() => { + const scrolledAmount = + scrolledWindow.contentWindow.pageYOffset + scrolledWindow.contentWindow.innerHeight; return scrolledAmount >= bottomOfLastLine; }); }); From 294f2a251f15df7dea7b4f50328b0bd7775a4269 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 01:57:37 -0500 Subject: [PATCH 251/357] lint: Fix bugs and style issues introduced in PR #4718 This fixes issues introduced in commit f8a19c4527cd1f4dd38ee45cf5769d47652421e0. --- src/tests/frontend/specs/drag_and_drop.js | 8 +- src/tests/frontend/specs/ordered_list.js | 220 +++++++++--------- src/tests/frontend/specs/pad_modal.js | 18 +- src/tests/frontend/specs/scrollTo.js | 42 ++-- .../specs/select_formatting_buttons.js | 10 +- src/tests/frontend/specs/unordered_list.js | 46 ++-- 6 files changed, 165 insertions(+), 179 deletions(-) diff --git a/src/tests/frontend/specs/drag_and_drop.js b/src/tests/frontend/specs/drag_and_drop.js index 1b08facd2..71141e055 100644 --- a/src/tests/frontend/specs/drag_and_drop.js +++ b/src/tests/frontend/specs/drag_and_drop.js @@ -27,9 +27,7 @@ describe('drag and drop', function () { const originalHTML = helper.padInner$('body').html(); const $undoButton = helper.padChrome$('.buttonicon-undo'); $undoButton.click(); - await helper.waitForPromise( - () => originalHTML !== helper.padInner$('body').html() - ); + await helper.waitForPromise(() => helper.padInner$('body').html() !== originalHTML); }); it('moves text back to its original place', function (done) { @@ -66,9 +64,7 @@ describe('drag and drop', function () { const originalHTML = helper.padInner$('body').html(); const $undoButton = helper.padChrome$('.buttonicon-undo'); $undoButton.click(); - await helper.waitForPromise( - () => originalHTML !== helper.padInner$('body').html() - ); + await helper.waitForPromise(() => helper.padInner$('body').html() !== originalHTML); }); it('moves text back to its original place', function (done) { diff --git a/src/tests/frontend/specs/ordered_list.js b/src/tests/frontend/specs/ordered_list.js index 3ad6f4ab1..d20c8138c 100644 --- a/src/tests/frontend/specs/ordered_list.js +++ b/src/tests/frontend/specs/ordered_list.js @@ -1,135 +1,137 @@ 'use strict'; -describe('assign ordered list', function () { - // create a new pad before each test run - beforeEach(function (cb) { - helper.newPad(cb); - this.timeout(60000); - }); - - it('inserts ordered list text', function (done) { - const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; - - const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist'); - $insertorderedlistButton.click(); - - helper.waitFor(() => inner$('div').first().find('ol li').length === 1).done(done); - }); - - context('when user presses Ctrl+Shift+N', function () { - context('and pad shortcut is enabled', function () { - beforeEach(async function () { - const originalHTML = helper.padInner$('body').html(); - makeSureShortcutIsEnabled('cmdShiftN'); - triggerCtrlShiftShortcut('N'); - await helper.waitForPromise( - () => helper.padInner$('body').html !== originalHTML); - }); - - it('inserts unordered list', function (done) { - helper.waitFor(() => helper.padInner$('div').first().find('ol li').length === 1).done(done); - }); +describe('ordered_list.js', function () { + describe('assign ordered list', function () { + // create a new pad before each test run + beforeEach(function (cb) { + helper.newPad(cb); + this.timeout(60000); }); - context('and pad shortcut is disabled', function () { - beforeEach(async function () { - const originalHTML = helper.padInner$('body').html(); - makeSureShortcutIsDisabled('cmdShiftN'); - triggerCtrlShiftShortcut('N'); - await helper.waitForPromise( - () => helper.padInner$('body').html !== originalHTML); + it('inserts ordered list text', function (done) { + const inner$ = helper.padInner$; + const chrome$ = helper.padChrome$; + + const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist'); + $insertorderedlistButton.click(); + + helper.waitFor(() => inner$('div').first().find('ol li').length === 1).done(done); + }); + + context('when user presses Ctrl+Shift+N', function () { + context('and pad shortcut is enabled', function () { + beforeEach(async function () { + const originalHTML = helper.padInner$('body').html(); + makeSureShortcutIsEnabled('cmdShiftN'); + triggerCtrlShiftShortcut('N'); + await helper.waitForPromise(() => helper.padInner$('body').html() !== originalHTML); + }); + + it('inserts unordered list', function (done) { + helper.waitFor(() => helper.padInner$('div').first().find('ol li').length === 1) + .done(done); + }); }); - it('does not insert unordered list', function (done) { - helper.waitFor( - () => helper.padInner$('div').first().find('ol li').length === 1).done(() => { - expect().fail(() => 'Unordered list inserted, should ignore shortcut'); - }).fail(() => { - done(); + context('and pad shortcut is disabled', function () { + beforeEach(async function () { + const originalHTML = helper.padInner$('body').html(); + makeSureShortcutIsDisabled('cmdShiftN'); + triggerCtrlShiftShortcut('N'); + await helper.waitForPromise(() => helper.padInner$('body').html() !== originalHTML); + }); + + it('does not insert unordered list', function (done) { + helper.waitFor(() => helper.padInner$('div').first().find('ol li').length === 1) + .done(() => { + expect().fail(() => 'Unordered list inserted, should ignore shortcut'); + }) + .fail(() => { + done(); + }); }); }); }); - }); - context('when user presses Ctrl+Shift+1', function () { - context('and pad shortcut is enabled', function () { - beforeEach(async function () { - const originalHTML = helper.padInner$('body').html(); - makeSureShortcutIsEnabled('cmdShift1'); - triggerCtrlShiftShortcut('1'); - await helper.waitForPromise( - () => helper.padInner$('body').html !== originalHTML); + context('when user presses Ctrl+Shift+1', function () { + context('and pad shortcut is enabled', function () { + beforeEach(async function () { + const originalHTML = helper.padInner$('body').html(); + makeSureShortcutIsEnabled('cmdShift1'); + triggerCtrlShiftShortcut('1'); + await helper.waitForPromise(() => helper.padInner$('body').html() !== originalHTML); + }); + + it('inserts unordered list', function (done) { + helper.waitFor(() => helper.padInner$('div').first().find('ol li').length === 1) + .done(done); + }); }); - it('inserts unordered list', function (done) { - helper.waitFor(() => helper.padInner$('div').first().find('ol li').length === 1).done(done); - }); - }); + context('and pad shortcut is disabled', function () { + beforeEach(async function () { + const originalHTML = helper.padInner$('body').html(); + makeSureShortcutIsDisabled('cmdShift1'); + triggerCtrlShiftShortcut('1'); + await helper.waitForPromise(() => helper.padInner$('body').html() !== originalHTML); + }); - context('and pad shortcut is disabled', function () { - beforeEach(async function () { - const originalHTML = helper.padInner$('body').html(); - makeSureShortcutIsDisabled('cmdShift1'); - triggerCtrlShiftShortcut('1'); - await helper.waitForPromise( - () => helper.padInner$('body').html !== originalHTML); - }); - - it('does not insert unordered list', function (done) { - helper.waitFor( - () => helper.padInner$('div').first().find('ol li').length === 1).done(() => { - expect().fail(() => 'Unordered list inserted, should ignore shortcut'); - }).fail(() => { - done(); + it('does not insert unordered list', function (done) { + helper.waitFor(() => helper.padInner$('div').first().find('ol li').length === 1) + .done(() => { + expect().fail(() => 'Unordered list inserted, should ignore shortcut'); + }) + .fail(() => { + done(); + }); }); }); }); - }); - xit('issue #1125 keeps the numbered list on enter for the new line', function (done) { - // EMULATES PASTING INTO A PAD - const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; + xit('issue #1125 keeps the numbered list on enter for the new line', function (done) { + // EMULATES PASTING INTO A PAD + const inner$ = helper.padInner$; + const chrome$ = helper.padChrome$; - const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist'); - $insertorderedlistButton.click(); + const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist'); + $insertorderedlistButton.click(); - // type a bit, make a line break and type again - const $firstTextElement = inner$('div span').first(); - $firstTextElement.sendkeys('line 1'); - $firstTextElement.sendkeys('{enter}'); - $firstTextElement.sendkeys('line 2'); - $firstTextElement.sendkeys('{enter}'); + // type a bit, make a line break and type again + const $firstTextElement = inner$('div span').first(); + $firstTextElement.sendkeys('line 1'); + $firstTextElement.sendkeys('{enter}'); + $firstTextElement.sendkeys('line 2'); + $firstTextElement.sendkeys('{enter}'); - helper.waitFor(() => inner$('div span').first().text().indexOf('line 2') === -1).done(() => { - const $newSecondLine = inner$('div').first().next(); - const hasOLElement = $newSecondLine.find('ol li').length === 1; - expect(hasOLElement).to.be(true); - expect($newSecondLine.text()).to.be('line 2'); - const hasLineNumber = $newSecondLine.find('ol').attr('start') === 2; - // This doesn't work because pasting in content doesn't work - expect(hasLineNumber).to.be(true); - done(); + helper.waitFor(() => inner$('div span').first().text().indexOf('line 2') === -1).done(() => { + const $newSecondLine = inner$('div').first().next(); + const hasOLElement = $newSecondLine.find('ol li').length === 1; + expect(hasOLElement).to.be(true); + expect($newSecondLine.text()).to.be('line 2'); + const hasLineNumber = $newSecondLine.find('ol').attr('start') === 2; + // This doesn't work because pasting in content doesn't work + expect(hasLineNumber).to.be(true); + done(); + }); }); + + const triggerCtrlShiftShortcut = (shortcutChar) => { + const inner$ = helper.padInner$; + const e = new inner$.Event(helper.evtType); + e.ctrlKey = true; + e.shiftKey = true; + e.which = shortcutChar.toString().charCodeAt(0); + inner$('#innerdocbody').trigger(e); + }; + + const makeSureShortcutIsDisabled = (shortcut) => { + helper.padChrome$.window.clientVars.padShortcutEnabled[shortcut] = false; + }; + const makeSureShortcutIsEnabled = (shortcut) => { + helper.padChrome$.window.clientVars.padShortcutEnabled[shortcut] = true; + }; }); - const triggerCtrlShiftShortcut = (shortcutChar) => { - const inner$ = helper.padInner$; - const e = new inner$.Event(helper.evtType); - e.ctrlKey = true; - e.shiftKey = true; - e.which = shortcutChar.toString().charCodeAt(0); - inner$('#innerdocbody').trigger(e); - }; - - const makeSureShortcutIsDisabled = (shortcut) => { - helper.padChrome$.window.clientVars.padShortcutEnabled[shortcut] = false; - }; - const makeSureShortcutIsEnabled = (shortcut) => { - helper.padChrome$.window.clientVars.padShortcutEnabled[shortcut] = true; - }; - describe('Pressing Tab in an OL increases and decreases indentation', function () { // create a new pad before each test run beforeEach(function (cb) { diff --git a/src/tests/frontend/specs/pad_modal.js b/src/tests/frontend/specs/pad_modal.js index fe665df54..03a97b82c 100644 --- a/src/tests/frontend/specs/pad_modal.js +++ b/src/tests/frontend/specs/pad_modal.js @@ -28,7 +28,7 @@ describe('Pad modal', function () { clickOnPadInner(); const $modal = helper.padChrome$(MODAL_SELECTOR); const modalIsVisible = $modal.hasClass('popup-show'); - helper.waitForPromise(() => $modal.hasClass('popup-show') === true); + expect(modalIsVisible).to.be(true); done(); @@ -37,12 +37,10 @@ describe('Pad modal', function () { context('and user clicks on pad outer', function () { it('does not close the modal', function (done) { + clickOnPadOuter(); const $modal = helper.padChrome$(MODAL_SELECTOR); const modalIsVisible = $modal.hasClass('popup-show'); - clickOnPadOuter(); - - helper.waitForPromise(() => $modal.hasClass('popup-show') === true); expect(modalIsVisible).to.be(true); done(); @@ -69,20 +67,16 @@ describe('Pad modal', function () { }); */ context('and user clicks on editor', function () { - it('closes the modal', function (done) { + it('closes the modal', async function () { clickOnPadInner(); - helper.waitForPromise(() => isModalOpened(MODAL_SELECTOR) === false); - expect(isModalOpened(MODAL_SELECTOR)).to.be(false); - done(); + await helper.waitForPromise(() => isModalOpened(MODAL_SELECTOR) === false); }); }); context('and user clicks on pad outer', function () { - it('closes the modal', function (done) { + it('closes the modal', async function () { clickOnPadOuter(); - helper.waitForPromise(() => isModalOpened(MODAL_SELECTOR) === false); - expect(isModalOpened(MODAL_SELECTOR)).to.be(false); - done(); + await helper.waitForPromise(() => isModalOpened(MODAL_SELECTOR) === false); }); }); }); diff --git a/src/tests/frontend/specs/scrollTo.js b/src/tests/frontend/specs/scrollTo.js index ba96f8164..0e89ab967 100755 --- a/src/tests/frontend/specs/scrollTo.js +++ b/src/tests/frontend/specs/scrollTo.js @@ -1,25 +1,24 @@ 'use strict'; -describe('scrolls to line', function () { - // create a new pad with URL hash set before each test run - before(async function () { - this.timeout(60000); - await new Promise((resolve, reject) => helper.newPad({ - cb: (err) => { - if (err != null) return reject(err); - resolve(); - }, - hash: 'L4', - })); - }); +describe('scrollTo.js', function () { + describe('scrolls to line', function () { + // create a new pad with URL hash set before each test run + before(async function () { + this.timeout(60000); + await new Promise((resolve, reject) => helper.newPad({ + cb: (err) => (err != null) ? reject(err) : resolve(), + hash: 'L4', + })); + }); - it('Scrolls down to Line 4', async function () { - this.timeout(10000); - const chrome$ = helper.padChrome$; - await helper.waitForPromise(() => { - const topOffset = parseInt(chrome$('iframe').first('iframe') - .contents().find('#outerdocbody').css('top')); - return (topOffset >= 100); + it('Scrolls down to Line 4', async function () { + this.timeout(10000); + const chrome$ = helper.padChrome$; + await helper.waitForPromise(() => { + const topOffset = parseInt(chrome$('iframe').first('iframe') + .contents().find('#outerdocbody').css('top')); + return (topOffset >= 100); + }); }); }); @@ -28,10 +27,7 @@ describe('scrolls to line', function () { before(async function () { this.timeout(60000); await new Promise((resolve, reject) => helper.newPad({ - cb: (err) => { - if (err != null) return reject(err); - resolve(); - }, + cb: (err) => (err != null) ? reject(err) : resolve(), hash: '#DEEZ123123NUTS', })); }); diff --git a/src/tests/frontend/specs/select_formatting_buttons.js b/src/tests/frontend/specs/select_formatting_buttons.js index 67b78ed0a..1e165e565 100644 --- a/src/tests/frontend/specs/select_formatting_buttons.js +++ b/src/tests/frontend/specs/select_formatting_buttons.js @@ -39,7 +39,7 @@ describe('select formatting buttons when selection has style applied', function const originalHTML = helper.padInner$('body').html(); const $undoButton = helper.padChrome$('.buttonicon-undo'); $undoButton.click(); - await helper.waitFor(() => originalHTML !== helper.padInner$('body').html()); + await helper.waitForPromise(() => helper.padInner$('body').html() !== originalHTML); }; const testIfFormattingButtonIsDeselected = function (style) { @@ -86,7 +86,6 @@ describe('select formatting buttons when selection has style applied', function const inner$ = helper.padInner$; const originalHTML = helper.padInner$('body').html(); - helper.waitFor(() => originalHTML !== helper.padInner$('body').html()); // get the first text element out of the inner iframe const $firstTextElement = inner$('div').first(); @@ -97,8 +96,7 @@ describe('select formatting buttons when selection has style applied', function e.ctrlKey = true; // Control key e.which = key.charCodeAt(0); // I, U, B, 5 inner$('#innerdocbody').trigger(e); - await helper.waitForPromise( - () => originalHTML !== helper.padInner$('body').html()); + await helper.waitForPromise(() => helper.padInner$('body').html() !== originalHTML); }; STYLES.forEach((style) => { @@ -130,12 +128,10 @@ describe('select formatting buttons when selection has style applied', function }); context('when user applies a style and the selection does not change', function () { - const style = STYLES[0]; // italic - it('selects the style button', async function () { + const style = STYLES[0]; // italic applyStyleOnLine(style, FIRST_LINE); await helper.waitForPromise(() => isButtonSelected(style) === true); - expect(isButtonSelected(style)).to.be(true); applyStyleOnLine(style, FIRST_LINE); }); }); diff --git a/src/tests/frontend/specs/unordered_list.js b/src/tests/frontend/specs/unordered_list.js index cd3bebc76..37173e593 100644 --- a/src/tests/frontend/specs/unordered_list.js +++ b/src/tests/frontend/specs/unordered_list.js @@ -1,33 +1,35 @@ 'use strict'; -describe('assign unordered list', function () { - // create a new pad before each test run - beforeEach(function (cb) { - helper.newPad(cb); - this.timeout(60000); - }); +describe('unordered_list.js', function () { + describe('assign unordered list', function () { + // create a new pad before each test run + beforeEach(function (cb) { + helper.newPad(cb); + this.timeout(60000); + }); - it('insert unordered list text then removes by outdent', function (done) { - const inner$ = helper.padInner$; - const chrome$ = helper.padChrome$; - const originalText = inner$('div').first().text(); + it('insert unordered list text then removes by outdent', function (done) { + const inner$ = helper.padInner$; + const chrome$ = helper.padChrome$; + const originalText = inner$('div').first().text(); - const $insertunorderedlistButton = chrome$('.buttonicon-insertunorderedlist'); - $insertunorderedlistButton.click(); + const $insertunorderedlistButton = chrome$('.buttonicon-insertunorderedlist'); + $insertunorderedlistButton.click(); - helper.waitFor(() => { - const newText = inner$('div').first().text(); - if (newText === originalText) { - return inner$('div').first().find('ul li').length === 1; - } - }).done(() => { - // remove indentation by bullet and ensure text string remains the same - chrome$('.buttonicon-outdent').click(); helper.waitFor(() => { const newText = inner$('div').first().text(); - return (newText === originalText); + if (newText === originalText) { + return inner$('div').first().find('ul li').length === 1; + } }).done(() => { - done(); + // remove indentation by bullet and ensure text string remains the same + chrome$('.buttonicon-outdent').click(); + helper.waitFor(() => { + const newText = inner$('div').first().text(); + return (newText === originalText); + }).done(() => { + done(); + }); }); }); }); From 2b112ac85170e5b8c36abe818e69e288b1c82fa9 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 7 Feb 2021 11:32:57 +0000 Subject: [PATCH 252/357] tests: Admin Frontend Test Coverage(#4717) Covers all frontend admin operations, runs separated in CI. --- .github/workflows/frontend-admin-tests.yml | 54 +++++++++ .github/workflows/frontend-tests.yml | 3 + .travis.yml | 7 ++ settings.json.template | 5 +- src/node/hooks/express/tests.js | 8 +- src/node/utils/Settings.js | 6 + src/tests/frontend/helper.js | 17 +++ src/tests/frontend/specs/adminplugins.js | 113 ++++++++++++++++++ src/tests/frontend/specs/adminroot.js | 29 +++++ src/tests/frontend/specs/adminsettings.js | 73 +++++++++++ .../frontend/specs/admintroubleshooting.js | 47 ++++++++ src/tests/frontend/travis/adminrunner.sh | 44 +++++++ src/tests/frontend/travis/remote_runner.js | 107 +++++++++-------- 13 files changed, 463 insertions(+), 50 deletions(-) create mode 100644 .github/workflows/frontend-admin-tests.yml create mode 100755 src/tests/frontend/specs/adminplugins.js create mode 100644 src/tests/frontend/specs/adminroot.js create mode 100644 src/tests/frontend/specs/adminsettings.js create mode 100755 src/tests/frontend/specs/admintroubleshooting.js create mode 100755 src/tests/frontend/travis/adminrunner.sh diff --git a/.github/workflows/frontend-admin-tests.yml b/.github/workflows/frontend-admin-tests.yml new file mode 100644 index 000000000..6f95a6c7f --- /dev/null +++ b/.github/workflows/frontend-admin-tests.yml @@ -0,0 +1,54 @@ +name: "Frontend admin tests" + +on: [push] + +jobs: + withplugins: + name: with plugins + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Run sauce-connect-action + shell: bash + env: + SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} + SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} + TRAVIS_JOB_NUMBER: ${{ github.run_id }}-${{ github.run_number }}-${{ github.job }} + run: src/tests/frontend/travis/sauce_tunnel.sh + + - name: Install all dependencies and symlink for ep_etherpad-lite + run: src/bin/installDeps.sh + + # We intentionally install a much old ep_align version to test update minor versions + - name: Install etherpad plugins + run: npm install ep_align@0.2.27 + + # Nuke plugin tests + - name: Install etherpad plugins + run: rm -Rf node_modules/ep_align/static/tests/* + + - name: export GIT_HASH to env + id: environment + run: echo "::set-output name=sha_short::$(git rev-parse --short ${{ github.sha }})" + + - name: Write custom settings.json with loglevel WARN + run: "sed 's/\"loglevel\": \"INFO\",/\"loglevel\": \"WARN\",/' < settings.json.template > settings.json" + + - name: Write custom settings.json that enables the Admin UI tests + run: "sed -i 's/\"enableAdminUITests\": false/\"enableAdminUITests\": true,\\n\"users\":{\"admin\":{\"password\":\"changeme\",\"is_admin\":true}}/' settings.json" + + - name: Remove standard frontend test files, so only admin tests are run + run: mv src/tests/frontend/specs/* /tmp && mv /tmp/admin*.js src/tests/frontend/specs + + - name: Run the frontend admin tests + shell: bash + env: + SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} + SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} + TRAVIS_JOB_NUMBER: ${{ github.run_id }}-${{ github.run_number }}-${{ github.job }} + GIT_HASH: ${{ steps.environment.outputs.sha_short }} + run: | + src/tests/frontend/travis/adminrunner.sh diff --git a/.github/workflows/frontend-tests.yml b/.github/workflows/frontend-tests.yml index f8a0e76b4..70cb197a3 100644 --- a/.github/workflows/frontend-tests.yml +++ b/.github/workflows/frontend-tests.yml @@ -88,6 +88,9 @@ jobs: - name: Write custom settings.json with loglevel WARN run: "sed 's/\"loglevel\": \"INFO\",/\"loglevel\": \"WARN\",/' < settings.json.template > settings.json" + - name: Write custom settings.json that enables the Admin UI tests + run: "sed -i 's/\"enableAdminUITests\": false/\"enableAdminUITests\": true,\\n\"users\":{\"admin\":{\"password\":\"changeme\",\"is_admin\":true}}/' settings.json" + # XXX we should probably run all tests, because plugins could effect their results - name: Remove standard frontend test files, so only plugin tests are run run: rm src/tests/frontend/specs/* diff --git a/.travis.yml b/.travis.yml index f6cf4dba8..8517e2a89 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,11 @@ _set_loglevel_warn: &set_loglevel_warn | settings.json.template >settings.json.template.new && mv settings.json.template.new settings.json.template +_enable_admin_tests: &enable_admin_tests | +sed -e 's/"enableAdminUITests": false/"enableAdminUITests": true,\n"users":{"admin":{"password":"changeme","is_admin":true}}/' \ + settings.json.template >settings.json.template.new && + mv settings.json.template.new settings.json.template + _install_libreoffice: &install_libreoffice >- sudo add-apt-repository -y ppa:libreoffice/ppa && sudo apt-get update && @@ -46,6 +51,7 @@ jobs: name: "Test the Frontend without Plugins" install: - *set_loglevel_warn + - *enable_admin_tests - "src/tests/frontend/travis/sauce_tunnel.sh" - "src/bin/installDeps.sh" - "export GIT_HASH=$(git rev-parse --verify --short HEAD)" @@ -80,6 +86,7 @@ jobs: name: "Test the Frontend Plugins only" install: - *set_loglevel_warn + - *enable_admin_tests - "src/tests/frontend/travis/sauce_tunnel.sh" - "src/bin/installDeps.sh" - "rm src/tests/frontend/specs/*" diff --git a/settings.json.template b/settings.json.template index 3c9c50837..b15b024ab 100644 --- a/settings.json.template +++ b/settings.json.template @@ -600,5 +600,8 @@ }, // logconfig /* Override any strings found in locale directories */ - "customLocaleStrings": {} + "customLocaleStrings": {}, + + /* Disable Admin UI tests */ + "enableAdminUITests": false } diff --git a/src/node/hooks/express/tests.js b/src/node/hooks/express/tests.js index de868ba3b..825c495ca 100644 --- a/src/node/hooks/express/tests.js +++ b/src/node/hooks/express/tests.js @@ -4,6 +4,7 @@ const path = require('path'); const npm = require('npm'); const fs = require('fs'); const util = require('util'); +const settings = require('../../utils/Settings'); exports.expressCreateServer = (hookName, args, cb) => { args.app.get('/tests/frontend/specs_list.js', async (req, res) => { @@ -18,6 +19,11 @@ exports.expressCreateServer = (hookName, args, cb) => { // Keep only *.js files files = files.filter((f) => f.endsWith('.js')); + // remove admin tests if the setting to enable them isn't in settings.json + if (!settings.enableAdminUITests) { + files = files.filter((file) => file.indexOf('admin') !== 0); + } + console.debug('Sent browser the following test specs:', files); res.setHeader('content-type', 'application/javascript'); res.end(`var specs_list = ${JSON.stringify(files)};\n`); @@ -49,7 +55,7 @@ exports.expressCreateServer = (hookName, args, cb) => { fs.readFile(specFilePath, (err, content) => { if (err) { return res.send(500); } - content = `describe(${JSON.stringify(specFileName)}, function(){ ${content} });`; + content = `describe(${JSON.stringify(specFileName)}, function(){${content}});`; if (!specFilePath.endsWith('index.html')) { res.setHeader('content-type', 'application/javascript'); diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index 65455ed18..9db195f64 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -380,6 +380,12 @@ exports.commitRateLimiting = { */ exports.importMaxFileSize = 50 * 1024 * 1024; +/* + * Disable Admin UI tests + */ +exports.enableAdminUITests = false; + + // checks if abiword is avaiable exports.abiwordAvailable = () => { if (exports.abiword != null) { diff --git a/src/tests/frontend/helper.js b/src/tests/frontend/helper.js index c38175fe1..0f8a32f1a 100644 --- a/src/tests/frontend/helper.js +++ b/src/tests/frontend/helper.js @@ -1,4 +1,5 @@ 'use strict'; + const helper = {}; // eslint-disable-line no-redeclare (function () { @@ -180,6 +181,22 @@ const helper = {}; // eslint-disable-line no-redeclare return padName; }; + helper.newAdmin = async function (page) { + // define the iframe + $iframe = $(``); + + // clean up inner iframe references + helper.admin$ = null; + + // remove old iframe + $('#iframe-container iframe').remove(); + // set new iframe + $('#iframe-container').append($iframe); + $iframe.one('load', () => { + helper.admin$ = getFrameJQuery($('#iframe-container iframe')); + }); + }; + helper.waitFor = function (conditionFunc, timeoutTime = 1900, intervalTime = 10) { const deferred = new $.Deferred(); diff --git a/src/tests/frontend/specs/adminplugins.js b/src/tests/frontend/specs/adminplugins.js new file mode 100755 index 000000000..ba00d3e48 --- /dev/null +++ b/src/tests/frontend/specs/adminplugins.js @@ -0,0 +1,113 @@ +'use strict'; + +describe('Plugins page', function () { + function timeout(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + before(async function () { + let success = false; + $.ajax({ + url: `${location.protocol}//admin:changeme@${location.hostname}:${location.port}/admin`, + type: 'GET', + success: () => success = true, + }); + await helper.waitForPromise(() => success === true); + }); + + // create a new pad before each test run + beforeEach(async function () { + helper.newAdmin('plugins'); + await helper.waitForPromise( + () => helper.admin$ && helper.admin$('.menu').find('li').length >= 3); + }); + + it('Lists some plugins', async function () { + await helper.waitForPromise(() => helper.admin$('.results').children().length > 50); + }); + + it('Searches for plugin', async function () { + helper.admin$('#search-query').val('ep_font_color'); + await helper.waitForPromise(() => helper.admin$('.results').children().length < 300, 5000); + await helper.waitForPromise(() => helper.admin$('.results').children().length > 0, 5000); + }); + + it('Attempt to Update a plugin', async function () { + this.timeout(120000); + + if (helper.admin$('.ep_align').length === 0) this.skip(); + + await helper.waitForPromise( + () => helper.admin$('.ep_align .version').text().split('.').length >= 2); + + const minorVersionBefore = + parseInt(helper.admin$('.ep_align .version').text().split('.')[1]); + + if (!minorVersionBefore) { + throw new Error('Unable to get minor number of plugin, is the plugin installed?'); + } + + if (minorVersionBefore !== 2) this.skip(); + + helper.waitForPromise( + () => helper.admin$('.ep_align .do-update').length === 1); + + await timeout(500); // HACK! Please submit better fix.. + const $doUpdateButton = helper.admin$('.ep_align .do-update'); + $doUpdateButton.click(); + + // ensure its showing as Updating + await helper.waitForPromise( + () => helper.admin$('.ep_align .message').text() === 'Updating'); + + // Ensure it's a higher minor version IE 0.3.x as 0.2.x was installed + // Coverage for https://github.com/ether/etherpad-lite/issues/4536 + await helper.waitForPromise(() => parseInt(helper.admin$( + '.ep_align .version' + ) + .text() + .split('.')[1]) > minorVersionBefore, 60000, 1000); + // allow 50 seconds, check every 1 second. + }); + it('Attempt to Install a plugin', async function () { + this.timeout(240000); + + helper.admin$('#search-query').val('ep_activepads'); + await helper.waitForPromise(() => helper.admin$('.results').children().length < 300, 6000); + await helper.waitForPromise(() => helper.admin$('.results').children().length > 0, 6000); + + // skip if we already have ep_activepads installed.. + if (helper.admin$('.ep_activepads .do-install').is(':visible') === false) this.skip(); + + helper.admin$('.ep_activepads .do-install').click(); + // ensure install has attempted to be started + await helper.waitForPromise( + () => helper.admin$('.ep_activepads .do-install').length !== 0, 120000); + // ensure its not showing installing any more + await helper.waitForPromise( + () => helper.admin$('.ep_activepads .message').text() === '', 180000); + // ensure uninstall button is visible + await helper.waitForPromise( + () => helper.admin$('.ep_activepads .do-uninstall').length !== 0, 120000); + }); + + it('Attempt to Uninstall a plugin', async function () { + this.timeout(360000); + await helper.waitForPromise( + () => helper.admin$('.ep_activepads .do-uninstall').length !== 0, 120000); + + helper.admin$('.ep_activepads .do-uninstall').click(); + + // ensure its showing uninstalling + await helper.waitForPromise( + () => helper.admin$('.ep_activepads .message') + .text() === 'Uninstalling', 120000); + // ensure its gone + await helper.waitForPromise( + () => helper.admin$('.ep_activepads').length === 0, 240000); + + helper.admin$('#search-query').val('ep_font'); + await helper.waitForPromise(() => helper.admin$('.results').children().length < 300, 240000); + await helper.waitForPromise(() => helper.admin$('.results').children().length > 0, 1000); + }); +}); diff --git a/src/tests/frontend/specs/adminroot.js b/src/tests/frontend/specs/adminroot.js new file mode 100644 index 000000000..7416c21b4 --- /dev/null +++ b/src/tests/frontend/specs/adminroot.js @@ -0,0 +1,29 @@ +'use strict'; + +describe('Admin page', function () { + before(async function () { + let success = false; + $.ajax({ + url: `${location.protocol}//admin:changeme@${location.hostname}:${location.port}/admin/`, + type: 'GET', + success: () => success = true, + }); + await helper.waitForPromise(() => success === true); + }); + + beforeEach(async function () { + helper.newAdmin(''); + await helper.waitForPromise( + () => helper.admin$ && helper.admin$('.menu').find('li').length >= 3); + }); + + it('Shows Plugin Manager Link', async function () { + helper.admin$('a[data-l10n-id="admin_plugins"]').is(':visible'); + }); + it('Shows Troubleshooting Info Link', async function () { + helper.admin$('a[data-l10n-id="admin_plugins_info"]').is(':visible'); + }); + it('Shows Settings Link', async function () { + helper.admin$('a[data-l10n-id="admin_settings"]').is(':visible'); + }); +}); diff --git a/src/tests/frontend/specs/adminsettings.js b/src/tests/frontend/specs/adminsettings.js new file mode 100644 index 000000000..0d7d0accb --- /dev/null +++ b/src/tests/frontend/specs/adminsettings.js @@ -0,0 +1,73 @@ +'use strict'; + +describe('Admin > Settings', function () { + this.timeout(480000); + + before(async function () { + let success = false; + $.ajax({ + url: `${location.protocol}//admin:changeme@${location.hostname}:${location.port}/admin/`, + type: 'GET', + success: () => success = true, + }); + await helper.waitForPromise(() => success === true); + }); + + beforeEach(async function () { + helper.newAdmin('settings'); + // needed, because the load event is fired to early + await helper.waitForPromise(() => helper.admin$ && helper.admin$('.settings').val().length > 0); + }); + + it('Are Settings visible, populated, does save work', async function () { + // save old value + const settings = helper.admin$('.settings').val(); + const settingsLength = settings.length; + + // set new value + helper.admin$('.settings').val((_, text) => `/* test */\n${text}`); + await helper.waitForPromise( + () => settingsLength + 11 === helper.admin$('.settings').val().length); + + // saves + helper.admin$('#saveSettings').click(); + await helper.waitForPromise(() => helper.admin$('#response').is(':visible')); + + // new value for settings.json should now be saved + // reset it to the old value + helper.newAdmin('settings'); + await helper.waitForPromise(() => helper.admin$ && helper.admin$('.settings').val().length > 0); + + // replace the test value with a line break + helper.admin$('.settings').val((_, text) => text.replace('/* test */\n', '')); + await helper.waitForPromise(() => settingsLength === helper.admin$('.settings').val().length); + + helper.admin$('#saveSettings').click(); // saves + await helper.waitForPromise(() => helper.admin$('#response').is(':visible')); + + // settings should have the old value + helper.newAdmin('settings'); + await helper.waitForPromise( + () => helper.admin$ && helper.admin$('.settings').val().length > 0, 36000); + expect(settings).to.be(helper.admin$('.settings').val()); + }); + + function timeout(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + it('restart works', async function () { + // restarts + helper.admin$('#restartEtherpad').click(); + + // Hacky... Other suggestions welcome.. + await timeout(200000); + let success = false; + $.ajax({ + url: `${location.protocol}//admin:changeme@${location.hostname}:${location.port}/admin`, + type: 'GET', + success: () => success = true, + }); + await helper.waitForPromise(() => success === true); + }); +}); diff --git a/src/tests/frontend/specs/admintroubleshooting.js b/src/tests/frontend/specs/admintroubleshooting.js new file mode 100755 index 000000000..6e428d3b1 --- /dev/null +++ b/src/tests/frontend/specs/admintroubleshooting.js @@ -0,0 +1,47 @@ +'use strict'; + +describe('Admin Troupbleshooting page', function () { + before(async function () { + let success = false; + $.ajax({ + url: `${location.protocol}//admin:changeme@${location.hostname}:${location.port}/admin`, + type: 'GET', + success: () => success = true, + }); + await helper.waitForPromise(() => success === true); + }); + + // create a new pad before each test run + beforeEach(async function () { + helper.newAdmin('plugins/info'); + await helper.waitForPromise( + () => helper.admin$ && helper.admin$('.menu').find('li').length >= 3); + }); + + it('Shows Troubleshooting page Manager', async function () { + helper.admin$('a[data-l10n-id="admin_plugins_info"]')[0].click(); + }); + + it('Shows a version number', async function () { + const content = helper.admin$('span[data-l10n-id="admin_plugins_info.version_number"]') + .parent().text(); + const version = content.split(': ')[1].split('.'); + if (version.length !== 3) { + throw new Error('Not displaying a semver version number'); + } + }); + + it('Lists installed parts', async function () { + const parts = helper.admin$('pre')[1]; + if (parts.textContent.indexOf('ep_etherpad-lite/adminsettings') === -1) { + throw new Error('No admin setting part being displayed...'); + } + }); + + it('Lists installed hooks', async function () { + const parts = helper.admin$('dt'); + if (parts.length <= 20) { + throw new Error('Not enough hooks being displayed...'); + } + }); +}); diff --git a/src/tests/frontend/travis/adminrunner.sh b/src/tests/frontend/travis/adminrunner.sh new file mode 100755 index 000000000..da20d2801 --- /dev/null +++ b/src/tests/frontend/travis/adminrunner.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +pecho() { printf %s\\n "$*"; } +log() { pecho "$@"; } +error() { log "ERROR: $@" >&2; } +fatal() { error "$@"; exit 1; } +try() { "$@" || fatal "'$@' failed"; } + +[ -n "${SAUCE_USERNAME}" ] || fatal "SAUCE_USERNAME is unset - exiting" +[ -n "${SAUCE_ACCESS_KEY}" ] || fatal "SAUCE_ACCESS_KEY is unset - exiting" + +# Move to the Etherpad base directory. +MY_DIR=$(try cd "${0%/*}" && try pwd -P) || exit 1 +try cd "${MY_DIR}/../../../.." + +log "Assuming src/bin/installDeps.sh has already been run" +node node_modules/ep_etherpad-lite/node/server.js --experimental-worker "${@}" & +ep_pid=$! + +log "Waiting for Etherpad to accept connections (http://localhost:9001)..." +connected=false +can_connect() { + curl -sSfo /dev/null http://localhost:9001/ || return 1 + connected=true +} +now() { date +%s; } +start=$(now) +while [ $(($(now) - $start)) -le 15 ] && ! can_connect; do + sleep 1 +done +[ "$connected" = true ] \ + || fatal "Timed out waiting for Etherpad to accept connections" +log "Successfully connected to Etherpad on http://localhost:9001" + +# start the remote runner +try cd "${MY_DIR}" +log "Starting the remote runner..." +node remote_runner.js admin +exit_code=$? + +kill "$(cat /tmp/sauce.pid)" +kill "$ep_pid" && wait "$ep_pid" +log "Done." +exit "$exit_code" diff --git a/src/tests/frontend/travis/remote_runner.js b/src/tests/frontend/travis/remote_runner.js index c5f3f1466..5a75dbe66 100644 --- a/src/tests/frontend/travis/remote_runner.js +++ b/src/tests/frontend/travis/remote_runner.js @@ -10,6 +10,8 @@ const config = { accessKey: process.env.SAUCE_ACCESS_KEY, }; +const isAdminRunner = process.argv[2] === 'admin'; + let allTestsPassed = true; // overwrite the default exit code // in case not all worker can be run (due to saucelabs limits), @@ -126,57 +128,66 @@ const sauceTestWorker = async.queue((testSettings, callback) => { }); }, 6); // run 6 tests in parrallel -// 1) Firefox on Linux -sauceTestWorker.push({ - platform: 'Windows 7', - browserName: 'firefox', - version: '52.0', -}); +if (!isAdminRunner) { + // 1) Firefox on Linux + sauceTestWorker.push({ + platform: 'Windows 7', + browserName: 'firefox', + version: '52.0', + }); -// 2) Chrome on Linux -sauceTestWorker.push({ - platform: 'Windows 7', - browserName: 'chrome', - version: '55.0', - args: ['--use-fake-device-for-media-stream'], -}); + // 2) Chrome on Linux + sauceTestWorker.push({ + platform: 'Windows 7', + browserName: 'chrome', + version: '55.0', + args: ['--use-fake-device-for-media-stream'], + }); -/* -// 3) Safari on OSX 10.15 -sauceTestWorker.push({ - 'platform' : 'OS X 10.15' - , 'browserName' : 'safari' - , 'version' : '13.1' -}); -*/ + /* + // 3) Safari on OSX 10.15 + sauceTestWorker.push({ + 'platform' : 'OS X 10.15' + , 'browserName' : 'safari' + , 'version' : '13.1' + }); + */ -// 4) Safari on OSX 10.14 -sauceTestWorker.push({ - platform: 'OS X 10.15', - browserName: 'safari', - version: '13.1', -}); -// IE 10 doesn't appear to be working anyway -/* -// 4) IE 10 on Win 8 -sauceTestWorker.push({ - 'platform' : 'Windows 8' - , 'browserName' : 'iexplore' - , 'version' : '10.0' -}); -*/ -// 5) Edge on Win 10 -sauceTestWorker.push({ - platform: 'Windows 10', - browserName: 'microsoftedge', - version: '83.0', -}); -// 6) Firefox on Win 7 -sauceTestWorker.push({ - platform: 'Windows 7', - browserName: 'firefox', - version: '78.0', -}); + // 4) Safari on OSX 10.14 + sauceTestWorker.push({ + platform: 'OS X 10.15', + browserName: 'safari', + version: '13.1', + }); + // IE 10 doesn't appear to be working anyway + /* + // 4) IE 10 on Win 8 + sauceTestWorker.push({ + 'platform' : 'Windows 8' + , 'browserName' : 'iexplore' + , 'version' : '10.0' + }); + */ + // 5) Edge on Win 10 + sauceTestWorker.push({ + platform: 'Windows 10', + browserName: 'microsoftedge', + version: '83.0', + }); + // 6) Firefox on Win 7 + sauceTestWorker.push({ + platform: 'Windows 7', + browserName: 'firefox', + version: '78.0', + }); +} else { + // 4) Safari on OSX 10.14 + sauceTestWorker.push({ + platform: 'OS X 10.15', + browserName: 'safari', + version: '13.1', + }); +} sauceTestWorker.drain(() => { process.exit(allTestsPassed ? 0 : 1); From 865a463154521d9d4b7c1a5b5e17eeb4f3be005c Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 7 Feb 2021 19:27:10 +0000 Subject: [PATCH 253/357] fix: release script output --- src/bin/release.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/release.js b/src/bin/release.js index 21f1ddbc2..42178caa9 100644 --- a/src/bin/release.js +++ b/src/bin/release.js @@ -39,7 +39,7 @@ if (!newVersion) { const changelogIncludesVersion = changelog.indexOf(newVersion) !== -1; if (!changelogIncludesVersion) { - throw new Error('No changelog record for ', newVersion, ' - please create changelog record'); + throw new Error(`No changelog record for ${newVersion}, please create changelog record`); } console.log('Okay looks good, lets create the package.json and package-lock.json'); From 3c2e0f0e16cbcae72b01b95c66d00d8a0bdbef63 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 28 Jan 2021 15:23:26 -0500 Subject: [PATCH 254/357] ace: Simplify Ace2Editor method creation * Delete the unused `optDoNow` parameter from `pendingInit()`. * Move the `setAuthorInfo()` 1st parameter check out of the wrapper and in to the `setAuthorInfo()` function itself. --- src/static/js/ace.js | 43 +++++++++++-------------------------- src/static/js/ace2_inner.js | 1 + 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/src/static/js/ace.js b/src/static/js/ace.js index 12e3dc535..c1c182728 100644 --- a/src/static/js/ace.js +++ b/src/static/js/ace.js @@ -53,23 +53,11 @@ function Ace2Editor() { let actionsPendingInit = []; - function pendingInit(func, optDoNow) { - return function () { - const that = this; - const args = arguments; - const action = function () { - func.apply(that, args); - }; - if (optDoNow) { - optDoNow.apply(that, args); - } - if (loaded) { - action(); - } else { - actionsPendingInit.push(action); - } - }; - } + const pendingInit = (func) => function (...args) { + const action = () => func.apply(this, args); + if (loaded) return action(); + actionsPendingInit.push(action); + }; function doActionsPendingInit() { _.each(actionsPendingInit, (fn, i) => { @@ -102,21 +90,14 @@ function Ace2Editor() { 'execCommand', 'replaceRange']; - _.each(aceFunctionsPendingInit, (fnName, i) => { - const prefix = 'ace_'; - const name = prefix + fnName; - editor[fnName] = pendingInit(function () { - if (fnName === 'setAuthorInfo') { - if (!arguments[0]) { - // setAuthorInfo AuthorId not set for some reason - } else { - info[prefix + fnName].apply(this, arguments); - } - } else { - info[prefix + fnName].apply(this, arguments); - } + for (const fnName of aceFunctionsPendingInit) { + // Note: info[`ace_${fnName}`] does not exist yet, so it can't be passed directly to + // pendingInit(). A simple wrapper is used to defer the info[`ace_${fnName}`] lookup until + // method invocation. + editor[fnName] = pendingInit(function (...args) { + info[`ace_${fnName}`].apply(this, args); }); - }); + } editor.exportText = function () { if (!loaded) return '(awaiting init)\n'; diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index e5de72f9c..31d6419ce 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -258,6 +258,7 @@ function Ace2Inner() { }; const setAuthorInfo = (author, info) => { + if (!author) return; // author ID not set for some reason if ((typeof author) !== 'string') { // Potentially caused by: https://github.com/ether/etherpad-lite/issues/2802"); throw new Error(`setAuthorInfo: author (${author}) is not a string`); From 8668017c623a9e673fb4070fd6a6ea1545b28ad8 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 28 Jan 2021 20:53:15 -0500 Subject: [PATCH 255/357] ace: Use `globalThis` instead of non-strict default context This is necessary before `'use strict';` can be added to the top of the file. --- src/static/js/ace.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/static/js/ace.js b/src/static/js/ace.js index c1c182728..12babadb7 100644 --- a/src/static/js/ace.js +++ b/src/static/js/ace.js @@ -255,13 +255,13 @@ plugins.ensure(function () {\n\ iframeHTML.push(' '); - // Expose myself to global for my child frame. - const thisFunctionsName = 'ChildAccessibleAce2Editor'; - (function () { return this; }())[thisFunctionsName] = Ace2Editor; + // eslint-disable-next-line node/no-unsupported-features/es-builtins + const gt = typeof globalThis === 'object' ? globalThis : window; + gt.ChildAccessibleAce2Editor = Ace2Editor; const outerScript = `\ editorId = ${JSON.stringify(info.id)};\n\ -editorInfo = parent[${JSON.stringify(thisFunctionsName)}].registry[editorId];\n\ +editorInfo = parent.ChildAccessibleAce2Editor.registry[editorId];\n\ window.onload = function () {\n\ window.onload = null;\n\ setTimeout(function () {\n\ From 1c9afa5168d92cf3fb256773fec18789a905b95f Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 29 Jan 2021 00:43:53 -0500 Subject: [PATCH 256/357] lint: src/static/js/ace.js --- src/static/js/ace.js | 141 ++++++++++++++++++------------------------- 1 file changed, 59 insertions(+), 82 deletions(-) diff --git a/src/static/js/ace.js b/src/static/js/ace.js index 12babadb7..c621566d9 100644 --- a/src/static/js/ace.js +++ b/src/static/js/ace.js @@ -1,3 +1,4 @@ +'use strict'; /** * This code is mostly from the old Etherpad. Please help us to comment this code. * This helps other people to understand this code better and helps them to improve it. @@ -25,28 +26,17 @@ const KERNEL_SOURCE = '../static/js/require-kernel.js'; -Ace2Editor.registry = { - nextId: 1, -}; - const hooks = require('./pluginfw/hooks'); const pluginUtils = require('./pluginfw/shared'); -const _ = require('./underscore'); -function scriptTag(source) { - return ( - `` - ); -} +const scriptTag = + (source) => ``; -function Ace2Editor() { +const Ace2Editor = function () { const ace2 = Ace2Editor; - const editor = {}; let info = { - editor, + editor: this, id: (ace2.registry.nextId++), }; let loaded = false; @@ -59,18 +49,17 @@ function Ace2Editor() { actionsPendingInit.push(action); }; - function doActionsPendingInit() { - _.each(actionsPendingInit, (fn, i) => { - fn(); - }); + const doActionsPendingInit = () => { + for (const fn of actionsPendingInit) fn(); actionsPendingInit = []; - } + }; ace2.registry[info.id] = info; // The following functions (prefixed by 'ace_') are exposed by editor, but // execution is delayed until init is complete - const aceFunctionsPendingInit = ['importText', + const aceFunctionsPendingInit = [ + 'importText', 'importAText', 'focus', 'setEditable', @@ -88,57 +77,41 @@ function Ace2Editor() { 'setAuthorSelectionRange', 'callWithAce', 'execCommand', - 'replaceRange']; + 'replaceRange', + ]; for (const fnName of aceFunctionsPendingInit) { // Note: info[`ace_${fnName}`] does not exist yet, so it can't be passed directly to // pendingInit(). A simple wrapper is used to defer the info[`ace_${fnName}`] lookup until // method invocation. - editor[fnName] = pendingInit(function (...args) { + this[fnName] = pendingInit(function (...args) { info[`ace_${fnName}`].apply(this, args); }); } - editor.exportText = function () { - if (!loaded) return '(awaiting init)\n'; - return info.ace_exportText(); - }; + this.exportText = () => loaded ? info.ace_exportText() : '(awaiting init)\n'; - editor.getFrame = function () { - return info.frame || null; - }; + this.getFrame = () => info.frame || null; - editor.getDebugProperty = function (prop) { - return info.ace_getDebugProperty(prop); - }; + this.getDebugProperty = (prop) => info.ace_getDebugProperty(prop); - editor.getInInternationalComposition = function () { - if (!loaded) return false; - return info.ace_getInInternationalComposition(); - }; + this.getInInternationalComposition = + () => loaded ? info.ace_getInInternationalComposition() : false; // prepareUserChangeset: // Returns null if no new changes or ACE not ready. Otherwise, bundles up all user changes // to the latest base text into a Changeset, which is returned (as a string if encodeAsString). - // If this method returns a truthy value, then applyPreparedChangesetToBase can be called - // at some later point to consider these changes part of the base, after which prepareUserChangeset - // must be called again before applyPreparedChangesetToBase. Multiple consecutive calls - // to prepareUserChangeset will return an updated changeset that takes into account the - // latest user changes, and modify the changeset to be applied by applyPreparedChangesetToBase - // accordingly. - editor.prepareUserChangeset = function () { - if (!loaded) return null; - return info.ace_prepareUserChangeset(); - }; + // If this method returns a truthy value, then applyPreparedChangesetToBase can be called at some + // later point to consider these changes part of the base, after which prepareUserChangeset must + // be called again before applyPreparedChangesetToBase. Multiple consecutive calls to + // prepareUserChangeset will return an updated changeset that takes into account the latest user + // changes, and modify the changeset to be applied by applyPreparedChangesetToBase accordingly. + this.prepareUserChangeset = () => loaded ? info.ace_prepareUserChangeset() : null; - editor.getUnhandledErrors = function () { - if (!loaded) return []; - // returns array of {error: , time: +new Date()} - return info.ace_getUnhandledErrors(); - }; + // returns array of {error: , time: +new Date()} + this.getUnhandledErrors = () => loaded ? info.ace_getUnhandledErrors() : []; - - function sortFilesByEmbeded(files) { + const sortFilesByEmbeded = (files) => { const embededFiles = []; let remoteFiles = []; @@ -156,43 +129,42 @@ function Ace2Editor() { } return {embeded: embededFiles, remote: remoteFiles}; - } - function pushStyleTagsFor(buffer, files) { + }; + + const pushStyleTagsFor = (buffer, files) => { const sorted = sortFilesByEmbeded(files); const embededFiles = sorted.embeded; const remoteFiles = sorted.remote; if (embededFiles.length > 0) { buffer.push(''); } - for (var i = 0, ii = remoteFiles.length; i < ii; i++) { - var file = remoteFiles[i]; - buffer.push(``); + for (const file of remoteFiles) { + buffer.push(``); } - } + }; - editor.destroy = pendingInit(() => { + this.destroy = pendingInit(() => { info.ace_dispose(); info.frame.parentNode.removeChild(info.frame); delete ace2.registry[info.id]; info = null; // prevent IE 6 closure memory leaks }); - editor.init = function (containerId, initialCode, doneFunc) { - editor.importText(initialCode); + this.init = function (containerId, initialCode, doneFunc) { + this.importText(initialCode); - info.onEditorReady = function () { + info.onEditorReady = () => { loaded = true; doActionsPendingInit(); doneFunc(); }; - (function () { + (() => { const doctype = ''; const iframeHTML = []; @@ -204,8 +176,8 @@ function Ace2Editor() { // and compressed, putting the compressed code from the named file directly into the // source here. // these lines must conform to a specific format because they are passed by the build script: - var includedCSS = []; - var $$INCLUDE_CSS = function (filename) { includedCSS.push(filename); }; + let includedCSS = []; + let $$INCLUDE_CSS = (filename) => { includedCSS.push(filename); }; $$INCLUDE_CSS('../static/css/iframe_editor.css'); // disableCustomScriptsAndStyles can be used to disable loading of custom scripts @@ -213,14 +185,15 @@ function Ace2Editor() { $$INCLUDE_CSS(`../static/css/pad.css?v=${clientVars.randomVersionString}`); } - var additionalCSS = _(hooks.callAll('aceEditorCSS')).map((path) => { + let additionalCSS = hooks.callAll('aceEditorCSS').map((path) => { if (path.match(/\/\//)) { // Allow urls to external CSS - http(s):// and //some/path.css return path; } return `../static/plugins/${path}`; }); includedCSS = includedCSS.concat(additionalCSS); - $$INCLUDE_CSS(`../static/skins/${clientVars.skinName}/pad.css?v=${clientVars.randomVersionString}`); + $$INCLUDE_CSS( + `../static/skins/${clientVars.skinName}/pad.css?v=${clientVars.randomVersionString}`); pushStyleTagsFor(iframeHTML, includedCSS); @@ -253,7 +226,8 @@ plugins.ensure(function () {\n\ iframeHTML, }); - iframeHTML.push(' '); + iframeHTML.push(' '); // eslint-disable-next-line node/no-unsupported-features/es-builtins const gt = typeof globalThis === 'object' ? globalThis : window; @@ -287,23 +261,24 @@ window.onload = function () {\n\ }, 0);\n\ }`; - const outerHTML = [doctype, ``]; + const outerHTML = + [doctype, ``]; - var includedCSS = []; - var $$INCLUDE_CSS = function (filename) { includedCSS.push(filename); }; + includedCSS = []; + $$INCLUDE_CSS = (filename) => { includedCSS.push(filename); }; $$INCLUDE_CSS('../static/css/iframe_editor.css'); $$INCLUDE_CSS(`../static/css/pad.css?v=${clientVars.randomVersionString}`); - var additionalCSS = _(hooks.callAll('aceEditorCSS')).map((path) => { + additionalCSS = hooks.callAll('aceEditorCSS').map((path) => { if (path.match(/\/\//)) { // Allow urls to external CSS - http(s):// and //some/path.css return path; } return `../static/plugins/${path}`; - } - ); + }); includedCSS = includedCSS.concat(additionalCSS); - $$INCLUDE_CSS(`../static/skins/${clientVars.skinName}/pad.css?v=${clientVars.randomVersionString}`); + $$INCLUDE_CSS( + `../static/skins/${clientVars.skinName}/pad.css?v=${clientVars.randomVersionString}`); pushStyleTagsFor(outerHTML, includedCSS); @@ -334,8 +309,10 @@ window.onload = function () {\n\ editorDocument.close(); })(); }; +}; - return editor; -} +Ace2Editor.registry = { + nextId: 1, +}; exports.Ace2Editor = Ace2Editor; From 5d96cf9754169d6a1efc7c194b69459eb3abb21b Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 7 Feb 2021 22:24:19 +0000 Subject: [PATCH 257/357] changelog 1.8.8 (#4725) * changelog 1.8.8 * for squash: refine changelog Co-authored-by: Richard Hansen --- CHANGELOG.md | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66f419735..c79240e7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,54 @@ -# Changes for the next release +# 1.8.8 ### Compatibility changes + * Node.js 10.17.0 or newer is now required. * The `bin/` and `tests/` directories were moved under `src/`. Symlinks were added at the old locations to hopefully avoid breaking user scripts and other - stuff. + tools. +* The top-level `package.json` file, added in v1.8.7, has been removed due to + problematic npm behavior. Whenever you install a plugin you will see the + following benign warnings that can be safely ignored: + + ``` + npm WARN saveError ENOENT: no such file or directory, open '.../package.json' + npm WARN enoent ENOENT: no such file or directory, open '.../package.json' + npm WARN develop No description + npm WARN develop No repository field. + npm WARN develop No README data + npm WARN develop No license field. + ``` ### Notable new features + +* You can now generate a link to a specific line number in a pad. Appending + `#L10` to a pad URL will cause your browser to scroll down to line 10. * Database performance is significantly improved. +* Admin UI now has test coverage in CI. (The tests are not enabled by default; + see `settings.json`.) +* New stats: `activePads`, `lastDisconnected`, `memoryUsageHeap`. +* For plugin authors: + * New `callAllSerial()` function that invokes hook functions like `callAll()` + except it supports asynchronous hook functions. + * `callFirst()` and `aCallFirst()` now support the same wide range of hook + function behaviors that `callAll()`, `aCallAll()`, and `callAllSerial()` + support. Also, they now warn when a hook function misbehaves. + * The `padCopy` and `padRemove` hooks now support asynchronous hook functions. + * Backend tests for plugins can now use the + [`ep_etherpad-lite/tests/backend/common`](src/tests/backend/common.js) + module to start the server and simplify API access. + +### Notable fixes + +* Enter key now stays in focus when inserted at bottom of viewport. +* Interface no longer loses color variants on disconnect/reconnect event. +* Removed npm.load to support npm7. +* General code quality is further significantly improved. +* Improved reliability of server shutdown and restart. +* No longer error if no buttons are visible. +* Update Socket.IO to address a minor security vulnerability. +* For plugin authors: + * Fixed `collectContentLineText` return value handling. # 1.8.7 ### Compatibility-breaking changes From 9070c71e9fc769cbfd944c09cf70eccd87f8f7b1 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 8 Feb 2021 11:01:27 +0000 Subject: [PATCH 258/357] color picker: allow full white --- src/static/js/farbtastic.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/static/js/farbtastic.js b/src/static/js/farbtastic.js index 78edcd094..c6e12da44 100644 --- a/src/static/js/farbtastic.js +++ b/src/static/js/farbtastic.js @@ -5,9 +5,9 @@ // https://github.com/mattfarina/farbtastic/blob/71ca15f4a09c8e5a08a1b0d1cf37ef028adf22f0/LICENSE.txt // edited by Sebastian Castro on 2020-04-06 (function ($) { - + var __debug = false; -var __factor = 0.8; +var __factor = 1; $.fn.farbtastic = function (options) { $.farbtastic(this, options); @@ -21,7 +21,7 @@ $.farbtastic = function (container, options) { $._farbtastic = function (container, options) { var fb = this; - + ///////////////////////////////////////////////////// /** @@ -141,7 +141,7 @@ $._farbtastic = function (container, options) { fb.ctxOverlay = fb.cnvOverlay[0].getContext('2d'); fb.ctxMask.translate(fb.mid, fb.mid); fb.ctxOverlay.translate(fb.mid, fb.mid); - + // Draw widget base layers. fb.drawCircle(); fb.drawMask(); @@ -215,7 +215,7 @@ $._farbtastic = function (container, options) { m.restore(); __debug && $('body').append('
                    drawCircle '+ (+(new Date()) - tm) +'ms'); }; - + /** * Draw the saturation/luminance mask. */ @@ -239,9 +239,9 @@ $._farbtastic = function (container, options) { outputPixel(x, y, c, a); } - } + } } - + // Method #1: direct pixel access (new Canvas). if (fb.ctxMask.getImageData) { // Create half-resolution buffer. @@ -302,7 +302,7 @@ $._farbtastic = function (container, options) { } cache.push([c, a]); }); - } + } __debug && $('body').append('
                    drawMask '+ (+(new Date()) - tm) +'ms'); } @@ -348,7 +348,7 @@ $._farbtastic = function (container, options) { // Draw markers fb.drawMarkers(); - + // Linked elements or callback if (typeof fb.callback == 'object') { // Set background/foreground color @@ -368,15 +368,15 @@ $._farbtastic = function (container, options) { fb.callback.call(fb, fb.color); } } - + /** * Helper for returning coordinates relative to the center. */ fb.widgetCoords = function (event) { return { - x: event.pageX - fb.offset.left - fb.mid, + x: event.pageX - fb.offset.left - fb.mid, y: event.pageY - fb.offset.top - fb.mid - }; + }; } /** @@ -439,7 +439,7 @@ $._farbtastic = function (container, options) { fb.packDX = function (c, a) { return '#' + fb.dec2hex(a) + fb.dec2hex(c) + fb.dec2hex(c) + fb.dec2hex(c); }; - + fb.pack = function (rgb) { var r = Math.round(rgb[0] * 255); var g = Math.round(rgb[1] * 255); From 7baa0cda02427d630fa858fdfd4de8cd24c75732 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 8 Feb 2021 11:07:07 +0000 Subject: [PATCH 259/357] tests: disabled checks shouldnt change behavior (#4729) --- src/tests/frontend/specs/ordered_list.js | 25 +++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/tests/frontend/specs/ordered_list.js b/src/tests/frontend/specs/ordered_list.js index d20c8138c..af922cb72 100644 --- a/src/tests/frontend/specs/ordered_list.js +++ b/src/tests/frontend/specs/ordered_list.js @@ -32,13 +32,22 @@ describe('ordered_list.js', function () { .done(done); }); }); - context('and pad shortcut is disabled', function () { beforeEach(async function () { const originalHTML = helper.padInner$('body').html(); makeSureShortcutIsDisabled('cmdShiftN'); triggerCtrlShiftShortcut('N'); - await helper.waitForPromise(() => helper.padInner$('body').html() !== originalHTML); + try { + // The HTML should not change. Briefly wait for it to change and fail if it does change. + await helper.waitForPromise( + () => helper.padInner$('body').html() !== originalHTML, 500); + } catch (err) { + // We want the test to pass if the above wait timed out. (If it timed out that + // means the HTML never changed, which is a good thing.) + // TODO: Re-throw non-"condition never became true" errors to avoid false positives. + } + // This will fail if the above `waitForPromise()` succeeded. + helper.waitForPromise(() => helper.padInner$('body').html() === originalHTML); }); it('does not insert unordered list', function (done) { @@ -73,7 +82,17 @@ describe('ordered_list.js', function () { const originalHTML = helper.padInner$('body').html(); makeSureShortcutIsDisabled('cmdShift1'); triggerCtrlShiftShortcut('1'); - await helper.waitForPromise(() => helper.padInner$('body').html() !== originalHTML); + try { + // The HTML should not change. Briefly wait for it to change and fail if it does change. + await helper.waitForPromise( + () => helper.padInner$('body').html() !== originalHTML, 500); + } catch (err) { + // We want the test to pass if the above wait timed out. (If it timed out that + // means the HTML never changed, which is a good thing.) + // TODO: Re-throw non-"condition never became true" errors to avoid false positives. + } + // This will fail if the above `waitForPromise()` succeeded. + helper.waitForPromise(() => helper.padInner$('body').html() === originalHTML); }); it('does not insert unordered list', function (done) { From e9bb2c410ec95207e4461d0862e3afdba6aa6113 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 8 Feb 2021 13:18:19 -0500 Subject: [PATCH 260/357] tests: Fix erroneous use of `waitForPromise()` in `ordered_list.js` `waitForPromise()` should always be used with `await` (either directly or with a later `await` on the returned Promise). In this case, the condition should be immediately true so `waitForPromise()` is not the right tool here. --- src/tests/frontend/specs/ordered_list.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tests/frontend/specs/ordered_list.js b/src/tests/frontend/specs/ordered_list.js index af922cb72..6f6b28144 100644 --- a/src/tests/frontend/specs/ordered_list.js +++ b/src/tests/frontend/specs/ordered_list.js @@ -32,6 +32,7 @@ describe('ordered_list.js', function () { .done(done); }); }); + context('and pad shortcut is disabled', function () { beforeEach(async function () { const originalHTML = helper.padInner$('body').html(); @@ -47,7 +48,7 @@ describe('ordered_list.js', function () { // TODO: Re-throw non-"condition never became true" errors to avoid false positives. } // This will fail if the above `waitForPromise()` succeeded. - helper.waitForPromise(() => helper.padInner$('body').html() === originalHTML); + expect(() => helper.padInner$('body').html()).to.be(originalHTML); }); it('does not insert unordered list', function (done) { @@ -92,7 +93,7 @@ describe('ordered_list.js', function () { // TODO: Re-throw non-"condition never became true" errors to avoid false positives. } // This will fail if the above `waitForPromise()` succeeded. - helper.waitForPromise(() => helper.padInner$('body').html() === originalHTML); + expect(() => helper.padInner$('body').html()).to.be(originalHTML); }); it('does not insert unordered list', function (done) { From 4c6a12ce2bb8f02e8ca90804fd1519f9f2f0b2ff Mon Sep 17 00:00:00 2001 From: Ole Langbehn Date: Mon, 8 Feb 2021 13:32:43 +0100 Subject: [PATCH 261/357] Add commitRateLimiting settings block to settings.json.docker The settings commitRateLimiting.duration and commitRateLimiting.points were not available in the settings.json.docker file, and therefore it was not possible to override their values via environment variables. Now, they can be overridden by setting the following env vars: * commitRateLimiting.duration: COMMIT_RATE_LIMIT_DURATION * commitRateLimiting.points: COMMIT_RATE_LIMIT_POINTS --- doc/docker.md | 2 ++ settings.json.docker | 16 ++++++++++++++++ settings.json.template | 4 ++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/doc/docker.md b/doc/docker.md index e12a8fb12..e6c343dcd 100644 --- a/doc/docker.md +++ b/doc/docker.md @@ -168,6 +168,8 @@ For the editor container, you can also make it full width by adding `full-width- | `IMPORT_MAX_FILE_SIZE` | maximum allowed file size when importing a pad, in bytes. | `52428800` (50 MB) | | `IMPORT_EXPORT_MAX_REQ_PER_IP` | maximum number of import/export calls per IP. | `10` | | `IMPORT_EXPORT_RATE_LIMIT_WINDOW` | the call rate for import/export requests will be estimated in this time window (in milliseconds) | `90000` | +| `COMMIT_RATE_LIMIT_DURATION` | duration of the rate limit window for commits by individual users/IPs (in seconds) | `1` | +| `COMMIT_RATE_LIMIT_POINTS` | maximum number of changes per IP to allow during the rate limit window | `10` | | `SUPPRESS_ERRORS_IN_PAD_TEXT` | Should we suppress errors from being visible in the default Pad Text? | `false` | | `REQUIRE_SESSION` | If this option is enabled, a user must have a session to access pads. This effectively allows only group pads to be accessed. | `false` | | `EDIT_ONLY` | Users may edit pads but not create new ones. Pad creation is only via the API. This applies both to group pads and regular pads. | `false` | diff --git a/settings.json.docker b/settings.json.docker index 6938b2713..de9cc3245 100644 --- a/settings.json.docker +++ b/settings.json.docker @@ -486,6 +486,22 @@ */ "importMaxFileSize": "${IMPORT_MAX_FILE_SIZE:52428800}", // 50 * 1024 * 1024 + /* + * From Etherpad 1.8.5 onwards, when Etherpad is in production mode commits from individual users are rate limited + * + * The default is to allow at most 10 changes per IP in a 1 second window. + * After that the change is rejected. + * + * See https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#websocket-single-connection-prevent-flooding for more options + */ + "commitRateLimiting": { + // duration of the rate limit window (seconds) + "duration": "${COMMIT_RATE_LIMIT_DURATION:1}", + + // maximum number of changes per IP to allow during the rate limit window + "points": "${COMMIT_RATE_LIMIT_POINTS:10}" + }, + /* * Toolbar buttons configuration. * diff --git a/settings.json.template b/settings.json.template index b15b024ab..d78035ad4 100644 --- a/settings.json.template +++ b/settings.json.template @@ -492,7 +492,7 @@ "importMaxFileSize": 52428800, // 50 * 1024 * 1024 /* - * From Etherpad 1.9.0 onwards, when Etherpad is in production mode commits from individual users are rate limited + * From Etherpad 1.8.5 onwards, when Etherpad is in production mode commits from individual users are rate limited * * The default is to allow at most 10 changes per IP in a 1 second window. * After that the change is rejected. @@ -503,7 +503,7 @@ // duration of the rate limit window (seconds) "duration": 1, - // maximum number of chanes per IP to allow during the rate limit window + // maximum number of changes per IP to allow during the rate limit window "points": 10 }, From 714e099b2a6e1e1a214cffe1834eedac1ea30e0e Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 9 Feb 2021 00:46:39 -0500 Subject: [PATCH 262/357] tests: Fix accidental functions in `ordered_list.js` This fixes a bug introduced in commit e9bb2c410ec95207e4461d0862e3afdba6aa6113. --- src/tests/frontend/specs/ordered_list.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/frontend/specs/ordered_list.js b/src/tests/frontend/specs/ordered_list.js index 6f6b28144..ddca576e2 100644 --- a/src/tests/frontend/specs/ordered_list.js +++ b/src/tests/frontend/specs/ordered_list.js @@ -48,7 +48,7 @@ describe('ordered_list.js', function () { // TODO: Re-throw non-"condition never became true" errors to avoid false positives. } // This will fail if the above `waitForPromise()` succeeded. - expect(() => helper.padInner$('body').html()).to.be(originalHTML); + expect(helper.padInner$('body').html()).to.be(originalHTML); }); it('does not insert unordered list', function (done) { @@ -93,7 +93,7 @@ describe('ordered_list.js', function () { // TODO: Re-throw non-"condition never became true" errors to avoid false positives. } // This will fail if the above `waitForPromise()` succeeded. - expect(() => helper.padInner$('body').html()).to.be(originalHTML); + expect(helper.padInner$('body').html()).to.be(originalHTML); }); it('does not insert unordered list', function (done) { From 1e3f35228166789d390fc254cc0ed62e234764b3 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 9 Feb 2021 01:25:11 -0500 Subject: [PATCH 263/357] openapi: Turn down logging verbosity --- src/node/hooks/express/openapi.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/node/hooks/express/openapi.js b/src/node/hooks/express/openapi.js index 950b56601..bf7e5b147 100644 --- a/src/node/hooks/express/openapi.js +++ b/src/node/hooks/express/openapi.js @@ -25,7 +25,7 @@ const settings = require('../../utils/Settings'); const isValidJSONPName = require('./isValidJSONPName'); const log4js = require('log4js'); -const apiLogger = log4js.getLogger('API'); +const logger = log4js.getLogger('API'); // https://github.com/OAI/OpenAPI-Specification/tree/master/schemas/v3.0 const OPENAPI_VERSION = '3.0.2'; // Swagger/OAS version @@ -607,8 +607,9 @@ exports.expressCreateServer = (hookName, args, cb) => { const fields = Object.assign({}, header, params, query, formData); - // log request - apiLogger.info(`REQUEST, v${version}:${funcName}, ${JSON.stringify(fields)}`); + if (logger.isDebugEnabled()) { + logger.debug(`REQUEST, v${version}:${funcName}, ${JSON.stringify(fields)}`); + } // pass to api handler const data = await apiHandler.handle(version, funcName, fields, req, res).catch((err) => { @@ -623,7 +624,7 @@ exports.expressCreateServer = (hookName, args, cb) => { } else { // an unknown error happened // log it and throw internal error - apiLogger.error(err.stack || err.toString()); + logger.error(err.stack || err.toString()); throw new createHTTPError.InternalError('internal error'); } }); @@ -631,8 +632,9 @@ exports.expressCreateServer = (hookName, args, cb) => { // return in common format const response = {code: 0, message: 'ok', data: data || null}; - // log response - apiLogger.info(`RESPONSE, ${funcName}, ${JSON.stringify(response)}`); + if (logger.isDebugEnabled()) { + logger.debug(`RESPONSE, ${funcName}, ${JSON.stringify(response)}`); + } // return the response data return response; From cb1116607e322e7e4d4de87338ac683854e984b2 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 9 Feb 2021 01:26:14 -0500 Subject: [PATCH 264/357] Revert "backend tests: change loglevel to WARN (#4514)" Logging verbosity of the openapi handlers was turned down so GitHub should be happier with INFO now. This makes it easier to troubleshoot problems. This reverts commit b98aaf4904f676649e87f8e9ccfd842dd04b3ac2. --- src/tests/frontend/travis/runnerBackend.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tests/frontend/travis/runnerBackend.sh b/src/tests/frontend/travis/runnerBackend.sh index 3bf2da1f0..8a2e1bab4 100755 --- a/src/tests/frontend/travis/runnerBackend.sh +++ b/src/tests/frontend/travis/runnerBackend.sh @@ -15,8 +15,6 @@ s!"soffice":[^,]*!"soffice": "/usr/bin/soffice"! # Reduce rate limit aggressiveness s!"max":[^,]*!"max": 100! s!"points":[^,]*!"points": 1000! -# GitHub does not like our output -s!"loglevel":[^,]*!"loglevel": "WARN"! ' settings.json.template >settings.json log "Assuming src/bin/installDeps.sh has already been run" From 105f8b0ccb0ff45668504bc9374ea2309fd7a059 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 8 Feb 2021 23:38:26 -0500 Subject: [PATCH 265/357] server: Use a log4js logger object for logging --- src/node/server.js | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/node/server.js b/src/node/server.js index 486975712..bdbc60636 100755 --- a/src/node/server.js +++ b/src/node/server.js @@ -49,6 +49,8 @@ const plugins = require('../static/js/pluginfw/plugins'); const settings = require('./utils/Settings'); const util = require('util'); +const logger = log4js.getLogger('server'); + const State = { INITIAL: 1, STARTING: 2, @@ -62,9 +64,9 @@ const State = { let state = State.INITIAL; const removeSignalListener = (signal, listener) => { - console.debug(`Removing ${signal} listener because it might interfere with shutdown tasks. ` + - `Function code:\n${listener.toString()}\n` + - `Current stack:\n${(new Error()).stack.split('\n').slice(1).join('\n')}`); + logger.debug(`Removing ${signal} listener because it might interfere with shutdown tasks. ` + + `Function code:\n${listener.toString()}\n` + + `Current stack:\n${(new Error()).stack.split('\n').slice(1).join('\n')}`); process.off(signal, listener); }; @@ -86,7 +88,7 @@ exports.start = async () => { default: throw new Error(`unknown State: ${state.toString()}`); } - console.log('Starting Etherpad...'); + logger.info('Starting Etherpad...'); state = State.STARTING; // Check if Etherpad version is up-to-date @@ -125,13 +127,13 @@ exports.start = async () => { .filter((plugin) => plugin.package.name !== 'ep_etherpad-lite') .map((plugin) => `${plugin.package.name}@${plugin.package.version}`) .join(', '); - console.info(`Installed plugins: ${installedPlugins}`); - console.debug(`Installed parts:\n${plugins.formatParts()}`); - console.debug(`Installed hooks:\n${plugins.formatHooks()}`); + logger.info(`Installed plugins: ${installedPlugins}`); + logger.debug(`Installed parts:\n${plugins.formatParts()}`); + logger.debug(`Installed hooks:\n${plugins.formatHooks()}`); await hooks.aCallAll('loadSettings', {settings}); await hooks.aCallAll('createServer'); - console.log('Etherpad is running'); + logger.info('Etherpad is running'); state = State.RUNNING; while (runningCallbacks.length > 0) setImmediate(runningCallbacks.pop()); @@ -159,7 +161,7 @@ exports.stop = async () => { default: throw new Error(`unknown State: ${state.toString()}`); } - console.log('Stopping Etherpad...'); + logger.info('Stopping Etherpad...'); state = State.STOPPING; let timeout = null; await Promise.race([ @@ -169,7 +171,7 @@ exports.stop = async () => { }), ]); clearTimeout(timeout); - console.log('Etherpad stopped'); + logger.info('Etherpad stopped'); state = State.STOPPED; while (stoppedCallbacks.length > 0) setImmediate(stoppedCallbacks.pop()); }; @@ -180,13 +182,13 @@ exports.exit = async (err = null) => { /* eslint-disable no-process-exit */ if (err === 'SIGTERM') { // Termination from SIGTERM is not treated as an abnormal termination. - console.log('Received SIGTERM signal'); + logger.info('Received SIGTERM signal'); err = null; } else if (err != null) { - console.error(err.stack || err.toString()); + logger.error(err.stack || err.toString()); process.exitCode = 1; if (exitCalled) { - console.error('Error occurred while waiting to exit. Forcing an immediate unclean exit...'); + logger.error('Error occurred while waiting to exit. Forcing an immediate unclean exit...'); process.exit(1); } } @@ -211,20 +213,20 @@ exports.exit = async (err = null) => { default: throw new Error(`unknown State: ${state.toString()}`); } - console.log('Exiting...'); + logger.info('Exiting...'); state = State.EXITING; while (exitCallbacks.length > 0) setImmediate(exitCallbacks.pop()); // Node.js should exit on its own without further action. Add a timeout to force Node.js to exit // just in case something failed to get cleaned up during the shutdown hook. unref() is called on // the timeout so that the timeout itself does not prevent Node.js from exiting. setTimeout(() => { - console.error('Something that should have been cleaned up during the shutdown hook (such as ' + - 'a timer, worker thread, or open connection) is preventing Node.js from exiting'); + logger.error('Something that should have been cleaned up during the shutdown hook (such as ' + + 'a timer, worker thread, or open connection) is preventing Node.js from exiting'); wtfnode.dump(); - console.error('Forcing an unclean exit...'); + logger.error('Forcing an unclean exit...'); process.exit(1); }, 5000).unref(); - console.log('Waiting for Node.js to exit...'); + logger.info('Waiting for Node.js to exit...'); state = State.WAITING_FOR_EXIT; /* eslint-enable no-process-exit */ }; From 5b327b63acdc5fb8bc96f75dde37082142b03d25 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 8 Feb 2021 23:48:20 -0500 Subject: [PATCH 266/357] server: Simplify gating of state transition waiters --- src/node/server.js | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/node/server.js b/src/node/server.js index bdbc60636..453860a0a 100755 --- a/src/node/server.js +++ b/src/node/server.js @@ -63,6 +63,14 @@ const State = { let state = State.INITIAL; +class Gate extends Promise { + constructor() { + let res; + super((resolve) => { res = resolve; }); + this.resolve = res; + } +} + const removeSignalListener = (signal, listener) => { logger.debug(`Removing ${signal} listener because it might interfere with shutdown tasks. ` + `Function code:\n${listener.toString()}\n` + @@ -70,13 +78,13 @@ const removeSignalListener = (signal, listener) => { process.off(signal, listener); }; -const runningCallbacks = []; +let startDoneGate; exports.start = async () => { switch (state) { case State.INITIAL: break; case State.STARTING: - await new Promise((resolve) => runningCallbacks.push(resolve)); + await startDoneGate; // fall through case State.RUNNING: return express.server; @@ -89,6 +97,7 @@ exports.start = async () => { throw new Error(`unknown State: ${state.toString()}`); } logger.info('Starting Etherpad...'); + startDoneGate = new Gate(); state = State.STARTING; // Check if Etherpad version is up-to-date @@ -135,13 +144,13 @@ exports.start = async () => { logger.info('Etherpad is running'); state = State.RUNNING; - while (runningCallbacks.length > 0) setImmediate(runningCallbacks.pop()); + startDoneGate.resolve(); // Return the HTTP server to make it easier to write tests. return express.server; }; -const stoppedCallbacks = []; +let stopDoneGate; exports.stop = async () => { switch (state) { case State.STARTING: @@ -151,7 +160,7 @@ exports.stop = async () => { case State.RUNNING: break; case State.STOPPING: - await new Promise((resolve) => stoppedCallbacks.push(resolve)); + await stopDoneGate; // fall through case State.INITIAL: case State.STOPPED: @@ -162,6 +171,7 @@ exports.stop = async () => { throw new Error(`unknown State: ${state.toString()}`); } logger.info('Stopping Etherpad...'); + let stopDoneGate = new Gate(); state = State.STOPPING; let timeout = null; await Promise.race([ @@ -173,10 +183,10 @@ exports.stop = async () => { clearTimeout(timeout); logger.info('Etherpad stopped'); state = State.STOPPED; - while (stoppedCallbacks.length > 0) setImmediate(stoppedCallbacks.pop()); + stopDoneGate.resolve(); }; -const exitCallbacks = []; +let exitGate; let exitCalled = false; exports.exit = async (err = null) => { /* eslint-disable no-process-exit */ @@ -206,7 +216,7 @@ exports.exit = async (err = null) => { case State.STOPPED: break; case State.EXITING: - await new Promise((resolve) => exitCallbacks.push(resolve)); + await exitGate; // fall through case State.WAITING_FOR_EXIT: return; @@ -214,8 +224,9 @@ exports.exit = async (err = null) => { throw new Error(`unknown State: ${state.toString()}`); } logger.info('Exiting...'); + exitGate = new Gate(); state = State.EXITING; - while (exitCallbacks.length > 0) setImmediate(exitCallbacks.pop()); + exitGate.resolve(); // Node.js should exit on its own without further action. Add a timeout to force Node.js to exit // just in case something failed to get cleaned up during the shutdown hook. unref() is called on // the timeout so that the timeout itself does not prevent Node.js from exiting. From 5999d8cd442d0beee5f4293f114991624dec03e7 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 9 Feb 2021 00:04:20 -0500 Subject: [PATCH 267/357] server: Only pass `err` arg to `exports.exit()` The `uncaughtException` event handlers are passed two arguments: error and "origin". The `exports.exit()` argument doesn't expect a second argument. --- src/node/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/server.js b/src/node/server.js index 453860a0a..c05cba17b 100755 --- a/src/node/server.js +++ b/src/node/server.js @@ -108,7 +108,7 @@ exports.start = async () => { stats.gauge('memoryUsage', () => process.memoryUsage().rss); stats.gauge('memoryUsageHeap', () => process.memoryUsage().heapUsed); - process.on('uncaughtException', exports.exit); + process.on('uncaughtException', (err) => exports.exit(err)); // As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an // unhandled rejection into an uncaught exception, which does cause Node.js to exit. process.on('unhandledRejection', (err) => { throw err; }); From ebdb2798ff857260b308b7552040758bafdc6dcd Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 9 Feb 2021 00:03:05 -0500 Subject: [PATCH 268/357] server: Fix handling of errors during startup and shutdown Before, an unhandled rejection or uncaught exception during startup would cause `exports.exit()` to wait forever for startup completion. Similarly, an error during shutdown would cause `exports.exit()` to wait forever for shutdown to complete. Now any error during startup or shutdown triggers an immediate exit. --- src/node/server.js | 110 ++++++++++++++++++++++++++------------------- 1 file changed, 64 insertions(+), 46 deletions(-) diff --git a/src/node/server.js b/src/node/server.js index c05cba17b..c1adfe073 100755 --- a/src/node/server.js +++ b/src/node/server.js @@ -59,6 +59,7 @@ const State = { STOPPED: 5, EXITING: 6, WAITING_FOR_EXIT: 7, + STATE_TRANSITION_FAILED: 8, }; let state = State.INITIAL; @@ -85,13 +86,15 @@ exports.start = async () => { break; case State.STARTING: await startDoneGate; - // fall through + // Retry. Don't fall through because it might have transitioned to STATE_TRANSITION_FAILED. + return await exports.start(); case State.RUNNING: return express.server; case State.STOPPING: case State.STOPPED: case State.EXITING: case State.WAITING_FOR_EXIT: + case State.STATE_TRANSITION_FAILED: throw new Error('restart not supported'); default: throw new Error(`unknown State: ${state.toString()}`); @@ -99,48 +102,54 @@ exports.start = async () => { logger.info('Starting Etherpad...'); startDoneGate = new Gate(); state = State.STARTING; + try { + // Check if Etherpad version is up-to-date + UpdateCheck.check(); - // Check if Etherpad version is up-to-date - UpdateCheck.check(); + // start up stats counting system + const stats = require('./stats'); + stats.gauge('memoryUsage', () => process.memoryUsage().rss); + stats.gauge('memoryUsageHeap', () => process.memoryUsage().heapUsed); - // start up stats counting system - const stats = require('./stats'); - stats.gauge('memoryUsage', () => process.memoryUsage().rss); - stats.gauge('memoryUsageHeap', () => process.memoryUsage().heapUsed); + process.on('uncaughtException', (err) => exports.exit(err)); + // As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an + // unhandled rejection into an uncaught exception, which does cause Node.js to exit. + process.on('unhandledRejection', (err) => { throw err; }); - process.on('uncaughtException', (err) => exports.exit(err)); - // As of v14, Node.js does not exit when there is an unhandled Promise rejection. Convert an - // unhandled rejection into an uncaught exception, which does cause Node.js to exit. - process.on('unhandledRejection', (err) => { throw err; }); - - for (const signal of ['SIGINT', 'SIGTERM']) { - // Forcibly remove other signal listeners to prevent them from terminating node before we are - // done cleaning up. See https://github.com/andywer/threads.js/pull/329 for an example of a - // problematic listener. This means that exports.exit is solely responsible for performing all - // necessary cleanup tasks. - for (const listener of process.listeners(signal)) { - removeSignalListener(signal, listener); + for (const signal of ['SIGINT', 'SIGTERM']) { + // Forcibly remove other signal listeners to prevent them from terminating node before we are + // done cleaning up. See https://github.com/andywer/threads.js/pull/329 for an example of a + // problematic listener. This means that exports.exit is solely responsible for performing all + // necessary cleanup tasks. + for (const listener of process.listeners(signal)) { + removeSignalListener(signal, listener); + } + process.on(signal, exports.exit); + // Prevent signal listeners from being added in the future. + process.on('newListener', (event, listener) => { + if (event !== signal) return; + removeSignalListener(signal, listener); + }); } - process.on(signal, exports.exit); - // Prevent signal listeners from being added in the future. - process.on('newListener', (event, listener) => { - if (event !== signal) return; - removeSignalListener(signal, listener); - }); - } - await util.promisify(npm.load)(); - await db.init(); - await plugins.update(); - const installedPlugins = Object.values(pluginDefs.plugins) - .filter((plugin) => plugin.package.name !== 'ep_etherpad-lite') - .map((plugin) => `${plugin.package.name}@${plugin.package.version}`) - .join(', '); - logger.info(`Installed plugins: ${installedPlugins}`); - logger.debug(`Installed parts:\n${plugins.formatParts()}`); - logger.debug(`Installed hooks:\n${plugins.formatHooks()}`); - await hooks.aCallAll('loadSettings', {settings}); - await hooks.aCallAll('createServer'); + await util.promisify(npm.load)(); + await db.init(); + await plugins.update(); + const installedPlugins = Object.values(pluginDefs.plugins) + .filter((plugin) => plugin.package.name !== 'ep_etherpad-lite') + .map((plugin) => `${plugin.package.name}@${plugin.package.version}`) + .join(', '); + logger.info(`Installed plugins: ${installedPlugins}`); + logger.debug(`Installed parts:\n${plugins.formatParts()}`); + logger.debug(`Installed hooks:\n${plugins.formatHooks()}`); + await hooks.aCallAll('loadSettings', {settings}); + await hooks.aCallAll('createServer'); + } catch (err) { + logger.error('Error occurred while starting Etherpad'); + state = State.STATE_TRANSITION_FAILED; + startDoneGate.resolve(); + return await exports.exit(err); + } logger.info('Etherpad is running'); state = State.RUNNING; @@ -166,6 +175,7 @@ exports.stop = async () => { case State.STOPPED: case State.EXITING: case State.WAITING_FOR_EXIT: + case State.STATE_TRANSITION_FAILED: return; default: throw new Error(`unknown State: ${state.toString()}`); @@ -173,14 +183,21 @@ exports.stop = async () => { logger.info('Stopping Etherpad...'); let stopDoneGate = new Gate(); state = State.STOPPING; - let timeout = null; - await Promise.race([ - hooks.aCallAll('shutdown'), - new Promise((resolve, reject) => { - timeout = setTimeout(() => reject(new Error('Timed out waiting for shutdown tasks')), 3000); - }), - ]); - clearTimeout(timeout); + try { + let timeout = null; + await Promise.race([ + hooks.aCallAll('shutdown'), + new Promise((resolve, reject) => { + timeout = setTimeout(() => reject(new Error('Timed out waiting for shutdown tasks')), 3000); + }), + ]); + clearTimeout(timeout); + } catch (err) { + logger.error('Error occurred while stopping Etherpad'); + state = State.STATE_TRANSITION_FAILED; + stopDoneGate.resolve(); + return await exports.exit(err); + } logger.info('Etherpad stopped'); state = State.STOPPED; stopDoneGate.resolve(); @@ -214,6 +231,7 @@ exports.exit = async (err = null) => { return await exports.exit(); case State.INITIAL: case State.STOPPED: + case State.STATE_TRANSITION_FAILED: break; case State.EXITING: await exitGate; From 94a65062c7cf494bc0d6ee89754f2b123a2e6ac8 Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 9 Feb 2021 09:41:41 +0000 Subject: [PATCH 269/357] checkPlugins: remove lib/travis.yml and add github badges if they aren't present (#4731) A future PR should remove all Travis badges and CI jobs. --- src/bin/plugins/checkPlugin.js | 57 +++++++++------------------ src/bin/plugins/lib/travis.yml | 70 ---------------------------------- 2 files changed, 18 insertions(+), 109 deletions(-) delete mode 100644 src/bin/plugins/lib/travis.yml diff --git a/src/bin/plugins/checkPlugin.js b/src/bin/plugins/checkPlugin.js index 6d1144bf2..ee96d63c3 100755 --- a/src/bin/plugins/checkPlugin.js +++ b/src/bin/plugins/checkPlugin.js @@ -277,7 +277,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.warn('Run npm install in the plugin folder and commit the package-lock.json file.'); } } - if (files.indexOf('readme') === -1 && files.indexOf('readme.md') === -1) { console.warn('README.md file not found, please create'); if (autoFix) { @@ -310,8 +309,8 @@ fs.readdir(pluginPath, (err, rootFiles) => { } - if (files.indexOf('readme') !== -1 && files.indexOf('readme.md') !== -1) { - const readme = + if (readMeFileName) { + let readme = fs.readFileSync(`${pluginPath}/${readMeFileName}`, {encoding: 'utf8', flag: 'r'}); if (readme.toLowerCase().indexOf('license') === -1) { console.warn('No license section in README'); @@ -319,6 +318,22 @@ fs.readdir(pluginPath, (err, rootFiles) => { console.warn('Please add License section to README manually.'); } } + // eslint-disable-next-line max-len + const publishBadge = `![Publish Status](https://github.com/ether/${pluginName}/workflows/Node.js%20Package/badge.svg)`; + // eslint-disable-next-line max-len + const testBadge = `![Backend Tests Status](https://github.com/ether/${pluginName}/workflows/Backend%20tests/badge.svg)`; + if (readme.toLowerCase().indexOf('travis') !== -1) { + console.warn('Remove Travis badges'); + } + if (readme.indexOf('workflows/Node.js%20Package/badge.svg') === -1) { + console.warn('No Github workflow badge detected'); + if (autoFix) { + readme = `${publishBadge} ${testBadge}\n\n${readme}`; + // write readme to file system + fs.writeFileSync(`${pluginPath}/${readMeFileName}`, readme); + console.log('Wrote Github workflow badges to README'); + } + } } if (files.indexOf('license') === -1 && files.indexOf('license.md') === -1) { @@ -333,41 +348,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { } } - let travisConfig = - fs.readFileSync('src/bin/plugins/lib/travis.yml', {encoding: 'utf8', flag: 'r'}); - travisConfig = travisConfig.replace(/\[plugin_name\]/g, pluginName); - - if (files.indexOf('.travis.yml') === -1) { - console.warn('.travis.yml file not found, please create. ' + - '.travis.yml is used for automatically CI testing Etherpad. ' + - 'It is useful to know if your plugin breaks another feature for example.'); - // TODO: Make it check version of the .travis file to see if it needs an update. - if (autoFix) { - console.log('Autofixing missing .travis.yml file'); - fs.writeFileSync(`${pluginPath}/.travis.yml`, travisConfig); - console.log('Travis file created, please sign into travis and enable this repository'); - } - } - if (autoFix) { - // checks the file versioning of .travis and updates it to the latest. - const existingConfig = - fs.readFileSync(`${pluginPath}/.travis.yml`, {encoding: 'utf8', flag: 'r'}); - const existingConfigLocation = existingConfig.indexOf('##ETHERPAD_TRAVIS_V='); - const existingValue = - parseInt(existingConfig.substr(existingConfigLocation + 20, existingConfig.length)); - - const newConfigLocation = travisConfig.indexOf('##ETHERPAD_TRAVIS_V='); - const newValue = parseInt(travisConfig.substr(newConfigLocation + 20, travisConfig.length)); - if (existingConfigLocation === -1) { - console.warn('no previous .travis.yml version found so writing new.'); - // we will write the newTravisConfig to the location. - fs.writeFileSync(`${pluginPath}/.travis.yml`, travisConfig); - } else if (newValue > existingValue) { - console.log('updating .travis.yml'); - fs.writeFileSync(`${pluginPath}/.travis.yml`, travisConfig); - }// - } - if (files.indexOf('.gitignore') === -1) { console.warn('.gitignore file not found, please create. .gitignore files are useful to ' + "ensure files aren't incorrectly commited to a repository."); @@ -433,7 +413,6 @@ fs.readdir(pluginPath, (err, rootFiles) => { // absolute path to etherpad-lite/src is used here so that pluginPath can be a symlink. execSync( `${npmInstall} --no-save ep_etherpad-lite@file:${__dirname}/../../`, {stdio: 'inherit'}); - // linting begins try { console.log('Linting...'); diff --git a/src/bin/plugins/lib/travis.yml b/src/bin/plugins/lib/travis.yml deleted file mode 100644 index 1cf103167..000000000 --- a/src/bin/plugins/lib/travis.yml +++ /dev/null @@ -1,70 +0,0 @@ -language: node_js - -node_js: - - "lts/*" - -cache: false - -services: - - docker - -install: - - "export GIT_HASH=$(git rev-parse --verify --short HEAD)" - -#script: -# - "src/tests/frontend/travis/runner.sh" - -env: - global: - - secure: "WMGxFkOeTTlhWB+ChMucRtIqVmMbwzYdNHuHQjKCcj8HBEPdZLfCuK/kf4rG\nVLcLQiIsyllqzNhBGVHG1nyqWr0/LTm8JRqSCDDVIhpyzp9KpCJQQJG2Uwjk\n6/HIJJh/wbxsEdLNV2crYU/EiVO3A4Bq0YTHUlbhUqG3mSCr5Ec=" - - secure: "gejXUAHYscbR6Bodw35XexpToqWkv2ifeECsbeEmjaLkYzXmUUNWJGknKSu7\nEUsSfQV8w+hxApr1Z+jNqk9aX3K1I4btL3cwk2trnNI8XRAvu1c1Iv60eerI\nkE82Rsd5lwUaMEh+/HoL8ztFCZamVndoNgX7HWp5J/NRZZMmh4g=" - -jobs: - include: - - name: "Lint test package-lock" - install: - - "npm install lockfile-lint" - script: - - npx lockfile-lint --path package-lock.json --validate-https --allowed-hosts npm - - name: "Run the Backend tests" - before_install: - - sudo add-apt-repository -y ppa:libreoffice/ppa - - sudo apt-get update - - sudo apt-get -y install libreoffice - - sudo apt-get -y install libreoffice-pdfimport - install: - - "npm install" - - "mkdir [plugin_name]" - - "mv !([plugin_name]) [plugin_name]" - - "git clone https://github.com/ether/etherpad-lite.git etherpad" - - "cd etherpad" - - "mkdir -p node_modules" - - "mv ../[plugin_name] node_modules" - - "src/bin/installDeps.sh" - - "export GIT_HASH=$(git rev-parse --verify --short HEAD)" - - "cd src && npm install && cd -" - script: - - "src/tests/frontend/travis/runnerBackend.sh" - - name: "Test the Frontend" - before_script: - - "src/tests/frontend/travis/sauce_tunnel.sh" - install: - - "npm install" - - "mkdir [plugin_name]" - - "mv !([plugin_name]) [plugin_name]" - - "git clone https://github.com/ether/etherpad-lite.git etherpad" - - "cd etherpad" - - "mkdir -p node_modules" - - "mv ../[plugin_name] node_modules" - - "src/bin/installDeps.sh" - - "export GIT_HASH=$(git rev-parse --verify --short HEAD)" - script: - - "src/tests/frontend/travis/runner.sh" - -notifications: - irc: - channels: - - "irc.freenode.org#etherpad-lite-dev" - -##ETHERPAD_TRAVIS_V=9 -## Travis configuration automatically created using src/bin/plugins/updateAllPluginsScript.sh From 83a519941b991037a0c1ddb1d92b9f463239a668 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 8 Feb 2021 16:21:50 -0500 Subject: [PATCH 270/357] /admin/plugins: Fix logging of error messages --- src/node/hooks/express/adminplugins.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/node/hooks/express/adminplugins.js b/src/node/hooks/express/adminplugins.js index 5963fb208..7c0db0973 100644 --- a/src/node/hooks/express/adminplugins.js +++ b/src/node/hooks/express/adminplugins.js @@ -65,8 +65,8 @@ exports.socketio = (hookName, args, cb) => { }); socket.emit('results:updatable', {updatable}); - } catch (er) { - console.warn(er); + } catch (err) { + console.warn(err.stack || err.toString()); socket.emit('results:updatable', {updatable: {}}); } @@ -99,22 +99,22 @@ exports.socketio = (hookName, args, cb) => { }); socket.on('install', (pluginName) => { - installer.install(pluginName, (er) => { - if (er) console.warn(er); + installer.install(pluginName, (err) => { + if (err) console.warn(err.stack || err.toString()); socket.emit('finished:install', { plugin: pluginName, - code: er ? er.code : null, - error: er ? er.message : null, + code: err ? err.code : null, + error: err ? err.message : null, }); }); }); socket.on('uninstall', (pluginName) => { - installer.uninstall(pluginName, (er) => { - if (er) console.warn(er); + installer.uninstall(pluginName, (err) => { + if (err) console.warn(err.stack || err.toString()); - socket.emit('finished:uninstall', {plugin: pluginName, error: er ? er.message : null}); + socket.emit('finished:uninstall', {plugin: pluginName, error: err ? err.message : null}); }); }); }); From 87341af42900c5d975cd39944611012cfe230914 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 9 Feb 2021 01:02:16 -0500 Subject: [PATCH 271/357] GitHub workflows: Install Node.js v12 I'm not sure how these tests ever worked. I guess some version of Node.js and npm come pre-installed on the ubuntu-latest images? I would have prefered to use Node.js v10 because that is our current minimum supported version, but we have a surprising number of tests that don't work on Node.js v10 (mostly due to `assert.match()`, which was added in Node.js v12). --- .github/workflows/backend-tests.yml | 8 ++++++++ .github/workflows/frontend-admin-tests.yml | 4 ++++ .github/workflows/frontend-tests.yml | 8 ++++++++ .github/workflows/lint-package-lock.yml | 4 ++++ .github/workflows/load-test.yml | 8 ++++++++ .github/workflows/rate-limit.yml | 4 ++++ 6 files changed, 36 insertions(+) diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index a6a0d0139..21bb0c698 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -17,6 +17,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 12 + - name: Install libreoffice run: | sudo add-apt-repository -y ppa:libreoffice/ppa @@ -43,6 +47,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 12 + - name: Install libreoffice run: | sudo add-apt-repository -y ppa:libreoffice/ppa diff --git a/.github/workflows/frontend-admin-tests.yml b/.github/workflows/frontend-admin-tests.yml index 6f95a6c7f..e42aa3bb2 100644 --- a/.github/workflows/frontend-admin-tests.yml +++ b/.github/workflows/frontend-admin-tests.yml @@ -11,6 +11,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 12 + - name: Run sauce-connect-action shell: bash env: diff --git a/.github/workflows/frontend-tests.yml b/.github/workflows/frontend-tests.yml index 70cb197a3..0d5832be7 100644 --- a/.github/workflows/frontend-tests.yml +++ b/.github/workflows/frontend-tests.yml @@ -11,6 +11,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 12 + - name: Run sauce-connect-action shell: bash env: @@ -47,6 +51,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 12 + - name: Run sauce-connect-action shell: bash env: diff --git a/.github/workflows/lint-package-lock.yml b/.github/workflows/lint-package-lock.yml index beef64ffe..a9596aa3c 100644 --- a/.github/workflows/lint-package-lock.yml +++ b/.github/workflows/lint-package-lock.yml @@ -17,6 +17,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 12 + - name: Install lockfile-lint run: npm install lockfile-lint diff --git a/.github/workflows/load-test.yml b/.github/workflows/load-test.yml index 056930ecb..98379dfe8 100644 --- a/.github/workflows/load-test.yml +++ b/.github/workflows/load-test.yml @@ -17,6 +17,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 12 + - name: Install all dependencies and symlink for ep_etherpad-lite run: src/bin/installDeps.sh @@ -39,6 +43,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 12 + - name: Install etherpad-load-test run: sudo npm install -g etherpad-load-test diff --git a/.github/workflows/rate-limit.yml b/.github/workflows/rate-limit.yml index 667558114..0849f8e06 100644 --- a/.github/workflows/rate-limit.yml +++ b/.github/workflows/rate-limit.yml @@ -16,6 +16,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 12 + - name: docker network run: docker network create --subnet=172.23.42.0/16 ep_net From ce1b69fedac9624c828fc395cd54dd7a39a02794 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 8 Feb 2021 19:01:21 -0500 Subject: [PATCH 272/357] plugins: Helper function to run the `npm` CLI --- src/node/utils/run_cmd.js | 92 +++++++++++++++++++++++++++++++++++++++ src/node/utils/run_npm.js | 49 +++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 src/node/utils/run_cmd.js create mode 100644 src/node/utils/run_npm.js diff --git a/src/node/utils/run_cmd.js b/src/node/utils/run_cmd.js new file mode 100644 index 000000000..f0d7c92d6 --- /dev/null +++ b/src/node/utils/run_cmd.js @@ -0,0 +1,92 @@ +'use strict'; + +const childProcess = require('child_process'); +const log4js = require('log4js'); +const path = require('path'); +const settings = require('./Settings'); + +const logger = log4js.getLogger('runCmd'); + +const logLines = (readable, logLineFn) => { + readable.setEncoding('utf8'); + // The process won't necessarily write full lines every time -- it might write a part of a line + // then write the rest of the line later. + let leftovers = ''; + readable.on('data', (chunk) => { + const lines = chunk.split('\n'); + if (lines.length === 0) return; + lines[0] = leftovers + lines[0]; + leftovers = lines.pop(); + for (const line of lines) { + logLineFn(line); + } + }); + readable.on('end', () => { + if (leftovers !== '') logLineFn(leftovers); + leftovers = ''; + }); +}; + +/** + * Similar to `util.promisify(childProcess.exec)`, except: + * - `cwd` defaults to the Etherpad root directory. + * - PATH is prefixed with src/node_modules/.bin so that utilities from installed dependencies + * (e.g., npm) are preferred over system utilities. + * - Output is passed to logger callback functions by default. See below for details. + * + * @param args Array of command-line arguments, where `args[0]` is the command to run. + * @param opts Optional options that will be passed to `childProcess.spawn()` with two extensions: + * - `stdoutLogger`: Callback that is called each time a line of text is written to stdout (utf8 + * is assumed). The line (without trailing newline) is passed as the only argument. If null, + * stdout is not logged. If unset, defaults to no-op. Ignored if stdout is not a pipe. + * - `stderrLogger`: Like `stdoutLogger` but for stderr. + * + * @returns A Promise with `stdout`, `stderr`, and `child` properties containing the stdout stream, + * stderr stream, and ChildProcess objects, respectively. + */ +module.exports = exports = (args, opts = {}) => { + logger.debug(`Executing command: ${args.join(' ')}`); + + const {stdoutLogger = () => {}, stderrLogger = () => {}} = opts; + // Avoid confusing childProcess.spawn() with our extensions. + opts = {...opts}; // Make a copy to avoid mutating the caller's copy. + delete opts.stdoutLogger; + delete opts.stderrLogger; + + // Set PATH so that utilities from installed dependencies (e.g., npm) are preferred over system + // (global) utilities. + let {env = process.env} = opts; + env = {...env}; // Copy to avoid modifying process.env. + // On Windows the PATH environment var might be spelled "Path". + const pathVarName = Object.keys(env).filter((k) => k.toUpperCase() === 'PATH')[0] || 'PATH'; + env[pathVarName] = [ + path.join(settings.root, 'src', 'node_modules', '.bin'), + path.join(settings.root, 'node_modules', '.bin'), + ...(env[pathVarName] ? env[pathVarName].split(path.delimiter) : []), + ].join(path.delimiter); + logger.debug(`${pathVarName}=${env[pathVarName]}`); + + // Create an error object to use in case the process fails. This is done here rather than in the + // process's `exit` handler so that we get a useful stack trace. + const procFailedErr = new Error(`Command exited non-zero: ${args.join(' ')}`); + + const proc = childProcess.spawn(args[0], args.slice(1), {cwd: settings.root, ...opts, env}); + if (proc.stdout != null && stdoutLogger != null) logLines(proc.stdout, stdoutLogger); + if (proc.stderr != null && stderrLogger != null) logLines(proc.stderr, stderrLogger); + const p = new Promise((resolve, reject) => { + proc.on('exit', (code, signal) => { + if (code !== 0) { + logger.debug(procFailedErr.stack); + procFailedErr.code = code; + procFailedErr.signal = signal; + return reject(procFailedErr); + } + logger.debug(`Command returned successfully: ${args.join(' ')}`); + resolve(); + }); + }); + p.stdout = proc.stdout; + p.stderr = proc.stderr; + p.child = proc; + return p; +}; diff --git a/src/node/utils/run_npm.js b/src/node/utils/run_npm.js new file mode 100644 index 000000000..ff2b7b0db --- /dev/null +++ b/src/node/utils/run_npm.js @@ -0,0 +1,49 @@ +'use strict'; + +const log4js = require('log4js'); +const runCmd = require('./run_cmd'); + +const logger = log4js.getLogger('runNpm'); +const npmLogger = log4js.getLogger('npm'); + +const stdoutLogger = (line) => npmLogger.info(line); +const stderrLogger = (line) => npmLogger.error(line); + +/** + * Wrapper around `runCmd()` that logs output to an npm logger by default. + * + * @param args Command-line arguments to pass to npm. + * @param opts See the documentation for `runCmd()`. The `stdoutLogger` and `stderrLogger` options + * default to a log4js logger. + * + * @returns A Promise with additional `stdout`, `stderr`, and `child` properties. See the + * documentation for `runCmd()`. + */ +module.exports = exports = (args, opts = {}) => { + const cmd = ['npm', ...args]; + logger.info(`Executing command: ${cmd.join(' ')}`); + const p = runCmd(cmd, {stdoutLogger, stderrLogger, ...opts}); + p.then( + () => logger.info(`Successfully ran command: ${cmd.join(' ')}`), + () => logger.error(`npm command failed: ${cmd.join(' ')}`)); + // MUST return the original Promise returned from runCmd so that the caller can access stdout. + return p; +}; + +// Log the version of npm at startup. +let loggedVersion = false; +(async () => { + if (loggedVersion) return; + loggedVersion = true; + const p = runCmd(['npm', '--version'], {stdoutLogger: null, stderrLogger}); + const chunks = []; + await Promise.all([ + (async () => { for await (const chunk of p.stdout) chunks.push(chunk); })(), + p, // Await in parallel to avoid unhandled rejection if np rejects during chunk read. + ]); + const version = Buffer.concat(chunks).toString().replace(/\n+$/g, ''); + logger.info(`npm --version: ${version}`); +})().catch((err) => { + logger.error(`Failed to get npm version: ${err.stack}`); + // This isn't a fatal error so don't re-throw. +}); From fc9f2369779a93594b19681eb848cd3f85c2175a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 8 Feb 2021 19:19:49 -0500 Subject: [PATCH 273/357] plugins: Use `npm ls` to list the installed plugins This speeds up startup considerably, and we get rid of a lot of buggy code. This works with both npm v6.x and v7.x. --- src/package.json | 1 - src/static/js/pluginfw/plugin_defs.js | 6 +- src/static/js/pluginfw/plugins.js | 45 ++-- src/static/js/pluginfw/read-installed.js | 330 ----------------------- 4 files changed, 27 insertions(+), 355 deletions(-) delete mode 100644 src/static/js/pluginfw/read-installed.js diff --git a/src/package.json b/src/package.json index 42e229751..71e7bd39d 100644 --- a/src/package.json +++ b/src/package.json @@ -44,7 +44,6 @@ "express-session": "1.17.1", "find-root": "1.1.0", "formidable": "1.2.1", - "graceful-fs": "4.2.4", "http-errors": "1.8.0", "js-cookie": "^2.2.1", "jsonminify": "0.4.1", diff --git a/src/static/js/pluginfw/plugin_defs.js b/src/static/js/pluginfw/plugin_defs.js index 768d99c3e..f7d10879e 100644 --- a/src/static/js/pluginfw/plugin_defs.js +++ b/src/static/js/pluginfw/plugin_defs.js @@ -21,6 +21,8 @@ exports.parts = []; // * parts: Each part from the ep.json object is augmented with the following properties: // - plugin: The name of the plugin. // - full_name: Equal to /. -// * package (server-side only): Object containing details about the plugin package (version, -// path). +// * package (server-side only): Object containing details about the plugin package: +// - version +// - path +// - realPath exports.plugins = {}; diff --git a/src/static/js/pluginfw/plugins.js b/src/static/js/pluginfw/plugins.js index ceda3cc47..8323cfc7c 100644 --- a/src/static/js/pluginfw/plugins.js +++ b/src/static/js/pluginfw/plugins.js @@ -2,8 +2,8 @@ const fs = require('fs').promises; const hooks = require('./hooks'); -const readInstalled = require('./read-installed.js'); const path = require('path'); +const runNpm = require('../../../node/utils/run_npm'); const tsort = require('./tsort'); const util = require('util'); const settings = require('../../../node/utils/Settings'); @@ -69,28 +69,29 @@ exports.update = async () => { }; exports.getPackages = async () => { - // Load list of installed NPM packages, flatten it to a list, - // and filter out only packages with names that - const dir = settings.root; - const data = await util.promisify(readInstalled)(dir); - - const packages = {}; - const flatten = (deps) => { - for (const [name, dep] of Object.entries(deps)) { - if (name.indexOf(exports.prefix) === 0) { - packages[name] = {...dep}; - // Delete anything that creates loops so that the plugin - // list can be sent as JSON to the web client - delete packages[name].dependencies; - delete packages[name].parent; - } + // Note: Do not pass `--prod` because it does not work if there is no package.json. + const np = runNpm(['ls', '--long', '--json', '--depth=0'], { + stdoutLogger: null, // We want to capture stdout, so don't attempt to log it. + env: { + ...process.env, + // NODE_ENV must be set to development for `npm ls` to show files without a package.json. + NODE_ENV: 'development', + }, + }); + const chunks = []; + await Promise.all([ + (async () => { for await (const chunk of np.stdout) chunks.push(chunk); })(), + np, // Await in parallel to avoid unhandled rejection if np rejects during chunk read. + ]); + const {dependencies = {}} = JSON.parse(Buffer.concat(chunks).toString()); + await Promise.all(Object.entries(dependencies).map(async ([pkg, info]) => { + if (!pkg.startsWith(exports.prefix)) { + delete dependencies[pkg]; + return; } - }; - - const tmp = {}; - tmp[data.name] = data; - flatten(tmp[data.name].dependencies); - return packages; + info.realPath = await fs.realpath(info.path); + })); + return dependencies; }; const loadPlugin = async (packages, pluginName, plugins, parts) => { diff --git a/src/static/js/pluginfw/read-installed.js b/src/static/js/pluginfw/read-installed.js deleted file mode 100644 index f24fe8dc8..000000000 --- a/src/static/js/pluginfw/read-installed.js +++ /dev/null @@ -1,330 +0,0 @@ -'use strict'; - -// A copy of npm/lib/utils/read-installed.js -// that is hacked to not cache everything :) - -// Walk through the file-system "database" of installed -// packages, and create a data object related to the -// installed versions of each package. - -/* -This will traverse through all node_modules folders, -resolving the dependencies object to the object corresponding to -the package that meets that dep, or just the version/range if -unmet. - -Assuming that you had this folder structure: - -/path/to -+-- package.json { name = "root" } -`-- node_modules - +-- foo {bar, baz, asdf} - | +-- node_modules - | +-- bar { baz } - | `-- baz - `-- asdf - -where "foo" depends on bar, baz, and asdf, bar depends on baz, -and bar and baz are bundled with foo, whereas "asdf" is at -the higher level (sibling to foo), you'd get this object structure: - -{ -, path: "/path/to" -, parent: null -, dependencies: - { foo : - { version: "1.2.3" - , path: "/path/to/node_modules/foo" - , parent: - , dependencies: - { bar: - { parent: - , path: "/path/to/node_modules/foo/node_modules/bar" - , version: "2.3.4" - , dependencies: { baz: } - } - , baz: { ... } - , asdf: - } - } - , asdf: { ... } - } -} - -Unmet deps are left as strings. -Extraneous deps are marked with extraneous:true -deps that don't meet a requirement are marked with invalid:true - -to READ(packagefolder, parentobj, name, reqver) -obj = read package.json -installed = ./node_modules/* -if parentobj is null, and no package.json - obj = {dependencies:{:"*"}} -deps = Object.keys(obj.dependencies) -obj.path = packagefolder -obj.parent = parentobj -if name, && obj.name !== name, obj.invalid = true -if reqver, && obj.version !satisfies reqver, obj.invalid = true -if !reqver && parentobj, obj.extraneous = true -for each folder in installed - obj.dependencies[folder] = READ(packagefolder+node_modules+folder, - obj, folder, obj.dependencies[folder]) -# walk tree to find unmet deps -for each dep in obj.dependencies not in installed - r = obj.parent - while r - if r.dependencies[dep] - if r.dependencies[dep].verion !satisfies obj.dependencies[dep] - WARN - r.dependencies[dep].invalid = true - obj.dependencies[dep] = r.dependencies[dep] - r = null - else r = r.parent -return obj - - -TODO: -1. Find unmet deps in parent directories, searching as node does up -as far as the left-most node_modules folder. -2. Ignore anything in node_modules that isn't a package folder. - -*/ - -const npm = require('npm/lib/npm.js'); -const fs = require('graceful-fs'); -const path = require('path'); -const semver = require('semver'); -const log = require('log4js').getLogger('pluginfw'); -const util = require('util'); - -let fuSeen = []; -let riSeen = []; -let rpSeen = {}; - -const readJson = (file, callback) => { - fs.readFile(file, (er, buf) => { - if (er) { - callback(er); - return; - } - try { - callback(null, JSON.parse(buf.toString())); - } catch (er) { - callback(er); - } - }); -}; - -const readInstalled = (folder, cb) => { - /* This is where we clear the cache, these three lines are all the - * new code there is */ - fuSeen = []; - rpSeen = {}; - riSeen = []; - - const d = npm.config.get('depth'); - readInstalled_(folder, null, null, null, 0, d, (er, obj) => { - if (er) return cb(er); - // now obj has all the installed things, where they're installed - // figure out the inheritance links, now that the object is built. - resolveInheritance(obj); - cb(null, obj); - }); -}; - -module.exports = readInstalled; - -const readInstalled_ = (folder, parent, name, reqver, depth, maxDepth, cb) => { - let installed, - obj, - real, - link; - let errState = null; - let called = false; - - const next = (er) => { - if (errState) return; - if (er) { - errState = er; - return cb(null, []); - } - if (!installed || !obj || !real || called) return; - called = true; - if (rpSeen[real]) return cb(null, rpSeen[real]); - if (obj === true) { - obj = {dependencies: {}, path: folder}; - for (const i of installed) { - obj.dependencies[i] = '*'; - } - } - if (name && obj.name !== name) obj.invalid = true; - obj.realName = name || obj.name; - obj.dependencies = obj.dependencies || {}; - - // "foo":"http://blah" is always presumed valid - if (reqver && - semver.validRange(reqver) && - !semver.satisfies(obj.version, reqver)) { - obj.invalid = true; - } - - if (parent && - !(name in parent.dependencies) && - !(name in (parent.devDependencies || {}))) { - obj.extraneous = true; - } - obj.path = obj.path || folder; - obj.realPath = real; - obj.link = link; - if (parent && !obj.link) obj.parent = parent; - rpSeen[real] = obj; - obj.depth = depth; - if (depth >= maxDepth) return cb(null, obj); - Promise.all(installed.map(async (pkg) => { - let rv = obj.dependencies[pkg]; - if (!rv && obj.devDependencies) rv = obj.devDependencies[pkg]; - const dir = path.resolve(folder, `node_modules/${pkg}`); - const deps = obj.dependencies[pkg]; - return await util.promisify(readInstalled_)(dir, obj, pkg, deps, depth + 1, maxDepth); - })).then((installedData) => { - for (const dep of installedData) { - obj.dependencies[dep.realName] = dep; - } - - // any strings here are unmet things. however, if it's - // optional, then that's fine, so just delete it. - if (obj.optionalDependencies) { - for (const dep of Object.keys(obj.optionalDependencies)) { - if (typeof obj.dependencies[dep] === 'string') { - delete obj.dependencies[dep]; - } - } - } - return cb(null, obj); - }, (err) => cb(err || new Error(err))); - }; - - fs.readdir(path.resolve(folder, 'node_modules'), (er, i) => { - // error indicates that nothing is installed here - if (er) i = []; - installed = i.filter((f) => f.charAt(0) !== '.'); - next(); - }); - - readJson(path.resolve(folder, 'package.json'), (er, data) => { - obj = copy(data); - - if (!parent) { - obj = obj || true; - er = null; - } - return next(er); - }); - - fs.lstat(folder, (er, st) => { - if (er) { - if (!parent) real = true; - return next(er); - } - fs.realpath(folder, (er, rp) => { - real = rp; - if (st.isSymbolicLink()) link = rp; - next(er); - }); - }); -}; - -// starting from a root object, call findUnmet on each layer of children -const resolveInheritance = (obj) => { - if (typeof obj !== 'object') return; - if (riSeen.indexOf(obj) !== -1) return; - riSeen.push(obj); - if (typeof obj.dependencies !== 'object') { - obj.dependencies = {}; - } - for (const dep of Object.keys(obj.dependencies)) { - findUnmet(obj.dependencies[dep]); - } - for (const dep of Object.keys(obj.dependencies)) { - resolveInheritance(obj.dependencies[dep]); - } -}; - -// find unmet deps by walking up the tree object. -// No I/O -const findUnmet = (obj) => { - if (typeof obj !== 'object') return; - if (fuSeen.indexOf(obj) !== -1) return; - fuSeen.push(obj); - const deps = obj.dependencies = obj.dependencies || {}; - for (const d of Object.keys(deps).filter((d) => typeof deps[d] === 'string')) { - let r = obj.parent; - let found = null; - while (r && !found && typeof deps[d] === 'string') { - // if r is a valid choice, then use that. - found = r.dependencies[d]; - if (!found && r.realName === d) found = r; - - if (!found) { - r = r.link ? null : r.parent; - continue; - } - if (typeof deps[d] === 'string' && - !semver.satisfies(found.version, deps[d])) { - // the bad thing will happen - log.warn(`${obj.path} requires ${d}@'${deps[d]}' but will load\n${found.path},\n` + - `which is version ${found.version}`, 'unmet dependency'); - found.invalid = true; - } - deps[d] = found; - } - } -}; - -const copy = (obj) => { - if (!obj || typeof obj !== 'object') return obj; - if (Array.isArray(obj)) return obj.map(copy); - - const o = {}; - for (const [i, v] of Object.entries(obj)) o[i] = copy(v); - return o; -}; - -if (module === require.main) { - const seen = []; - - const cleanup = (map) => { - if (seen.indexOf(map) !== -1) return; - seen.push(map); - for (const i of Object.keys(map)) { - switch (i) { - case '_id': - case 'path': - case 'extraneous': case 'invalid': - case 'dependencies': case 'name': - continue; - default: delete map[i]; - } - } - for (const dep of Object.values(map.dependencies || {})) { - if (typeof dep === 'object') { - cleanup(dep); - } - } - return map; - }; - - const util = require('util'); - console.error('testing'); - - let called = 0; - npm.load({}, (err) => { - if (err != null) throw err; - readInstalled(process.cwd(), (er, map) => { - console.error(called++); - if (er) return console.error(er.stack || er.message); - cleanup(map); - console.error(util.inspect(map, true, 10, true)); - }); - }); -} From 2301c6ec83a75f68bf31a26579eb9d34ae5c2f3f Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Wed, 10 Feb 2021 18:54:19 -0500 Subject: [PATCH 274/357] pad: Don't throw on socket.io error --- CHANGELOG.md | 1 + src/static/js/pad.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c79240e7b..78b933406 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ ### Notable fixes * Enter key now stays in focus when inserted at bottom of viewport. +* Suppressed benign socket.io connection errors * Interface no longer loses color variants on disconnect/reconnect event. * Removed npm.load to support npm7. * General code quality is further significantly improved. diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 753ab7d02..55460b3fb 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -271,7 +271,9 @@ const handshake = () => { pad.collabClient.setStateIdle(); pad.collabClient.setIsPendingRevision(true); } - throw new Error(`socket.io connection error: ${JSON.stringify(error)}`); + // Don't throw an exception. Error events do not indicate problems that are not already + // addressed by reconnection logic, so throwing an exception each time there's a socket.io error + // just annoys users and fills logs. }); socket.on('message', (obj) => { From 2f1d64878753206840eacf6c1bc93af2fdcf3808 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 11 Feb 2021 18:48:51 +0100 Subject: [PATCH 275/357] Localisation updates from https://translatewiki.net. --- src/locales/cs.json | 53 ++++++++++++++++++++++++++++++++++++++---- src/locales/diq.json | 4 ++-- src/locales/ko.json | 8 ++++++- src/locales/pt-br.json | 1 + 4 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/locales/cs.json b/src/locales/cs.json index 0e0140931..a47b1a3be 100644 --- a/src/locales/cs.json +++ b/src/locales/cs.json @@ -10,11 +10,46 @@ "Leanes", "Mormegil", "Peldrjan", - "Quinn" + "Quinn", + "Spotter" ] }, + "admin.page-title": "Ovládací panel Správce - Etherpad", + "admin_plugins": "Správce zásuvných moodulů", + "admin_plugins.available": "Dostupné zásuvné moduly", + "admin_plugins.available_not-found": "Nejsou žádné zásuvné moduly", + "admin_plugins.available_fetching": "Načítání...", + "admin_plugins.available_install.value": "Instalovat", + "admin_plugins.available_search.placeholder": "Vyhledat zásuvné moduly k instalaci", + "admin_plugins.description": "Popis", + "admin_plugins.installed": "Nainstalované zásuvné moduly", + "admin_plugins.installed_fetching": "Načítání instalovaných zásuvných modulů...", + "admin_plugins.installed_nothing": "Dosud jste nenainstalovali žádné zásuvné moduly.", + "admin_plugins.installed_uninstall.value": "Odinstalovat", + "admin_plugins.last-update": "Poslední aktualizace", + "admin_plugins.name": "Název", + "admin_plugins.page-title": "Správce zásuvných modulů - Etherpad", + "admin_plugins.version": "Verze", + "admin_plugins_info": "Informace o řešení problému", + "admin_plugins_info.hooks": "Instalované hooks", + "admin_plugins_info.hooks_client": "hooks na straně klienta", + "admin_plugins_info.hooks_server": "hooks na straně serveru", + "admin_plugins_info.parts": "Nainstalované součásti", + "admin_plugins_info.plugins": "Nainstalované zásuvné moduly", + "admin_plugins_info.page-title": "Informace o zásuvných modulech - Etherpad", + "admin_plugins_info.version": "Verze Etherpad", + "admin_plugins_info.version_latest": "Poslední dostupná verze", + "admin_plugins_info.version_number": "Číslo verze", + "admin_settings": "Nastavení", + "admin_settings.current": "Aktuální konfugurace", + "admin_settings.current_example-devel": "Příklad ukázkové vývojové šablony", + "admin_settings.current_example-prod": "Příklad šablony nastavení výroby", + "admin_settings.current_restart.value": "Restartovat Etherpad", + "admin_settings.current_save.value": "Uložit nastavení", + "admin_settings.page-title": "Nastavení - Etherpad", "index.newPad": "Založ nový Pad", "index.createOpenPad": "nebo vytvoř/otevři Pad s názvem:", + "index.openPad": "otevřít existující Pad se jménem:", "pad.toolbar.bold.title": "Tučný text (Ctrl-B)", "pad.toolbar.italic.title": "Kurzíva (Ctrl-I)", "pad.toolbar.underline.title": "Podtržené písmo (Ctrl-U)", @@ -35,7 +70,7 @@ "pad.colorpicker.save": "Uložit", "pad.colorpicker.cancel": "Zrušit", "pad.loading": "Načítání...", - "pad.noCookie": "Nelze nalézt cookie. Povolte prosím cookie ve Vašem prohlížeči.", + "pad.noCookie": "Soubor cookie nebyl nalezen. Povolte prosím cookies ve svém prohlížeči! Vaše relace a nastavení se mezi návštěvami neuloží. Může to být způsobeno tím, že je Etherpad v některých prohlížečích zahrnut do iFrame. Zkontrolujte, zda je Etherpad ve stejné subdoméně / doméně jako nadřazený iFrame", "pad.permissionDenied": "Nemáte oprávnění pro přístup k tomuto Padu", "pad.settings.padSettings": "Nastavení Padu", "pad.settings.myView": "Vlastní pohled", @@ -47,6 +82,8 @@ "pad.settings.fontType": "Typ písma:", "pad.settings.fontType.normal": "Normální", "pad.settings.language": "Jazyk:", + "pad.settings.about": "O projektu", + "pad.settings.poweredBy": "Běží na", "pad.importExport.import_export": "Import/Export", "pad.importExport.import": "Nahrát libovolný textový soubor nebo dokument", "pad.importExport.importSuccessful": "Úspěšně!", @@ -57,9 +94,9 @@ "pad.importExport.exportword": "Microsoft Word", "pad.importExport.exportpdf": "PDF", "pad.importExport.exportopen": "ODF (Open Document Format)", - "pad.importExport.abiword.innerHTML": "Importovat můžeš pouze prostý text nebo HTML formátování. Pro pokročilejší funkce importu, prosím, nainstaluj „AbiWord“.", + "pad.importExport.abiword.innerHTML": "Importovat lze pouze z formátů prostého textu nebo HTML. Pokročilejší funkce pro import naleznete v instalaci AbiWord nebo LibreOffice.", "pad.modals.connected": "Připojeno.", - "pad.modals.reconnecting": "Znovupřipojování k Padu…", + "pad.modals.reconnecting": "Opětovné připojení k Padu...", "pad.modals.forcereconnect": "Vynutit znovupřipojení", "pad.modals.reconnecttimer": "Zkouším se znovu připojit", "pad.modals.cancel": "Zrušit", @@ -81,6 +118,10 @@ "pad.modals.corruptPad.cause": "To může být kvůli špatné konfiguraci serveru, nebo kvůli jinému neočekávanému chování. Kontaktujte prosím správce služby.", "pad.modals.deleted": "Odstraněno.", "pad.modals.deleted.explanation": "Tento Pad byl odebrán.", + "pad.modals.rateLimited": "Rychlost je omezená.", + "pad.modals.rateLimited.explanation": "Na tento Pad jste poslali příliš mnoho zpráv, takže vás odpojil.", + "pad.modals.rejected.explanation": "Server odmítl zprávu odeslanou vaším prohlížečem.", + "pad.modals.rejected.cause": "Server mohl být aktualizován, když jste sledovali podložku, nebo možná došlo k chybě v Etherpadu. Zkuste stránku znovu načíst.", "pad.modals.disconnected": "Byl jste odpojen.", "pad.modals.disconnected.explanation": "Připojení k serveru bylo přerušeno", "pad.modals.disconnected.cause": "Server může být nedostupný. Upozorněte administrátora služby, pokud se to bude opakovat.", @@ -93,6 +134,7 @@ "pad.chat.loadmessages": "Načíst více zpráv", "pad.chat.stick.title": "Přichytit chat k obrazovce", "pad.chat.writeMessage.placeholder": "Zde napište zprávu", + "timeslider.followContents": "Sledovat aktualizace obsahu Padu", "timeslider.pageTitle": "Časová osa {{appTitle}}", "timeslider.toolbar.returnbutton": "Návrat do Padu", "timeslider.toolbar.authors": "Autoři:", @@ -131,5 +173,6 @@ "pad.impexp.uploadFailed": "Nahrávání selhalo, zkuste to znovu", "pad.impexp.importfailed": "Import selhal", "pad.impexp.copypaste": "Vložte prosím kopii", - "pad.impexp.exportdisabled": "Export do formátu {{type}} je zakázán. Kontaktujte svého administrátora pro zjištění detailů." + "pad.impexp.exportdisabled": "Export do formátu {{type}} je zakázán. Kontaktujte svého administrátora pro zjištění detailů.", + "pad.impexp.maxFileSize": "Soubor je příliš velký. Požádejte svého správce webu o zvýšení povolené velikosti souboru pro import" } diff --git a/src/locales/diq.json b/src/locales/diq.json index 8f97e1045..ef7275712 100644 --- a/src/locales/diq.json +++ b/src/locales/diq.json @@ -11,7 +11,7 @@ ] }, "admin.page-title": "Panoyê İdarekari - Etherpad", - "admin_plugins": "İdarekarê Dekerdeki", + "admin_plugins": "Gıredayışê raverberi", "admin_plugins.available": "Mewcud Dekerdeki", "admin_plugins.available_not-found": "Dekerdek nevineya", "admin_plugins.available_fetching": "Aniyeno...", @@ -134,7 +134,7 @@ "timeslider.pageTitle": "Ğızagê zemani {{appTitle}}", "timeslider.toolbar.returnbutton": "Peyser şo bloknot", "timeslider.toolbar.authors": "Nuştoği:", - "timeslider.toolbar.authorsList": "Nuştoği çıniyê", + "timeslider.toolbar.authorsList": "Nuştekari çıniyê", "timeslider.toolbar.exportlink.title": "Teberdayış", "timeslider.exportCurrent": "Versiyonê enewki teber de:", "timeslider.version": "Versiyonê {{version}}", diff --git a/src/locales/ko.json b/src/locales/ko.json index 01857abf3..04c279a05 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -40,11 +40,14 @@ "admin_plugins_info.version_number": "버전 번호", "admin_settings": "설정", "admin_settings.current": "현재 구성", + "admin_settings.current_example-devel": "예시 개발용 설정 템플릿", + "admin_settings.current_example-prod": "예시 운영용 설정 템플릿", "admin_settings.current_restart.value": "이더패드 다시 시작", "admin_settings.current_save.value": "설정 저장", "admin_settings.page-title": "설정 - 이더패드", "index.newPad": "새 패드", "index.createOpenPad": "또는 다음 이름으로 패드 만들기/열기:", + "index.openPad": "이름으로 기존 패드 열기:", "pad.toolbar.bold.title": "굵게 (Ctrl+B)", "pad.toolbar.italic.title": "기울임꼴 (Ctrl+I)", "pad.toolbar.underline.title": "밑줄 (Ctrl+U)", @@ -65,7 +68,7 @@ "pad.colorpicker.save": "저장", "pad.colorpicker.cancel": "취소", "pad.loading": "불러오는 중...", - "pad.noCookie": "쿠키를 찾을 수 없습니다. 브라우저에서 쿠키를 허용해주세요!", + "pad.noCookie": "쿠키를 찾지 못했습니다. 브라우저에서 쿠키를 허용해 주십시오! 세션과 설정은 방문 간 저장되지 않습니다. 일부 브라우저의 iFrame에 이더패드가 포함된 것이 그 이유일 수 있습니다. 이더패드가 부모 iFrame과 동일한 서브도메인/도메인에 위치하는지 확인해 주십시오", "pad.permissionDenied": "이 패드에 접근할 권한이 없습니다", "pad.settings.padSettings": "패드 설정", "pad.settings.myView": "내 보기", @@ -113,7 +116,10 @@ "pad.modals.corruptPad.cause": "잘못된 서버 구성 또는 다른 예기치 않은 오류 때문에 발생했을 수 있습니다. 서버 관리자와 연락하세요.", "pad.modals.deleted": "삭제되었습니다.", "pad.modals.deleted.explanation": "이 패드를 제거했습니다.", + "pad.modals.rateLimited": "속도 제한됨.", + "pad.modals.rateLimited.explanation": "이 패드에 너무 많은 메시지를 송신하였으므로 연결을 해제했습니다.", "pad.modals.rejected.explanation": "브라우저가 보낸 메시지를 서버가 거부했습니다.", + "pad.modals.rejected.cause": "패드를 보는 동안 서버가 업데이트되었거나 이더패드의 버그일 수 있습니다. 페이지를 다시 로드해 보십시오.", "pad.modals.disconnected": "연결이 끊어졌습니다.", "pad.modals.disconnected.explanation": "서버에서 연결을 잃었습니다", "pad.modals.disconnected.cause": "서버를 사용할 수 없습니다. 이 문제가 계속 발생하면 서비스 관리자에게 알려주시기 바랍니다.", diff --git a/src/locales/pt-br.json b/src/locales/pt-br.json index f067466f6..0e6d9993a 100644 --- a/src/locales/pt-br.json +++ b/src/locales/pt-br.json @@ -4,6 +4,7 @@ "Cainamarques", "Dianakc", "Eduardo Addad de Oliveira", + "Eduardoaddad", "Fasouzafreitas", "Gusta", "Lpagliari", From b50fcb065a6370dddc895af5e14d81339fee9d58 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Tue, 9 Feb 2021 21:54:44 +0000 Subject: [PATCH 276/357] fix: upgrade underscore from 1.8.3 to 1.12.0 Snyk has created this PR to upgrade underscore from 1.8.3 to 1.12.0. See this package in npm: https://www.npmjs.com/package/underscore See this project in Snyk: https://app.snyk.io/org/johnmclear/project/d9a12bfb-7ccd-443f-9e22-f30d339cc8c5?utm_source=github&utm_medium=upgrade-pr --- src/package-lock.json | 6 +++--- src/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/package-lock.json b/src/package-lock.json index 2227ac23e..f028521f4 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -8360,9 +8360,9 @@ } }, "underscore": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", - "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.0.tgz", + "integrity": "sha512-21rQzss/XPMjolTiIezSu3JAjgagXKROtNrYFEOWK109qY1Uv2tVjPTZ1ci2HgvQDA16gHYSthQIJfB+XId/rQ==" }, "unified": { "version": "9.2.0", diff --git a/src/package.json b/src/package.json index 71e7bd39d..0f84487b9 100644 --- a/src/package.json +++ b/src/package.json @@ -69,7 +69,7 @@ "tiny-worker": "^2.3.0", "tinycon": "0.0.1", "ueberdb2": "^1.2.5", - "underscore": "1.8.3", + "underscore": "1.12.0", "unorm": "1.4.1", "wtfnode": "^0.8.4" }, From 60a55ec4282d5fa159e5d44b3f54e990a95160ee Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Tue, 9 Feb 2021 21:54:40 +0000 Subject: [PATCH 277/357] fix: upgrade resolve from 1.1.7 to 1.19.0 Snyk has created this PR to upgrade resolve from 1.1.7 to 1.19.0. See this package in npm: https://www.npmjs.com/package/resolve See this project in Snyk: https://app.snyk.io/org/johnmclear/project/d9a12bfb-7ccd-443f-9e22-f30d339cc8c5?utm_source=github&utm_medium=upgrade-pr --- src/package-lock.json | 18 +++++++++--------- src/package.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/package-lock.json b/src/package-lock.json index f028521f4..567b15c79 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -2292,8 +2292,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "functional-red-black-tree": { "version": "1.0.1", @@ -2402,7 +2401,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -2703,7 +2701,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", - "dev": true, "requires": { "has": "^1.0.3" } @@ -7043,8 +7040,7 @@ "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "path-to-regexp": { "version": "0.1.7", @@ -7428,9 +7424,13 @@ } }, "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } }, "resolve-from": { "version": "2.0.0", diff --git a/src/package.json b/src/package.json index 0f84487b9..dbd8e30c3 100644 --- a/src/package.json +++ b/src/package.json @@ -60,7 +60,7 @@ "rehype": "^10.0.0", "rehype-minify-whitespace": "^4.0.5", "request": "2.88.2", - "resolve": "1.1.7", + "resolve": "1.19.0", "security": "1.0.0", "semver": "5.6.0", "socket.io": "^2.4.1", From cc7f11560f59f9645cb0e6aaff1a1deab9c92406 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 11 Feb 2021 18:26:37 +0000 Subject: [PATCH 278/357] Fix OL list items not increasing (#4749) * tests: additional test coverage for OL items * parseInt means we can do a proper check * tests: use ol check value test for #4748 --- src/static/js/domline.js | 2 +- src/tests/frontend/specs/ordered_list.js | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/static/js/domline.js b/src/static/js/domline.js index 8667a022d..324e13535 100644 --- a/src/static/js/domline.js +++ b/src/static/js/domline.js @@ -101,7 +101,7 @@ domline.createDomLine = (nonEmpty, doesWrap, optBrowser, optDocument) => { postHtml = `
                  ${postHtml}`; } else { if (start) { // is it a start of a list with more than one item in? - if (start[1] === 1) { // if its the first one at this level? + if (Number.parseInt(start[1]) === 1) { // if its the first one at this level? // Add start class to DIV node lineClass = `${lineClass} ` + `list-start-${listType}`; } diff --git a/src/tests/frontend/specs/ordered_list.js b/src/tests/frontend/specs/ordered_list.js index ddca576e2..9fe647ebe 100644 --- a/src/tests/frontend/specs/ordered_list.js +++ b/src/tests/frontend/specs/ordered_list.js @@ -108,6 +108,21 @@ describe('ordered_list.js', function () { }); }); + it('issue #4748 keeps numbers increment on OL', function (done) { + this.timeout(5000); + const inner$ = helper.padInner$; + const chrome$ = helper.padChrome$; + const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist'); + const $firstLine = inner$('div').first(); + $firstLine.sendkeys('{selectall}'); + $insertorderedlistButton.click(); + const $secondLine = inner$('div').first().next(); + $secondLine.sendkeys('{selectall}'); + $insertorderedlistButton.click(); + expect($secondLine.find('ol').attr('start') === 2); + done(); + }); + xit('issue #1125 keeps the numbered list on enter for the new line', function (done) { // EMULATES PASTING INTO A PAD const inner$ = helper.padInner$; From f6df9ffad08e2dc17ad0391be88d0c7034b4e1c0 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Tue, 9 Feb 2021 21:54:36 +0000 Subject: [PATCH 279/357] fix: upgrade measured-core from 1.11.2 to 1.51.1 Snyk has created this PR to upgrade measured-core from 1.11.2 to 1.51.1. See this package in npm: https://www.npmjs.com/package/measured-core See this project in Snyk: https://app.snyk.io/org/johnmclear/project/d9a12bfb-7ccd-443f-9e22-f30d339cc8c5?utm_source=github&utm_medium=upgrade-pr --- src/package-lock.json | 6 +++--- src/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/package-lock.json b/src/package-lock.json index 567b15c79..b3ba5d56d 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -3185,9 +3185,9 @@ } }, "measured-core": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/measured-core/-/measured-core-1.11.2.tgz", - "integrity": "sha1-nb6m0gdBtW9hq9hm5Jbri4Xmk0k=", + "version": "1.51.1", + "resolved": "https://registry.npmjs.org/measured-core/-/measured-core-1.51.1.tgz", + "integrity": "sha512-DZQP9SEwdqqYRvT2slMK81D/7xwdxXosZZBtLVfPSo6y5P672FBTbzHVdN4IQyUkUpcVOR9pIvtUy5Ryl7NKyg==", "requires": { "binary-search": "^1.3.3", "optional-js": "^2.0.0" diff --git a/src/package.json b/src/package.json index dbd8e30c3..4fdee2909 100644 --- a/src/package.json +++ b/src/package.json @@ -50,7 +50,7 @@ "languages4translatewiki": "0.1.3", "lodash.clonedeep": "4.5.0", "log4js": "0.6.35", - "measured-core": "1.11.2", + "measured-core": "1.51.1", "mime-types": "^2.1.27", "nodeify": "1.0.1", "npm": "6.14.8", From 0fae34009d385643213bcf1591c8fd3a63c0a2cf Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 20:45:53 -0500 Subject: [PATCH 280/357] chat tests: Delete unnecessary `expect()` --- src/tests/frontend/specs/chat_load_messages.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tests/frontend/specs/chat_load_messages.js b/src/tests/frontend/specs/chat_load_messages.js index 63d90fd63..6015b894f 100644 --- a/src/tests/frontend/specs/chat_load_messages.js +++ b/src/tests/frontend/specs/chat_load_messages.js @@ -26,7 +26,6 @@ describe('chat-load-messages', function () { chatInput.sendkeys('{enter}'); } helper.waitFor(() => chatText.children('p').length === messages, 60000).always(() => { - expect(chatText.children('p').length).to.be(messages); helper.newPad(done, padName); }); }); From 020df7567731fedfdd837ff9223ec57a6496fdae Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 20:46:45 -0500 Subject: [PATCH 281/357] chat tests: Asyncify --- src/tests/frontend/specs/chat_load_messages.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/tests/frontend/specs/chat_load_messages.js b/src/tests/frontend/specs/chat_load_messages.js index 6015b894f..8d25f13bc 100644 --- a/src/tests/frontend/specs/chat_load_messages.js +++ b/src/tests/frontend/specs/chat_load_messages.js @@ -8,7 +8,7 @@ describe('chat-load-messages', function () { this.timeout(60000); }); - it('adds a lot of messages', function (done) { + it('adds a lot of messages', async function () { const chrome$ = helper.padChrome$; const chatButton = chrome$('#chaticon'); chatButton.click(); @@ -25,9 +25,8 @@ describe('chat-load-messages', function () { chatInput.sendkeys(`msg${num}`); chatInput.sendkeys('{enter}'); } - helper.waitFor(() => chatText.children('p').length === messages, 60000).always(() => { - helper.newPad(done, padName); - }); + await helper.waitForPromise(() => chatText.children('p').length === messages, 60000); + await new Promise((resolve) => helper.newPad(() => resolve(), padName)); }); it('checks initial message count', function (done) { From 085ab452a0bb9ea4fc4e95aa8484adcc0ba6f284 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 20:30:08 -0500 Subject: [PATCH 282/357] chat tests: Give some time to process each of the 140 chat messages This avoids overflowing socket.io's receive buffer. --- src/tests/frontend/specs/chat_load_messages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/frontend/specs/chat_load_messages.js b/src/tests/frontend/specs/chat_load_messages.js index 8d25f13bc..dcab043e9 100644 --- a/src/tests/frontend/specs/chat_load_messages.js +++ b/src/tests/frontend/specs/chat_load_messages.js @@ -24,8 +24,8 @@ describe('chat-load-messages', function () { if (num.length === 2) num = `0${num}`; chatInput.sendkeys(`msg${num}`); chatInput.sendkeys('{enter}'); + await helper.waitForPromise(() => chatText.children('p').length === i); } - await helper.waitForPromise(() => chatText.children('p').length === messages, 60000); await new Promise((resolve) => helper.newPad(() => resolve(), padName)); }); From ab127289c41550460f1a97ad45e718119f30535e Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 11 Feb 2021 17:48:12 +0000 Subject: [PATCH 283/357] security: limit socketio to 1M chars --- src/node/hooks/express/socketio.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node/hooks/express/socketio.js b/src/node/hooks/express/socketio.js index 58d2f5a44..a44e74e3a 100644 --- a/src/node/hooks/express/socketio.js +++ b/src/node/hooks/express/socketio.js @@ -56,6 +56,7 @@ exports.expressCreateServer = (hookName, args, cb) => { * https://github.com/socketio/socket.io/issues/2276#issuecomment-147184662 (not totally true, actually, see above) */ cookie: false, + maxHttpBufferSize: 10E3, }); io.use((socket, next) => { From 8919f63c98d385b2e2ea11a1a96c10243e9378a7 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 9 Feb 2021 16:54:25 -0500 Subject: [PATCH 284/357] lint: Replace use of underscore.js with plain ECMAScript --- src/node/hooks/express/static.js | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/node/hooks/express/static.js b/src/node/hooks/express/static.js index 950fe3f78..58528a6c7 100644 --- a/src/node/hooks/express/static.js +++ b/src/node/hooks/express/static.js @@ -4,7 +4,6 @@ const minify = require('../../utils/Minify'); const plugins = require('../../../static/js/pluginfw/plugin_defs'); const CachingMiddleware = require('../../utils/caching_middleware'); const Yajsml = require('etherpad-yajsml'); -const _ = require('underscore'); exports.expressCreateServer = (hookName, args, cb) => { // Cache both minified and static. @@ -38,19 +37,12 @@ exports.expressCreateServer = (hookName, args, cb) => { // not very static, but served here so that client can do // require("pluginfw/static/js/plugin-definitions.js"); args.app.get('/pluginfw/plugin-definitions.json', (req, res, next) => { - const clientParts = _(plugins.parts) - .filter((part) => _(part).has('client_hooks')); - + const clientParts = plugins.parts.filter((part) => part.client_hooks != null); const clientPlugins = {}; - - _(clientParts).chain() - .map((part) => part.plugin) - .uniq() - .each((name) => { - clientPlugins[name] = _(plugins.plugins[name]).clone(); - delete clientPlugins[name].package; - }); - + for (const name of new Set(clientParts.map((part) => part.plugin))) { + clientPlugins[name] = {...plugins.plugins[name]}; + delete clientPlugins[name].package; + } res.header('Content-Type', 'application/json; charset=utf-8'); res.write(JSON.stringify({plugins: clientPlugins, parts: clientParts})); res.end(); From 50929fe7f7657de8bd8440f6653bdb27d894e616 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 9 Feb 2021 16:46:37 -0500 Subject: [PATCH 285/357] express: Call expressConfigure, expressCreateServer hooks asynchronously --- CHANGELOG.md | 3 ++- src/node/hooks/express.js | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78b933406..cac510eeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,8 @@ * `callFirst()` and `aCallFirst()` now support the same wide range of hook function behaviors that `callAll()`, `aCallAll()`, and `callAllSerial()` support. Also, they now warn when a hook function misbehaves. - * The `padCopy` and `padRemove` hooks now support asynchronous hook functions. + * The following server-side hooks now support asynchronous hook functions: + `expressConfigure`, `expressCreateServer`, `padCopy`, `padRemove` * Backend tests for plugins can now use the [`ep_etherpad-lite/tests/backend/common`](src/tests/backend/common.js) module to start the server and simplify API access. diff --git a/src/node/hooks/express.js b/src/node/hooks/express.js index b3d4f34e4..9b70c6c55 100644 --- a/src/node/hooks/express.js +++ b/src/node/hooks/express.js @@ -182,9 +182,10 @@ exports.restartServer = async () => { app.use(cookieParser(settings.sessionKey, {})); - hooks.callAll('expressConfigure', {app}); - hooks.callAll('expressCreateServer', {app, server: exports.server}); - + await Promise.all([ + hooks.aCallAll('expressConfigure', {app}), + hooks.aCallAll('expressCreateServer', {app, server: exports.server}), + ]); await util.promisify(exports.server.listen).bind(exports.server)(settings.port, settings.ip); }; From 8ae8710a1419b59572f889015a58f1fc1084baca Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 9 Feb 2021 16:55:41 -0500 Subject: [PATCH 286/357] ace: Fix EMBEDDED check --- src/static/js/ace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/js/ace.js b/src/static/js/ace.js index c621566d9..e648df07e 100644 --- a/src/static/js/ace.js +++ b/src/static/js/ace.js @@ -197,7 +197,7 @@ const Ace2Editor = function () { pushStyleTagsFor(iframeHTML, includedCSS); - if (!Ace2Editor.EMBEDED && Ace2Editor.EMBEDED[KERNEL_SOURCE]) { + if (!Ace2Editor.EMBEDED || !Ace2Editor.EMBEDED[KERNEL_SOURCE]) { // Remotely src'd script tag will not work in IE; it must be embedded, so // throw an error if it is not. throw new Error('Require kernel could not be found.'); From 996dc818255ce2295f4ebfe0029d2414a93bdfd4 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 9 Feb 2021 17:11:44 -0500 Subject: [PATCH 287/357] Minify: Move tar processing into a function This reduces the overhead of `require()`ing the module, and it will make it easier for a future commit to asyncify everything in `Minify.js`. --- src/node/hooks/express/static.js | 3 +-- src/node/utils/Minify.js | 31 ++++++++++++++++--------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/node/hooks/express/static.js b/src/node/hooks/express/static.js index 58528a6c7..00f0798ea 100644 --- a/src/node/hooks/express/static.js +++ b/src/node/hooks/express/static.js @@ -26,8 +26,7 @@ exports.expressCreateServer = (hookName, args, cb) => { }); const StaticAssociator = Yajsml.associators.StaticAssociator; - const associations = - Yajsml.associators.associationsForSimpleMapping(minify.tar); + const associations = Yajsml.associators.associationsForSimpleMapping(minify.getTar()); const associator = new StaticAssociator(associations); jsServer.setAssociator(associator); diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 49d26ba6b..3b7a89c19 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -36,8 +36,6 @@ const log4js = require('log4js'); const logger = log4js.getLogger('Minify'); const ROOT_DIR = path.normalize(`${__dirname}/../../static/`); -const TAR_PATH = path.join(__dirname, 'tar.json'); -const tar = JSON.parse(fs.readFileSync(TAR_PATH, 'utf8')); const threadsPool = new Threads.Pool(() => Threads.spawn(new Threads.Worker('./MinifyWorker')), 2); @@ -51,21 +49,24 @@ const LIBRARY_WHITELIST = [ ]; // Rewrite tar to include modules with no extensions and proper rooted paths. -const LIBRARY_PREFIX = 'ep_etherpad-lite/static/js'; -exports.tar = {}; -const prefixLocalLibraryPath = (path) => { - if (path.charAt(0) === '$') { - return path.slice(1); - } else { - return `${LIBRARY_PREFIX}/${path}`; +exports.getTar = () => { + const prefixLocalLibraryPath = (path) => { + if (path.charAt(0) === '$') { + return path.slice(1); + } else { + return `ep_etherpad-lite/static/js/${path}`; + } + }; + const tarJson = fs.readFileSync(path.join(__dirname, 'tar.json'), 'utf8'); + const tar = {}; + for (const [key, relativeFiles] of Object.entries(JSON.parse(tarJson))) { + const files = relativeFiles.map(prefixLocalLibraryPath); + tar[prefixLocalLibraryPath(key)] = files + .concat(files.map((p) => p.replace(/\.js$/, ''))) + .concat(files.map((p) => `${p.replace(/\.js$/, '')}/index.js`)); } + return tar; }; -for (const [key, relativeFiles] of Object.entries(tar)) { - const files = relativeFiles.map(prefixLocalLibraryPath); - exports.tar[prefixLocalLibraryPath(key)] = files - .concat(files.map((p) => p.replace(/\.js$/, ''))) - .concat(files.map((p) => `${p.replace(/\.js$/, '')}/index.js`)); -} // What follows is a terrible hack to avoid loop-back within the server. // TODO: Serve files from another service, or directly from the file system. From 7f4a7156e261e42a7dd22ff167d445e8fec5045b Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 13:43:10 -0500 Subject: [PATCH 288/357] Minify: Move `getTar()` to `static.js` `static.js` is the only file that uses it. --- src/node/hooks/express/static.js | 25 ++++++++++++++++++++++++- src/node/utils/Minify.js | 20 -------------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/node/hooks/express/static.js b/src/node/hooks/express/static.js index 00f0798ea..5908312e7 100644 --- a/src/node/hooks/express/static.js +++ b/src/node/hooks/express/static.js @@ -1,10 +1,33 @@ 'use strict'; +const fs = require('fs'); const minify = require('../../utils/Minify'); +const path = require('path'); const plugins = require('../../../static/js/pluginfw/plugin_defs'); +const settings = require('../../utils/Settings'); const CachingMiddleware = require('../../utils/caching_middleware'); const Yajsml = require('etherpad-yajsml'); +// Rewrite tar to include modules with no extensions and proper rooted paths. +const getTar = () => { + const prefixLocalLibraryPath = (path) => { + if (path.charAt(0) === '$') { + return path.slice(1); + } else { + return `ep_etherpad-lite/static/js/${path}`; + } + }; + const tarJson = fs.readFileSync(path.join(settings.root, 'src/node/utils/tar.json'), 'utf8'); + const tar = {}; + for (const [key, relativeFiles] of Object.entries(JSON.parse(tarJson))) { + const files = relativeFiles.map(prefixLocalLibraryPath); + tar[prefixLocalLibraryPath(key)] = files + .concat(files.map((p) => p.replace(/\.js$/, ''))) + .concat(files.map((p) => `${p.replace(/\.js$/, '')}/index.js`)); + } + return tar; +}; + exports.expressCreateServer = (hookName, args, cb) => { // Cache both minified and static. const assetCache = new CachingMiddleware(); @@ -26,7 +49,7 @@ exports.expressCreateServer = (hookName, args, cb) => { }); const StaticAssociator = Yajsml.associators.StaticAssociator; - const associations = Yajsml.associators.associationsForSimpleMapping(minify.getTar()); + const associations = Yajsml.associators.associationsForSimpleMapping(getTar()); const associator = new StaticAssociator(associations); jsServer.setAssociator(associator); diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 3b7a89c19..8f49abec4 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -48,26 +48,6 @@ const LIBRARY_WHITELIST = [ 'unorm', ]; -// Rewrite tar to include modules with no extensions and proper rooted paths. -exports.getTar = () => { - const prefixLocalLibraryPath = (path) => { - if (path.charAt(0) === '$') { - return path.slice(1); - } else { - return `ep_etherpad-lite/static/js/${path}`; - } - }; - const tarJson = fs.readFileSync(path.join(__dirname, 'tar.json'), 'utf8'); - const tar = {}; - for (const [key, relativeFiles] of Object.entries(JSON.parse(tarJson))) { - const files = relativeFiles.map(prefixLocalLibraryPath); - tar[prefixLocalLibraryPath(key)] = files - .concat(files.map((p) => p.replace(/\.js$/, ''))) - .concat(files.map((p) => `${p.replace(/\.js$/, '')}/index.js`)); - } - return tar; -}; - // What follows is a terrible hack to avoid loop-back within the server. // TODO: Serve files from another service, or directly from the file system. const requestURI = (url, method, headers, callback) => { From d9607f7c662c9bdfddacf38ec930112f9a4bc314 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 9 Feb 2021 17:26:46 -0500 Subject: [PATCH 289/357] static: Asyncify --- src/node/hooks/express/static.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/node/hooks/express/static.js b/src/node/hooks/express/static.js index 5908312e7..0b73072e5 100644 --- a/src/node/hooks/express/static.js +++ b/src/node/hooks/express/static.js @@ -1,6 +1,6 @@ 'use strict'; -const fs = require('fs'); +const fs = require('fs').promises; const minify = require('../../utils/Minify'); const path = require('path'); const plugins = require('../../../static/js/pluginfw/plugin_defs'); @@ -9,7 +9,7 @@ const CachingMiddleware = require('../../utils/caching_middleware'); const Yajsml = require('etherpad-yajsml'); // Rewrite tar to include modules with no extensions and proper rooted paths. -const getTar = () => { +const getTar = async () => { const prefixLocalLibraryPath = (path) => { if (path.charAt(0) === '$') { return path.slice(1); @@ -17,7 +17,7 @@ const getTar = () => { return `ep_etherpad-lite/static/js/${path}`; } }; - const tarJson = fs.readFileSync(path.join(settings.root, 'src/node/utils/tar.json'), 'utf8'); + const tarJson = await fs.readFile(path.join(settings.root, 'src/node/utils/tar.json'), 'utf8'); const tar = {}; for (const [key, relativeFiles] of Object.entries(JSON.parse(tarJson))) { const files = relativeFiles.map(prefixLocalLibraryPath); @@ -28,7 +28,7 @@ const getTar = () => { return tar; }; -exports.expressCreateServer = (hookName, args, cb) => { +exports.expressCreateServer = async (hookName, args) => { // Cache both minified and static. const assetCache = new CachingMiddleware(); args.app.all(/\/javascripts\/(.*)/, assetCache.handle); @@ -49,7 +49,7 @@ exports.expressCreateServer = (hookName, args, cb) => { }); const StaticAssociator = Yajsml.associators.StaticAssociator; - const associations = Yajsml.associators.associationsForSimpleMapping(getTar()); + const associations = Yajsml.associators.associationsForSimpleMapping(await getTar()); const associator = new StaticAssociator(associations); jsServer.setAssociator(associator); @@ -69,6 +69,4 @@ exports.expressCreateServer = (hookName, args, cb) => { res.write(JSON.stringify({plugins: clientPlugins, parts: clientParts})); res.end(); }); - - return cb(); }; From a952df2cf50385b73f83cae9156443413945fc6d Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 15:26:32 -0500 Subject: [PATCH 290/357] Minify: Un-export `requestURI()` Nobody outside this file uses it. --- src/node/utils/Minify.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 8f49abec4..19d6c0f5d 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -384,7 +384,6 @@ const getFile = (filename, callback) => { exports.minify = minify; -exports.requestURI = requestURI; exports.requestURIs = requestURIs; exports.shutdown = async (hookName, context) => { From 1ec29e0d4583dace70a2ac797b9c2f7b580a7607 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 15:25:36 -0500 Subject: [PATCH 291/357] Minify: Asyncify `requestURI()` --- src/node/utils/Minify.js | 78 ++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 19d6c0f5d..2bb8a8d66 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -50,44 +50,44 @@ const LIBRARY_WHITELIST = [ // What follows is a terrible hack to avoid loop-back within the server. // TODO: Serve files from another service, or directly from the file system. -const requestURI = (url, method, headers, callback) => { - const parsedURL = urlutil.parse(url); - - let status = 500; +const requestURI = async (url, method, headers) => { var headers = {}; - const content = []; - const mockRequest = { - url, - method, - params: {filename: parsedURL.path.replace(/^\/static\//, '')}, - headers, - }; - const mockResponse = { - writeHead: (_status, _headers) => { - status = _status; - for (const header in _headers) { - if (Object.prototype.hasOwnProperty.call(_headers, header)) { - headers[header] = _headers[header]; + return await new Promise((resolve) => { + const parsedURL = urlutil.parse(url); + let status = 500; + const content = []; + const mockRequest = { + url, + method, + params: {filename: parsedURL.path.replace(/^\/static\//, '')}, + headers, + }; + const mockResponse = { + writeHead: (_status, _headers) => { + status = _status; + for (const header in _headers) { + if (Object.prototype.hasOwnProperty.call(_headers, header)) { + headers[header] = _headers[header]; + } } - } - }, - setHeader: (header, value) => { - headers[header.toLowerCase()] = value.toString(); - }, - header: (header, value) => { - headers[header.toLowerCase()] = value.toString(); - }, - write: (_content) => { - _content && content.push(_content); - }, - end: (_content) => { - _content && content.push(_content); - callback(status, headers, content.join('')); - }, - }; - - minify(mockRequest, mockResponse); + }, + setHeader: (header, value) => { + headers[header.toLowerCase()] = value.toString(); + }, + header: (header, value) => { + headers[header.toLowerCase()] = value.toString(); + }, + write: (_content) => { + _content && content.push(_content); + }, + end: (_content) => { + _content && content.push(_content); + resolve([status, headers, content.join('')]); + }, + }; + minify(mockRequest, mockResponse); + }); }; const requestURIs = (locations, method, headers, callback) => { @@ -101,15 +101,15 @@ const requestURIs = (locations, method, headers, callback) => { callback(statuss, headerss, contentss); }; - const respondFor = (i) => (status, headers, content) => { - responses[i] = [status, headers, content]; + const respondFor = (i) => (response) => { + responses[i] = response; if (--pendingRequests === 0) { completed(); } }; for (let i = 0, ii = locations.length; i < ii; i++) { - requestURI(locations[i], method, headers, respondFor(i)); + requestURI(locations[i], method, headers).then(respondFor(i)); } }; @@ -233,7 +233,7 @@ const getAceFile = (callback) => { let resourceURI = baseURI + path.normalize(path.join('/static/', filename)); resourceURI = resourceURI.replace(/\\/g, '/'); // Windows (safe generally?) - requestURI(resourceURI, 'GET', {}, (status, headers, body) => { + requestURI(resourceURI, 'GET', {}).then(([status, headers, body]) => { const error = !(status === 200 || status === 404); if (!error) { data += `Ace2Editor.EMBEDED[${JSON.stringify(filename)}] = ${ From 0c428e068e761ad53405a0939baaab86cc828df3 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 17:48:48 -0500 Subject: [PATCH 292/357] Minify: Use `Promise.all()` to simplify `requestURIs()` --- src/node/utils/Minify.js | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 2bb8a8d66..0e6b4c4dc 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -91,26 +91,12 @@ const requestURI = async (url, method, headers) => { }; const requestURIs = (locations, method, headers, callback) => { - let pendingRequests = locations.length; - const responses = []; - - const completed = () => { + Promise.all(locations.map((loc) => requestURI(loc, method, headers))).then((responses) => { const statuss = responses.map((x) => x[0]); const headerss = responses.map((x) => x[1]); const contentss = responses.map((x) => x[2]); callback(statuss, headerss, contentss); - }; - - const respondFor = (i) => (response) => { - responses[i] = response; - if (--pendingRequests === 0) { - completed(); - } - }; - - for (let i = 0, ii = locations.length; i < ii; i++) { - requestURI(locations[i], method, headers).then(respondFor(i)); - } + }); }; /** From 5cc191f185d0b48e513a5b088aea08353507a783 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 15:16:45 -0500 Subject: [PATCH 293/357] Minify: Replace `async.forEach()` with `Promise.all()` --- src/node/utils/Minify.js | 66 ++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 39 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 0e6b4c4dc..9ea0117a6 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -23,7 +23,6 @@ const ERR = require('async-stacktrace'); const settings = require('./Settings'); -const async = require('async'); const fs = require('fs'); const path = require('path'); const plugins = require('../../static/js/pluginfw/plugin_defs'); @@ -32,6 +31,7 @@ const urlutil = require('url'); const mime = require('mime-types'); const Threads = require('threads'); const log4js = require('log4js'); +const util = require('util'); const logger = log4js.getLogger('Minify'); @@ -213,25 +213,21 @@ const getAceFile = (callback) => { // Request the contents of the included file on the server-side and write // them into the file. - async.forEach(filenames, (filename, callback) => { + Promise.all(filenames.map(async (filename) => { // Hostname "invalid.invalid" is a dummy value to allow parsing as a URI. const baseURI = 'http://invalid.invalid'; let resourceURI = baseURI + path.normalize(path.join('/static/', filename)); resourceURI = resourceURI.replace(/\\/g, '/'); // Windows (safe generally?) - requestURI(resourceURI, 'GET', {}).then(([status, headers, body]) => { - const error = !(status === 200 || status === 404); - if (!error) { - data += `Ace2Editor.EMBEDED[${JSON.stringify(filename)}] = ${ - JSON.stringify(status === 200 ? body || '' : null)};\n`; - } else { - console.error(`getAceFile(): error getting ${resourceURI}. Status code: ${status}`); - } - callback(); - }); - }, (error) => { - callback(error, data); - }); + const [status, , body] = await requestURI(resourceURI, 'GET', {}); + const error = !(status === 200 || status === 404); + if (!error) { + data += `Ace2Editor.EMBEDED[${JSON.stringify(filename)}] = ${ + JSON.stringify(status === 200 ? body || '' : null)};\n`; + } else { + console.error(`getAceFile(): error getting ${resourceURI}. Status code: ${status}`); + } + })).then(() => callback(null, data), (err) => callback(err || new Error(err))); }); }; @@ -279,35 +275,27 @@ const lastModifiedDateOfEverything = (callback) => { const folders2check = [`${ROOT_DIR}js/`, `${ROOT_DIR}css/`]; let latestModification = 0; // go through this two folders - async.forEach(folders2check, (path, callback) => { + Promise.all(folders2check.map(async (path) => { // read the files in the folder - fs.readdir(path, (err, files) => { - if (ERR(err, callback)) return; + const files = await util.promisify(fs.readdir)(path); - // we wanna check the directory itself for changes too - files.push('.'); + // we wanna check the directory itself for changes too + files.push('.'); - // go through all files in this folder - async.forEach(files, (filename, callback) => { - // get the stat data of this file - fs.stat(`${path}/${filename}`, (err, stats) => { - if (ERR(err, callback)) return; + // go through all files in this folder + await Promise.all(files.map(async (filename) => { + // get the stat data of this file + const stats = await util.promisify(fs.stat)(`${path}/${filename}`); - // get the modification time - const modificationTime = stats.mtime.getTime(); + // get the modification time + const modificationTime = stats.mtime.getTime(); - // compare the modification time to the highest found - if (modificationTime > latestModification) { - latestModification = modificationTime; - } - - callback(); - }); - }, callback); - }); - }, () => { - callback(null, latestModification); - }); + // compare the modification time to the highest found + if (modificationTime > latestModification) { + latestModification = modificationTime; + } + })); + })).then(() => callback(null, latestModification), (err) => callback(err || new Error(err))); }; // This should be provided by the module, but until then, just use startup From e573276755d88fa80bcc17c11a55dc97575d5060 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 16:08:32 -0500 Subject: [PATCH 294/357] Minify: Asyncify `getAceFile()` --- src/node/utils/Minify.js | 69 ++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 9ea0117a6..323bba02a 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -190,45 +190,44 @@ const minify = (req, res) => { }; // find all includes in ace.js and embed them. -const getAceFile = (callback) => { - fs.readFile(`${ROOT_DIR}js/ace.js`, 'utf8', (err, data) => { - if (ERR(err, callback)) return; +const getAceFile = async () => { + let data = await util.promisify(fs.readFile)(`${ROOT_DIR}js/ace.js`, 'utf8'); - // Find all includes in ace.js and embed them - const filenames = []; - if (settings.minify) { - const regex = /\$\$INCLUDE_[a-zA-Z_]+\((['"])([^'"]*)\1\)/gi; - // This logic can be simplified via String.prototype.matchAll() once support for Node.js - // v11.x and older is dropped. - let matches; - while ((matches = regex.exec(data)) != null) { - filenames.push(matches[2]); - } + // Find all includes in ace.js and embed them + const filenames = []; + if (settings.minify) { + const regex = /\$\$INCLUDE_[a-zA-Z_]+\((['"])([^'"]*)\1\)/gi; + // This logic can be simplified via String.prototype.matchAll() once support for Node.js + // v11.x and older is dropped. + let matches; + while ((matches = regex.exec(data)) != null) { + filenames.push(matches[2]); } - // Always include the require kernel. - filenames.push('../static/js/require-kernel.js'); + } + // Always include the require kernel. + filenames.push('../static/js/require-kernel.js'); - data += ';\n'; - data += 'Ace2Editor.EMBEDED = Ace2Editor.EMBEDED || {};\n'; + data += ';\n'; + data += 'Ace2Editor.EMBEDED = Ace2Editor.EMBEDED || {};\n'; - // Request the contents of the included file on the server-side and write - // them into the file. - Promise.all(filenames.map(async (filename) => { - // Hostname "invalid.invalid" is a dummy value to allow parsing as a URI. - const baseURI = 'http://invalid.invalid'; - let resourceURI = baseURI + path.normalize(path.join('/static/', filename)); - resourceURI = resourceURI.replace(/\\/g, '/'); // Windows (safe generally?) + // Request the contents of the included file on the server-side and write + // them into the file. + await Promise.all(filenames.map(async (filename) => { + // Hostname "invalid.invalid" is a dummy value to allow parsing as a URI. + const baseURI = 'http://invalid.invalid'; + let resourceURI = baseURI + path.normalize(path.join('/static/', filename)); + resourceURI = resourceURI.replace(/\\/g, '/'); // Windows (safe generally?) - const [status, , body] = await requestURI(resourceURI, 'GET', {}); - const error = !(status === 200 || status === 404); - if (!error) { - data += `Ace2Editor.EMBEDED[${JSON.stringify(filename)}] = ${ - JSON.stringify(status === 200 ? body || '' : null)};\n`; - } else { - console.error(`getAceFile(): error getting ${resourceURI}. Status code: ${status}`); - } - })).then(() => callback(null, data), (err) => callback(err || new Error(err))); - }); + const [status, , body] = await requestURI(resourceURI, 'GET', {}); + const error = !(status === 200 || status === 404); + if (!error) { + data += `Ace2Editor.EMBEDED[${JSON.stringify(filename)}] = ${ + JSON.stringify(status === 200 ? body || '' : null)};\n`; + } else { + console.error(`getAceFile(): error getting ${resourceURI}. Status code: ${status}`); + } + })); + return data; }; // Check for the existance of the file and get the last modification date. @@ -348,7 +347,7 @@ const getFileCompressed = (filename, contentType, callback) => { const getFile = (filename, callback) => { if (filename === 'js/ace.js') { - getAceFile(callback); + util.callbackify(getAceFile)(callback); } else if (filename === 'js/require-kernel.js') { callback(undefined, requireDefinition()); } else { From 5d7c07e81cede2ac5ddc9503c5ad69f101ca5675 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 16:26:03 -0500 Subject: [PATCH 295/357] Minify: Asyncify `lastModifiedDateOfEverything()` --- src/node/utils/Minify.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 323bba02a..7e9537f12 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -245,9 +245,9 @@ const statFile = (filename, callback, dirStatLimit) => { } else if (filename === 'js/ace.js') { // Sometimes static assets are inlined into this file, so we have to stat // everything. - lastModifiedDateOfEverything((error, date) => { - callback(error, date, !error); - }); + lastModifiedDateOfEverything().then( + (date) => callback(null, date, true), + (err) => callback(err || new Error(err))); } else if (filename === 'js/require-kernel.js') { callback(null, requireLastModified(), true); } else { @@ -270,11 +270,11 @@ const statFile = (filename, callback, dirStatLimit) => { } }; -const lastModifiedDateOfEverything = (callback) => { +const lastModifiedDateOfEverything = async () => { const folders2check = [`${ROOT_DIR}js/`, `${ROOT_DIR}css/`]; let latestModification = 0; // go through this two folders - Promise.all(folders2check.map(async (path) => { + await Promise.all(folders2check.map(async (path) => { // read the files in the folder const files = await util.promisify(fs.readdir)(path); @@ -294,7 +294,8 @@ const lastModifiedDateOfEverything = (callback) => { latestModification = modificationTime; } })); - })).then(() => callback(null, latestModification), (err) => callback(err || new Error(err))); + })); + return latestModification; }; // This should be provided by the module, but until then, just use startup From 947dc8eeed8dfb13488f8b9798f3d9e0d07c27f0 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 16:32:29 -0500 Subject: [PATCH 296/357] Minify: Asyncify `getFile()` --- src/node/utils/Minify.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 7e9537f12..53357aa28 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -305,7 +305,7 @@ const requireLastModified = () => _requireLastModified.toUTCString(); const requireDefinition = () => `var require = ${RequireKernel.kernelSource};\n`; const getFileCompressed = (filename, contentType, callback) => { - getFile(filename, (error, content) => { + util.callbackify(getFile)(filename, (error, content) => { if (error || !content || !settings.minify) { callback(error, content); } else if (contentType === 'application/javascript') { @@ -346,14 +346,10 @@ const getFileCompressed = (filename, contentType, callback) => { }); }; -const getFile = (filename, callback) => { - if (filename === 'js/ace.js') { - util.callbackify(getAceFile)(callback); - } else if (filename === 'js/require-kernel.js') { - callback(undefined, requireDefinition()); - } else { - fs.readFile(ROOT_DIR + filename, callback); - } +const getFile = async (filename) => { + if (filename === 'js/ace.js') return await getAceFile(); + if (filename === 'js/require-kernel.js') return requireDefinition(); + return await util.promisify(fs.readFile)(ROOT_DIR + filename); }; exports.minify = minify; From dd7ea1a8f9ee91e16db13379f0b0124e061fea6e Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 16:54:25 -0500 Subject: [PATCH 297/357] Minify: Asyncify `statFile()` --- src/node/utils/Minify.js | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 53357aa28..2346d57ca 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -145,7 +145,7 @@ const minify = (req, res) => { const contentType = mime.lookup(filename); - statFile(filename, (error, date, exists) => { + util.callbackify(statFile)(filename, 3, (error, [date, exists]) => { if (date) { date = new Date(date); date.setMilliseconds(0); @@ -186,7 +186,7 @@ const minify = (req, res) => { res.writeHead(405, {allow: 'HEAD, GET'}); res.end(); } - }, 3); + }); }; // find all includes in ace.js and embed them. @@ -231,7 +231,7 @@ const getAceFile = async () => { }; // Check for the existance of the file and get the last modification date. -const statFile = (filename, callback, dirStatLimit) => { +const statFile = async (filename, dirStatLimit) => { /* * The only external call to this function provides an explicit value for * dirStatLimit: this check could be removed. @@ -241,32 +241,26 @@ const statFile = (filename, callback, dirStatLimit) => { } if (dirStatLimit < 1 || filename === '' || filename === '/') { - callback(null, null, false); + return [null, false]; } else if (filename === 'js/ace.js') { // Sometimes static assets are inlined into this file, so we have to stat // everything. - lastModifiedDateOfEverything().then( - (date) => callback(null, date, true), - (err) => callback(err || new Error(err))); + return [await lastModifiedDateOfEverything(), true]; } else if (filename === 'js/require-kernel.js') { - callback(null, requireLastModified(), true); + return [requireLastModified(), true]; } else { - fs.stat(ROOT_DIR + filename, (error, stats) => { - if (error) { - if (error.code === 'ENOENT') { - // Stat the directory instead. - statFile(path.dirname(filename), (error, date, exists) => { - callback(error, date, false); - }, dirStatLimit - 1); - } else { - callback(error); - } - } else if (stats.isFile()) { - callback(null, stats.mtime.getTime(), true); - } else { - callback(null, stats.mtime.getTime(), false); + let stats; + try { + stats = await util.promisify(fs.stat)(ROOT_DIR + filename); + } catch (err) { + if (err.code === 'ENOENT') { + // Stat the directory instead. + const [date] = await statFile(path.dirname(filename), dirStatLimit - 1); + return [date, false]; } - }); + throw err; + } + return [stats.mtime.getTime(), stats.isFile()]; } }; From 84190793dcb16b66fb122de6316097ac77918011 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 17:10:13 -0500 Subject: [PATCH 298/357] Minify: Asyncify `getFileCompressed()` --- src/node/utils/Minify.js | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 2346d57ca..eb6171ca2 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -172,7 +172,7 @@ const minify = (req, res) => { res.writeHead(200, {}); res.end(); } else if (req.method === 'GET') { - getFileCompressed(filename, contentType, (error, content) => { + util.callbackify(getFileCompressed)(filename, contentType, (error, content) => { if (ERR(error, () => { res.writeHead(500, {}); res.end(); @@ -298,11 +298,12 @@ const _requireLastModified = new Date(); const requireLastModified = () => _requireLastModified.toUTCString(); const requireDefinition = () => `var require = ${RequireKernel.kernelSource};\n`; -const getFileCompressed = (filename, contentType, callback) => { - util.callbackify(getFile)(filename, (error, content) => { - if (error || !content || !settings.minify) { - callback(error, content); - } else if (contentType === 'application/javascript') { +const getFileCompressed = async (filename, contentType) => { + let content = await getFile(filename); + if (!content || !settings.minify) { + return content; + } else if (contentType === 'application/javascript') { + return await new Promise((resolve) => { threadsPool.queue(async ({compressJS}) => { try { logger.info('Compress JS file %s.', filename); @@ -319,10 +320,11 @@ const getFileCompressed = (filename, contentType, callback) => { console.error('getFile() returned an error in ' + `getFileCompressed(${filename}, ${contentType}): ${error}`); } - - callback(null, content); + resolve(content); }); - } else if (contentType === 'text/css') { + }); + } else if (contentType === 'text/css') { + return await new Promise((resolve) => { threadsPool.queue(async ({compressCSS}) => { try { logger.info('Compress CSS file %s.', filename); @@ -331,13 +333,12 @@ const getFileCompressed = (filename, contentType, callback) => { } catch (error) { console.error(`CleanCSS.minify() returned an error on ${filename}: ${error}`); } - - callback(null, content); + resolve(content); }); - } else { - callback(null, content); - } - }); + }); + } else { + return content; + } }; const getFile = async (filename) => { From 3eefe7183410a07b764492c9ac36ff866f634fde Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 19:16:55 -0500 Subject: [PATCH 299/357] Minify: Don't set cache headers if `statFile()` causes 500 --- src/node/utils/Minify.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index eb6171ca2..2c3fb7c1f 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -146,6 +146,11 @@ const minify = (req, res) => { const contentType = mime.lookup(filename); util.callbackify(statFile)(filename, 3, (error, [date, exists]) => { + if (error) { + res.writeHead(500, {}); + res.end(); + return; + } if (date) { date = new Date(date); date.setMilliseconds(0); @@ -158,10 +163,7 @@ const minify = (req, res) => { } } - if (error) { - res.writeHead(500, {}); - res.end(); - } else if (!exists) { + if (!exists) { res.writeHead(404, {}); res.end(); } else if (new Date(req.headers['if-modified-since']) >= date) { From 073052ac661b0e7f239a0861a7c2b8c4ae1f8016 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 17:15:31 -0500 Subject: [PATCH 300/357] Minify: Asyncify `minify()` --- src/node/utils/Minify.js | 92 ++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 45 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 2c3fb7c1f..383ec6f73 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -21,7 +21,6 @@ * limitations under the License. */ -const ERR = require('async-stacktrace'); const settings = require('./Settings'); const fs = require('fs'); const path = require('path'); @@ -53,7 +52,7 @@ const LIBRARY_WHITELIST = [ const requestURI = async (url, method, headers) => { var headers = {}; - return await new Promise((resolve) => { + return await new Promise((resolve, reject) => { const parsedURL = urlutil.parse(url); let status = 500; const content = []; @@ -86,7 +85,7 @@ const requestURI = async (url, method, headers) => { resolve([status, headers, content.join('')]); }, }; - minify(mockRequest, mockResponse); + minify(mockRequest, mockResponse).catch(reject); }); }; @@ -104,7 +103,7 @@ const requestURIs = (locations, method, headers, callback) => { * @param req the Express request * @param res the Express response */ -const minify = (req, res) => { +const minify = async (req, res) => { let filename = req.params.filename; // No relative paths, especially if they may go up the file hierarchy. @@ -145,50 +144,53 @@ const minify = (req, res) => { const contentType = mime.lookup(filename); - util.callbackify(statFile)(filename, 3, (error, [date, exists]) => { - if (error) { + let date, exists; + try { + [date, exists] = await statFile(filename, 3); + } catch (err) { + res.writeHead(500, {}); + res.end(); + return; + } + if (date) { + date = new Date(date); + date.setMilliseconds(0); + res.setHeader('last-modified', date.toUTCString()); + res.setHeader('date', (new Date()).toUTCString()); + if (settings.maxAge !== undefined) { + const expiresDate = new Date(Date.now() + settings.maxAge * 1000); + res.setHeader('expires', expiresDate.toUTCString()); + res.setHeader('cache-control', `max-age=${settings.maxAge}`); + } + } + + if (!exists) { + res.writeHead(404, {}); + res.end(); + } else if (new Date(req.headers['if-modified-since']) >= date) { + res.writeHead(304, {}); + res.end(); + } else if (req.method === 'HEAD') { + res.header('Content-Type', contentType); + res.writeHead(200, {}); + res.end(); + } else if (req.method === 'GET') { + let content; + try { + content = await getFileCompressed(filename, contentType); + } catch (err) { res.writeHead(500, {}); res.end(); return; } - if (date) { - date = new Date(date); - date.setMilliseconds(0); - res.setHeader('last-modified', date.toUTCString()); - res.setHeader('date', (new Date()).toUTCString()); - if (settings.maxAge !== undefined) { - const expiresDate = new Date(Date.now() + settings.maxAge * 1000); - res.setHeader('expires', expiresDate.toUTCString()); - res.setHeader('cache-control', `max-age=${settings.maxAge}`); - } - } - - if (!exists) { - res.writeHead(404, {}); - res.end(); - } else if (new Date(req.headers['if-modified-since']) >= date) { - res.writeHead(304, {}); - res.end(); - } else if (req.method === 'HEAD') { - res.header('Content-Type', contentType); - res.writeHead(200, {}); - res.end(); - } else if (req.method === 'GET') { - util.callbackify(getFileCompressed)(filename, contentType, (error, content) => { - if (ERR(error, () => { - res.writeHead(500, {}); - res.end(); - })) return; - res.header('Content-Type', contentType); - res.writeHead(200, {}); - res.write(content); - res.end(); - }); - } else { - res.writeHead(405, {allow: 'HEAD, GET'}); - res.end(); - } - }); + res.header('Content-Type', contentType); + res.writeHead(200, {}); + res.write(content); + res.end(); + } else { + res.writeHead(405, {allow: 'HEAD, GET'}); + res.end(); + } }; // find all includes in ace.js and embed them. @@ -349,7 +351,7 @@ const getFile = async (filename) => { return await util.promisify(fs.readFile)(ROOT_DIR + filename); }; -exports.minify = minify; +exports.minify = (req, res, next) => minify(req, res).catch((err) => next(err || new Error(err))); exports.requestURIs = requestURIs; From aa11667ff7ee763ff29abf9f472e2fc992919277 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 18:19:31 -0500 Subject: [PATCH 301/357] Minify: Use `fs.promises` --- src/node/utils/Minify.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 383ec6f73..4ff5a8297 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -22,7 +22,7 @@ */ const settings = require('./Settings'); -const fs = require('fs'); +const fs = require('fs').promises; const path = require('path'); const plugins = require('../../static/js/pluginfw/plugin_defs'); const RequireKernel = require('etherpad-require-kernel'); @@ -30,7 +30,6 @@ const urlutil = require('url'); const mime = require('mime-types'); const Threads = require('threads'); const log4js = require('log4js'); -const util = require('util'); const logger = log4js.getLogger('Minify'); @@ -195,7 +194,7 @@ const minify = async (req, res) => { // find all includes in ace.js and embed them. const getAceFile = async () => { - let data = await util.promisify(fs.readFile)(`${ROOT_DIR}js/ace.js`, 'utf8'); + let data = await fs.readFile(`${ROOT_DIR}js/ace.js`, 'utf8'); // Find all includes in ace.js and embed them const filenames = []; @@ -255,7 +254,7 @@ const statFile = async (filename, dirStatLimit) => { } else { let stats; try { - stats = await util.promisify(fs.stat)(ROOT_DIR + filename); + stats = await fs.stat(ROOT_DIR + filename); } catch (err) { if (err.code === 'ENOENT') { // Stat the directory instead. @@ -274,7 +273,7 @@ const lastModifiedDateOfEverything = async () => { // go through this two folders await Promise.all(folders2check.map(async (path) => { // read the files in the folder - const files = await util.promisify(fs.readdir)(path); + const files = await fs.readdir(path); // we wanna check the directory itself for changes too files.push('.'); @@ -282,7 +281,7 @@ const lastModifiedDateOfEverything = async () => { // go through all files in this folder await Promise.all(files.map(async (filename) => { // get the stat data of this file - const stats = await util.promisify(fs.stat)(`${path}/${filename}`); + const stats = await fs.stat(`${path}/${filename}`); // get the modification time const modificationTime = stats.mtime.getTime(); @@ -348,7 +347,7 @@ const getFileCompressed = async (filename, contentType) => { const getFile = async (filename) => { if (filename === 'js/ace.js') return await getAceFile(); if (filename === 'js/require-kernel.js') return requireDefinition(); - return await util.promisify(fs.readFile)(ROOT_DIR + filename); + return await fs.readFile(ROOT_DIR + filename); }; exports.minify = (req, res, next) => minify(req, res).catch((err) => next(err || new Error(err))); From 44e420b6c53fc6fb19b05cd5963aff56759d6550 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 18:42:27 -0500 Subject: [PATCH 302/357] Minify: Return `Date` objects from `statFile()` --- src/node/utils/Minify.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 4ff5a8297..df98d4490 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -152,7 +152,6 @@ const minify = async (req, res) => { return; } if (date) { - date = new Date(date); date.setMilliseconds(0); res.setHeader('last-modified', date.toUTCString()); res.setHeader('date', (new Date()).toUTCString()); @@ -250,7 +249,7 @@ const statFile = async (filename, dirStatLimit) => { // everything. return [await lastModifiedDateOfEverything(), true]; } else if (filename === 'js/require-kernel.js') { - return [requireLastModified(), true]; + return [_requireLastModified, true]; } else { let stats; try { @@ -263,13 +262,13 @@ const statFile = async (filename, dirStatLimit) => { } throw err; } - return [stats.mtime.getTime(), stats.isFile()]; + return [stats.mtime, stats.isFile()]; } }; const lastModifiedDateOfEverything = async () => { const folders2check = [`${ROOT_DIR}js/`, `${ROOT_DIR}css/`]; - let latestModification = 0; + let latestModification = null; // go through this two folders await Promise.all(folders2check.map(async (path) => { // read the files in the folder @@ -283,12 +282,9 @@ const lastModifiedDateOfEverything = async () => { // get the stat data of this file const stats = await fs.stat(`${path}/${filename}`); - // get the modification time - const modificationTime = stats.mtime.getTime(); - // compare the modification time to the highest found - if (modificationTime > latestModification) { - latestModification = modificationTime; + if (latestModification == null || stats.mtime > latestModification) { + latestModification = stats.mtime; } })); })); @@ -298,7 +294,6 @@ const lastModifiedDateOfEverything = async () => { // This should be provided by the module, but until then, just use startup // time. const _requireLastModified = new Date(); -const requireLastModified = () => _requireLastModified.toUTCString(); const requireDefinition = () => `var require = ${RequireKernel.kernelSource};\n`; const getFileCompressed = async (filename, contentType) => { From 7a003cb9e213f9ff586df1a7b54f73a1430ac992 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 19:11:50 -0500 Subject: [PATCH 303/357] Minify: Let Express render the 500 error page --- src/node/utils/Minify.js | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index df98d4490..d9f5d508c 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -143,14 +143,7 @@ const minify = async (req, res) => { const contentType = mime.lookup(filename); - let date, exists; - try { - [date, exists] = await statFile(filename, 3); - } catch (err) { - res.writeHead(500, {}); - res.end(); - return; - } + const [date, exists] = await statFile(filename, 3); if (date) { date.setMilliseconds(0); res.setHeader('last-modified', date.toUTCString()); @@ -173,14 +166,7 @@ const minify = async (req, res) => { res.writeHead(200, {}); res.end(); } else if (req.method === 'GET') { - let content; - try { - content = await getFileCompressed(filename, contentType); - } catch (err) { - res.writeHead(500, {}); - res.end(); - return; - } + const content = await getFileCompressed(filename, contentType); res.header('Content-Type', contentType); res.writeHead(200, {}); res.write(content); From 7efca7dc7d87c23467f74badb8c527a492a71b0a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 19:31:42 -0500 Subject: [PATCH 304/357] Minify: Don't ignore request headers in `requestURI()` --- src/node/utils/Minify.js | 72 +++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index d9f5d508c..8689ce8f6 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -48,45 +48,41 @@ const LIBRARY_WHITELIST = [ // What follows is a terrible hack to avoid loop-back within the server. // TODO: Serve files from another service, or directly from the file system. -const requestURI = async (url, method, headers) => { - var headers = {}; - - return await new Promise((resolve, reject) => { - const parsedURL = urlutil.parse(url); - let status = 500; - const content = []; - const mockRequest = { - url, - method, - params: {filename: parsedURL.path.replace(/^\/static\//, '')}, - headers, - }; - const mockResponse = { - writeHead: (_status, _headers) => { - status = _status; - for (const header in _headers) { - if (Object.prototype.hasOwnProperty.call(_headers, header)) { - headers[header] = _headers[header]; - } +const requestURI = async (url, method, headers) => await new Promise((resolve, reject) => { + const parsedURL = urlutil.parse(url); + let status = 500; + const content = []; + const mockRequest = { + url, + method, + params: {filename: parsedURL.path.replace(/^\/static\//, '')}, + headers, + }; + const mockResponse = { + writeHead: (_status, _headers) => { + status = _status; + for (const header in _headers) { + if (Object.prototype.hasOwnProperty.call(_headers, header)) { + headers[header] = _headers[header]; } - }, - setHeader: (header, value) => { - headers[header.toLowerCase()] = value.toString(); - }, - header: (header, value) => { - headers[header.toLowerCase()] = value.toString(); - }, - write: (_content) => { - _content && content.push(_content); - }, - end: (_content) => { - _content && content.push(_content); - resolve([status, headers, content.join('')]); - }, - }; - minify(mockRequest, mockResponse).catch(reject); - }); -}; + } + }, + setHeader: (header, value) => { + headers[header.toLowerCase()] = value.toString(); + }, + header: (header, value) => { + headers[header.toLowerCase()] = value.toString(); + }, + write: (_content) => { + _content && content.push(_content); + }, + end: (_content) => { + _content && content.push(_content); + resolve([status, headers, content.join('')]); + }, + }; + minify(mockRequest, mockResponse).catch(reject); +}); const requestURIs = (locations, method, headers, callback) => { Promise.all(locations.map((loc) => requestURI(loc, method, headers))).then((responses) => { From 73d31b12a868fbc4c478c4a2d3ed7230d178df55 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Thu, 11 Feb 2021 19:44:43 -0500 Subject: [PATCH 305/357] Minify: Replace deprecated `url.parse()` with `new URL()` --- src/node/utils/Minify.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/node/utils/Minify.js b/src/node/utils/Minify.js index 8689ce8f6..2a7ee2bfa 100644 --- a/src/node/utils/Minify.js +++ b/src/node/utils/Minify.js @@ -26,7 +26,6 @@ const fs = require('fs').promises; const path = require('path'); const plugins = require('../../static/js/pluginfw/plugin_defs'); const RequireKernel = require('etherpad-require-kernel'); -const urlutil = require('url'); const mime = require('mime-types'); const Threads = require('threads'); const log4js = require('log4js'); @@ -49,13 +48,13 @@ const LIBRARY_WHITELIST = [ // What follows is a terrible hack to avoid loop-back within the server. // TODO: Serve files from another service, or directly from the file system. const requestURI = async (url, method, headers) => await new Promise((resolve, reject) => { - const parsedURL = urlutil.parse(url); + const parsedUrl = new URL(url); let status = 500; const content = []; const mockRequest = { url, method, - params: {filename: parsedURL.path.replace(/^\/static\//, '')}, + params: {filename: (parsedUrl.pathname + parsedUrl.search).replace(/^\/static\//, '')}, headers, }; const mockResponse = { From fcd9adf20de4b4395aafef693a296d3de239d608 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 12 Feb 2021 20:04:42 +0000 Subject: [PATCH 306/357] tests: admin test timeout increase, bugfix and removal of buggy test (#4762) --- src/tests/frontend/runner.js | 12 +++---- src/tests/frontend/specs/adminroot.js | 29 ----------------- src/tests/frontend/specs/adminsettings.js | 10 +++--- ...{adminplugins.js => adminupdateplugins.js} | 32 ++++++++++--------- 4 files changed, 29 insertions(+), 54 deletions(-) delete mode 100644 src/tests/frontend/specs/adminroot.js rename src/tests/frontend/specs/{adminplugins.js => adminupdateplugins.js} (77%) diff --git a/src/tests/frontend/runner.js b/src/tests/frontend/runner.js index fcc56a7d6..d9f37bcae 100644 --- a/src/tests/frontend/runner.js +++ b/src/tests/frontend/runner.js @@ -65,8 +65,8 @@ $(() => { runner.on('pass', (test) => { if (killTimeout) clearTimeout(killTimeout); killTimeout = setTimeout(() => { - append('FINISHED - [red]no test started since 3 minutes, tests stopped[clear]'); - }, 60000 * 3); + append('FINISHED - [red]no test started since 5 minutes, tests stopped[clear]'); + }, 60000 * 5); const medium = test.slow() / 2; test.speed = test.duration > test.slow() @@ -82,8 +82,8 @@ $(() => { runner.on('fail', (test, err) => { if (killTimeout) clearTimeout(killTimeout); killTimeout = setTimeout(() => { - append('FINISHED - [red]no test started since 3 minutes, tests stopped[clear]'); - }, 60000 * 3); + append('FINISHED - [red]no test started since 5 minutes, tests stopped[clear]'); + }, 60000 * 5); stats.failures++; test.err = err; @@ -93,8 +93,8 @@ $(() => { runner.on('pending', (test) => { if (killTimeout) clearTimeout(killTimeout); killTimeout = setTimeout(() => { - append('FINISHED - [red]no test started since 3 minutes, tests stopped[clear]'); - }, 60000 * 3); + append('FINISHED - [red]no test started since 5 minutes, tests stopped[clear]'); + }, 60000 * 5); stats.pending++; append(`-> [yellow]PENDING[clear]: ${test.title}`); diff --git a/src/tests/frontend/specs/adminroot.js b/src/tests/frontend/specs/adminroot.js deleted file mode 100644 index 7416c21b4..000000000 --- a/src/tests/frontend/specs/adminroot.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -describe('Admin page', function () { - before(async function () { - let success = false; - $.ajax({ - url: `${location.protocol}//admin:changeme@${location.hostname}:${location.port}/admin/`, - type: 'GET', - success: () => success = true, - }); - await helper.waitForPromise(() => success === true); - }); - - beforeEach(async function () { - helper.newAdmin(''); - await helper.waitForPromise( - () => helper.admin$ && helper.admin$('.menu').find('li').length >= 3); - }); - - it('Shows Plugin Manager Link', async function () { - helper.admin$('a[data-l10n-id="admin_plugins"]').is(':visible'); - }); - it('Shows Troubleshooting Info Link', async function () { - helper.admin$('a[data-l10n-id="admin_plugins_info"]').is(':visible'); - }); - it('Shows Settings Link', async function () { - helper.admin$('a[data-l10n-id="admin_settings"]').is(':visible'); - }); -}); diff --git a/src/tests/frontend/specs/adminsettings.js b/src/tests/frontend/specs/adminsettings.js index 0d7d0accb..d139d6056 100644 --- a/src/tests/frontend/specs/adminsettings.js +++ b/src/tests/frontend/specs/adminsettings.js @@ -16,7 +16,8 @@ describe('Admin > Settings', function () { beforeEach(async function () { helper.newAdmin('settings'); // needed, because the load event is fired to early - await helper.waitForPromise(() => helper.admin$ && helper.admin$('.settings').val().length > 0); + await helper.waitForPromise( + () => helper.admin$ && helper.admin$('.settings').val().length > 0, 5000); }); it('Are Settings visible, populated, does save work', async function () { @@ -27,16 +28,17 @@ describe('Admin > Settings', function () { // set new value helper.admin$('.settings').val((_, text) => `/* test */\n${text}`); await helper.waitForPromise( - () => settingsLength + 11 === helper.admin$('.settings').val().length); + () => settingsLength + 11 === helper.admin$('.settings').val().length, 5000); // saves helper.admin$('#saveSettings').click(); - await helper.waitForPromise(() => helper.admin$('#response').is(':visible')); + await helper.waitForPromise(() => helper.admin$('#response').is(':visible'), 5000); // new value for settings.json should now be saved // reset it to the old value helper.newAdmin('settings'); - await helper.waitForPromise(() => helper.admin$ && helper.admin$('.settings').val().length > 0); + await helper.waitForPromise( + () => helper.admin$ && helper.admin$('.settings').val().length > 0, 20000); // replace the test value with a line break helper.admin$('.settings').val((_, text) => text.replace('/* test */\n', '')); diff --git a/src/tests/frontend/specs/adminplugins.js b/src/tests/frontend/specs/adminupdateplugins.js similarity index 77% rename from src/tests/frontend/specs/adminplugins.js rename to src/tests/frontend/specs/adminupdateplugins.js index ba00d3e48..6bcf9cafc 100755 --- a/src/tests/frontend/specs/adminplugins.js +++ b/src/tests/frontend/specs/adminupdateplugins.js @@ -19,11 +19,11 @@ describe('Plugins page', function () { beforeEach(async function () { helper.newAdmin('plugins'); await helper.waitForPromise( - () => helper.admin$ && helper.admin$('.menu').find('li').length >= 3); + () => helper.admin$ && helper.admin$('.menu').find('li').length >= 3, 30000); }); it('Lists some plugins', async function () { - await helper.waitForPromise(() => helper.admin$('.results').children().length > 50); + await helper.waitForPromise(() => helper.admin$('.results').children().length > 50, 20000); }); it('Searches for plugin', async function () { @@ -33,7 +33,9 @@ describe('Plugins page', function () { }); it('Attempt to Update a plugin', async function () { - this.timeout(120000); + this.timeout(280000); + + await helper.waitForPromise(() => helper.admin$('.results').children().length > 50, 20000); if (helper.admin$('.ep_align').length === 0) this.skip(); @@ -70,41 +72,41 @@ describe('Plugins page', function () { // allow 50 seconds, check every 1 second. }); it('Attempt to Install a plugin', async function () { - this.timeout(240000); + this.timeout(280000); - helper.admin$('#search-query').val('ep_activepads'); + helper.admin$('#search-query').val('ep_headings2'); await helper.waitForPromise(() => helper.admin$('.results').children().length < 300, 6000); await helper.waitForPromise(() => helper.admin$('.results').children().length > 0, 6000); - // skip if we already have ep_activepads installed.. - if (helper.admin$('.ep_activepads .do-install').is(':visible') === false) this.skip(); + // skip if we already have ep_headings2 installed.. + if (helper.admin$('.ep_headings2 .do-install').is(':visible') === false) this.skip(); - helper.admin$('.ep_activepads .do-install').click(); + helper.admin$('.ep_headings2 .do-install').click(); // ensure install has attempted to be started await helper.waitForPromise( - () => helper.admin$('.ep_activepads .do-install').length !== 0, 120000); + () => helper.admin$('.ep_headings2 .do-install').length !== 0, 120000); // ensure its not showing installing any more await helper.waitForPromise( - () => helper.admin$('.ep_activepads .message').text() === '', 180000); + () => helper.admin$('.ep_headings2 .message').text() === '', 180000); // ensure uninstall button is visible await helper.waitForPromise( - () => helper.admin$('.ep_activepads .do-uninstall').length !== 0, 120000); + () => helper.admin$('.ep_headings2 .do-uninstall').length !== 0, 120000); }); it('Attempt to Uninstall a plugin', async function () { this.timeout(360000); await helper.waitForPromise( - () => helper.admin$('.ep_activepads .do-uninstall').length !== 0, 120000); + () => helper.admin$('.ep_headings2 .do-uninstall').length !== 0, 120000); - helper.admin$('.ep_activepads .do-uninstall').click(); + helper.admin$('.ep_headings2 .do-uninstall').click(); // ensure its showing uninstalling await helper.waitForPromise( - () => helper.admin$('.ep_activepads .message') + () => helper.admin$('.ep_headings2 .message') .text() === 'Uninstalling', 120000); // ensure its gone await helper.waitForPromise( - () => helper.admin$('.ep_activepads').length === 0, 240000); + () => helper.admin$('.ep_headings2').length === 0, 240000); helper.admin$('#search-query').val('ep_font'); await helper.waitForPromise(() => helper.admin$('.results').children().length < 300, 240000); From 55c96e557767220fe2b6a235e41c7c08067cb4ce Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 12 Feb 2021 13:55:48 +0000 Subject: [PATCH 307/357] socketio: increase socketio limit to 1MiB --- src/node/hooks/express/socketio.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/hooks/express/socketio.js b/src/node/hooks/express/socketio.js index a44e74e3a..178cf9155 100644 --- a/src/node/hooks/express/socketio.js +++ b/src/node/hooks/express/socketio.js @@ -56,7 +56,7 @@ exports.expressCreateServer = (hookName, args, cb) => { * https://github.com/socketio/socket.io/issues/2276#issuecomment-147184662 (not totally true, actually, see above) */ cookie: false, - maxHttpBufferSize: 10E3, + maxHttpBufferSize: 1 << 20, // 1MiB }); io.use((socket, next) => { From bcd5e36688725a3e8e43162f0f32f03dcec89411 Mon Sep 17 00:00:00 2001 From: John McLear Date: Fri, 12 Feb 2021 14:01:52 +0000 Subject: [PATCH 308/357] tests: backend and frontend tests for image upload --- .github/workflows/backend-tests.yml | 1 + .github/workflows/frontend-tests.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index 21bb0c698..7ab58c410 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -66,6 +66,7 @@ jobs: ep_font_size ep_hash_auth ep_headings2 + ep_image_upload ep_markdown ep_readonly_guest ep_set_title_on_pad diff --git a/.github/workflows/frontend-tests.yml b/.github/workflows/frontend-tests.yml index 0d5832be7..00c8dd6b5 100644 --- a/.github/workflows/frontend-tests.yml +++ b/.github/workflows/frontend-tests.yml @@ -72,6 +72,7 @@ jobs: ep_font_size ep_hash_auth ep_headings2 + ep_image_upload ep_markdown ep_readonly_guest ep_set_title_on_pad From db8ca2818f24e0ded237f96573f861a63254c94e Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 13 Feb 2021 00:27:30 -0500 Subject: [PATCH 309/357] lint: Treat `helper.js` and friends as normal browser files This enables the prefer-arrow/prefer-arrow-functions rule. --- src/package.json | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/package.json b/src/package.json index 4fdee2909..fca557978 100644 --- a/src/package.json +++ b/src/package.json @@ -125,7 +125,9 @@ }, { "files": [ - "static/**/*" + "static/**/*", + "tests/frontend/helper.js", + "tests/frontend/helper/**/*" ], "excludedFiles": [ "**/.eslintrc.js" @@ -133,7 +135,17 @@ "extends": "etherpad/browser", "env": { "shared-node-browser": true - } + }, + "overrides": [ + { + "files": [ + "tests/frontend/helper/**/*" + ], + "globals": { + "helper": "readonly" + } + } + ] }, { "files": [ @@ -142,6 +154,8 @@ "excludedFiles": [ "**/.eslintrc.js", "tests/frontend/travis/**/*", + "tests/frontend/helper.js", + "tests/frontend/helper/**/*", "tests/ratelimit/**/*" ], "extends": "etherpad/tests", @@ -179,6 +193,8 @@ ], "excludedFiles": [ "**/.eslintrc.js", + "tests/frontend/helper.js", + "tests/frontend/helper/**/*", "tests/frontend/travis/**/*" ], "extends": "etherpad/tests/frontend", From 8f2f6593bed533fd34e50fe3e659bcde87344a4b Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 13 Feb 2021 00:31:36 -0500 Subject: [PATCH 310/357] lint: Re-run `eslint --fix` --- src/node/handler/PadMessageHandler.js | 7 +++---- src/node/server.js | 2 +- src/node/utils/Settings.js | 2 +- src/static/js/ace2_inner.js | 2 +- src/tests/backend/specs/api/sessionsAndGroups.js | 1 - 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 8c106c615..7e290b9e3 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -94,15 +94,14 @@ exports.handleConnect = (socket) => { stats.gauge('activePads', () => { const padIds = []; for (const session of Object.keys(sessioninfos)) { - if(sessioninfos[session].padId) { - if(padIds.indexOf(sessioninfos[session].padId) === -1) { + if (sessioninfos[session].padId) { + if (padIds.indexOf(sessioninfos[session].padId) === -1) { padIds.push(sessioninfos[session].padId); } } - }; + } return padIds.length; }); - }; /** diff --git a/src/node/server.js b/src/node/server.js index c1adfe073..0f0b0147f 100755 --- a/src/node/server.js +++ b/src/node/server.js @@ -181,7 +181,7 @@ exports.stop = async () => { throw new Error(`unknown State: ${state.toString()}`); } logger.info('Stopping Etherpad...'); - let stopDoneGate = new Gate(); + const stopDoneGate = new Gate(); state = State.STOPPING; try { let timeout = null; diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index 9db195f64..0be086090 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -381,7 +381,7 @@ exports.commitRateLimiting = { exports.importMaxFileSize = 50 * 1024 * 1024; /* - * Disable Admin UI tests + * Disable Admin UI tests */ exports.enableAdminUITests = false; diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 31d6419ce..72fb9400f 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -2180,7 +2180,7 @@ function Ace2Inner() { isScrollableEditEvent(currentCallStack.type); const innerHeight = getInnerHeight(); scroll.scrollWhenCaretIsInTheLastLineOfViewportWhenNecessary( - rep, isScrollableEvent, innerHeight*2 + rep, isScrollableEvent, innerHeight * 2 ); } diff --git a/src/tests/backend/specs/api/sessionsAndGroups.js b/src/tests/backend/specs/api/sessionsAndGroups.js index 16bbca000..a1f439eb3 100644 --- a/src/tests/backend/specs/api/sessionsAndGroups.js +++ b/src/tests/backend/specs/api/sessionsAndGroups.js @@ -170,7 +170,6 @@ describe(__filename, function () { }); }); // End of coverage for https://github.com/ether/etherpad-lite/issues/4227 - }); describe('API: Author creation', function () { From eb9d5bb470c08d1cd88286bb61189e6b187ddb94 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 13 Feb 2021 00:41:01 -0500 Subject: [PATCH 311/357] lint: `src/tests/frontend/helper.js` and friends --- src/tests/frontend/helper.js | 46 +++++++++++----------- src/tests/frontend/helper/methods.js | 41 +++++++++---------- src/tests/frontend/helper/ui.js | 59 ++++++++++------------------ 3 files changed, 60 insertions(+), 86 deletions(-) diff --git a/src/tests/frontend/helper.js b/src/tests/frontend/helper.js index 0f8a32f1a..707a8b72c 100644 --- a/src/tests/frontend/helper.js +++ b/src/tests/frontend/helper.js @@ -2,11 +2,11 @@ const helper = {}; // eslint-disable-line no-redeclare -(function () { - let $iframe; const - jsLibraries = {}; +(() => { + let $iframe; + const jsLibraries = {}; - helper.init = function (cb) { + helper.init = (cb) => { $.get('/static/js/jquery.js').done((code) => { // make sure we don't override existing jquery jsLibraries.jquery = `if(typeof $ === 'undefined') {\n${code}\n}`; @@ -19,7 +19,7 @@ const helper = {}; // eslint-disable-line no-redeclare }); }; - helper.randomString = function randomString(len) { + helper.randomString = (len) => { const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; let randomstring = ''; for (let i = 0; i < len; i++) { @@ -29,7 +29,7 @@ const helper = {}; // eslint-disable-line no-redeclare return randomstring; }; - const getFrameJQuery = function ($iframe) { + const getFrameJQuery = ($iframe) => { /* I tried over 9001 ways to inject javascript into iframes. This is the only way I found that worked in IE 7+8+9, FF and Chrome @@ -38,7 +38,7 @@ const helper = {}; // eslint-disable-line no-redeclare const doc = win.document; // IE 8+9 Hack to make eval appear - // http://stackoverflow.com/questions/2720444/why-does-this-window-object-not-have-the-eval-function + // https://stackoverflow.com/q/2720444 win.execScript && win.execScript('null'); win.eval(jsLibraries.jquery); @@ -50,15 +50,15 @@ const helper = {}; // eslint-disable-line no-redeclare return win.$; }; - helper.clearSessionCookies = function () { - // Expire cookies, so author and language are changed after reloading the pad. - // See https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#Example_4_Reset_the_previous_cookie + helper.clearSessionCookies = () => { + // Expire cookies, so author and language are changed after reloading the pad. See: + // https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#example_4_reset_the_previous_cookie window.document.cookie = 'token=;expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; window.document.cookie = 'language=;expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; }; // Can only happen when the iframe exists, so we're doing it separately from other cookies - helper.clearPadPrefCookie = function () { + helper.clearPadPrefCookie = () => { helper.padChrome$.document.cookie = 'prefsHttp=;expires=Thu, 01 Jan 1970 00:00:00 GMT'; }; @@ -68,7 +68,7 @@ const helper = {}; // eslint-disable-line no-redeclare // seem to have independent cookies, UNLESS we put path=/ here (which we don't). // I don't fully understand it, but this function seems to properly simulate // padCookie.setPref in the client code - helper.setPadPrefCookie = function (prefs) { + helper.setPadPrefCookie = (prefs) => { helper.padChrome$.document.cookie = (`prefsHttp=${escape(JSON.stringify(prefs))};expires=Thu, 01 Jan 3000 00:00:00 GMT`); }; @@ -94,7 +94,7 @@ const helper = {}; // eslint-disable-line no-redeclare // This ensures that tests run regardless of this problem helper.retry = 0; - helper.newPad = function (cb, padName) { + helper.newPad = (cb, padName) => { // build opts object let opts = {clearCookies: true}; if (typeof cb === 'function') { @@ -181,7 +181,7 @@ const helper = {}; // eslint-disable-line no-redeclare return padName; }; - helper.newAdmin = async function (page) { + helper.newAdmin = async (page) => { // define the iframe $iframe = $(``); @@ -197,7 +197,7 @@ const helper = {}; // eslint-disable-line no-redeclare }); }; - helper.waitFor = function (conditionFunc, timeoutTime = 1900, intervalTime = 10) { + helper.waitFor = (conditionFunc, timeoutTime = 1900, intervalTime = 10) => { const deferred = new $.Deferred(); const _fail = deferred.fail.bind(deferred); @@ -242,14 +242,12 @@ const helper = {}; // eslint-disable-line no-redeclare * @returns {Promise} * */ - helper.waitForPromise = async function (...args) { - // Note: waitFor() has a strange API: On timeout it rejects, but it also throws an uncatchable - // exception unless .fail() has been called. That uncatchable exception is disabled here by - // passing a no-op function to .fail(). - return await this.waitFor(...args).fail(() => {}); - }; + // Note: waitFor() has a strange API: On timeout it rejects, but it also throws an uncatchable + // exception unless .fail() has been called. That uncatchable exception is disabled here by + // passing a no-op function to .fail(). + helper.waitForPromise = async (...args) => await helper.waitFor(...args).fail(() => {}); - helper.selectLines = function ($startLine, $endLine, startOffset, endOffset) { + helper.selectLines = ($startLine, $endLine, startOffset, endOffset) => { // if no offset is provided, use beginning of start line and end of end line startOffset = startOffset || 0; endOffset = endOffset === undefined ? $endLine.text().length : endOffset; @@ -268,7 +266,7 @@ const helper = {}; // eslint-disable-line no-redeclare selection.addRange(range); }; - const getTextNodeAndOffsetOf = function ($targetLine, targetOffsetAtLine) { + const getTextNodeAndOffsetOf = ($targetLine, targetOffsetAtLine) => { const $textNodes = $targetLine.find('*').contents().filter(function () { return this.nodeType === Node.TEXT_NODE; }); @@ -310,5 +308,5 @@ const helper = {}; // eslint-disable-line no-redeclare /* Ensure console.log doesn't blow up in IE, ugly but ok for a test framework imho*/ window.console = window.console || {}; - window.console.log = window.console.log || function () {}; + window.console.log = window.console.log || (() => {}); })(); diff --git a/src/tests/frontend/helper/methods.js b/src/tests/frontend/helper/methods.js index 157ba6aba..4b0391774 100644 --- a/src/tests/frontend/helper/methods.js +++ b/src/tests/frontend/helper/methods.js @@ -4,7 +4,7 @@ * Spys on socket.io messages and saves them into several arrays * that are visible in tests */ -helper.spyOnSocketIO = function () { +helper.spyOnSocketIO = () => { helper.contentWindow().pad.socket.on('message', (msg) => { if (msg.type === 'COLLABROOM') { if (msg.data.type === 'ACCEPT_COMMIT') { @@ -30,7 +30,7 @@ helper.spyOnSocketIO = function () { * @todo needs to support writing to a specified caret position * */ -helper.edit = async function (message, line) { +helper.edit = async (message, line) => { const editsNum = helper.commits.length; line = line ? line - 1 : 0; helper.linesDiv()[line].sendkeys(message); @@ -45,7 +45,7 @@ helper.edit = async function (message, line) { * * @returns {Array.} array of divs */ -helper.linesDiv = function () { +helper.linesDiv = () => { return helper.padInner$('.ace-line').map(function () { return $(this); }).get(); @@ -57,18 +57,15 @@ helper.linesDiv = function () { * * @returns {Array.} lines of text */ -helper.textLines = function () { - return helper.linesDiv().map((div) => div.text()); -}; +helper.textLines = () => helper.linesDiv().map((div) => div.text()); /** * The default pad text transmitted via `clientVars` * * @returns {string} */ -helper.defaultText = function () { - return helper.padChrome$.window.clientVars.collab_client_vars.initialAttributedText.text; -}; +helper.defaultText = + () => helper.padChrome$.window.clientVars.collab_client_vars.initialAttributedText.text; /** * Sends a chat `message` via `sendKeys` @@ -84,7 +81,7 @@ helper.defaultText = function () { * @param {string} message the chat message to be sent * @returns {Promise} */ -helper.sendChatMessage = function (message) { +helper.sendChatMessage = (message) => { const noOfChatMessages = helper.chatMessages.length; helper.padChrome$('#chatinput').sendkeys(message); return helper.waitForPromise(() => noOfChatMessages + 1 === helper.chatMessages.length); @@ -95,7 +92,7 @@ helper.sendChatMessage = function (message) { * * @returns {Promise} */ -helper.showSettings = function () { +helper.showSettings = () => { if (!helper.isSettingsShown()) { helper.settingsButton().click(); return helper.waitForPromise(() => helper.isSettingsShown(), 2000); @@ -108,7 +105,7 @@ helper.showSettings = function () { * @returns {Promise} * @todo untested */ -helper.hideSettings = function () { +helper.hideSettings = () => { if (helper.isSettingsShown()) { helper.settingsButton().click(); return helper.waitForPromise(() => !helper.isSettingsShown(), 2000); @@ -121,7 +118,7 @@ helper.hideSettings = function () { * * @returns {Promise} */ -helper.enableStickyChatviaSettings = function () { +helper.enableStickyChatviaSettings = () => { const stickyChat = helper.padChrome$('#options-stickychat'); if (helper.isSettingsShown() && !stickyChat.is(':checked')) { stickyChat.click(); @@ -135,7 +132,7 @@ helper.enableStickyChatviaSettings = function () { * * @returns {Promise} */ -helper.disableStickyChatviaSettings = function () { +helper.disableStickyChatviaSettings = () => { const stickyChat = helper.padChrome$('#options-stickychat'); if (helper.isSettingsShown() && stickyChat.is(':checked')) { stickyChat.click(); @@ -149,7 +146,7 @@ helper.disableStickyChatviaSettings = function () { * * @returns {Promise} */ -helper.enableStickyChatviaIcon = function () { +helper.enableStickyChatviaIcon = () => { const stickyChat = helper.padChrome$('#titlesticky'); if (helper.isChatboxShown() && !helper.isChatboxSticky()) { stickyChat.click(); @@ -163,7 +160,7 @@ helper.enableStickyChatviaIcon = function () { * * @returns {Promise} */ -helper.disableStickyChatviaIcon = function () { +helper.disableStickyChatviaIcon = () => { if (helper.isChatboxShown() && helper.isChatboxSticky()) { helper.titlecross().click(); return helper.waitForPromise(() => !helper.isChatboxSticky(), 2000); @@ -182,7 +179,7 @@ helper.disableStickyChatviaIcon = function () { * @todo for some reason this does only work the first time, you cannot * goto rev 0 and then via the same method to rev 5. Use buttons instead */ -helper.gotoTimeslider = function (revision) { +helper.gotoTimeslider = (revision) => { revision = Number.isInteger(revision) ? `#${revision}` : ''; const iframe = $('#iframe-container iframe'); iframe.attr('src', `${iframe.attr('src')}/timeslider${revision}`); @@ -198,7 +195,7 @@ helper.gotoTimeslider = function (revision) { * @todo no mousemove test * @param {number} X coordinate */ -helper.sliderClick = function (X) { +helper.sliderClick = (X) => { const sliderBar = helper.sliderBar(); const edown = new jQuery.Event('mousedown'); const eup = new jQuery.Event('mouseup'); @@ -214,11 +211,9 @@ helper.sliderClick = function (X) { * * @returns {Array.} lines of text */ -helper.timesliderTextLines = function () { - return helper.contentWindow().$('.ace-line').map(function () { - return $(this).text(); - }).get(); -}; +helper.timesliderTextLines = () => helper.contentWindow().$('.ace-line').map(function () { + return $(this).text(); +}).get(); helper.padIsEmpty = () => ( !helper.padInner$.document.getSelection().isCollapsed || diff --git a/src/tests/frontend/helper/ui.js b/src/tests/frontend/helper/ui.js index 7ab8b990d..84d00589b 100644 --- a/src/tests/frontend/helper/ui.js +++ b/src/tests/frontend/helper/ui.js @@ -5,9 +5,7 @@ * * @returns {HTMLElement} contentWindow */ -helper.contentWindow = function () { - return $('#iframe-container iframe')[0].contentWindow; -}; +helper.contentWindow = () => $('#iframe-container iframe')[0].contentWindow; /** * Opens the chat unless it is already open via an @@ -15,7 +13,7 @@ helper.contentWindow = function () { * * @returns {Promise} */ -helper.showChat = function () { +helper.showChat = () => { const chaticon = helper.chatIcon(); if (chaticon.hasClass('visible')) { chaticon.click(); @@ -28,7 +26,7 @@ helper.showChat = function () { * * @returns {Promise} */ -helper.hideChat = function () { +helper.hideChat = () => { if (helper.isChatboxShown() && !helper.isChatboxSticky()) { helper.titlecross().click(); return helper.waitForPromise(() => !helper.isChatboxShown(), 2000); @@ -40,53 +38,48 @@ helper.hideChat = function () { * * @returns {HTMLElement} the chat icon */ -helper.chatIcon = function () { return helper.padChrome$('#chaticon'); }; +helper.chatIcon = () => helper.padChrome$('#chaticon'); /** * The chat messages from the UI * * @returns {Array.} */ -helper.chatTextParagraphs = function () { return helper.padChrome$('#chattext').children('p'); }; +helper.chatTextParagraphs = () => helper.padChrome$('#chattext').children('p'); /** * Returns true if the chat box is sticky * * @returns {boolean} stickyness of the chat box */ -helper.isChatboxSticky = function () { - return helper.padChrome$('#chatbox').hasClass('stickyChat'); -}; +helper.isChatboxSticky = () => helper.padChrome$('#chatbox').hasClass('stickyChat'); /** * Returns true if the chat box is shown * * @returns {boolean} visibility of the chat box */ -helper.isChatboxShown = function () { - return helper.padChrome$('#chatbox').hasClass('visible'); -}; +helper.isChatboxShown = () => helper.padChrome$('#chatbox').hasClass('visible'); /** * Gets the settings menu * * @returns {HTMLElement} the settings menu */ -helper.settingsMenu = function () { return helper.padChrome$('#settings'); }; +helper.settingsMenu = () => helper.padChrome$('#settings'); /** * Gets the settings button * * @returns {HTMLElement} the settings button */ -helper.settingsButton = function () { - return helper.padChrome$("button[data-l10n-id='pad.toolbar.settings.title']"); -}; +helper.settingsButton = + () => helper.padChrome$("button[data-l10n-id='pad.toolbar.settings.title']"); /** * Toggles user list */ -helper.toggleUserList = async function () { +helper.toggleUserList = async () => { const isVisible = helper.userListShown(); const button = helper.padChrome$("button[data-l10n-id='pad.toolbar.showusers.title']"); button.click(); @@ -98,18 +91,14 @@ helper.toggleUserList = async function () { * * @returns {HTMLElement} user name input field */ -helper.usernameField = function () { - return helper.padChrome$("input[data-l10n-id='pad.userlist.entername']"); -}; +helper.usernameField = () => helper.padChrome$("input[data-l10n-id='pad.userlist.entername']"); /** * Is the user list popup shown? * * @returns {boolean} */ -helper.userListShown = function () { - return helper.padChrome$('div#users').hasClass('popup-show'); -}; +helper.userListShown = () => helper.padChrome$('div#users').hasClass('popup-show'); /** * Sets the user name @@ -128,23 +117,21 @@ helper.setUserName = async (name) => { * * @returns {HTMLElement} the titlecross icon */ -helper.titlecross = function () { return helper.padChrome$('#titlecross'); }; +helper.titlecross = () => helper.padChrome$('#titlecross'); /** * Returns true if the settings menu is visible * * @returns {boolean} is the settings menu shown? */ -helper.isSettingsShown = function () { - return helper.padChrome$('#settings').hasClass('popup-show'); -}; +helper.isSettingsShown = () => helper.padChrome$('#settings').hasClass('popup-show'); /** * Gets the timer div of a timeslider that has the datetime of the revision * * @returns {HTMLElement} timer */ -helper.timesliderTimer = function () { +helper.timesliderTimer = () => { if (typeof helper.contentWindow().$ === 'function') { return helper.contentWindow().$('#timer'); } @@ -155,7 +142,7 @@ helper.timesliderTimer = function () { * * @returns {HTMLElement} timer */ -helper.timesliderTimerTime = function () { +helper.timesliderTimerTime = () => { if (helper.timesliderTimer()) { return helper.timesliderTimer().text(); } @@ -166,9 +153,7 @@ helper.timesliderTimerTime = function () { * * @returns {HTMLElement} */ -helper.sliderBar = function () { - return helper.contentWindow().$('#ui-slider-bar'); -}; +helper.sliderBar = () => helper.contentWindow().$('#ui-slider-bar'); /** * revision_date element @@ -176,9 +161,7 @@ helper.sliderBar = function () { * * @returns {HTMLElement} */ -helper.revisionDateElem = function () { - return helper.contentWindow().$('#revision_date').text(); -}; +helper.revisionDateElem = () => helper.contentWindow().$('#revision_date').text(); /** * revision_label element @@ -186,6 +169,4 @@ helper.revisionDateElem = function () { * * @returns {HTMLElement} */ -helper.revisionLabelElem = function () { - return helper.contentWindow().$('#revision_label'); -}; +helper.revisionLabelElem = () => helper.contentWindow().$('#revision_label'); From 66544be35410d010ed4398fe1166ec50c8c99975 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 13 Feb 2021 00:42:20 -0500 Subject: [PATCH 312/357] lint: `src/tests/backend/specs/api/api.js` --- src/package.json | 1 + src/tests/backend/specs/api/api.js | 31 +++++++++++++++--------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/package.json b/src/package.json index fca557978..215d7e809 100644 --- a/src/package.json +++ b/src/package.json @@ -88,6 +88,7 @@ "etherpad-cli-client": "0.0.9", "mocha": "7.1.2", "mocha-froth": "^0.2.10", + "openapi-schema-validation": "^0.4.2", "set-cookie-parser": "^2.4.6", "sinon": "^9.2.0", "superagent": "^3.8.3", diff --git a/src/tests/backend/specs/api/api.js b/src/tests/backend/specs/api/api.js index 359bb3f6a..54ba61cd2 100644 --- a/src/tests/backend/specs/api/api.js +++ b/src/tests/backend/specs/api/api.js @@ -1,3 +1,5 @@ +'use strict'; + /** * API specs * @@ -15,8 +17,20 @@ const api = supertest(`http://${settings.ip}:${settings.port}`); const apiKey = common.apiKey; let apiVersion = 1; +const makeid = () => { + let text = ''; + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + + for (let i = 0; i < 5; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +}; + const testPadId = makeid(); +const endPoint = (point) => `/api/${apiVersion}/${point}?apikey=${apiKey}`; + describe(__filename, function () { describe('API Versioning', function () { it('errors if can not connect', function (done) { @@ -39,7 +53,8 @@ describe(__filename, function () { const {valid, errors} = validateOpenAPI(res.body, 3); if (!valid) { const prettyErrors = JSON.stringify(errors, null, 2); - throw new Error(`Document is not valid OpenAPI. ${errors.length} validation errors:\n${prettyErrors}`); + throw new Error(`Document is not valid OpenAPI. ${errors.length} ` + + `validation errors:\n${prettyErrors}`); } return; }) @@ -59,17 +74,3 @@ describe(__filename, function () { }); }); }); - -var endPoint = function (point) { - return `/api/${apiVersion}/${point}?apikey=${apiKey}`; -}; - -function makeid() { - let text = ''; - const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - - for (let i = 0; i < 5; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; -} From 01c83917d1b8d0f508520986b281595697b09418 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 13 Feb 2021 01:15:14 -0500 Subject: [PATCH 313/357] socket.io: Manually track client connections/disconnections This change is required for socket.io 3.x because in 3.x `io.sockets.clients()` no longer returns all client Socket objects. --- src/node/hooks/express/socketio.js | 55 ++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/src/node/hooks/express/socketio.js b/src/node/hooks/express/socketio.js index 178cf9155..c6ea079b4 100644 --- a/src/node/hooks/express/socketio.js +++ b/src/node/hooks/express/socketio.js @@ -1,31 +1,49 @@ 'use strict'; +const events = require('events'); const express = require('../express'); +const log4js = require('log4js'); const proxyaddr = require('proxy-addr'); const settings = require('../../utils/Settings'); const socketio = require('socket.io'); const socketIORouter = require('../../handler/SocketIORouter'); const hooks = require('../../../static/js/pluginfw/hooks'); const padMessageHandler = require('../../handler/PadMessageHandler'); -const util = require('util'); let io; +const logger = log4js.getLogger('socket.io'); +const sockets = new Set(); +const socketsEvents = new events.EventEmitter(); exports.expressCloseServer = async () => { - // According to the socket.io documentation every client is always in the default namespace (and - // may also be in other namespaces). - const ns = io.sockets; // The Namespace object for the default namespace. - // Disconnect all socket.io clients. This probably isn't necessary; closing the socket.io Engine - // (see below) probably gracefully disconnects all clients. But that is not documented, and this - // doesn't seem to hurt, so hedge against surprising and undocumented socket.io behavior. - for (const id of await util.promisify(ns.clients.bind(ns))()) { - ns.connected[id].disconnect(true); - } - // Don't call io.close() because that closes the underlying HTTP server, which is already done - // elsewhere. (Closing an HTTP server twice throws an exception.) The `engine` property of - // socket.io Server objects is undocumented, but I don't see any other way to shut down socket.io - // without also closing the HTTP server. + if (io == null) return; + logger.info('Closing socket.io engine...'); + // Close the socket.io engine to disconnect existing clients and reject new clients. Don't call + // io.close() because that closes the underlying HTTP server, which is already done elsewhere. + // (Closing an HTTP server twice throws an exception.) The `engine` property of socket.io Server + // objects is undocumented, but I don't see any other way to shut down socket.io without also + // closing the HTTP server. io.engine.close(); + // Closing the socket.io engine should disconnect all clients but it is not documented. Wait for + // all of the connections to close to make sure, and log the progress so that we can troubleshoot + // if socket.io's behavior ever changes. + // + // Note: `io.sockets.clients()` should not be used here to track the remaining clients. + // `io.sockets.clients()` works with socket.io 2.x, but not with 3.x: With socket.io 2.x all + // clients are always added to the default namespace (`io.sockets`) even if they specified a + // different namespace upon connection, but with socket.io 3.x clients are NOT added to the + // default namespace if they have specified a different namespace. With socket.io 3.x there does + // not appear to be a way to get all clients across all namespaces without tracking them + // ourselves, so that is what we do. + let lastLogged = 0; + while (sockets.size > 0) { + if (Date.now() - lastLogged > 1000) { // Rate limit to avoid filling logs. + logger.info(`Waiting for ${sockets.size} socket.io clients to disconnect...`); + lastLogged = Date.now(); + } + await events.once(socketsEvents, 'updated'); + } + logger.info('All socket.io clients have disconnected'); }; exports.expressCreateServer = (hookName, args, cb) => { @@ -59,6 +77,15 @@ exports.expressCreateServer = (hookName, args, cb) => { maxHttpBufferSize: 1 << 20, // 1MiB }); + io.on('connect', (socket) => { + sockets.add(socket); + socketsEvents.emit('updated'); + socket.on('disconnect', () => { + sockets.delete(socket); + socketsEvents.emit('updated'); + }); + }); + io.use((socket, next) => { const req = socket.request; // Express sets req.ip but socket.io does not. Replicate Express's behavior here. From 4c4c7b526d68bdcff891d7bfb6494a4ab1ba1383 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 13 Feb 2021 07:35:25 +0000 Subject: [PATCH 314/357] performance: i18n maxage (#4759) --- src/node/hooks/i18n.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/node/hooks/i18n.js b/src/node/hooks/i18n.js index 5b4b0ec12..31848b484 100644 --- a/src/node/hooks/i18n.js +++ b/src/node/hooks/i18n.js @@ -109,6 +109,7 @@ exports.expressCreateServer = (n, args, cb) => { // works with /locale/en and /locale/en.json requests const locale = req.params.locale.split('.')[0]; if (exports.availableLangs.hasOwnProperty(locale)) { + res.setHeader('Cache-Control', `public, max-age=${settings.maxAge}`); res.setHeader('Content-Type', 'application/json; charset=utf-8'); res.send(`{"${locale}":${JSON.stringify(locales[locale])}}`); } else { @@ -117,6 +118,7 @@ exports.expressCreateServer = (n, args, cb) => { }); args.app.get('/locales.json', (req, res) => { + res.setHeader('Cache-Control', `public, max-age=${settings.maxAge}`); res.setHeader('Content-Type', 'application/json; charset=utf-8'); res.send(localeIndex); }); From d56a02c85ad1ba79a4e801c2799556c93312ea76 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 13 Feb 2021 01:10:56 -0500 Subject: [PATCH 315/357] express: Forcibly terminate HTTP connections when restarting This should make restarts via `/admin` actions (e.g., plugin installation) more reliable. --- src/node/hooks/express.js | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/node/hooks/express.js b/src/node/hooks/express.js index 9b70c6c55..86055bf25 100644 --- a/src/node/hooks/express.js +++ b/src/node/hooks/express.js @@ -2,6 +2,7 @@ const _ = require('underscore'); const cookieParser = require('cookie-parser'); +const events = require('events'); const express = require('express'); const expressSession = require('express-session'); const fs = require('fs'); @@ -14,16 +15,35 @@ const util = require('util'); const logger = log4js.getLogger('http'); let serverName; +const sockets = new Set(); +const socketsEvents = new events.EventEmitter(); exports.server = null; const closeServer = async () => { if (exports.server == null) return; logger.info('Closing HTTP server...'); - await Promise.all([ - util.promisify(exports.server.close.bind(exports.server))(), - hooks.aCallAll('expressCloseServer'), - ]); + // Call exports.server.close() to reject new connections but don't await just yet because the + // Promise won't resolve until all preexisting connections are closed. + const p = util.promisify(exports.server.close.bind(exports.server))(); + await hooks.aCallAll('expressCloseServer'); + // Give existing connections some time to close on their own before forcibly terminating. The time + // should be long enough to avoid interrupting most preexisting transmissions but short enough to + // avoid a noticeable outage. + const timeout = setTimeout(async () => { + logger.info(`Forcibly terminating remaining ${sockets.size} HTTP connections...`); + for (const socket of sockets) socket.destroy(new Error('HTTP server is closing')); + }, 5000); + let lastLogged = 0; + while (sockets.size > 0) { + if (Date.now() - lastLogged > 1000) { // Rate limit to avoid filling logs. + logger.info(`Waiting for ${sockets.size} HTTP clients to disconnect...`); + lastLogged = Date.now(); + } + await events.once(socketsEvents, 'updated'); + } + await p; + clearTimeout(timeout); exports.server = null; logger.info('HTTP server closed'); }; @@ -186,6 +206,14 @@ exports.restartServer = async () => { hooks.aCallAll('expressConfigure', {app}), hooks.aCallAll('expressCreateServer', {app, server: exports.server}), ]); + exports.server.on('connection', (socket) => { + sockets.add(socket); + socketsEvents.emit('updated'); + socket.on('close', () => { + sockets.delete(socket); + socketsEvents.emit('updated'); + }); + }); await util.promisify(exports.server.listen).bind(exports.server)(settings.port, settings.ip); }; From 1c8a9134118f8032721264dab9d989bba4934939 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 13 Feb 2021 01:05:50 -0500 Subject: [PATCH 316/357] lint: Delete unnecessary `eslint-disable-line` comment --- src/tests/frontend/helper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/frontend/helper.js b/src/tests/frontend/helper.js index 707a8b72c..8fbc28d0f 100644 --- a/src/tests/frontend/helper.js +++ b/src/tests/frontend/helper.js @@ -1,6 +1,6 @@ 'use strict'; -const helper = {}; // eslint-disable-line no-redeclare +const helper = {}; (() => { let $iframe; From 6953e40c7551169c2a768a736e6b14fa494f2cab Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 12 Feb 2021 19:41:55 -0500 Subject: [PATCH 317/357] tests: Wait for `common.init()` to complete before returning --- src/tests/backend/common.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tests/backend/common.js b/src/tests/backend/common.js index 3fe80de3b..93536bc6d 100644 --- a/src/tests/backend/common.js +++ b/src/tests/backend/common.js @@ -9,7 +9,7 @@ const supertest = require('supertest'); const webaccess = require('../../node/hooks/express/webaccess'); const backups = {}; -let inited = false; +let agentPromise = null; exports.apiKey = apiHandler.exportedForTestingOnly.apiKey; exports.agent = null; @@ -24,8 +24,9 @@ const logLevel = exports.logger.level; process.on('unhandledRejection', (reason, promise) => { throw reason; }); exports.init = async function () { - if (inited) return exports.agent; - inited = true; + if (agentPromise != null) return await agentPromise; + let agentResolve; + agentPromise = new Promise((resolve) => { agentResolve = resolve; }); if (!logLevel.isLessThanOrEqualTo(log4js.levels.DEBUG)) { exports.logger.warn('Disabling non-test logging for the duration of the test. ' + @@ -56,5 +57,6 @@ exports.init = async function () { await server.exit(); }); + agentResolve(exports.agent); return exports.agent; }; From fc9b22475afda5b2ac4c4fa9ce8418cfb13608b0 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 12 Feb 2021 19:39:31 -0500 Subject: [PATCH 318/357] tests: Always call backend `common.init()` at startup This provides a place to set the timeout for `common.init()` so that individual tests don't have to. --- src/tests/backend/common.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tests/backend/common.js b/src/tests/backend/common.js index 93536bc6d..917d0c21a 100644 --- a/src/tests/backend/common.js +++ b/src/tests/backend/common.js @@ -23,6 +23,11 @@ const logLevel = exports.logger.level; // https://github.com/mochajs/mocha/issues/2640 process.on('unhandledRejection', (reason, promise) => { throw reason; }); +before(async function () { + this.timeout(60000); + await exports.init(); +}); + exports.init = async function () { if (agentPromise != null) return await agentPromise; let agentResolve; From 08124ba7331e2fd7d699def4e8f6d9157b5792ce Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 12 Feb 2021 18:51:42 -0500 Subject: [PATCH 319/357] tests: Delete unnecessary `describe()` calls in `api.js` --- src/tests/backend/specs/api/api.js | 66 ++++++++++++++---------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/src/tests/backend/specs/api/api.js b/src/tests/backend/specs/api/api.js index 54ba61cd2..665dd04e7 100644 --- a/src/tests/backend/specs/api/api.js +++ b/src/tests/backend/specs/api/api.js @@ -32,45 +32,39 @@ const testPadId = makeid(); const endPoint = (point) => `/api/${apiVersion}/${point}?apikey=${apiKey}`; describe(__filename, function () { - describe('API Versioning', function () { - it('errors if can not connect', function (done) { - api - .get('/api/') - .expect((res) => { - apiVersion = res.body.currentVersion; - if (!res.body.currentVersion) throw new Error('No version set in API'); - return; - }) - .expect(200, done); - }); + it('can obtain API version', function (done) { + api + .get('/api/') + .expect((res) => { + apiVersion = res.body.currentVersion; + if (!res.body.currentVersion) throw new Error('No version set in API'); + return; + }) + .expect(200, done); }); - describe('OpenAPI definition', function () { - it('generates valid openapi definition document', function (done) { - api - .get('/api/openapi.json') - .expect((res) => { - const {valid, errors} = validateOpenAPI(res.body, 3); - if (!valid) { - const prettyErrors = JSON.stringify(errors, null, 2); - throw new Error(`Document is not valid OpenAPI. ${errors.length} ` + - `validation errors:\n${prettyErrors}`); - } - return; - }) - .expect(200, done); - }); + it('can obtain valid openapi definition document', function (done) { + api + .get('/api/openapi.json') + .expect((res) => { + const {valid, errors} = validateOpenAPI(res.body, 3); + if (!valid) { + const prettyErrors = JSON.stringify(errors, null, 2); + throw new Error(`Document is not valid OpenAPI. ${errors.length} ` + + `validation errors:\n${prettyErrors}`); + } + return; + }) + .expect(200, done); }); - describe('jsonp support', function () { - it('supports jsonp calls', function (done) { - api - .get(`${endPoint('createPad')}&jsonp=jsonp_1&padID=${testPadId}`) - .expect((res) => { - if (!res.text.match('jsonp_1')) throw new Error('no jsonp call seen'); - }) - .expect('Content-Type', /javascript/) - .expect(200, done); - }); + it('supports jsonp calls', function (done) { + api + .get(`${endPoint('createPad')}&jsonp=jsonp_1&padID=${testPadId}`) + .expect((res) => { + if (!res.text.match('jsonp_1')) throw new Error('no jsonp call seen'); + }) + .expect('Content-Type', /javascript/) + .expect(200, done); }); }); From e0f499cf5a3ba29d9f02d3a87689665284e9539a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 12 Feb 2021 18:57:07 -0500 Subject: [PATCH 320/357] tests: Use the supertest agent from `common.js` for `api.js` --- src/tests/backend/specs/api/api.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tests/backend/specs/api/api.js b/src/tests/backend/specs/api/api.js index 665dd04e7..b70d63fee 100644 --- a/src/tests/backend/specs/api/api.js +++ b/src/tests/backend/specs/api/api.js @@ -9,11 +9,9 @@ */ const common = require('../../common'); -const settings = require('../../../../node/utils/Settings'); -const supertest = require('supertest'); const validateOpenAPI = require('openapi-schema-validation').validate; -const api = supertest(`http://${settings.ip}:${settings.port}`); +let agent; const apiKey = common.apiKey; let apiVersion = 1; @@ -32,8 +30,10 @@ const testPadId = makeid(); const endPoint = (point) => `/api/${apiVersion}/${point}?apikey=${apiKey}`; describe(__filename, function () { + before(async function () { agent = await common.init(); }); + it('can obtain API version', function (done) { - api + agent .get('/api/') .expect((res) => { apiVersion = res.body.currentVersion; @@ -44,7 +44,7 @@ describe(__filename, function () { }); it('can obtain valid openapi definition document', function (done) { - api + agent .get('/api/openapi.json') .expect((res) => { const {valid, errors} = validateOpenAPI(res.body, 3); @@ -59,7 +59,7 @@ describe(__filename, function () { }); it('supports jsonp calls', function (done) { - api + agent .get(`${endPoint('createPad')}&jsonp=jsonp_1&padID=${testPadId}`) .expect((res) => { if (!res.text.match('jsonp_1')) throw new Error('no jsonp call seen'); From 71c18991646fa4224b8231e5782341f9b66386cf Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Fri, 12 Feb 2021 19:01:46 -0500 Subject: [PATCH 321/357] tests: Asyncify tests in `api.js` --- src/tests/backend/specs/api/api.js | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/tests/backend/specs/api/api.js b/src/tests/backend/specs/api/api.js index b70d63fee..070632b0b 100644 --- a/src/tests/backend/specs/api/api.js +++ b/src/tests/backend/specs/api/api.js @@ -32,20 +32,19 @@ const endPoint = (point) => `/api/${apiVersion}/${point}?apikey=${apiKey}`; describe(__filename, function () { before(async function () { agent = await common.init(); }); - it('can obtain API version', function (done) { - agent - .get('/api/') + it('can obtain API version', async function () { + await agent.get('/api/') + .expect(200) .expect((res) => { apiVersion = res.body.currentVersion; if (!res.body.currentVersion) throw new Error('No version set in API'); return; - }) - .expect(200, done); + }); }); - it('can obtain valid openapi definition document', function (done) { - agent - .get('/api/openapi.json') + it('can obtain valid openapi definition document', async function () { + await agent.get('/api/openapi.json') + .expect(200) .expect((res) => { const {valid, errors} = validateOpenAPI(res.body, 3); if (!valid) { @@ -53,18 +52,15 @@ describe(__filename, function () { throw new Error(`Document is not valid OpenAPI. ${errors.length} ` + `validation errors:\n${prettyErrors}`); } - return; - }) - .expect(200, done); + }); }); - it('supports jsonp calls', function (done) { - agent - .get(`${endPoint('createPad')}&jsonp=jsonp_1&padID=${testPadId}`) + it('supports jsonp calls', async function () { + await agent.get(`${endPoint('createPad')}&jsonp=jsonp_1&padID=${testPadId}`) + .expect(200) + .expect('Content-Type', /javascript/) .expect((res) => { if (!res.text.match('jsonp_1')) throw new Error('no jsonp call seen'); - }) - .expect('Content-Type', /javascript/) - .expect(200, done); + }); }); }); From 8dca4cb16fb89051e8f6b216350af98a4b2e79af Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 13 Feb 2021 00:00:28 -0500 Subject: [PATCH 322/357] tests: Give `helper.waitFor()` timeout errors a useful stack trace --- src/tests/frontend/helper.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/tests/frontend/helper.js b/src/tests/frontend/helper.js index 8fbc28d0f..eaa57fb89 100644 --- a/src/tests/frontend/helper.js +++ b/src/tests/frontend/helper.js @@ -198,6 +198,10 @@ const helper = {}; }; helper.waitFor = (conditionFunc, timeoutTime = 1900, intervalTime = 10) => { + // Create an Error object to use if the condition is never satisfied. This is created here so + // that the Error has a useful stack trace associated with it. + const timeoutError = + new Error(`waitFor condition never became true ${conditionFunc.toString()}`); const deferred = new $.Deferred(); const _fail = deferred.fail.bind(deferred); @@ -222,11 +226,10 @@ const helper = {}; const timeout = setTimeout(() => { clearInterval(intervalCheck); - const error = new Error(`wait for condition never became true ${conditionFunc.toString()}`); - deferred.reject(error); + deferred.reject(timeoutError); if (!listenForFail) { - throw error; + throw timeoutError; } }, timeoutTime); From 09e9c36098fc240cba91050c49a6696a6ced027a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 13 Feb 2021 00:02:25 -0500 Subject: [PATCH 323/357] tests: Accept async condition functions for `helper.waitFor()` --- src/tests/frontend/helper.js | 4 ++-- src/tests/frontend/specs/helper.js | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/tests/frontend/helper.js b/src/tests/frontend/helper.js index eaa57fb89..4dd7f22ac 100644 --- a/src/tests/frontend/helper.js +++ b/src/tests/frontend/helper.js @@ -211,9 +211,9 @@ const helper = {}; return _fail(...args); }; - const check = () => { + const check = async () => { try { - if (!conditionFunc()) return; + if (!await conditionFunc()) return; deferred.resolve(); } catch (err) { deferred.reject(err); diff --git a/src/tests/frontend/specs/helper.js b/src/tests/frontend/specs/helper.js index cb72fcf7a..fdd896ea9 100644 --- a/src/tests/frontend/specs/helper.js +++ b/src/tests/frontend/specs/helper.js @@ -210,6 +210,19 @@ describe('the test helper', function () { await helper.waitFor(() => true, 0); }); }); + + it('accepts async functions', async function () { + await helper.waitFor(async () => true).fail(() => {}); + // Make sure it checks the truthiness of the Promise's resolved value, not the truthiness of + // the Promise itself (a Promise is always truthy). + let ok = false; + try { + await helper.waitFor(async () => false, 0).fail(() => {}); + } catch (err) { + ok = true; + } + expect(ok).to.be(true); + }); }); describe('the waitForPromise method', function () { From 483f4344c23c148d5ddcf13d39931b0b1ef9ed82 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 13 Feb 2021 08:13:48 +0000 Subject: [PATCH 324/357] performance: maxAge for favicon and plugin definitions (#4761) --- src/node/hooks/express/specialpages.js | 2 +- src/node/hooks/express/static.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/node/hooks/express/specialpages.js b/src/node/hooks/express/specialpages.js index c11e374e9..61471348d 100644 --- a/src/node/hooks/express/specialpages.js +++ b/src/node/hooks/express/specialpages.js @@ -83,7 +83,7 @@ exports.expressCreateServer = (hookName, args, cb) => { settings.skinName, 'favicon.ico' ); - + res.setHeader('Cache-Control', `public, max-age=${settings.maxAge}`); res.sendFile(filePath, (err) => { // there is no custom favicon, send the default favicon if (err) { diff --git a/src/node/hooks/express/static.js b/src/node/hooks/express/static.js index 0b73072e5..d1dec8714 100644 --- a/src/node/hooks/express/static.js +++ b/src/node/hooks/express/static.js @@ -65,7 +65,8 @@ exports.expressCreateServer = async (hookName, args) => { clientPlugins[name] = {...plugins.plugins[name]}; delete clientPlugins[name].package; } - res.header('Content-Type', 'application/json; charset=utf-8'); + res.setHeader('Content-Type', 'application/json; charset=utf-8'); + res.setHeader('Cache-Control', `public, max-age=${settings.maxAge}`); res.write(JSON.stringify({plugins: clientPlugins, parts: clientParts})); res.end(); }); From e22d8dffc0fc5e6df1745215e6b82b071b65c1da Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 13 Feb 2021 07:42:28 +0000 Subject: [PATCH 325/357] deps: use ci --no-optional flags, this might break some things as it requires npm 6.31.4 --- src/bin/installDeps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/installDeps.sh b/src/bin/installDeps.sh index 6ea555dbf..94d8630cc 100755 --- a/src/bin/installDeps.sh +++ b/src/bin/installDeps.sh @@ -40,7 +40,7 @@ log "Ensure that all dependencies are up to date... If this is the first time y cd node_modules [ -e ep_etherpad-lite ] || ln -s ../src ep_etherpad-lite cd ep_etherpad-lite - npm ci + npm ci --no-optional ) || { rm -rf src/node_modules exit 1 From ac52fb8a9d85592f0a9dd6c35186151c8152abdf Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 13 Feb 2021 01:40:54 -0500 Subject: [PATCH 326/357] express: New `httpUptime` metric --- CHANGELOG.md | 3 ++- src/node/hooks/express.js | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cac510eeb..d29419353 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,8 @@ * Database performance is significantly improved. * Admin UI now has test coverage in CI. (The tests are not enabled by default; see `settings.json`.) -* New stats: `activePads`, `lastDisconnected`, `memoryUsageHeap`. +* New stats/metrics: `activePads`, `httpUptime`, `lastDisconnected`, + `memoryUsageHeap`. * For plugin authors: * New `callAllSerial()` function that invokes hook functions like `callAll()` except it supports asynchronous hook functions. diff --git a/src/node/hooks/express.js b/src/node/hooks/express.js index 86055bf25..36bf2e695 100644 --- a/src/node/hooks/express.js +++ b/src/node/hooks/express.js @@ -17,6 +17,7 @@ const logger = log4js.getLogger('http'); let serverName; const sockets = new Set(); const socketsEvents = new events.EventEmitter(); +let startTime = null; exports.server = null; @@ -49,6 +50,7 @@ const closeServer = async () => { }; exports.createServer = async () => { + stats.gauge('httpUptime', () => startTime == null ? 0 : new Date() - startTime); console.log('Report bugs at https://github.com/ether/etherpad-lite/issues'); serverName = `Etherpad ${settings.getGitCommit()} (https://etherpad.org)`; @@ -215,6 +217,8 @@ exports.restartServer = async () => { }); }); await util.promisify(exports.server.listen).bind(exports.server)(settings.port, settings.ip); + startTime = new Date(); + logger.info('HTTP server listening for connections'); }; exports.shutdown = async (hookName, context) => { From f9ec49d7ac06d8896b12d6cf5b3b0b0594b9f225 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sat, 13 Feb 2021 01:43:08 -0500 Subject: [PATCH 327/357] tests: Improve `/admin/settings` restart test --- src/tests/frontend/specs/adminsettings.js | 38 ++++++++++++++--------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/tests/frontend/specs/adminsettings.js b/src/tests/frontend/specs/adminsettings.js index d139d6056..71535c68f 100644 --- a/src/tests/frontend/specs/adminsettings.js +++ b/src/tests/frontend/specs/adminsettings.js @@ -54,22 +54,30 @@ describe('Admin > Settings', function () { expect(settings).to.be(helper.admin$('.settings').val()); }); - function timeout(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); - } - it('restart works', async function () { - // restarts + this.timeout(60000); + const getUptime = async () => { + try { + const {httpUptime} = await $.ajax({ + url: new URL('/stats', window.location.href), + method: 'GET', + dataType: 'json', + timeout: 450, // Slightly less than the waitForPromise() interval. + }); + return httpUptime; + } catch (err) { + return null; + } + }; + await helper.waitForPromise(async () => { + const uptime = await getUptime(); + return uptime != null && uptime > 0; + }, 1000, 500); + const clickTime = Date.now(); helper.admin$('#restartEtherpad').click(); - - // Hacky... Other suggestions welcome.. - await timeout(200000); - let success = false; - $.ajax({ - url: `${location.protocol}//admin:changeme@${location.hostname}:${location.port}/admin`, - type: 'GET', - success: () => success = true, - }); - await helper.waitForPromise(() => success === true); + await helper.waitForPromise(async () => { + const uptime = await getUptime(); + return uptime != null && Date.now() - uptime >= clickTime; + }, 60000, 500); }); }); From e585d321f9357dacf4a3a0d1e079e44e3bf6d798 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 13 Feb 2021 19:00:06 +0000 Subject: [PATCH 328/357] tests: timeouts for tests (#4773) --- src/tests/backend/specs/api/api.js | 2 + .../backend/specs/api/characterEncoding.js | 6 ++ src/tests/backend/specs/api/chat.js | 5 + src/tests/backend/specs/api/importexport.js | 4 + .../backend/specs/api/importexportGetPost.js | 27 ++++++ src/tests/backend/specs/api/instance.js | 2 + src/tests/backend/specs/api/pad.js | 54 +++++++++++ .../backend/specs/api/sessionsAndGroups.js | 29 ++++++ src/tests/backend/specs/contentcollector.js | 1 + src/tests/backend/specs/hooks.js | 94 +++++++++++++++++++ src/tests/backend/specs/socketio.js | 25 +++++ src/tests/backend/specs/specialpages.js | 2 + src/tests/backend/specs/webaccess.js | 47 ++++++++++ src/tests/frontend/specs/alphabet.js | 1 + src/tests/frontend/specs/bold.js | 2 + src/tests/frontend/specs/change_user_color.js | 3 +- src/tests/frontend/specs/change_user_name.js | 2 + src/tests/frontend/specs/chat.js | 5 +- .../frontend/specs/chat_load_messages.js | 3 + .../frontend/specs/clear_authorship_colors.js | 2 + src/tests/frontend/specs/delete.js | 1 + src/tests/frontend/specs/drag_and_drop.js | 2 + src/tests/frontend/specs/embed_value.js | 4 + src/tests/frontend/specs/enter.js | 1 + src/tests/frontend/specs/font_type.js | 1 + src/tests/frontend/specs/indentation.js | 6 ++ src/tests/frontend/specs/italic.js | 2 + src/tests/frontend/specs/language.js | 3 + ...ultiple_authors_clear_authorship_colors.js | 1 + src/tests/frontend/specs/ordered_list.js | 7 ++ src/tests/frontend/specs/pad_modal.js | 5 + src/tests/frontend/specs/redo.js | 2 + src/tests/frontend/specs/scrollTo.js | 4 +- .../specs/select_formatting_buttons.js | 3 + src/tests/frontend/specs/strikethrough.js | 1 + src/tests/frontend/specs/timeslider_follow.js | 1 + src/tests/frontend/specs/timeslider_labels.js | 1 + .../specs/timeslider_numeric_padID.js | 1 + .../frontend/specs/timeslider_revisions.js | 1 + src/tests/frontend/specs/undo.js | 3 + src/tests/frontend/specs/unordered_list.js | 5 + .../frontend/specs/urls_become_clickable.js | 1 + 42 files changed, 368 insertions(+), 4 deletions(-) diff --git a/src/tests/backend/specs/api/api.js b/src/tests/backend/specs/api/api.js index 070632b0b..cd8506519 100644 --- a/src/tests/backend/specs/api/api.js +++ b/src/tests/backend/specs/api/api.js @@ -43,6 +43,7 @@ describe(__filename, function () { }); it('can obtain valid openapi definition document', async function () { + this.timeout(15000); await agent.get('/api/openapi.json') .expect(200) .expect((res) => { @@ -56,6 +57,7 @@ describe(__filename, function () { }); it('supports jsonp calls', async function () { + this.timeout(150); await agent.get(`${endPoint('createPad')}&jsonp=jsonp_1&padID=${testPadId}`) .expect(200) .expect('Content-Type', /javascript/) diff --git a/src/tests/backend/specs/api/characterEncoding.js b/src/tests/backend/specs/api/characterEncoding.js index 211885a28..00183e12a 100644 --- a/src/tests/backend/specs/api/characterEncoding.js +++ b/src/tests/backend/specs/api/characterEncoding.js @@ -19,6 +19,7 @@ const testPadId = makeid(); describe(__filename, function () { describe('Connectivity For Character Encoding', function () { it('can connect', function (done) { + this.timeout(250); api.get('/api/') .expect('Content-Type', /json/) .expect(200, done); @@ -26,6 +27,7 @@ describe(__filename, function () { }); describe('API Versioning', function () { + this.timeout(150); it('finds the version tag', function (done) { api.get('/api/') .expect((res) => { @@ -39,6 +41,7 @@ describe(__filename, function () { describe('Permission', function () { it('errors with invalid APIKey', function (done) { + this.timeout(150); // This is broken because Etherpad doesn't handle HTTP codes properly see #2343 // If your APIKey is password you deserve to fail all tests anyway const permErrorURL = `/api/${apiVersion}/createPad?apikey=password&padID=test`; @@ -49,6 +52,7 @@ describe(__filename, function () { describe('createPad', function () { it('creates a new Pad', function (done) { + this.timeout(150); api.get(`${endPoint('createPad')}&padID=${testPadId}`) .expect((res) => { if (res.body.code !== 0) throw new Error('Unable to create new Pad'); @@ -60,6 +64,7 @@ describe(__filename, function () { describe('setHTML', function () { it('Sets the HTML of a Pad attempting to weird utf8 encoded content', function (done) { + this.timeout(1000); fs.readFile('tests/backend/specs/api/emojis.html', 'utf8', (err, html) => { api.post(endPoint('setHTML')) .send({ @@ -77,6 +82,7 @@ describe(__filename, function () { describe('getHTML', function () { it('get the HTML of Pad with emojis', function (done) { + this.timeout(400); api.get(`${endPoint('getHTML')}&padID=${testPadId}`) .expect((res) => { if (res.body.data.html.indexOf('🇼') === -1) { diff --git a/src/tests/backend/specs/api/chat.js b/src/tests/backend/specs/api/chat.js index e2ddf0540..570a8aaaa 100644 --- a/src/tests/backend/specs/api/chat.js +++ b/src/tests/backend/specs/api/chat.js @@ -35,6 +35,7 @@ describe(__filename, function () { */ describe('createPad', function () { + this.timeout(400); it('creates a new Pad', function (done) { api.get(`${endPoint('createPad')}&padID=${padID}`) .expect((res) => { @@ -46,6 +47,7 @@ describe(__filename, function () { }); describe('createAuthor', function () { + this.timeout(100); it('Creates an author with a name set', function (done) { api.get(endPoint('createAuthor')) .expect((res) => { @@ -58,6 +60,7 @@ describe(__filename, function () { }); describe('appendChatMessage', function () { + this.timeout(100); it('Adds a chat message to the pad', function (done) { api.get(`${endPoint('appendChatMessage')}&padID=${padID}&text=blalblalbha&authorID=${authorID}&time=${timestamp}`) .expect((res) => { @@ -70,6 +73,7 @@ describe(__filename, function () { describe('getChatHead', function () { + this.timeout(100); it('Gets the head of chat', function (done) { api.get(`${endPoint('getChatHead')}&padID=${padID}`) .expect((res) => { @@ -83,6 +87,7 @@ describe(__filename, function () { }); describe('getChatHistory', function () { + this.timeout(40); it('Gets Chat History of a Pad', function (done) { api.get(`${endPoint('getChatHistory')}&padID=${padID}`) .expect((res) => { diff --git a/src/tests/backend/specs/api/importexport.js b/src/tests/backend/specs/api/importexport.js index 1c93c11c4..960f019bd 100644 --- a/src/tests/backend/specs/api/importexport.js +++ b/src/tests/backend/specs/api/importexport.js @@ -236,6 +236,7 @@ describe(__filename, function () { }); } it('createPad', function (done) { + this.timeout(200); api.get(`${endPoint('createPad')}&padID=${testPadId}`) .expect((res) => { if (res.body.code !== 0) throw new Error('Unable to create new Pad'); @@ -245,6 +246,7 @@ describe(__filename, function () { }); it('setHTML', function (done) { + this.timeout(150); api.get(`${endPoint('setHTML')}&padID=${testPadId}&html=${encodeURIComponent(test.input)}`) .expect((res) => { if (res.body.code !== 0) throw new Error(`Error:${testName}`); @@ -254,6 +256,7 @@ describe(__filename, function () { }); it('getHTML', function (done) { + this.timeout(150); api.get(`${endPoint('getHTML')}&padID=${testPadId}`) .expect((res) => { const gotHtml = res.body.data.html; @@ -277,6 +280,7 @@ describe(__filename, function () { }); it('getText', function (done) { + this.timeout(100); api.get(`${endPoint('getText')}&padID=${testPadId}`) .expect((res) => { const gotText = res.body.data.text; diff --git a/src/tests/backend/specs/api/importexportGetPost.js b/src/tests/backend/specs/api/importexportGetPost.js index ce03c5768..73b58a2f3 100644 --- a/src/tests/backend/specs/api/importexportGetPost.js +++ b/src/tests/backend/specs/api/importexportGetPost.js @@ -26,10 +26,12 @@ const testPadId = makeid(); const testPadIdEnc = encodeURIComponent(testPadId); describe(__filename, function () { + this.timeout(45000); before(async function () { agent = await common.init(); }); describe('Connectivity', function () { it('can connect', async function () { + this.timeout(250); await agent.get('/api/') .expect(200) .expect('Content-Type', /json/); @@ -38,6 +40,7 @@ describe(__filename, function () { describe('API Versioning', function () { it('finds the version tag', async function () { + this.timeout(250); await agent.get('/api/') .expect(200) .expect((res) => assert(res.body.currentVersion)); @@ -93,6 +96,7 @@ describe(__filename, function () { }); it('creates a new Pad, imports content to it, checks that content', async function () { + this.timeout(500); await agent.get(`${endPoint('createPad')}&padID=${testPadId}`) .expect(200) .expect('Content-Type', /json/) @@ -106,6 +110,7 @@ describe(__filename, function () { }); it('gets read only pad Id and exports the html and text for this pad', async function () { + this.timeout(250); const ro = await agent.get(`${endPoint('getReadOnlyID')}&padID=${testPadId}`) .expect(200) .expect((res) => assert.ok(JSON.parse(res.text).data.readOnlyID)); @@ -132,6 +137,7 @@ describe(__filename, function () { // For some reason word import does not work in testing.. // TODO: fix support for .doc files.. it('Tries to import .doc that uses soffice or abiword', async function () { + this.timeout(10000); await agent.post(`/p/${testPadId}/import`) .attach('file', wordDoc, {filename: '/test.doc', contentType: 'application/msword'}) .expect(200) @@ -139,6 +145,7 @@ describe(__filename, function () { }); it('exports DOC', async function () { + this.timeout(3000); await agent.get(`/p/${testPadId}/export/doc`) .buffer(true).parse(superagent.parse['application/octet-stream']) .expect(200) @@ -146,6 +153,7 @@ describe(__filename, function () { }); it('Tries to import .docx that uses soffice or abiword', async function () { + this.timeout(3000); await agent.post(`/p/${testPadId}/import`) .attach('file', wordXDoc, { filename: '/test.docx', @@ -157,6 +165,7 @@ describe(__filename, function () { }); it('exports DOC from imported DOCX', async function () { + this.timeout(3000); await agent.get(`/p/${testPadId}/export/doc`) .buffer(true).parse(superagent.parse['application/octet-stream']) .expect(200) @@ -164,6 +173,7 @@ describe(__filename, function () { }); it('Tries to import .pdf that uses soffice or abiword', async function () { + this.timeout(3000); await agent.post(`/p/${testPadId}/import`) .attach('file', pdfDoc, {filename: '/test.pdf', contentType: 'application/pdf'}) .expect(200) @@ -171,6 +181,7 @@ describe(__filename, function () { }); it('exports PDF', async function () { + this.timeout(3000); await agent.get(`/p/${testPadId}/export/pdf`) .buffer(true).parse(superagent.parse['application/octet-stream']) .expect(200) @@ -178,6 +189,7 @@ describe(__filename, function () { }); it('Tries to import .odt that uses soffice or abiword', async function () { + this.timeout(3000); await agent.post(`/p/${testPadId}/import`) .attach('file', odtDoc, {filename: '/test.odt', contentType: 'application/odt'}) .expect(200) @@ -185,6 +197,7 @@ describe(__filename, function () { }); it('exports ODT', async function () { + this.timeout(3000); await agent.get(`/p/${testPadId}/export/odt`) .buffer(true).parse(superagent.parse['application/octet-stream']) .expect(200) @@ -193,6 +206,7 @@ describe(__filename, function () { }); // End of AbiWord/LibreOffice tests. it('Tries to import .etherpad', async function () { + this.timeout(3000); await agent.post(`/p/${testPadId}/import`) .attach('file', etherpadDoc, { filename: '/test.etherpad', @@ -203,6 +217,7 @@ describe(__filename, function () { }); it('exports Etherpad', async function () { + this.timeout(3000); await agent.get(`/p/${testPadId}/export/etherpad`) .buffer(true).parse(superagent.parse.text) .expect(200) @@ -210,6 +225,7 @@ describe(__filename, function () { }); it('exports HTML for this Etherpad file', async function () { + this.timeout(3000); await agent.get(`/p/${testPadId}/export/html`) .expect(200) .expect('content-type', 'text/html; charset=utf-8') @@ -217,6 +233,7 @@ describe(__filename, function () { }); it('Tries to import unsupported file type', async function () { + this.timeout(3000); settings.allowUnknownFileEnds = false; await agent.post(`/p/${testPadId}/import`) .attach('file', padText, {filename: '/test.xasdasdxx', contentType: 'weirdness/jobby'}) @@ -252,6 +269,7 @@ describe(__filename, function () { }); it('!authn !exist -> create', async function () { + this.timeout(100); await agent.post(`/p/${testPadIdEnc}/import`) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .expect(200); @@ -261,6 +279,7 @@ describe(__filename, function () { }); it('!authn exist -> replace', async function () { + this.timeout(100); const pad = await createTestPad('before import'); await agent.post(`/p/${testPadIdEnc}/import`) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) @@ -270,6 +289,7 @@ describe(__filename, function () { }); it('authn anonymous !exist -> fail', async function () { + this.timeout(100); settings.requireAuthentication = true; await agent.post(`/p/${testPadIdEnc}/import`) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) @@ -278,6 +298,7 @@ describe(__filename, function () { }); it('authn anonymous exist -> fail', async function () { + this.timeout(100); settings.requireAuthentication = true; const pad = await createTestPad('before import\n'); await agent.post(`/p/${testPadIdEnc}/import`) @@ -287,6 +308,7 @@ describe(__filename, function () { }); it('authn user create !exist -> create', async function () { + this.timeout(100); settings.requireAuthentication = true; await agent.post(`/p/${testPadIdEnc}/import`) .auth('user', 'user-password') @@ -298,6 +320,7 @@ describe(__filename, function () { }); it('authn user modify !exist -> fail', async function () { + this.timeout(100); settings.requireAuthentication = true; authorize = () => 'modify'; await agent.post(`/p/${testPadIdEnc}/import`) @@ -308,6 +331,7 @@ describe(__filename, function () { }); it('authn user readonly !exist -> fail', async function () { + this.timeout(100); settings.requireAuthentication = true; authorize = () => 'readOnly'; await agent.post(`/p/${testPadIdEnc}/import`) @@ -318,6 +342,7 @@ describe(__filename, function () { }); it('authn user create exist -> replace', async function () { + this.timeout(100); settings.requireAuthentication = true; const pad = await createTestPad('before import\n'); await agent.post(`/p/${testPadIdEnc}/import`) @@ -328,6 +353,7 @@ describe(__filename, function () { }); it('authn user modify exist -> replace', async function () { + this.timeout(100); settings.requireAuthentication = true; authorize = () => 'modify'; const pad = await createTestPad('before import\n'); @@ -339,6 +365,7 @@ describe(__filename, function () { }); it('authn user readonly exist -> fail', async function () { + this.timeout(100); const pad = await createTestPad('before import\n'); settings.requireAuthentication = true; authorize = () => 'readOnly'; diff --git a/src/tests/backend/specs/api/instance.js b/src/tests/backend/specs/api/instance.js index 833c75d5a..ab465033e 100644 --- a/src/tests/backend/specs/api/instance.js +++ b/src/tests/backend/specs/api/instance.js @@ -15,6 +15,7 @@ const apiVersion = '1.2.14'; describe(__filename, function () { describe('Connectivity for instance-level API tests', function () { it('can connect', function (done) { + this.timeout(150); api.get('/api/') .expect('Content-Type', /json/) .expect(200, done); @@ -23,6 +24,7 @@ describe(__filename, function () { describe('getStats', function () { it('Gets the stats of a running instance', function (done) { + this.timeout(100); api.get(endPoint('getStats')) .expect((res) => { if (res.body.code !== 0) throw new Error('getStats() failed'); diff --git a/src/tests/backend/specs/api/pad.js b/src/tests/backend/specs/api/pad.js index 92467cb1f..c94ad810d 100644 --- a/src/tests/backend/specs/api/pad.js +++ b/src/tests/backend/specs/api/pad.js @@ -47,6 +47,7 @@ const expectedSpaceHtml = '
                  • one describe(__filename, function () { describe('Connectivity', function () { it('can connect', function (done) { + this.timeout(200); api.get('/api/') .expect('Content-Type', /json/) .expect(200, done); @@ -55,6 +56,7 @@ describe(__filename, function () { describe('API Versioning', function () { it('finds the version tag', function (done) { + this.timeout(150); api.get('/api/') .expect((res) => { apiVersion = res.body.currentVersion; @@ -67,6 +69,7 @@ describe(__filename, function () { describe('Permission', function () { it('errors with invalid APIKey', function (done) { + this.timeout(150); // This is broken because Etherpad doesn't handle HTTP codes properly see #2343 // If your APIKey is password you deserve to fail all tests anyway const permErrorURL = `/api/${apiVersion}/createPad?apikey=password&padID=test`; @@ -119,6 +122,7 @@ describe(__filename, function () { describe('deletePad', function () { it('deletes a Pad', function (done) { + this.timeout(150); api.get(`${endPoint('deletePad')}&padID=${testPadId}`) .expect('Content-Type', /json/) .expect(200, done); // @TODO: we shouldn't expect 200 here since the pad may not exist @@ -127,6 +131,7 @@ describe(__filename, function () { describe('createPad', function () { it('creates a new Pad', function (done) { + this.timeout(150); api.get(`${endPoint('createPad')}&padID=${testPadId}`) .expect((res) => { if (res.body.code !== 0) throw new Error('Unable to create new Pad'); @@ -138,6 +143,7 @@ describe(__filename, function () { describe('getRevisionsCount', function () { it('gets revision count of Pad', function (done) { + this.timeout(150); api.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`) .expect((res) => { if (res.body.code !== 0) throw new Error('Unable to get Revision Count'); @@ -150,6 +156,7 @@ describe(__filename, function () { describe('getSavedRevisionsCount', function () { it('gets saved revisions count of Pad', function (done) { + this.timeout(150); api.get(`${endPoint('getSavedRevisionsCount')}&padID=${testPadId}`) .expect((res) => { if (res.body.code !== 0) throw new Error('Unable to get Saved Revisions Count'); @@ -162,6 +169,7 @@ describe(__filename, function () { describe('listSavedRevisions', function () { it('gets saved revision list of Pad', function (done) { + this.timeout(150); api.get(`${endPoint('listSavedRevisions')}&padID=${testPadId}`) .expect((res) => { if (res.body.code !== 0) throw new Error('Unable to get Saved Revisions List'); @@ -174,6 +182,7 @@ describe(__filename, function () { describe('getHTML', function () { it('get the HTML of Pad', function (done) { + this.timeout(150); api.get(`${endPoint('getHTML')}&padID=${testPadId}`) .expect((res) => { if (res.body.data.html.length <= 1) throw new Error('Unable to get the HTML'); @@ -185,6 +194,7 @@ describe(__filename, function () { describe('listAllPads', function () { it('list all pads', function (done) { + this.timeout(150); api.get(endPoint('listAllPads')) .expect((res) => { if (res.body.data.padIDs.includes(testPadId) !== true) { @@ -198,6 +208,7 @@ describe(__filename, function () { describe('deletePad', function () { it('deletes a Pad', function (done) { + this.timeout(150); api.get(`${endPoint('deletePad')}&padID=${testPadId}`) .expect((res) => { if (res.body.code !== 0) throw new Error('Pad Deletion failed'); @@ -209,6 +220,7 @@ describe(__filename, function () { describe('listAllPads', function () { it('list all pads', function (done) { + this.timeout(150); api.get(endPoint('listAllPads')) .expect((res) => { if (res.body.data.padIDs.includes(testPadId) !== false) { @@ -222,6 +234,7 @@ describe(__filename, function () { describe('getHTML', function () { it('get the HTML of a Pad -- Should return a failure', function (done) { + this.timeout(150); api.get(`${endPoint('getHTML')}&padID=${testPadId}`) .expect((res) => { if (res.body.code !== 1) throw new Error('Pad deletion failed'); @@ -233,6 +246,7 @@ describe(__filename, function () { describe('createPad', function () { it('creates a new Pad with text', function (done) { + this.timeout(200); api.get(`${endPoint('createPad')}&padID=${testPadId}&text=testText`) .expect((res) => { if (res.body.code !== 0) throw new Error('Pad Creation failed'); @@ -244,6 +258,7 @@ describe(__filename, function () { describe('getText', function () { it('gets the Pad text and expect it to be testText with \n which is a line break', function (done) { + this.timeout(150); api.get(`${endPoint('getText')}&padID=${testPadId}`) .expect((res) => { if (res.body.data.text !== 'testText\n') throw new Error('Pad Creation with text'); @@ -255,6 +270,7 @@ describe(__filename, function () { describe('setText', function () { it('creates a new Pad with text', function (done) { + this.timeout(200); api.post(endPoint('setText')) .send({ padID: testPadId, @@ -270,6 +286,7 @@ describe(__filename, function () { describe('getText', function () { it('gets the Pad text', function (done) { + this.timeout(150); api.get(`${endPoint('getText')}&padID=${testPadId}`) .expect((res) => { if (res.body.data.text !== 'testTextTwo\n') throw new Error('Setting Text'); @@ -281,6 +298,7 @@ describe(__filename, function () { describe('getRevisionsCount', function () { it('gets Revision Count of a Pad', function (done) { + this.timeout(150); api.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`) .expect((res) => { if (res.body.data.revisions !== 1) throw new Error('Unable to get text revision count'); @@ -292,6 +310,7 @@ describe(__filename, function () { describe('saveRevision', function () { it('saves Revision', function (done) { + this.timeout(150); api.get(`${endPoint('saveRevision')}&padID=${testPadId}`) .expect((res) => { if (res.body.code !== 0) throw new Error('Unable to save Revision'); @@ -303,6 +322,7 @@ describe(__filename, function () { describe('getSavedRevisionsCount', function () { it('gets saved revisions count of Pad', function (done) { + this.timeout(150); api.get(`${endPoint('getSavedRevisionsCount')}&padID=${testPadId}`) .expect((res) => { if (res.body.code !== 0) throw new Error('Unable to get Saved Revisions Count'); @@ -315,6 +335,7 @@ describe(__filename, function () { describe('listSavedRevisions', function () { it('gets saved revision list of Pad', function (done) { + this.timeout(150); api.get(`${endPoint('listSavedRevisions')}&padID=${testPadId}`) .expect((res) => { if (res.body.code !== 0) throw new Error('Unable to get Saved Revisions List'); @@ -326,6 +347,7 @@ describe(__filename, function () { }); describe('padUsersCount', function () { it('gets User Count of a Pad', function (done) { + this.timeout(150); api.get(`${endPoint('padUsersCount')}&padID=${testPadId}`) .expect((res) => { if (res.body.data.padUsersCount !== 0) throw new Error('Incorrect Pad User count'); @@ -337,6 +359,7 @@ describe(__filename, function () { describe('getReadOnlyID', function () { it('Gets the Read Only ID of a Pad', function (done) { + this.timeout(150); api.get(`${endPoint('getReadOnlyID')}&padID=${testPadId}`) .expect((res) => { if (!res.body.data.readOnlyID) throw new Error('No Read Only ID for Pad'); @@ -348,6 +371,7 @@ describe(__filename, function () { describe('listAuthorsOfPad', function () { it('Get Authors of the Pad', function (done) { + this.timeout(150); api.get(`${endPoint('listAuthorsOfPad')}&padID=${testPadId}`) .expect((res) => { if (res.body.data.authorIDs.length !== 0) throw new Error('# of Authors of pad is not 0'); @@ -359,6 +383,7 @@ describe(__filename, function () { describe('getLastEdited', function () { it('Get When Pad was left Edited', function (done) { + this.timeout(150); api.get(`${endPoint('getLastEdited')}&padID=${testPadId}`) .expect((res) => { if (!res.body.data.lastEdited) { @@ -374,6 +399,7 @@ describe(__filename, function () { describe('setText', function () { it('creates a new Pad with text', function (done) { + this.timeout(200); api.post(endPoint('setText')) .send({ padID: testPadId, @@ -389,6 +415,7 @@ describe(__filename, function () { describe('getLastEdited', function () { it('Get When Pad was left Edited', function (done) { + this.timeout(150); api.get(`${endPoint('getLastEdited')}&padID=${testPadId}`) .expect((res) => { if (res.body.data.lastEdited <= lastEdited) { @@ -402,6 +429,7 @@ describe(__filename, function () { describe('padUsers', function () { it('gets User Count of a Pad', function (done) { + this.timeout(150); api.get(`${endPoint('padUsers')}&padID=${testPadId}`) .expect((res) => { if (res.body.data.padUsers.length !== 0) throw new Error('Incorrect Pad Users'); @@ -413,6 +441,7 @@ describe(__filename, function () { describe('deletePad', function () { it('deletes a Pad', function (done) { + this.timeout(150); api.get(`${endPoint('deletePad')}&padID=${testPadId}`) .expect((res) => { if (res.body.code !== 0) throw new Error('Pad Deletion failed'); @@ -428,6 +457,7 @@ describe(__filename, function () { describe('createPad', function () { it('creates a new Pad with text', function (done) { + this.timeout(200); api.get(`${endPoint('createPad')}&padID=${testPadId}`) .expect((res) => { if (res.body.code !== 0) throw new Error('Pad Creation failed'); @@ -439,6 +469,7 @@ describe(__filename, function () { describe('setText', function () { it('Sets text on a pad Id', function (done) { + this.timeout(150); api.post(`${endPoint('setText')}&padID=${testPadId}`) .field({text}) .expect((res) => { @@ -451,6 +482,7 @@ describe(__filename, function () { describe('getText', function () { it('Gets text on a pad Id', function (done) { + this.timeout(200); api.get(`${endPoint('getText')}&padID=${testPadId}`) .expect((res) => { if (res.body.code !== 0) throw new Error('Pad Get Text failed'); @@ -463,6 +495,7 @@ describe(__filename, function () { describe('setText', function () { it('Sets text on a pad Id including an explicit newline', function (done) { + this.timeout(200); api.post(`${endPoint('setText')}&padID=${testPadId}`) .field({text: `${text}\n`}) .expect((res) => { @@ -475,6 +508,7 @@ describe(__filename, function () { describe('getText', function () { it("Gets text on a pad Id and doesn't have an excess newline", function (done) { + this.timeout(150); api.get(`${endPoint('getText')}&padID=${testPadId}`) .expect((res) => { if (res.body.code !== 0) throw new Error('Pad Get Text failed'); @@ -487,6 +521,7 @@ describe(__filename, function () { describe('getLastEdited', function () { it('Gets when pad was last edited', function (done) { + this.timeout(150); api.get(`${endPoint('getLastEdited')}&padID=${testPadId}`) .expect((res) => { if (res.body.lastEdited === 0) throw new Error('Get Last Edited Failed'); @@ -498,6 +533,7 @@ describe(__filename, function () { describe('movePad', function () { it('Move a Pad to a different Pad ID', function (done) { + this.timeout(200); api.get(`${endPoint('movePad')}&sourceID=${testPadId}&destinationID=${newPadId}&force=true`) .expect((res) => { if (res.body.code !== 0) throw new Error('Moving Pad Failed'); @@ -509,6 +545,7 @@ describe(__filename, function () { describe('getText', function () { it('Gets text on a pad Id', function (done) { + this.timeout(150); api.get(`${endPoint('getText')}&padID=${newPadId}`) .expect((res) => { if (res.body.data.text !== `${text}\n`) throw new Error('Pad Get Text failed'); @@ -520,6 +557,7 @@ describe(__filename, function () { describe('movePad', function () { it('Move a Pad to a different Pad ID', function (done) { + this.timeout(200); api.get(`${endPoint('movePad')}&sourceID=${newPadId}&destinationID=${testPadId}&force=false`) .expect((res) => { if (res.body.code !== 0) throw new Error('Moving Pad Failed'); @@ -531,6 +569,7 @@ describe(__filename, function () { describe('getText', function () { it('Gets text on a pad Id', function (done) { + this.timeout(150); api.get(`${endPoint('getText')}&padID=${testPadId}`) .expect((res) => { if (res.body.data.text !== `${text}\n`) throw new Error('Pad Get Text failed'); @@ -542,6 +581,7 @@ describe(__filename, function () { describe('getLastEdited', function () { it('Gets when pad was last edited', function (done) { + this.timeout(150); api.get(`${endPoint('getLastEdited')}&padID=${testPadId}`) .expect((res) => { if (res.body.lastEdited === 0) throw new Error('Get Last Edited Failed'); @@ -553,6 +593,7 @@ describe(__filename, function () { describe('appendText', function () { it('Append text to a pad Id', function (done) { + this.timeout(150); api.get(`${endPoint('appendText', '1.2.13')}&padID=${testPadId}&text=hello`) .expect((res) => { if (res.body.code !== 0) throw new Error('Pad Append Text failed'); @@ -564,6 +605,7 @@ describe(__filename, function () { describe('getText', function () { it('Gets text on a pad Id', function (done) { + this.timeout(150); api.get(`${endPoint('getText')}&padID=${testPadId}`) .expect((res) => { if (res.body.code !== 0) throw new Error('Pad Get Text failed'); @@ -577,6 +619,7 @@ describe(__filename, function () { describe('setHTML', function () { it('Sets the HTML of a Pad attempting to pass ugly HTML', function (done) { + this.timeout(200); const html = '
                    Hello HTML
                    '; api.post(endPoint('setHTML')) .send({ @@ -593,6 +636,7 @@ describe(__filename, function () { describe('setHTML', function () { it('Sets the HTML of a Pad with complex nested lists of different types', function (done) { + this.timeout(200); api.post(endPoint('setHTML')) .send({ padID: testPadId, @@ -608,6 +652,7 @@ describe(__filename, function () { describe('getHTML', function () { it('Gets back the HTML of a Pad with complex nested lists of different types', function (done) { + this.timeout(150); api.get(`${endPoint('getHTML')}&padID=${testPadId}`) .expect((res) => { const receivedHtml = res.body.data.html.replace('
                    ', '').toLowerCase(); @@ -631,6 +676,7 @@ describe(__filename, function () { describe('setHTML', function () { it('Sets the HTML of a Pad with white space between list items', function (done) { + this.timeout(200); api.get(`${endPoint('setHTML')}&padID=${testPadId}&html=${ulSpaceHtml}`) .expect((res) => { if (res.body.code !== 0) throw new Error('List HTML cant be imported'); @@ -642,6 +688,7 @@ describe(__filename, function () { describe('getHTML', function () { it('Gets back the HTML of a Pad with complex nested lists of different types', function (done) { + this.timeout(150); api.get(`${endPoint('getHTML')}&padID=${testPadId}`) .expect((res) => { const receivedHtml = res.body.data.html.replace('
                    ', '').toLowerCase(); @@ -664,6 +711,7 @@ describe(__filename, function () { describe('createPad', function () { it('errors if pad can be created', function (done) { + this.timeout(150); const badUrlChars = ['/', '%23', '%3F', '%26']; async.map( badUrlChars, @@ -681,6 +729,7 @@ describe(__filename, function () { describe('copyPad', function () { it('copies the content of a existent pad', function (done) { + this.timeout(200); api.get(`${endPoint('copyPad')}&sourceID=${testPadId}&destinationID=${copiedPadId}&force=true`) .expect((res) => { if (res.body.code !== 0) throw new Error('Copy Pad Failed'); @@ -703,6 +752,7 @@ describe(__filename, function () { }); it('returns a successful response', function (done) { + this.timeout(200); api.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}&destinationID=${newPad}&force=false`) .expect((res) => { if (res.body.code !== 0) throw new Error('Copy Pad Without History Failed'); @@ -713,6 +763,7 @@ describe(__filename, function () { // this test validates if the source pad's text and attributes are kept it('creates a new pad with the same content as the source pad', function (done) { + this.timeout(200); api.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}&destinationID=${newPad}&force=false`) .expect((res) => { if (res.body.code !== 0) throw new Error('Copy Pad Without History Failed'); @@ -742,6 +793,7 @@ describe(__filename, function () { const padId = makeid(); const padWithNonExistentGroup = `notExistentGroup$${padId}`; it('throws an error', function (done) { + this.timeout(150); api.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}&destinationID=${padWithNonExistentGroup}&force=true`) .expect((res) => { // code 1, it means an error has happened @@ -760,6 +812,7 @@ describe(__filename, function () { context('and force is false', function () { it('throws an error', function (done) { + this.timeout(150); api.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}&destinationID=${padIdExistent}&force=false`) .expect((res) => { // code 1, it means an error has happened @@ -771,6 +824,7 @@ describe(__filename, function () { context('and force is true', function () { it('returns a successful response', function (done) { + this.timeout(200); api.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}&destinationID=${padIdExistent}&force=true`) .expect((res) => { // code 1, it means an error has happened diff --git a/src/tests/backend/specs/api/sessionsAndGroups.js b/src/tests/backend/specs/api/sessionsAndGroups.js index a1f439eb3..c3cc38752 100644 --- a/src/tests/backend/specs/api/sessionsAndGroups.js +++ b/src/tests/backend/specs/api/sessionsAndGroups.js @@ -15,6 +15,7 @@ let padID = makeid(); describe(__filename, function () { describe('API Versioning', function () { it('errors if can not connect', async function () { + this.timeout(200); await api.get('/api/') .expect(200) .expect((res) => { @@ -56,6 +57,7 @@ describe(__filename, function () { describe('API: Group creation and deletion', function () { it('createGroup', async function () { + this.timeout(100); await api.get(endPoint('createGroup')) .expect(200) .expect('Content-Type', /json/) @@ -67,6 +69,7 @@ describe(__filename, function () { }); it('listSessionsOfGroup for empty group', async function () { + this.timeout(100); await api.get(`${endPoint('listSessionsOfGroup')}&groupID=${groupID}`) .expect(200) .expect('Content-Type', /json/) @@ -77,6 +80,7 @@ describe(__filename, function () { }); it('deleteGroup', async function () { + this.timeout(100); await api.get(`${endPoint('deleteGroup')}&groupID=${groupID}`) .expect(200) .expect('Content-Type', /json/) @@ -86,6 +90,7 @@ describe(__filename, function () { }); it('createGroupIfNotExistsFor', async function () { + this.timeout(100); await api.get(`${endPoint('createGroupIfNotExistsFor')}&groupMapper=management`) .expect(200) .expect('Content-Type', /json/) @@ -98,6 +103,7 @@ describe(__filename, function () { // Test coverage for https://github.com/ether/etherpad-lite/issues/4227 // Creates a group, creates 2 sessions, 2 pads and then deletes the group. it('createGroup', async function () { + this.timeout(100); await api.get(endPoint('createGroup')) .expect(200) .expect('Content-Type', /json/) @@ -109,6 +115,7 @@ describe(__filename, function () { }); it('createAuthor', async function () { + this.timeout(100); await api.get(endPoint('createAuthor')) .expect(200) .expect('Content-Type', /json/) @@ -120,6 +127,7 @@ describe(__filename, function () { }); it('createSession', async function () { + this.timeout(100); await api.get(`${endPoint('createSession') }&authorID=${authorID}&groupID=${groupID}&validUntil=999999999999`) .expect(200) @@ -132,6 +140,7 @@ describe(__filename, function () { }); it('createSession', async function () { + this.timeout(100); await api.get(`${endPoint('createSession') }&authorID=${authorID}&groupID=${groupID}&validUntil=999999999999`) .expect(200) @@ -144,6 +153,7 @@ describe(__filename, function () { }); it('createGroupPad', async function () { + this.timeout(100); await api.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=x1234567`) .expect(200) .expect('Content-Type', /json/) @@ -153,6 +163,7 @@ describe(__filename, function () { }); it('createGroupPad', async function () { + this.timeout(100); await api.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=x12345678`) .expect(200) .expect('Content-Type', /json/) @@ -162,6 +173,7 @@ describe(__filename, function () { }); it('deleteGroup', async function () { + this.timeout(100); await api.get(`${endPoint('deleteGroup')}&groupID=${groupID}`) .expect(200) .expect('Content-Type', /json/) @@ -174,6 +186,7 @@ describe(__filename, function () { describe('API: Author creation', function () { it('createGroup', async function () { + this.timeout(100); await api.get(endPoint('createGroup')) .expect(200) .expect('Content-Type', /json/) @@ -185,6 +198,7 @@ describe(__filename, function () { }); it('createAuthor', async function () { + this.timeout(100); await api.get(endPoint('createAuthor')) .expect(200) .expect('Content-Type', /json/) @@ -195,6 +209,7 @@ describe(__filename, function () { }); it('createAuthor with name', async function () { + this.timeout(100); await api.get(`${endPoint('createAuthor')}&name=john`) .expect(200) .expect('Content-Type', /json/) @@ -206,6 +221,7 @@ describe(__filename, function () { }); it('createAuthorIfNotExistsFor', async function () { + this.timeout(100); await api.get(`${endPoint('createAuthorIfNotExistsFor')}&authorMapper=chris`) .expect(200) .expect('Content-Type', /json/) @@ -216,6 +232,7 @@ describe(__filename, function () { }); it('getAuthorName', async function () { + this.timeout(100); await api.get(`${endPoint('getAuthorName')}&authorID=${authorID}`) .expect(200) .expect('Content-Type', /json/) @@ -228,6 +245,7 @@ describe(__filename, function () { describe('API: Sessions', function () { it('createSession', async function () { + this.timeout(100); await api.get(`${endPoint('createSession') }&authorID=${authorID}&groupID=${groupID}&validUntil=999999999999`) .expect(200) @@ -240,6 +258,7 @@ describe(__filename, function () { }); it('getSessionInfo', async function () { + this.timeout(100); await api.get(`${endPoint('getSessionInfo')}&sessionID=${sessionID}`) .expect(200) .expect('Content-Type', /json/) @@ -252,6 +271,7 @@ describe(__filename, function () { }); it('listSessionsOfGroup', async function () { + this.timeout(100); await api.get(`${endPoint('listSessionsOfGroup')}&groupID=${groupID}`) .expect(200) .expect('Content-Type', /json/) @@ -262,6 +282,7 @@ describe(__filename, function () { }); it('deleteSession', async function () { + this.timeout(100); await api.get(`${endPoint('deleteSession')}&sessionID=${sessionID}`) .expect(200) .expect('Content-Type', /json/) @@ -271,6 +292,7 @@ describe(__filename, function () { }); it('getSessionInfo of deleted session', async function () { + this.timeout(100); await api.get(`${endPoint('getSessionInfo')}&sessionID=${sessionID}`) .expect(200) .expect('Content-Type', /json/) @@ -282,6 +304,7 @@ describe(__filename, function () { describe('API: Group pad management', function () { it('listPads', async function () { + this.timeout(100); await api.get(`${endPoint('listPads')}&groupID=${groupID}`) .expect(200) .expect('Content-Type', /json/) @@ -292,6 +315,7 @@ describe(__filename, function () { }); it('createGroupPad', async function () { + this.timeout(100); await api.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=${padID}`) .expect(200) .expect('Content-Type', /json/) @@ -302,6 +326,7 @@ describe(__filename, function () { }); it('listPads after creating a group pad', async function () { + this.timeout(100); await api.get(`${endPoint('listPads')}&groupID=${groupID}`) .expect(200) .expect('Content-Type', /json/) @@ -314,6 +339,7 @@ describe(__filename, function () { describe('API: Pad security', function () { it('getPublicStatus', async function () { + this.timeout(100); await api.get(`${endPoint('getPublicStatus')}&padID=${padID}`) .expect(200) .expect('Content-Type', /json/) @@ -324,6 +350,7 @@ describe(__filename, function () { }); it('setPublicStatus', async function () { + this.timeout(100); await api.get(`${endPoint('setPublicStatus')}&padID=${padID}&publicStatus=true`) .expect(200) .expect('Content-Type', /json/) @@ -333,6 +360,7 @@ describe(__filename, function () { }); it('getPublicStatus after changing public status', async function () { + this.timeout(100); await api.get(`${endPoint('getPublicStatus')}&padID=${padID}`) .expect(200) .expect('Content-Type', /json/) @@ -349,6 +377,7 @@ describe(__filename, function () { describe('API: Misc', function () { it('listPadsOfAuthor', async function () { + this.timeout(100); await api.get(`${endPoint('listPadsOfAuthor')}&authorID=${authorID}`) .expect(200) .expect('Content-Type', /json/) diff --git a/src/tests/backend/specs/contentcollector.js b/src/tests/backend/specs/contentcollector.js index f9b183f96..1739bd7a0 100644 --- a/src/tests/backend/specs/contentcollector.js +++ b/src/tests/backend/specs/contentcollector.js @@ -285,6 +285,7 @@ describe(__filename, function () { } it(testObj.description, async function () { + this.timeout(250); const $ = cheerio.load(testObj.html); // Load HTML into Cheerio const doc = $('body')[0]; // Creates a dom-like representation of HTML // Create an empty attribute pool diff --git a/src/tests/backend/specs/hooks.js b/src/tests/backend/specs/hooks.js index 47d45ae6c..9dbf6974b 100644 --- a/src/tests/backend/specs/hooks.js +++ b/src/tests/backend/specs/hooks.js @@ -93,11 +93,13 @@ describe(__filename, function () { describe('basic behavior', function () { it('passes hook name', async function () { + this.timeout(30); hook.hook_fn = (hn) => { assert.equal(hn, hookName); }; callHookFnSync(hook); }); it('passes context', async function () { + this.timeout(30); for (const val of ['value', null, undefined]) { hook.hook_fn = (hn, ctx) => { assert.equal(ctx, val); }; callHookFnSync(hook, val); @@ -105,6 +107,7 @@ describe(__filename, function () { }); it('returns the value provided to the callback', async function () { + this.timeout(30); for (const val of ['value', null, undefined]) { hook.hook_fn = (hn, ctx, cb) => { cb(ctx); }; assert.equal(callHookFnSync(hook, val), val); @@ -112,6 +115,7 @@ describe(__filename, function () { }); it('returns the value returned by the hook function', async function () { + this.timeout(30); for (const val of ['value', null, undefined]) { // Must not have the cb parameter otherwise returning undefined will error. hook.hook_fn = (hn, ctx) => ctx; @@ -120,16 +124,19 @@ describe(__filename, function () { }); it('does not catch exceptions', async function () { + this.timeout(30); hook.hook_fn = () => { throw new Error('test exception'); }; assert.throws(() => callHookFnSync(hook), {message: 'test exception'}); }); it('callback returns undefined', async function () { + this.timeout(30); hook.hook_fn = (hn, ctx, cb) => { assert.equal(cb('foo'), undefined); }; callHookFnSync(hook); }); it('checks for deprecation', async function () { + this.timeout(30); sinon.stub(console, 'warn'); hooks.deprecationNotices[hookName] = 'test deprecation'; callHookFnSync(hook); @@ -142,6 +149,7 @@ describe(__filename, function () { describe('supported hook function styles', function () { for (const tc of supportedSyncHookFunctions) { it(tc.name, async function () { + this.timeout(30); sinon.stub(console, 'warn'); sinon.stub(console, 'error'); hook.hook_fn = tc.fn; @@ -186,6 +194,7 @@ describe(__filename, function () { for (const tc of testCases) { it(tc.name, async function () { + this.timeout(30); sinon.stub(console, 'error'); hook.hook_fn = tc.fn; assert.equal(callHookFnSync(hook), tc.wantVal); @@ -237,6 +246,7 @@ describe(__filename, function () { if (step1.async && step2.async) continue; it(`${step1.name} then ${step2.name} (diff. outcomes) -> log+throw`, async function () { + this.timeout(30); hook.hook_fn = (hn, ctx, cb) => { step1.fn(cb, new Error(ctx.ret1), ctx.ret1); return step2.fn(cb, new Error(ctx.ret2), ctx.ret2); @@ -300,6 +310,7 @@ describe(__filename, function () { if (step1.rejects !== step2.rejects) continue; it(`${step1.name} then ${step2.name} (same outcome) -> only log`, async function () { + this.timeout(30); const err = new Error('val'); hook.hook_fn = (hn, ctx, cb) => { step1.fn(cb, err, 'val'); @@ -325,27 +336,32 @@ describe(__filename, function () { describe('hooks.callAll', function () { describe('basic behavior', function () { it('calls all in order', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(1), makeHook(2), makeHook(3)); assert.deepEqual(hooks.callAll(hookName), [1, 2, 3]); }); it('passes hook name', async function () { + this.timeout(30); hook.hook_fn = (hn) => { assert.equal(hn, hookName); }; hooks.callAll(hookName); }); it('undefined context -> {}', async function () { + this.timeout(30); hook.hook_fn = (hn, ctx) => { assert.deepEqual(ctx, {}); }; hooks.callAll(hookName); }); it('null context -> {}', async function () { + this.timeout(30); hook.hook_fn = (hn, ctx) => { assert.deepEqual(ctx, {}); }; hooks.callAll(hookName, null); }); it('context unmodified', async function () { + this.timeout(30); const wantContext = {}; hook.hook_fn = (hn, ctx) => { assert.equal(ctx, wantContext); }; hooks.callAll(hookName, wantContext); @@ -354,34 +370,40 @@ describe(__filename, function () { describe('result processing', function () { it('no registered hooks (undefined) -> []', async function () { + this.timeout(30); delete plugins.hooks.testHook; assert.deepEqual(hooks.callAll(hookName), []); }); it('no registered hooks (empty list) -> []', async function () { + this.timeout(30); testHooks.length = 0; assert.deepEqual(hooks.callAll(hookName), []); }); it('flattens one level', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(1), makeHook([2]), makeHook([[3]])); assert.deepEqual(hooks.callAll(hookName), [1, 2, [3]]); }); it('filters out undefined', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(), makeHook([2]), makeHook([[3]])); assert.deepEqual(hooks.callAll(hookName), [2, [3]]); }); it('preserves null', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(null), makeHook([2]), makeHook([[3]])); assert.deepEqual(hooks.callAll(hookName), [null, 2, [3]]); }); it('all undefined -> []', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(), makeHook()); assert.deepEqual(hooks.callAll(hookName), []); @@ -391,37 +413,44 @@ describe(__filename, function () { describe('hooks.callFirst', function () { it('no registered hooks (undefined) -> []', async function () { + this.timeout(30); delete plugins.hooks.testHook; assert.deepEqual(hooks.callFirst(hookName), []); }); it('no registered hooks (empty list) -> []', async function () { + this.timeout(30); testHooks.length = 0; assert.deepEqual(hooks.callFirst(hookName), []); }); it('passes hook name => {}', async function () { + this.timeout(30); hook.hook_fn = (hn) => { assert.equal(hn, hookName); }; hooks.callFirst(hookName); }); it('undefined context => {}', async function () { + this.timeout(30); hook.hook_fn = (hn, ctx) => { assert.deepEqual(ctx, {}); }; hooks.callFirst(hookName); }); it('null context => {}', async function () { + this.timeout(30); hook.hook_fn = (hn, ctx) => { assert.deepEqual(ctx, {}); }; hooks.callFirst(hookName, null); }); it('context unmodified', async function () { + this.timeout(30); const wantContext = {}; hook.hook_fn = (hn, ctx) => { assert.equal(ctx, wantContext); }; hooks.callFirst(hookName, wantContext); }); it('predicate never satisfied -> calls all in order', async function () { + this.timeout(30); const gotCalls = []; testHooks.length = 0; for (let i = 0; i < 3; i++) { @@ -434,30 +463,35 @@ describe(__filename, function () { }); it('stops when predicate is satisfied', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(), makeHook('val1'), makeHook('val2')); assert.deepEqual(hooks.callFirst(hookName), ['val1']); }); it('skips values that do not satisfy predicate (undefined)', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(), makeHook('val1')); assert.deepEqual(hooks.callFirst(hookName), ['val1']); }); it('skips values that do not satisfy predicate (empty list)', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook([]), makeHook('val1')); assert.deepEqual(hooks.callFirst(hookName), ['val1']); }); it('null satisifes the predicate', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(null), makeHook('val1')); assert.deepEqual(hooks.callFirst(hookName), [null]); }); it('non-empty arrays are returned unmodified', async function () { + this.timeout(30); const want = ['val1']; testHooks.length = 0; testHooks.push(makeHook(want), makeHook(['val2'])); @@ -465,6 +499,7 @@ describe(__filename, function () { }); it('value can be passed via callback', async function () { + this.timeout(30); const want = {}; hook.hook_fn = (hn, ctx, cb) => { cb(want); }; const got = hooks.callFirst(hookName); @@ -478,11 +513,13 @@ describe(__filename, function () { describe('basic behavior', function () { it('passes hook name', async function () { + this.timeout(30); hook.hook_fn = (hn) => { assert.equal(hn, hookName); }; await callHookFnAsync(hook); }); it('passes context', async function () { + this.timeout(30); for (const val of ['value', null, undefined]) { hook.hook_fn = (hn, ctx) => { assert.equal(ctx, val); }; await callHookFnAsync(hook, val); @@ -490,6 +527,7 @@ describe(__filename, function () { }); it('returns the value provided to the callback', async function () { + this.timeout(30); for (const val of ['value', null, undefined]) { hook.hook_fn = (hn, ctx, cb) => { cb(ctx); }; assert.equal(await callHookFnAsync(hook, val), val); @@ -498,6 +536,7 @@ describe(__filename, function () { }); it('returns the value returned by the hook function', async function () { + this.timeout(30); for (const val of ['value', null, undefined]) { // Must not have the cb parameter otherwise returning undefined will never resolve. hook.hook_fn = (hn, ctx) => ctx; @@ -507,26 +546,31 @@ describe(__filename, function () { }); it('rejects if it throws an exception', async function () { + this.timeout(30); hook.hook_fn = () => { throw new Error('test exception'); }; await assert.rejects(callHookFnAsync(hook), {message: 'test exception'}); }); it('rejects if rejected Promise passed to callback', async function () { + this.timeout(30); hook.hook_fn = (hn, ctx, cb) => cb(Promise.reject(new Error('test exception'))); await assert.rejects(callHookFnAsync(hook), {message: 'test exception'}); }); it('rejects if rejected Promise returned', async function () { + this.timeout(30); hook.hook_fn = (hn, ctx, cb) => Promise.reject(new Error('test exception')); await assert.rejects(callHookFnAsync(hook), {message: 'test exception'}); }); it('callback returns undefined', async function () { + this.timeout(30); hook.hook_fn = (hn, ctx, cb) => { assert.equal(cb('foo'), undefined); }; await callHookFnAsync(hook); }); it('checks for deprecation', async function () { + this.timeout(30); sinon.stub(console, 'warn'); hooks.deprecationNotices[hookName] = 'test deprecation'; await callHookFnAsync(hook); @@ -619,6 +663,7 @@ describe(__filename, function () { for (const tc of supportedSyncHookFunctions.concat(supportedHookFunctions)) { it(tc.name, async function () { + this.timeout(30); sinon.stub(console, 'warn'); sinon.stub(console, 'error'); hook.hook_fn = tc.fn; @@ -766,6 +811,7 @@ describe(__filename, function () { if (step1.name.startsWith('return ') || step1.name === 'throw') continue; for (const step2 of behaviors) { it(`${step1.name} then ${step2.name} (diff. outcomes) -> log+throw`, async function () { + this.timeout(30); hook.hook_fn = (hn, ctx, cb) => { step1.fn(cb, new Error(ctx.ret1), ctx.ret1); return step2.fn(cb, new Error(ctx.ret2), ctx.ret2); @@ -819,6 +865,7 @@ describe(__filename, function () { if (step1.rejects !== step2.rejects) continue; it(`${step1.name} then ${step2.name} (same outcome) -> only log`, async function () { + this.timeout(30); const err = new Error('val'); hook.hook_fn = (hn, ctx, cb) => { step1.fn(cb, err, 'val'); @@ -844,6 +891,7 @@ describe(__filename, function () { describe('hooks.aCallAll', function () { describe('basic behavior', function () { it('calls all asynchronously, returns values in order', async function () { + this.timeout(30); testHooks.length = 0; // Delete the boilerplate hook -- this test doesn't use it. let nextIndex = 0; const hookPromises = []; @@ -878,21 +926,25 @@ describe(__filename, function () { }); it('passes hook name', async function () { + this.timeout(30); hook.hook_fn = async (hn) => { assert.equal(hn, hookName); }; await hooks.aCallAll(hookName); }); it('undefined context -> {}', async function () { + this.timeout(30); hook.hook_fn = async (hn, ctx) => { assert.deepEqual(ctx, {}); }; await hooks.aCallAll(hookName); }); it('null context -> {}', async function () { + this.timeout(30); hook.hook_fn = async (hn, ctx) => { assert.deepEqual(ctx, {}); }; await hooks.aCallAll(hookName, null); }); it('context unmodified', async function () { + this.timeout(30); const wantContext = {}; hook.hook_fn = async (hn, ctx) => { assert.equal(ctx, wantContext); }; await hooks.aCallAll(hookName, wantContext); @@ -901,11 +953,13 @@ describe(__filename, function () { describe('aCallAll callback', function () { it('exception in callback rejects', async function () { + this.timeout(30); const p = hooks.aCallAll(hookName, {}, () => { throw new Error('test exception'); }); await assert.rejects(p, {message: 'test exception'}); }); it('propagates error on exception', async function () { + this.timeout(30); hook.hook_fn = () => { throw new Error('test exception'); }; await hooks.aCallAll(hookName, {}, (err) => { assert(err instanceof Error); @@ -914,12 +968,14 @@ describe(__filename, function () { }); it('propagages null error on success', async function () { + this.timeout(30); await hooks.aCallAll(hookName, {}, (err) => { assert(err == null, `got non-null error: ${err}`); }); }); it('propagages results on success', async function () { + this.timeout(30); hook.hook_fn = () => 'val'; await hooks.aCallAll(hookName, {}, (err, results) => { assert.deepEqual(results, ['val']); @@ -927,40 +983,47 @@ describe(__filename, function () { }); it('returns callback return value', async function () { + this.timeout(30); assert.equal(await hooks.aCallAll(hookName, {}, () => 'val'), 'val'); }); }); describe('result processing', function () { it('no registered hooks (undefined) -> []', async function () { + this.timeout(30); delete plugins.hooks[hookName]; assert.deepEqual(await hooks.aCallAll(hookName), []); }); it('no registered hooks (empty list) -> []', async function () { + this.timeout(30); testHooks.length = 0; assert.deepEqual(await hooks.aCallAll(hookName), []); }); it('flattens one level', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(1), makeHook([2]), makeHook([[3]])); assert.deepEqual(await hooks.aCallAll(hookName), [1, 2, [3]]); }); it('filters out undefined', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(), makeHook([2]), makeHook([[3]]), makeHook(Promise.resolve())); assert.deepEqual(await hooks.aCallAll(hookName), [2, [3]]); }); it('preserves null', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(null), makeHook([2]), makeHook(Promise.resolve(null))); assert.deepEqual(await hooks.aCallAll(hookName), [null, 2, null]); }); it('all undefined -> []', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(), makeHook(Promise.resolve())); assert.deepEqual(await hooks.aCallAll(hookName), []); @@ -971,6 +1034,7 @@ describe(__filename, function () { describe('hooks.callAllSerial', function () { describe('basic behavior', function () { it('calls all asynchronously, serially, in order', async function () { + this.timeout(30); const gotCalls = []; testHooks.length = 0; for (let i = 0; i < 3; i++) { @@ -993,21 +1057,25 @@ describe(__filename, function () { }); it('passes hook name', async function () { + this.timeout(30); hook.hook_fn = async (hn) => { assert.equal(hn, hookName); }; await hooks.callAllSerial(hookName); }); it('undefined context -> {}', async function () { + this.timeout(30); hook.hook_fn = async (hn, ctx) => { assert.deepEqual(ctx, {}); }; await hooks.callAllSerial(hookName); }); it('null context -> {}', async function () { + this.timeout(30); hook.hook_fn = async (hn, ctx) => { assert.deepEqual(ctx, {}); }; await hooks.callAllSerial(hookName, null); }); it('context unmodified', async function () { + this.timeout(30); const wantContext = {}; hook.hook_fn = async (hn, ctx) => { assert.equal(ctx, wantContext); }; await hooks.callAllSerial(hookName, wantContext); @@ -1016,34 +1084,40 @@ describe(__filename, function () { describe('result processing', function () { it('no registered hooks (undefined) -> []', async function () { + this.timeout(30); delete plugins.hooks[hookName]; assert.deepEqual(await hooks.callAllSerial(hookName), []); }); it('no registered hooks (empty list) -> []', async function () { + this.timeout(30); testHooks.length = 0; assert.deepEqual(await hooks.callAllSerial(hookName), []); }); it('flattens one level', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(1), makeHook([2]), makeHook([[3]])); assert.deepEqual(await hooks.callAllSerial(hookName), [1, 2, [3]]); }); it('filters out undefined', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(), makeHook([2]), makeHook([[3]]), makeHook(Promise.resolve())); assert.deepEqual(await hooks.callAllSerial(hookName), [2, [3]]); }); it('preserves null', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(null), makeHook([2]), makeHook(Promise.resolve(null))); assert.deepEqual(await hooks.callAllSerial(hookName), [null, 2, null]); }); it('all undefined -> []', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(), makeHook(Promise.resolve())); assert.deepEqual(await hooks.callAllSerial(hookName), []); @@ -1053,37 +1127,44 @@ describe(__filename, function () { describe('hooks.aCallFirst', function () { it('no registered hooks (undefined) -> []', async function () { + this.timeout(30); delete plugins.hooks.testHook; assert.deepEqual(await hooks.aCallFirst(hookName), []); }); it('no registered hooks (empty list) -> []', async function () { + this.timeout(30); testHooks.length = 0; assert.deepEqual(await hooks.aCallFirst(hookName), []); }); it('passes hook name => {}', async function () { + this.timeout(30); hook.hook_fn = (hn) => { assert.equal(hn, hookName); }; await hooks.aCallFirst(hookName); }); it('undefined context => {}', async function () { + this.timeout(30); hook.hook_fn = (hn, ctx) => { assert.deepEqual(ctx, {}); }; await hooks.aCallFirst(hookName); }); it('null context => {}', async function () { + this.timeout(30); hook.hook_fn = (hn, ctx) => { assert.deepEqual(ctx, {}); }; await hooks.aCallFirst(hookName, null); }); it('context unmodified', async function () { + this.timeout(30); const wantContext = {}; hook.hook_fn = (hn, ctx) => { assert.equal(ctx, wantContext); }; await hooks.aCallFirst(hookName, wantContext); }); it('default predicate: predicate never satisfied -> calls all in order', async function () { + this.timeout(30); const gotCalls = []; testHooks.length = 0; for (let i = 0; i < 3; i++) { @@ -1096,6 +1177,7 @@ describe(__filename, function () { }); it('calls hook functions serially', async function () { + this.timeout(30); const gotCalls = []; testHooks.length = 0; for (let i = 0; i < 3; i++) { @@ -1118,30 +1200,35 @@ describe(__filename, function () { }); it('default predicate: stops when satisfied', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(), makeHook('val1'), makeHook('val2')); assert.deepEqual(await hooks.aCallFirst(hookName), ['val1']); }); it('default predicate: skips values that do not satisfy (undefined)', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(), makeHook('val1')); assert.deepEqual(await hooks.aCallFirst(hookName), ['val1']); }); it('default predicate: skips values that do not satisfy (empty list)', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook([]), makeHook('val1')); assert.deepEqual(await hooks.aCallFirst(hookName), ['val1']); }); it('default predicate: null satisifes', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(null), makeHook('val1')); assert.deepEqual(await hooks.aCallFirst(hookName), [null]); }); it('custom predicate: called for each hook function', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(0), makeHook(1), makeHook(2)); let got = 0; @@ -1150,6 +1237,7 @@ describe(__filename, function () { }); it('custom predicate: boolean false/true continues/stops iteration', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(1), makeHook(2), makeHook(3)); let nCall = 0; @@ -1162,6 +1250,7 @@ describe(__filename, function () { }); it('custom predicate: non-boolean falsy/truthy continues/stops iteration', async function () { + this.timeout(30); testHooks.length = 0; testHooks.push(makeHook(1), makeHook(2), makeHook(3)); let nCall = 0; @@ -1174,6 +1263,7 @@ describe(__filename, function () { }); it('custom predicate: array value passed unmodified to predicate', async function () { + this.timeout(30); const want = [0]; hook.hook_fn = () => want; const predicate = (got) => { assert.equal(got, want); }; // Note: *NOT* deepEqual! @@ -1181,17 +1271,20 @@ describe(__filename, function () { }); it('custom predicate: normalized value passed to predicate (undefined)', async function () { + this.timeout(30); const predicate = (got) => { assert.deepEqual(got, []); }; await hooks.aCallFirst(hookName, null, null, predicate); }); it('custom predicate: normalized value passed to predicate (null)', async function () { + this.timeout(30); hook.hook_fn = () => null; const predicate = (got) => { assert.deepEqual(got, [null]); }; await hooks.aCallFirst(hookName, null, null, predicate); }); it('non-empty arrays are returned unmodified', async function () { + this.timeout(30); const want = ['val1']; testHooks.length = 0; testHooks.push(makeHook(want), makeHook(['val2'])); @@ -1199,6 +1292,7 @@ describe(__filename, function () { }); it('value can be passed via callback', async function () { + this.timeout(30); const want = {}; hook.hook_fn = (hn, ctx, cb) => { cb(want); }; const got = await hooks.aCallFirst(hookName); diff --git a/src/tests/backend/specs/socketio.js b/src/tests/backend/specs/socketio.js index c6d6db720..fdb578b55 100644 --- a/src/tests/backend/specs/socketio.js +++ b/src/tests/backend/specs/socketio.js @@ -89,6 +89,7 @@ const handshake = async (socket, padID) => { }; describe(__filename, function () { + this.timeout(30000); let agent; let authorize; const backups = {}; @@ -136,23 +137,27 @@ describe(__filename, function () { describe('Normal accesses', function () { it('!authn anonymous cookie /p/pad -> 200, ok', async function () { + this.timeout(600); const res = await agent.get('/p/pad').expect(200); socket = await connect(res); const clientVars = await handshake(socket, 'pad'); assert.equal(clientVars.type, 'CLIENT_VARS'); }); it('!authn !cookie -> ok', async function () { + this.timeout(400); socket = await connect(null); const clientVars = await handshake(socket, 'pad'); assert.equal(clientVars.type, 'CLIENT_VARS'); }); it('!authn user /p/pad -> 200, ok', async function () { + this.timeout(400); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); socket = await connect(res); const clientVars = await handshake(socket, 'pad'); assert.equal(clientVars.type, 'CLIENT_VARS'); }); it('authn user /p/pad -> 200, ok', async function () { + this.timeout(400); settings.requireAuthentication = true; const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); socket = await connect(res); @@ -160,6 +165,7 @@ describe(__filename, function () { assert.equal(clientVars.type, 'CLIENT_VARS'); }); it('authz user /p/pad -> 200, ok', async function () { + this.timeout(400); settings.requireAuthentication = true; settings.requireAuthorization = true; const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); @@ -168,6 +174,7 @@ describe(__filename, function () { assert.equal(clientVars.type, 'CLIENT_VARS'); }); it('supports pad names with characters that must be percent-encoded', async function () { + this.timeout(400); settings.requireAuthentication = true; // requireAuthorization is set to true here to guarantee that the user's padAuthorizations // object is populated. Technically this isn't necessary because the user's padAuthorizations @@ -184,6 +191,7 @@ describe(__filename, function () { describe('Abnormal access attempts', function () { it('authn anonymous /p/pad -> 401, error', async function () { + this.timeout(400); settings.requireAuthentication = true; const res = await agent.get('/p/pad').expect(401); // Despite the 401, try to create the pad via a socket.io connection anyway. @@ -192,12 +200,14 @@ describe(__filename, function () { assert.equal(message.accessStatus, 'deny'); }); it('authn !cookie -> error', async function () { + this.timeout(400); settings.requireAuthentication = true; socket = await connect(null); const message = await handshake(socket, 'pad'); assert.equal(message.accessStatus, 'deny'); }); it('authorization bypass attempt -> error', async function () { + this.timeout(400); // Only allowed to access /p/pad. authorize = (req) => req.path === '/p/pad'; settings.requireAuthentication = true; @@ -218,6 +228,7 @@ describe(__filename, function () { }); it("level='create' -> can create", async function () { + this.timeout(400); authorize = () => 'create'; const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); socket = await connect(res); @@ -226,6 +237,7 @@ describe(__filename, function () { assert.equal(clientVars.data.readonly, false); }); it('level=true -> can create', async function () { + this.timeout(400); authorize = () => true; const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); socket = await connect(res); @@ -234,6 +246,7 @@ describe(__filename, function () { assert.equal(clientVars.data.readonly, false); }); it("level='modify' -> can modify", async function () { + this.timeout(400); await padManager.getPad('pad'); // Create the pad. authorize = () => 'modify'; const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); @@ -243,6 +256,7 @@ describe(__filename, function () { assert.equal(clientVars.data.readonly, false); }); it("level='create' settings.editOnly=true -> unable to create", async function () { + this.timeout(400); authorize = () => 'create'; settings.editOnly = true; const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); @@ -251,6 +265,7 @@ describe(__filename, function () { assert.equal(message.accessStatus, 'deny'); }); it("level='modify' settings.editOnly=false -> unable to create", async function () { + this.timeout(400); authorize = () => 'modify'; settings.editOnly = false; const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); @@ -259,6 +274,7 @@ describe(__filename, function () { assert.equal(message.accessStatus, 'deny'); }); it("level='readOnly' -> unable to create", async function () { + this.timeout(400); authorize = () => 'readOnly'; const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); socket = await connect(res); @@ -266,6 +282,7 @@ describe(__filename, function () { assert.equal(message.accessStatus, 'deny'); }); it("level='readOnly' -> unable to modify", async function () { + this.timeout(400); await padManager.getPad('pad'); // Create the pad. authorize = () => 'readOnly'; const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); @@ -282,6 +299,7 @@ describe(__filename, function () { }); it('user.canCreate = true -> can create and modify', async function () { + this.timeout(400); settings.users.user.canCreate = true; const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); socket = await connect(res); @@ -290,6 +308,7 @@ describe(__filename, function () { assert.equal(clientVars.data.readonly, false); }); it('user.canCreate = false -> unable to create', async function () { + this.timeout(400); settings.users.user.canCreate = false; const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); socket = await connect(res); @@ -297,6 +316,7 @@ describe(__filename, function () { assert.equal(message.accessStatus, 'deny'); }); it('user.readOnly = true -> unable to create', async function () { + this.timeout(400); settings.users.user.readOnly = true; const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); socket = await connect(res); @@ -304,6 +324,7 @@ describe(__filename, function () { assert.equal(message.accessStatus, 'deny'); }); it('user.readOnly = true -> unable to modify', async function () { + this.timeout(400); await padManager.getPad('pad'); // Create the pad. settings.users.user.readOnly = true; const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); @@ -313,6 +334,7 @@ describe(__filename, function () { assert.equal(clientVars.data.readonly, true); }); it('user.readOnly = false -> can create and modify', async function () { + this.timeout(400); settings.users.user.readOnly = false; const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); socket = await connect(res); @@ -321,6 +343,7 @@ describe(__filename, function () { assert.equal(clientVars.data.readonly, false); }); it('user.readOnly = true, user.canCreate = true -> unable to create', async function () { + this.timeout(400); settings.users.user.canCreate = true; settings.users.user.readOnly = true; const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); @@ -337,6 +360,7 @@ describe(__filename, function () { }); it('authorize hook does not elevate level from user settings', async function () { + this.timeout(400); settings.users.user.readOnly = true; authorize = () => 'create'; const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); @@ -345,6 +369,7 @@ describe(__filename, function () { assert.equal(message.accessStatus, 'deny'); }); it('user settings does not elevate level from authorize hook', async function () { + this.timeout(400); settings.users.user.readOnly = false; settings.users.user.canCreate = true; authorize = () => 'readOnly'; diff --git a/src/tests/backend/specs/specialpages.js b/src/tests/backend/specs/specialpages.js index 571ca6d35..8b372c959 100644 --- a/src/tests/backend/specs/specialpages.js +++ b/src/tests/backend/specs/specialpages.js @@ -2,6 +2,7 @@ const common = require('../common'); const settings = require('../../../node/utils/Settings'); describe(__filename, function () { + this.timeout(30000); let agent; const backups = {}; before(async function () { agent = await common.init(); }); @@ -19,6 +20,7 @@ describe(__filename, function () { describe('/javascript', function () { it('/javascript -> 200', async function () { + this.timeout(200); await agent.get('/javascript').expect(200); }); }); diff --git a/src/tests/backend/specs/webaccess.js b/src/tests/backend/specs/webaccess.js index 8079d6a22..fe8c4c5c9 100644 --- a/src/tests/backend/specs/webaccess.js +++ b/src/tests/backend/specs/webaccess.js @@ -6,6 +6,7 @@ const plugins = require('../../../static/js/pluginfw/plugin_defs'); const settings = require('../../../node/utils/Settings'); describe(__filename, function () { + this.timeout(30000); let agent; const backups = {}; const authHookNames = ['preAuthorize', 'authenticate', 'authorize']; @@ -42,56 +43,67 @@ describe(__filename, function () { describe('webaccess: without plugins', function () { it('!authn !authz anonymous / -> 200', async function () { + this.timeout(150); settings.requireAuthentication = false; settings.requireAuthorization = false; await agent.get('/').expect(200); }); it('!authn !authz anonymous /admin/ -> 401', async function () { + this.timeout(100); settings.requireAuthentication = false; settings.requireAuthorization = false; await agent.get('/admin/').expect(401); }); it('authn !authz anonymous / -> 401', async function () { + this.timeout(100); settings.requireAuthentication = true; settings.requireAuthorization = false; await agent.get('/').expect(401); }); it('authn !authz user / -> 200', async function () { + this.timeout(100); settings.requireAuthentication = true; settings.requireAuthorization = false; await agent.get('/').auth('user', 'user-password').expect(200); }); it('authn !authz user /admin/ -> 403', async function () { + this.timeout(100); settings.requireAuthentication = true; settings.requireAuthorization = false; await agent.get('/admin/').auth('user', 'user-password').expect(403); }); it('authn !authz admin / -> 200', async function () { + this.timeout(100); settings.requireAuthentication = true; settings.requireAuthorization = false; await agent.get('/').auth('admin', 'admin-password').expect(200); }); it('authn !authz admin /admin/ -> 200', async function () { + this.timeout(100); settings.requireAuthentication = true; settings.requireAuthorization = false; await agent.get('/admin/').auth('admin', 'admin-password').expect(200); }); it('authn authz user / -> 403', async function () { + this.timeout(100); settings.requireAuthentication = true; settings.requireAuthorization = true; await agent.get('/').auth('user', 'user-password').expect(403); }); it('authn authz user /admin/ -> 403', async function () { + this.timeout(100); settings.requireAuthentication = true; settings.requireAuthorization = true; await agent.get('/admin/').auth('user', 'user-password').expect(403); }); it('authn authz admin / -> 200', async function () { + this.timeout(100); settings.requireAuthentication = true; settings.requireAuthorization = true; await agent.get('/').auth('admin', 'admin-password').expect(200); }); it('authn authz admin /admin/ -> 200', async function () { + this.timeout(100); settings.requireAuthentication = true; settings.requireAuthorization = true; await agent.get('/admin/').auth('admin', 'admin-password').expect(200); @@ -105,6 +117,7 @@ describe(__filename, function () { // parsing, resulting in successful comparisons against a null or undefined password. for (const creds of ['admin', 'admin:']) { it(`admin password: ${adminPassword} credentials: ${creds}`, async function () { + this.timeout(100); settings.users.admin.password = adminPassword; const encCreds = Buffer.from(creds).toString('base64'); await agent.get('/admin/').set('Authorization', `Basic ${encCreds}`).expect(401); @@ -160,11 +173,13 @@ describe(__filename, function () { }); it('defers if it returns []', async function () { + this.timeout(100); await agent.get('/').expect(200); // Note: The preAuthorize hook always runs even if requireAuthorization is false. assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1']); }); it('bypasses authenticate and authorize hooks when true is returned', async function () { + this.timeout(100); settings.requireAuthentication = true; settings.requireAuthorization = true; handlers.preAuthorize[0].innerHandle = () => [true]; @@ -172,6 +187,7 @@ describe(__filename, function () { assert.deepEqual(callOrder, ['preAuthorize_0']); }); it('bypasses authenticate and authorize hooks when false is returned', async function () { + this.timeout(100); settings.requireAuthentication = true; settings.requireAuthorization = true; handlers.preAuthorize[0].innerHandle = () => [false]; @@ -179,12 +195,14 @@ describe(__filename, function () { assert.deepEqual(callOrder, ['preAuthorize_0']); }); it('bypasses authenticate and authorize hooks for static content, defers', async function () { + this.timeout(100); settings.requireAuthentication = true; settings.requireAuthorization = true; await agent.get('/static/robots.txt').expect(200); assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1']); }); it('cannot grant access to /admin', async function () { + this.timeout(100); handlers.preAuthorize[0].innerHandle = () => [true]; await agent.get('/admin/').expect(401); // Notes: @@ -198,11 +216,13 @@ describe(__filename, function () { 'authenticate_1']); }); it('can deny access to /admin', async function () { + this.timeout(100); handlers.preAuthorize[0].innerHandle = () => [false]; await agent.get('/admin/').auth('admin', 'admin-password').expect(403); assert.deepEqual(callOrder, ['preAuthorize_0']); }); it('runs preAuthzFailure hook when access is denied', async function () { + this.timeout(100); handlers.preAuthorize[0].innerHandle = () => [false]; let called = false; plugins.hooks.preAuthzFailure = [makeHook('preAuthzFailure', (hookName, {req, res}, cb) => { @@ -218,6 +238,7 @@ describe(__filename, function () { assert(called); }); it('returns 500 if an exception is thrown', async function () { + this.timeout(100); handlers.preAuthorize[0].innerHandle = () => { throw new Error('exception test'); }; await agent.get('/').expect(500); }); @@ -230,11 +251,13 @@ describe(__filename, function () { }); it('is not called if !requireAuthentication and not /admin/*', async function () { + this.timeout(100); settings.requireAuthentication = false; await agent.get('/').expect(200); assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1']); }); it('is called if !requireAuthentication and /admin/*', async function () { + this.timeout(100); settings.requireAuthentication = false; await agent.get('/admin/').expect(401); assert.deepEqual(callOrder, ['preAuthorize_0', @@ -243,6 +266,7 @@ describe(__filename, function () { 'authenticate_1']); }); it('defers if empty list returned', async function () { + this.timeout(100); await agent.get('/').expect(401); assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', @@ -250,18 +274,21 @@ describe(__filename, function () { 'authenticate_1']); }); it('does not defer if return [true], 200', async function () { + this.timeout(100); handlers.authenticate[0].innerHandle = (req) => { req.session.user = {}; return [true]; }; await agent.get('/').expect(200); // Note: authenticate_1 was not called because authenticate_0 handled it. assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', 'authenticate_0']); }); it('does not defer if return [false], 401', async function () { + this.timeout(100); handlers.authenticate[0].innerHandle = (req) => [false]; await agent.get('/').expect(401); // Note: authenticate_1 was not called because authenticate_0 handled it. assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', 'authenticate_0']); }); it('falls back to HTTP basic auth', async function () { + this.timeout(100); await agent.get('/').auth('user', 'user-password').expect(200); assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', @@ -269,6 +296,7 @@ describe(__filename, function () { 'authenticate_1']); }); it('passes settings.users in context', async function () { + this.timeout(100); handlers.authenticate[0].checkContext = ({users}) => { assert.equal(users, settings.users); }; @@ -279,6 +307,7 @@ describe(__filename, function () { 'authenticate_1']); }); it('passes user, password in context if provided', async function () { + this.timeout(100); handlers.authenticate[0].checkContext = ({username, password}) => { assert.equal(username, 'user'); assert.equal(password, 'user-password'); @@ -290,6 +319,7 @@ describe(__filename, function () { 'authenticate_1']); }); it('does not pass user, password in context if not provided', async function () { + this.timeout(100); handlers.authenticate[0].checkContext = ({username, password}) => { assert(username == null); assert(password == null); @@ -301,11 +331,13 @@ describe(__filename, function () { 'authenticate_1']); }); it('errors if req.session.user is not created', async function () { + this.timeout(100); handlers.authenticate[0].innerHandle = () => [true]; await agent.get('/').expect(500); assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', 'authenticate_0']); }); it('returns 500 if an exception is thrown', async function () { + this.timeout(100); handlers.authenticate[0].innerHandle = () => { throw new Error('exception test'); }; await agent.get('/').expect(500); assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', 'authenticate_0']); @@ -319,6 +351,7 @@ describe(__filename, function () { }); it('is not called if !requireAuthorization (non-/admin)', async function () { + this.timeout(100); settings.requireAuthorization = false; await agent.get('/').auth('user', 'user-password').expect(200); assert.deepEqual(callOrder, ['preAuthorize_0', @@ -327,6 +360,7 @@ describe(__filename, function () { 'authenticate_1']); }); it('is not called if !requireAuthorization (/admin)', async function () { + this.timeout(100); settings.requireAuthorization = false; await agent.get('/admin/').auth('admin', 'admin-password').expect(200); assert.deepEqual(callOrder, ['preAuthorize_0', @@ -335,6 +369,7 @@ describe(__filename, function () { 'authenticate_1']); }); it('defers if empty list returned', async function () { + this.timeout(100); await agent.get('/').auth('user', 'user-password').expect(403); assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', @@ -344,6 +379,7 @@ describe(__filename, function () { 'authorize_1']); }); it('does not defer if return [true], 200', async function () { + this.timeout(100); handlers.authorize[0].innerHandle = () => [true]; await agent.get('/').auth('user', 'user-password').expect(200); // Note: authorize_1 was not called because authorize_0 handled it. @@ -354,6 +390,7 @@ describe(__filename, function () { 'authorize_0']); }); it('does not defer if return [false], 403', async function () { + this.timeout(100); handlers.authorize[0].innerHandle = (req) => [false]; await agent.get('/').auth('user', 'user-password').expect(403); // Note: authorize_1 was not called because authorize_0 handled it. @@ -364,6 +401,7 @@ describe(__filename, function () { 'authorize_0']); }); it('passes req.path in context', async function () { + this.timeout(100); handlers.authorize[0].checkContext = ({resource}) => { assert.equal(resource, '/'); }; @@ -376,6 +414,7 @@ describe(__filename, function () { 'authorize_1']); }); it('returns 500 if an exception is thrown', async function () { + this.timeout(100); handlers.authorize[0].innerHandle = () => { throw new Error('exception test'); }; await agent.get('/').auth('user', 'user-password').expect(500); assert.deepEqual(callOrder, ['preAuthorize_0', @@ -422,12 +461,14 @@ describe(__filename, function () { // authn failure tests it('authn fail, no hooks handle -> 401', async function () { + this.timeout(100); await agent.get('/').expect(401); assert(handlers.authnFailure.called); assert(!handlers.authzFailure.called); assert(handlers.authFailure.called); }); it('authn fail, authnFailure handles', async function () { + this.timeout(100); handlers.authnFailure.shouldHandle = true; await agent.get('/').expect(200, 'authnFailure'); assert(handlers.authnFailure.called); @@ -435,6 +476,7 @@ describe(__filename, function () { assert(!handlers.authFailure.called); }); it('authn fail, authFailure handles', async function () { + this.timeout(100); handlers.authFailure.shouldHandle = true; await agent.get('/').expect(200, 'authFailure'); assert(handlers.authnFailure.called); @@ -442,6 +484,7 @@ describe(__filename, function () { assert(handlers.authFailure.called); }); it('authnFailure trumps authFailure', async function () { + this.timeout(100); handlers.authnFailure.shouldHandle = true; handlers.authFailure.shouldHandle = true; await agent.get('/').expect(200, 'authnFailure'); @@ -451,12 +494,14 @@ describe(__filename, function () { // authz failure tests it('authz fail, no hooks handle -> 403', async function () { + this.timeout(100); await agent.get('/').auth('user', 'user-password').expect(403); assert(!handlers.authnFailure.called); assert(handlers.authzFailure.called); assert(handlers.authFailure.called); }); it('authz fail, authzFailure handles', async function () { + this.timeout(100); handlers.authzFailure.shouldHandle = true; await agent.get('/').auth('user', 'user-password').expect(200, 'authzFailure'); assert(!handlers.authnFailure.called); @@ -464,6 +509,7 @@ describe(__filename, function () { assert(!handlers.authFailure.called); }); it('authz fail, authFailure handles', async function () { + this.timeout(100); handlers.authFailure.shouldHandle = true; await agent.get('/').auth('user', 'user-password').expect(200, 'authFailure'); assert(!handlers.authnFailure.called); @@ -471,6 +517,7 @@ describe(__filename, function () { assert(handlers.authFailure.called); }); it('authzFailure trumps authFailure', async function () { + this.timeout(100); handlers.authzFailure.shouldHandle = true; handlers.authFailure.shouldHandle = true; await agent.get('/').auth('user', 'user-password').expect(200, 'authzFailure'); diff --git a/src/tests/frontend/specs/alphabet.js b/src/tests/frontend/specs/alphabet.js index cc50e7d87..158bc734c 100644 --- a/src/tests/frontend/specs/alphabet.js +++ b/src/tests/frontend/specs/alphabet.js @@ -10,6 +10,7 @@ describe('All the alphabet works n stuff', function () { }); it('when you enter any char it appears right', function (done) { + this.timeout(250); const inner$ = helper.padInner$; // get the first text element out of the inner iframe diff --git a/src/tests/frontend/specs/bold.js b/src/tests/frontend/specs/bold.js index 613de4699..61d8133ad 100644 --- a/src/tests/frontend/specs/bold.js +++ b/src/tests/frontend/specs/bold.js @@ -8,6 +8,7 @@ describe('bold button', function () { }); it('makes text bold on click', function (done) { + this.timeout(100); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; @@ -36,6 +37,7 @@ describe('bold button', function () { }); it('makes text bold on keypress', function (done) { + this.timeout(100); const inner$ = helper.padInner$; // get the first text element out of the inner iframe diff --git a/src/tests/frontend/specs/change_user_color.js b/src/tests/frontend/specs/change_user_color.js index c8a3bf5b9..1f41dcce2 100644 --- a/src/tests/frontend/specs/change_user_color.js +++ b/src/tests/frontend/specs/change_user_color.js @@ -9,7 +9,7 @@ describe('change user color', function () { it('Color picker matches original color and remembers the user color' + ' after a refresh', function (done) { - this.timeout(60000); + this.timeout(10000); const chrome$ = helper.padChrome$; // click on the settings button to make settings visible @@ -63,6 +63,7 @@ describe('change user color', function () { }); it('Own user color is shown when you enter a chat', function (done) { + this.timeout(1000); const chrome$ = helper.padChrome$; const $colorOption = helper.padChrome$('#options-colorscheck'); diff --git a/src/tests/frontend/specs/change_user_name.js b/src/tests/frontend/specs/change_user_name.js index 3c4b8b5bc..8ba5e637a 100644 --- a/src/tests/frontend/specs/change_user_name.js +++ b/src/tests/frontend/specs/change_user_name.js @@ -7,6 +7,7 @@ describe('change username value', function () { }); it('Remembers the user name after a refresh', async function () { + this.timeout(1500); helper.toggleUserList(); helper.setUserName('😃'); @@ -21,6 +22,7 @@ describe('change username value', function () { }); it('Own user name is shown when you enter a chat', async function () { + this.timeout(1500); helper.toggleUserList(); helper.setUserName('😃'); diff --git a/src/tests/frontend/specs/chat.js b/src/tests/frontend/specs/chat.js index fbc6ce788..be080755a 100644 --- a/src/tests/frontend/specs/chat.js +++ b/src/tests/frontend/specs/chat.js @@ -8,6 +8,7 @@ describe('Chat messages and UI', function () { it('opens chat, sends a message, makes sure it exists ' + 'on the page and hides chat', async function () { + this.timeout(3000); const chatValue = 'JohnMcLear'; await helper.showChat(); @@ -50,6 +51,7 @@ describe('Chat messages and UI', function () { it('makes chat stick to right side of the screen via settings, ' + 'remove sticky via settings, close it', async function () { + this.timeout(5000); await helper.showSettings(); await helper.enableStickyChatviaSettings(); @@ -67,6 +69,7 @@ describe('Chat messages and UI', function () { it('makes chat stick to right side of the screen via icon on the top' + ' right, remove sticky via icon, close it', async function () { + this.timeout(5000); await helper.showChat(); await helper.enableStickyChatviaIcon(); @@ -111,6 +114,6 @@ describe('Chat messages and UI', function () { }, 1000); }, }); - }, 1000); + }, 3000); }); }); diff --git a/src/tests/frontend/specs/chat_load_messages.js b/src/tests/frontend/specs/chat_load_messages.js index dcab043e9..6b34e614b 100644 --- a/src/tests/frontend/specs/chat_load_messages.js +++ b/src/tests/frontend/specs/chat_load_messages.js @@ -30,6 +30,7 @@ describe('chat-load-messages', function () { }); it('checks initial message count', function (done) { + this.timeout(1000); let chatText; const expectedCount = 101; const chrome$ = helper.padChrome$; @@ -45,6 +46,7 @@ describe('chat-load-messages', function () { }); it('loads more messages', function (done) { + this.timeout(3000); const expectedCount = 122; const chrome$ = helper.padChrome$; const chatButton = chrome$('#chaticon'); @@ -60,6 +62,7 @@ describe('chat-load-messages', function () { }); it('checks for button vanishing', function (done) { + this.timeout(2000); const expectedDisplay = 'none'; const chrome$ = helper.padChrome$; const chatButton = chrome$('#chaticon'); diff --git a/src/tests/frontend/specs/clear_authorship_colors.js b/src/tests/frontend/specs/clear_authorship_colors.js index 63d4c2f54..ae5603949 100644 --- a/src/tests/frontend/specs/clear_authorship_colors.js +++ b/src/tests/frontend/specs/clear_authorship_colors.js @@ -8,6 +8,7 @@ describe('clear authorship colors button', function () { }); it('makes text clear authorship colors', function (done) { + this.timeout(2500); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; @@ -55,6 +56,7 @@ describe('clear authorship colors button', function () { }); it("makes text clear authorship colors and checks it can't be undone", function (done) { + this.timeout(1500); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; diff --git a/src/tests/frontend/specs/delete.js b/src/tests/frontend/specs/delete.js index 6cde43f47..1ffbbd51c 100644 --- a/src/tests/frontend/specs/delete.js +++ b/src/tests/frontend/specs/delete.js @@ -8,6 +8,7 @@ describe('delete keystroke', function () { }); it('makes text delete', function (done) { + this.timeout(50); const inner$ = helper.padInner$; // get the first text element out of the inner iframe diff --git a/src/tests/frontend/specs/drag_and_drop.js b/src/tests/frontend/specs/drag_and_drop.js index 71141e055..8937b375e 100644 --- a/src/tests/frontend/specs/drag_and_drop.js +++ b/src/tests/frontend/specs/drag_and_drop.js @@ -31,6 +31,7 @@ describe('drag and drop', function () { }); it('moves text back to its original place', function (done) { + this.timeout(50); // test text was removed from drop target const $targetLine = getLine(TARGET_LINE); expect($targetLine.text()).to.be('Target line []'); @@ -68,6 +69,7 @@ describe('drag and drop', function () { }); it('moves text back to its original place', function (done) { + this.timeout(50); // test text was removed from drop target const $targetLine = getLine(TARGET_LINE); expect($targetLine.text()).to.be('Target line []'); diff --git a/src/tests/frontend/specs/embed_value.js b/src/tests/frontend/specs/embed_value.js index dac4c869d..ee1bf8966 100644 --- a/src/tests/frontend/specs/embed_value.js +++ b/src/tests/frontend/specs/embed_value.js @@ -57,6 +57,7 @@ describe('embed links', function () { describe('the share link', function () { it('is the actual pad url', function (done) { + this.timeout(50); const chrome$ = helper.padChrome$; // open share dropdown @@ -73,6 +74,7 @@ describe('embed links', function () { describe('the embed as iframe code', function () { it('is an iframe with the the correct url parameters and correct size', function (done) { + this.timeout(50); const chrome$ = helper.padChrome$; // open share dropdown @@ -96,6 +98,7 @@ describe('embed links', function () { describe('the share link', function () { it('shows a read only url', function (done) { + this.timeout(50); const chrome$ = helper.padChrome$; // open share dropdown @@ -114,6 +117,7 @@ describe('embed links', function () { describe('the embed as iframe code', function () { it('is an iframe with the the correct url parameters and correct size', function (done) { + this.timeout(50); const chrome$ = helper.padChrome$; // open share dropdown diff --git a/src/tests/frontend/specs/enter.js b/src/tests/frontend/specs/enter.js index 85ab43a2e..69cd9d48a 100644 --- a/src/tests/frontend/specs/enter.js +++ b/src/tests/frontend/specs/enter.js @@ -7,6 +7,7 @@ describe('enter keystroke', function () { this.timeout(60000); }); it('creates a new line & puts cursor onto a new line', function (done) { + this.timeout(2000); const inner$ = helper.padInner$; // get the first text element out of the inner iframe diff --git a/src/tests/frontend/specs/font_type.js b/src/tests/frontend/specs/font_type.js index 68df2f5e7..9790873b3 100644 --- a/src/tests/frontend/specs/font_type.js +++ b/src/tests/frontend/specs/font_type.js @@ -8,6 +8,7 @@ describe('font select', function () { }); it('makes text RobotoMono', function (done) { + this.timeout(100); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; diff --git a/src/tests/frontend/specs/indentation.js b/src/tests/frontend/specs/indentation.js index 006c52bb4..377b1af74 100644 --- a/src/tests/frontend/specs/indentation.js +++ b/src/tests/frontend/specs/indentation.js @@ -8,6 +8,7 @@ describe('indentation button', function () { }); it('indent text with keypress', function (done) { + this.timeout(100); const inner$ = helper.padInner$; // get the first text element out of the inner iframe @@ -24,6 +25,7 @@ describe('indentation button', function () { }); it('indent text with button', function (done) { + this.timeout(100); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; @@ -34,6 +36,7 @@ describe('indentation button', function () { }); it('keeps the indent on enter for the new line', function (done) { + this.timeout(1200); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; @@ -59,6 +62,7 @@ describe('indentation button', function () { it('indents text with spaces on enter if previous line ends ' + "with ':', '[', '(', or '{'", function (done) { + this.timeout(1200); const inner$ = helper.padInner$; // type a bit, make a line break and type again @@ -110,6 +114,7 @@ describe('indentation button', function () { it('appends indentation to the indent of previous line if previous line ends ' + "with ':', '[', '(', or '{'", function (done) { + this.timeout(1200); const inner$ = helper.padInner$; // type a bit, make a line break and type again @@ -135,6 +140,7 @@ describe('indentation button', function () { it("issue #2772 shows '*' when multiple indented lines " + ' receive a style and are outdented', async function () { + this.timeout(1200); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; diff --git a/src/tests/frontend/specs/italic.js b/src/tests/frontend/specs/italic.js index cbaf9e3da..e7d551c8e 100644 --- a/src/tests/frontend/specs/italic.js +++ b/src/tests/frontend/specs/italic.js @@ -8,6 +8,7 @@ describe('italic some text', function () { }); it('makes text italic using button', function (done) { + this.timeout(100); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; @@ -37,6 +38,7 @@ describe('italic some text', function () { }); it('makes text italic using keypress', function (done) { + this.timeout(100); const inner$ = helper.padInner$; // get the first text element out of the inner iframe diff --git a/src/tests/frontend/specs/language.js b/src/tests/frontend/specs/language.js index 072c64e92..e5240aaab 100644 --- a/src/tests/frontend/specs/language.js +++ b/src/tests/frontend/specs/language.js @@ -16,6 +16,7 @@ describe('Language select and change', function () { // Destroy language cookies it('makes text german', function (done) { + this.timeout(1000); const chrome$ = helper.padChrome$; // click on the settings button to make settings visible @@ -45,6 +46,7 @@ describe('Language select and change', function () { }); it('makes text English', function (done) { + this.timeout(1000); const chrome$ = helper.padChrome$; // click on the settings button to make settings visible @@ -75,6 +77,7 @@ describe('Language select and change', function () { }); it('changes direction when picking an rtl lang', function (done) { + this.timeout(1000); const chrome$ = helper.padChrome$; // click on the settings button to make settings visible diff --git a/src/tests/frontend/specs/multiple_authors_clear_authorship_colors.js b/src/tests/frontend/specs/multiple_authors_clear_authorship_colors.js index 19dfb44f9..818dd161f 100755 --- a/src/tests/frontend/specs/multiple_authors_clear_authorship_colors.js +++ b/src/tests/frontend/specs/multiple_authors_clear_authorship_colors.js @@ -27,6 +27,7 @@ describe('author of pad edition', function () { // author 2 makes some changes on the pad it('Clears Authorship by second user', function (done) { + this.timeout(100); clearAuthorship(done); }); diff --git a/src/tests/frontend/specs/ordered_list.js b/src/tests/frontend/specs/ordered_list.js index 9fe647ebe..a7403d6bc 100644 --- a/src/tests/frontend/specs/ordered_list.js +++ b/src/tests/frontend/specs/ordered_list.js @@ -9,6 +9,7 @@ describe('ordered_list.js', function () { }); it('inserts ordered list text', function (done) { + this.timeout(200); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; @@ -28,6 +29,7 @@ describe('ordered_list.js', function () { }); it('inserts unordered list', function (done) { + this.timeout(50); helper.waitFor(() => helper.padInner$('div').first().find('ol li').length === 1) .done(done); }); @@ -52,6 +54,7 @@ describe('ordered_list.js', function () { }); it('does not insert unordered list', function (done) { + this.timeout(3000); helper.waitFor(() => helper.padInner$('div').first().find('ol li').length === 1) .done(() => { expect().fail(() => 'Unordered list inserted, should ignore shortcut'); @@ -73,6 +76,7 @@ describe('ordered_list.js', function () { }); it('inserts unordered list', function (done) { + this.timeout(200); helper.waitFor(() => helper.padInner$('div').first().find('ol li').length === 1) .done(done); }); @@ -97,6 +101,7 @@ describe('ordered_list.js', function () { }); it('does not insert unordered list', function (done) { + this.timeout(3000); helper.waitFor(() => helper.padInner$('div').first().find('ol li').length === 1) .done(() => { expect().fail(() => 'Unordered list inserted, should ignore shortcut'); @@ -175,6 +180,7 @@ describe('ordered_list.js', function () { }); it('indent and de-indent list item with keypress', function (done) { + this.timeout(200); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; @@ -210,6 +216,7 @@ describe('ordered_list.js', function () { }); it('indent and de-indent list item with indent button', function (done) { + this.timeout(1000); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; diff --git a/src/tests/frontend/specs/pad_modal.js b/src/tests/frontend/specs/pad_modal.js index 03a97b82c..5c3b9d5c4 100644 --- a/src/tests/frontend/specs/pad_modal.js +++ b/src/tests/frontend/specs/pad_modal.js @@ -18,6 +18,7 @@ describe('Pad modal', function () { }); it('disables editor', function (done) { + this.timeout(200); expect(isEditorDisabled()).to.be(true); done(); @@ -25,6 +26,7 @@ describe('Pad modal', function () { context('and user clicks on editor', function () { it('does not close the modal', function (done) { + this.timeout(200); clickOnPadInner(); const $modal = helper.padChrome$(MODAL_SELECTOR); const modalIsVisible = $modal.hasClass('popup-show'); @@ -37,6 +39,7 @@ describe('Pad modal', function () { context('and user clicks on pad outer', function () { it('does not close the modal', function (done) { + this.timeout(200); clickOnPadOuter(); const $modal = helper.padChrome$(MODAL_SELECTOR); const modalIsVisible = $modal.hasClass('popup-show'); @@ -68,6 +71,7 @@ describe('Pad modal', function () { */ context('and user clicks on editor', function () { it('closes the modal', async function () { + this.timeout(200); clickOnPadInner(); await helper.waitForPromise(() => isModalOpened(MODAL_SELECTOR) === false); }); @@ -75,6 +79,7 @@ describe('Pad modal', function () { context('and user clicks on pad outer', function () { it('closes the modal', async function () { + this.timeout(200); clickOnPadOuter(); await helper.waitForPromise(() => isModalOpened(MODAL_SELECTOR) === false); }); diff --git a/src/tests/frontend/specs/redo.js b/src/tests/frontend/specs/redo.js index b0334ba82..67d49c659 100644 --- a/src/tests/frontend/specs/redo.js +++ b/src/tests/frontend/specs/redo.js @@ -7,6 +7,7 @@ describe('undo button then redo button', function () { }); it('redo some typing with button', function (done) { + this.timeout(200); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; @@ -34,6 +35,7 @@ describe('undo button then redo button', function () { }); it('redo some typing with keypress', function (done) { + this.timeout(200); const inner$ = helper.padInner$; // get the first text element inside the editable space diff --git a/src/tests/frontend/specs/scrollTo.js b/src/tests/frontend/specs/scrollTo.js index 0e89ab967..20b24a56e 100755 --- a/src/tests/frontend/specs/scrollTo.js +++ b/src/tests/frontend/specs/scrollTo.js @@ -12,7 +12,7 @@ describe('scrollTo.js', function () { }); it('Scrolls down to Line 4', async function () { - this.timeout(10000); + this.timeout(100); const chrome$ = helper.padChrome$; await helper.waitForPromise(() => { const topOffset = parseInt(chrome$('iframe').first('iframe') @@ -33,7 +33,7 @@ describe('scrollTo.js', function () { }); it('Does NOT change scroll', async function () { - this.timeout(10000); + this.timeout(100); const chrome$ = helper.padChrome$; await helper.waitForPromise(() => { const topOffset = parseInt(chrome$('iframe').first('iframe') diff --git a/src/tests/frontend/specs/select_formatting_buttons.js b/src/tests/frontend/specs/select_formatting_buttons.js index 1e165e565..63be6f8e2 100644 --- a/src/tests/frontend/specs/select_formatting_buttons.js +++ b/src/tests/frontend/specs/select_formatting_buttons.js @@ -44,12 +44,14 @@ describe('select formatting buttons when selection has style applied', function const testIfFormattingButtonIsDeselected = function (style) { it(`deselects the ${style} button`, function (done) { + this.timeout(50); helper.waitFor(() => isButtonSelected(style) === false).done(done); }); }; const testIfFormattingButtonIsSelected = function (style) { it(`selects the ${style} button`, function (done) { + this.timeout(50); helper.waitFor(() => isButtonSelected(style)).done(done); }); }; @@ -129,6 +131,7 @@ describe('select formatting buttons when selection has style applied', function context('when user applies a style and the selection does not change', function () { it('selects the style button', async function () { + this.timeout(50); const style = STYLES[0]; // italic applyStyleOnLine(style, FIRST_LINE); await helper.waitForPromise(() => isButtonSelected(style) === true); diff --git a/src/tests/frontend/specs/strikethrough.js b/src/tests/frontend/specs/strikethrough.js index 9731ec75c..9d5461226 100644 --- a/src/tests/frontend/specs/strikethrough.js +++ b/src/tests/frontend/specs/strikethrough.js @@ -8,6 +8,7 @@ describe('strikethrough button', function () { }); it('makes text strikethrough', function (done) { + this.timeout(100); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; diff --git a/src/tests/frontend/specs/timeslider_follow.js b/src/tests/frontend/specs/timeslider_follow.js index 9f86ddc77..eaa3cd7e7 100644 --- a/src/tests/frontend/specs/timeslider_follow.js +++ b/src/tests/frontend/specs/timeslider_follow.js @@ -9,6 +9,7 @@ describe('timeslider follow', function () { // TODO needs test if content is also followed, when user a makes edits // while user b is in the timeslider it("content as it's added to timeslider", async function () { + this.timeout(10000); // send 6 revisions const revs = 6; const message = 'a\n\n\n\n\n\n\n\n\n\n'; diff --git a/src/tests/frontend/specs/timeslider_labels.js b/src/tests/frontend/specs/timeslider_labels.js index dd418d976..08a4eb33a 100644 --- a/src/tests/frontend/specs/timeslider_labels.js +++ b/src/tests/frontend/specs/timeslider_labels.js @@ -10,6 +10,7 @@ describe('timeslider', function () { * @todo test authorsList */ it("Shows a date/time in the timeslider and make sure it doesn't include NaN", async function () { + this.timeout(12000); // make some changes to produce 3 revisions const revs = 3; diff --git a/src/tests/frontend/specs/timeslider_numeric_padID.js b/src/tests/frontend/specs/timeslider_numeric_padID.js index 4d05f95b3..019ad5307 100644 --- a/src/tests/frontend/specs/timeslider_numeric_padID.js +++ b/src/tests/frontend/specs/timeslider_numeric_padID.js @@ -9,6 +9,7 @@ describe('timeslider', function () { }); it('Makes sure the export URIs are as expected when the padID is numeric', async function () { + this.timeout(60000); await helper.edit('a\n'); await helper.gotoTimeslider(1); diff --git a/src/tests/frontend/specs/timeslider_revisions.js b/src/tests/frontend/specs/timeslider_revisions.js index 2f5cc170a..1bae6b254 100644 --- a/src/tests/frontend/specs/timeslider_revisions.js +++ b/src/tests/frontend/specs/timeslider_revisions.js @@ -8,6 +8,7 @@ describe('timeslider', function () { }); it('loads adds a hundred revisions', function (done) { // passes + this.timeout(100000); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; diff --git a/src/tests/frontend/specs/undo.js b/src/tests/frontend/specs/undo.js index 0daa282fc..039ba6903 100644 --- a/src/tests/frontend/specs/undo.js +++ b/src/tests/frontend/specs/undo.js @@ -7,6 +7,8 @@ describe('undo button', function () { }); it('undo some typing by clicking undo button', function (done) { + this.timeout(100); + this.timeout(150); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; @@ -31,6 +33,7 @@ describe('undo button', function () { }); it('undo some typing using a keypress', function (done) { + this.timeout(150); const inner$ = helper.padInner$; // get the first text element inside the editable space diff --git a/src/tests/frontend/specs/unordered_list.js b/src/tests/frontend/specs/unordered_list.js index 37173e593..f82b620c9 100644 --- a/src/tests/frontend/specs/unordered_list.js +++ b/src/tests/frontend/specs/unordered_list.js @@ -9,6 +9,7 @@ describe('unordered_list.js', function () { }); it('insert unordered list text then removes by outdent', function (done) { + this.timeout(150); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; const originalText = inner$('div').first().text(); @@ -42,6 +43,7 @@ describe('unordered_list.js', function () { }); it('insert unordered list text then remove by clicking list again', function (done) { + this.timeout(150); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; const originalText = inner$('div').first().text(); @@ -77,6 +79,7 @@ describe('unordered_list.js', function () { }); it('Keeps the unordered list on enter for the new line', function (done) { + this.timeout(250); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; @@ -108,6 +111,7 @@ describe('unordered_list.js', function () { }); it('indent and de-indent list item with keypress', function (done) { + this.timeout(150); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; @@ -142,6 +146,7 @@ describe('unordered_list.js', function () { }); it('indent and de-indent list item with indent button', function (done) { + this.timeout(150); const inner$ = helper.padInner$; const chrome$ = helper.padChrome$; diff --git a/src/tests/frontend/specs/urls_become_clickable.js b/src/tests/frontend/specs/urls_become_clickable.js index a027de9ff..8a038b8b1 100644 --- a/src/tests/frontend/specs/urls_become_clickable.js +++ b/src/tests/frontend/specs/urls_become_clickable.js @@ -20,6 +20,7 @@ describe('urls', function () { describe('entering a URL makes a link', function () { for (const url of ['https://etherpad.org', 'www.etherpad.org']) { it(url, async function () { + this.timeout(5000); const url = 'https://etherpad.org'; await helper.edit(url); await helper.waitForPromise(() => txt().find('a').length === 1, 2000); From 13a0b0688f6bcddc3047aae7557d7e3e3157c400 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 14 Feb 2021 06:16:41 +0000 Subject: [PATCH 329/357] docs: changelog update (#4776) Co-authored-by: Richard Hansen --- CHANGELOG.md | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d29419353..5b082c646 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ * The `bin/` and `tests/` directories were moved under `src/`. Symlinks were added at the old locations to hopefully avoid breaking user scripts and other tools. +* Dependencies are now installed with the `--no-optional` flag to speed + installation. Optional dependencies such as `sqlite3` must now be manually + installed (e.g., `(cd src && npm i sqlite3)`). +* Socket.IO messages are now limited to 1MiB to make denial of service attacks + more difficult. This may cause issues with plugins that send large messages, + e.g., `ep_image_upload`. * The top-level `package.json` file, added in v1.8.7, has been removed due to problematic npm behavior. Whenever you install a plugin you will see the following benign warnings that can be safely ignored: @@ -19,15 +25,19 @@ npm WARN develop No license field. ``` -### Notable new features +### Notable enhancements * You can now generate a link to a specific line number in a pad. Appending `#L10` to a pad URL will cause your browser to scroll down to line 10. * Database performance is significantly improved. -* Admin UI now has test coverage in CI. (The tests are not enabled by default; - see `settings.json`.) +* Browser caching improvements. * New stats/metrics: `activePads`, `httpUptime`, `lastDisconnected`, `memoryUsageHeap`. +* Users can now pick absolute white (`#fff`) as their color. +* The `settings.json` template used for Docker images has new variables for + controlling rate limiting. +* Admin UI now has test coverage in CI. (The tests are not enabled by default + because the admin password is required; see `settings.json`.) * For plugin authors: * New `callAllSerial()` function that invokes hook functions like `callAll()` except it supports asynchronous hook functions. @@ -39,17 +49,20 @@ * Backend tests for plugins can now use the [`ep_etherpad-lite/tests/backend/common`](src/tests/backend/common.js) module to start the server and simplify API access. + * The `checkPlugins.js` script now automatically adds GitHub CI test coverage + badges for backend tests and npm publish. ### Notable fixes * Enter key now stays in focus when inserted at bottom of viewport. +* Numbering for ordered list items now properly increments when exported to + text. * Suppressed benign socket.io connection errors * Interface no longer loses color variants on disconnect/reconnect event. -* Removed npm.load to support npm7. * General code quality is further significantly improved. +* Restarting Etherpad via `/admin` actions is more robust. * Improved reliability of server shutdown and restart. * No longer error if no buttons are visible. -* Update Socket.IO to address a minor security vulnerability. * For plugin authors: * Fixed `collectContentLineText` return value handling. From e674d9789ec33b91db5986e0654136a2fb0f3c95 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 14 Feb 2021 02:50:10 -0500 Subject: [PATCH 330/357] express: Change `httpUptime` to `httpStartTime` (#4777) It's better to provide a primitive value and let the consumer of the metric do math if desired. Co-authored-by: John McLear --- CHANGELOG.md | 6 ++++-- src/node/hooks/express.js | 6 +++--- src/tests/frontend/specs/adminsettings.js | 14 +++++++------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b082c646..4460795a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,9 +30,11 @@ * You can now generate a link to a specific line number in a pad. Appending `#L10` to a pad URL will cause your browser to scroll down to line 10. * Database performance is significantly improved. -* Browser caching improvements. -* New stats/metrics: `activePads`, `httpUptime`, `lastDisconnected`, +* Admin UI now has test coverage in CI. (The tests are not enabled by default; + see `settings.json`.) +* New stats/metrics: `activePads`, `httpStartTime`, `lastDisconnected`, `memoryUsageHeap`. +* Browser caching improvements. * Users can now pick absolute white (`#fff`) as their color. * The `settings.json` template used for Docker images has new variables for controlling rate limiting. diff --git a/src/node/hooks/express.js b/src/node/hooks/express.js index 36bf2e695..351ab5bf2 100644 --- a/src/node/hooks/express.js +++ b/src/node/hooks/express.js @@ -17,7 +17,7 @@ const logger = log4js.getLogger('http'); let serverName; const sockets = new Set(); const socketsEvents = new events.EventEmitter(); -let startTime = null; +const startTime = stats.settableGauge('httpStartTime'); exports.server = null; @@ -46,11 +46,11 @@ const closeServer = async () => { await p; clearTimeout(timeout); exports.server = null; + startTime.setValue(0); logger.info('HTTP server closed'); }; exports.createServer = async () => { - stats.gauge('httpUptime', () => startTime == null ? 0 : new Date() - startTime); console.log('Report bugs at https://github.com/ether/etherpad-lite/issues'); serverName = `Etherpad ${settings.getGitCommit()} (https://etherpad.org)`; @@ -217,7 +217,7 @@ exports.restartServer = async () => { }); }); await util.promisify(exports.server.listen).bind(exports.server)(settings.port, settings.ip); - startTime = new Date(); + startTime.setValue(Date.now()); logger.info('HTTP server listening for connections'); }; diff --git a/src/tests/frontend/specs/adminsettings.js b/src/tests/frontend/specs/adminsettings.js index 71535c68f..3eb6e41a6 100644 --- a/src/tests/frontend/specs/adminsettings.js +++ b/src/tests/frontend/specs/adminsettings.js @@ -56,28 +56,28 @@ describe('Admin > Settings', function () { it('restart works', async function () { this.timeout(60000); - const getUptime = async () => { + const getStartTime = async () => { try { - const {httpUptime} = await $.ajax({ + const {httpStartTime} = await $.ajax({ url: new URL('/stats', window.location.href), method: 'GET', dataType: 'json', timeout: 450, // Slightly less than the waitForPromise() interval. }); - return httpUptime; + return httpStartTime; } catch (err) { return null; } }; await helper.waitForPromise(async () => { - const uptime = await getUptime(); - return uptime != null && uptime > 0; + const startTime = await getStartTime(); + return startTime != null && startTime > 0 && Date.now() > startTime; }, 1000, 500); const clickTime = Date.now(); helper.admin$('#restartEtherpad').click(); await helper.waitForPromise(async () => { - const uptime = await getUptime(); - return uptime != null && Date.now() - uptime >= clickTime; + const startTime = await getStartTime(); + return startTime != null && startTime >= clickTime; }, 60000, 500); }); }); From f59e0993a681ded64f9767576ee7dcbc7918576d Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 14 Feb 2021 08:04:50 +0000 Subject: [PATCH 331/357] tests: test runner output HTML and CSS improvements to show duration (#4775) --- src/tests/frontend/lib/mocha.js | 4 ++-- src/tests/frontend/runner.css | 14 +++----------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/tests/frontend/lib/mocha.js b/src/tests/frontend/lib/mocha.js index a9669b1b7..031b6e446 100644 --- a/src/tests/frontend/lib/mocha.js +++ b/src/tests/frontend/lib/mocha.js @@ -3216,10 +3216,10 @@ function HTML(runner, options) { runner.on(EVENT_TEST_PASS, function(test) { var url = self.testURL(test); var markup = - '
                  • %e%ems ' + + '
                  • %e

                    %ems ' + '' + playIcon + - '
                  • '; + ''; var el = fragment(markup, test.speed, test.title, test.duration, url); self.addCodeToggle(el, test.body); appendToStack(el); diff --git a/src/tests/frontend/runner.css b/src/tests/frontend/runner.css index 14cc96986..b35918028 100644 --- a/src/tests/frontend/runner.css +++ b/src/tests/frontend/runner.css @@ -102,11 +102,11 @@ body { } #mocha .test.pass.medium .duration { - background: #C09853; + background: #ffd285; } #mocha .test.pass.slow .duration { - background: #B94A48; + background: #ffc2c0; } #mocha .test.pass::before { @@ -122,19 +122,11 @@ body { font-size: 9px; margin-left: 5px; padding: 2px 5px; - color: white; - -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); - -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); - box-shadow: inset 0 1px 1px rgba(0,0,0,.2); - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - -ms-border-radius: 5px; - -o-border-radius: 5px; border-radius: 5px; } #mocha .test.pass.fast .duration { - display: none; + background: #d3ffe9; } #mocha .test.pending { From 48205c1ddb813f6b270ff90a5c988af800d5fbde Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 19:56:07 -0500 Subject: [PATCH 332/357] import/export: Make sure Express sees async errors Express v4.x does not check to see if a Promise returned from a middleware function will be rejected, so explicitly pass the Promise rejection reason to `next()`. We can revert this change after we upgrade to Express v5.0. See https://expressjs.com/en/guide/error-handling.html for details. --- src/node/hooks/express/importexport.js | 94 ++++++++++++++------------ 1 file changed, 49 insertions(+), 45 deletions(-) diff --git a/src/node/hooks/express/importexport.js b/src/node/hooks/express/importexport.js index 4dbe816f6..598629632 100644 --- a/src/node/hooks/express/importexport.js +++ b/src/node/hooks/express/importexport.js @@ -21,58 +21,62 @@ const limiter = rateLimit(settings.importExportRateLimiting); exports.expressCreateServer = (hookName, args, cb) => { // handle export requests args.app.use('/p/:pad/:rev?/export/:type', limiter); - args.app.get('/p/:pad/:rev?/export/:type', async (req, res, next) => { - const types = ['pdf', 'doc', 'txt', 'html', 'odt', 'etherpad']; - // send a 404 if we don't support this filetype - if (types.indexOf(req.params.type) === -1) { - return next(); - } - - // if abiword is disabled, and this is a format we only support with abiword, output a message - if (settings.exportAvailable() === 'no' && - ['odt', 'pdf', 'doc'].indexOf(req.params.type) !== -1) { - console.error(`Impossible to export pad "${req.params.pad}" in ${req.params.type} format.` + - ' There is no converter configured'); - - // ACHTUNG: do not include req.params.type in res.send() because there is - // no HTML escaping and it would lead to an XSS - res.send('This export is not enabled at this Etherpad instance. Set the path to Abiword' + - ' or soffice (LibreOffice) in settings.json to enable this feature'); - return; - } - - res.header('Access-Control-Allow-Origin', '*'); - - if (await hasPadAccess(req, res)) { - let padId = req.params.pad; - - let readOnlyId = null; - if (readOnlyManager.isReadOnlyId(padId)) { - readOnlyId = padId; - padId = await readOnlyManager.getPadId(readOnlyId); - } - - const exists = await padManager.doesPadExists(padId); - if (!exists) { - console.warn(`Someone tried to export a pad that doesn't exist (${padId})`); + args.app.get('/p/:pad/:rev?/export/:type', (req, res, next) => { + (async () => { + const types = ['pdf', 'doc', 'txt', 'html', 'odt', 'etherpad']; + // send a 404 if we don't support this filetype + if (types.indexOf(req.params.type) === -1) { return next(); } - console.log(`Exporting pad "${req.params.pad}" in ${req.params.type} format`); - exportHandler.doExport(req, res, padId, readOnlyId, req.params.type); - } + // if abiword is disabled, and this is a format we only support with abiword, output a message + if (settings.exportAvailable() === 'no' && + ['odt', 'pdf', 'doc'].indexOf(req.params.type) !== -1) { + console.error(`Impossible to export pad "${req.params.pad}" in ${req.params.type} format.` + + ' There is no converter configured'); + + // ACHTUNG: do not include req.params.type in res.send() because there is + // no HTML escaping and it would lead to an XSS + res.send('This export is not enabled at this Etherpad instance. Set the path to Abiword' + + ' or soffice (LibreOffice) in settings.json to enable this feature'); + return; + } + + res.header('Access-Control-Allow-Origin', '*'); + + if (await hasPadAccess(req, res)) { + let padId = req.params.pad; + + let readOnlyId = null; + if (readOnlyManager.isReadOnlyId(padId)) { + readOnlyId = padId; + padId = await readOnlyManager.getPadId(readOnlyId); + } + + const exists = await padManager.doesPadExists(padId); + if (!exists) { + console.warn(`Someone tried to export a pad that doesn't exist (${padId})`); + return next(); + } + + console.log(`Exporting pad "${req.params.pad}" in ${req.params.type} format`); + exportHandler.doExport(req, res, padId, readOnlyId, req.params.type); + } + })().catch((err) => next(err || new Error(err))); }); // handle import requests args.app.use('/p/:pad/import', limiter); - args.app.post('/p/:pad/import', async (req, res, next) => { - const {session: {user} = {}} = req; - const {accessStatus} = await securityManager.checkAccess( - req.params.pad, req.cookies.sessionID, req.cookies.token, user); - if (accessStatus !== 'grant' || !webaccess.userCanModify(req.params.pad, req)) { - return res.status(403).send('Forbidden'); - } - await importHandler.doImport(req, res, req.params.pad); + args.app.post('/p/:pad/import', (req, res, next) => { + (async () => { + const {session: {user} = {}} = req; + const {accessStatus} = await securityManager.checkAccess( + req.params.pad, req.cookies.sessionID, req.cookies.token, user); + if (accessStatus !== 'grant' || !webaccess.userCanModify(req.params.pad, req)) { + return res.status(403).send('Forbidden'); + } + await importHandler.doImport(req, res, req.params.pad); + })().catch((err) => next(err || new Error(err))); }); return cb(); From 8a221ca773ea63a20e6dc5563d5ccf63f0f38dd8 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 5 Oct 2020 21:11:03 -0400 Subject: [PATCH 333/357] pad: Delete dead code --- src/static/js/pad.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 55460b3fb..d8aaa4fef 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -720,10 +720,6 @@ const pad = { .val(JSON.stringify(pad.collabClient.getMissedChanges())); $('form#reconnectform').submit(); }, - // this is called from code put into a frame from the server: - handleImportExportFrameCall: (callName, varargs) => { - padimpexp.handleFrameCall.call(padimpexp, callName, Array.prototype.slice.call(arguments, 1)); - }, callWhenNotCommitting: (f) => { pad.collabClient.callWhenNotCommitting(f); }, From ce4ce8ce9511c557d7916b45103194c9edc69b8b Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 5 Oct 2020 21:12:07 -0400 Subject: [PATCH 334/357] pad_impexp: Delete unnecessary `importFailed` wrapper --- src/static/js/pad_impexp.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.js index a44c69d8a..867971045 100644 --- a/src/static/js/pad_impexp.js +++ b/src/static/js/pad_impexp.js @@ -51,7 +51,7 @@ const padimpexp = (() => { return; } currentImportTimer = null; - importFailed('Request timed out.'); + importErrorMessage('Request timed out.'); importDone(); }, 25000); // time out after some number of seconds $('#importsubmitinput').attr( @@ -71,10 +71,6 @@ const padimpexp = (() => { return ret; }; - const importFailed = (msg) => { - importErrorMessage(msg); - }; - const importDone = () => { $('#importsubmitinput').removeAttr('disabled').val(html10n.get('pad.impexp.importbutton')); window.setTimeout(() => { @@ -196,7 +192,7 @@ const padimpexp = (() => { handleFrameCall: (directDatabaseAccess, status) => { if (directDatabaseAccess === 'undefined') directDatabaseAccess = false; if (status !== 'ok') { - importFailed(status); + importErrorMessage(status); } else { $('#import_export').removeClass('popup-show'); } From efe07cd02782d1f23721d5b5c68e47c993a2eb4b Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 20:23:15 -0500 Subject: [PATCH 335/357] pad_impexp: Invert logic to improve readability --- src/static/js/pad_impexp.js | 44 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.js index 867971045..b69bbd1fc 100644 --- a/src/static/js/pad_impexp.js +++ b/src/static/js/pad_impexp.js @@ -44,31 +44,29 @@ const padimpexp = (() => { const fileInputSubmit = () => { $('#importmessagefail').fadeOut('fast'); - const ret = window.confirm(html10n.get('pad.impexp.confirmimport')); - if (ret) { - currentImportTimer = window.setTimeout(() => { - if (!currentImportTimer) { - return; - } - currentImportTimer = null; - importErrorMessage('Request timed out.'); - importDone(); - }, 25000); // time out after some number of seconds - $('#importsubmitinput').attr( + if (!window.confirm(html10n.get('pad.impexp.confirmimport'))) return false; + currentImportTimer = window.setTimeout(() => { + if (!currentImportTimer) { + return; + } + currentImportTimer = null; + importErrorMessage('Request timed out.'); + importDone(); + }, 25000); // time out after some number of seconds + $('#importsubmitinput').attr( + { + disabled: true, + }).val(html10n.get('pad.impexp.importing')); + + window.setTimeout(() => { + $('#importfileinput').attr( { disabled: true, - }).val(html10n.get('pad.impexp.importing')); - - window.setTimeout(() => { - $('#importfileinput').attr( - { - disabled: true, - }); - }, 0); - $('#importarrow').stop(true, true).hide(); - $('#importstatusball').show(); - } - return ret; + }); + }, 0); + $('#importarrow').stop(true, true).hide(); + $('#importstatusball').show(); + return true; }; const importDone = () => { From 890e16f6fc69c75a0b056ccb5d7a13775c8b659f Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 20:30:07 -0500 Subject: [PATCH 336/357] pad_impexp: Style fixes to improve readability --- src/static/js/pad_impexp.js | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.js index b69bbd1fc..9ac2824db 100644 --- a/src/static/js/pad_impexp.js +++ b/src/static/js/pad_impexp.js @@ -46,24 +46,13 @@ const padimpexp = (() => { $('#importmessagefail').fadeOut('fast'); if (!window.confirm(html10n.get('pad.impexp.confirmimport'))) return false; currentImportTimer = window.setTimeout(() => { - if (!currentImportTimer) { - return; - } + if (!currentImportTimer) return; currentImportTimer = null; importErrorMessage('Request timed out.'); importDone(); }, 25000); // time out after some number of seconds - $('#importsubmitinput').attr( - { - disabled: true, - }).val(html10n.get('pad.impexp.importing')); - - window.setTimeout(() => { - $('#importfileinput').attr( - { - disabled: true, - }); - }, 0); + $('#importsubmitinput').attr({disabled: true}).val(html10n.get('pad.impexp.importing')); + window.setTimeout(() => $('#importfileinput').attr({disabled: true}), 0); $('#importarrow').stop(true, true).hide(); $('#importstatusball').show(); return true; @@ -71,9 +60,7 @@ const padimpexp = (() => { const importDone = () => { $('#importsubmitinput').removeAttr('disabled').val(html10n.get('pad.impexp.importbutton')); - window.setTimeout(() => { - $('#importfileinput').removeAttr('disabled'); - }, 0); + window.setTimeout(() => $('#importfileinput').removeAttr('disabled'), 0); $('#importstatusball').hide(); importClearTimeout(); addImportFrames(); @@ -109,9 +96,7 @@ const padimpexp = (() => { if ($('#importexport .importmessage').is(':visible')) { $('#importmessagesuccess').fadeOut('fast'); - $('#importmessagefail').fadeOut('fast', () => { - showError(true); - }); + $('#importmessagefail').fadeOut('fast', () => showError(true)); } else { showError(); } From d869d96a2b28c4037741044153529f80fb96608d Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 20:51:26 -0500 Subject: [PATCH 337/357] pad_impexp: Simplify creation of import failure message --- src/static/js/pad_impexp.js | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.js index 9ac2824db..e05b97a54 100644 --- a/src/static/js/pad_impexp.js +++ b/src/static/js/pad_impexp.js @@ -74,24 +74,19 @@ const padimpexp = (() => { }; const importErrorMessage = (status) => { - let msg = ''; - - if (status === 'convertFailed') { - msg = html10n.get('pad.impexp.convertFailed'); - } else if (status === 'uploadFailed') { - msg = html10n.get('pad.impexp.uploadFailed'); - } else if (status === 'padHasData') { - msg = html10n.get('pad.impexp.padHasData'); - } else if (status === 'maxFileSize') { - msg = html10n.get('pad.impexp.maxFileSize'); - } else if (status === 'permission') { - msg = html10n.get('pad.impexp.permission'); - } + const known = [ + 'convertFailed', + 'uploadFailed', + 'padHasData', + 'maxFileSize', + 'permission', + ]; + const msg = html10n.get(`pad.impexp.${known.indexOf(status) !== -1 ? status : 'copypaste'}`); const showError = (fade) => { $('#importmessagefail').html( `${html10n.get('pad.impexp.importfailed')}: ` + - `${msg || html10n.get('pad.impexp.copypaste', '')}`)[(fade ? 'fadeIn' : 'show')](); + `${msg}`)[(fade ? 'fadeIn' : 'show')](); }; if ($('#importexport .importmessage').is(':visible')) { From cc52811cd0a8178d2f33cdb1db7cbac818229c1b Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 20:52:28 -0500 Subject: [PATCH 338/357] pad_impexp: Use jQuery to build the import failure popup This reduces the chances of accidentally introducing an XSS vulnerability. --- src/static/js/pad_impexp.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.js index e05b97a54..ea93de7c9 100644 --- a/src/static/js/pad_impexp.js +++ b/src/static/js/pad_impexp.js @@ -84,9 +84,12 @@ const padimpexp = (() => { const msg = html10n.get(`pad.impexp.${known.indexOf(status) !== -1 ? status : 'copypaste'}`); const showError = (fade) => { - $('#importmessagefail').html( - `${html10n.get('pad.impexp.importfailed')}: ` + - `${msg}`)[(fade ? 'fadeIn' : 'show')](); + const popup = $('#importmessagefail').empty() + .append($('') + .css('color', 'red') + .text(`${html10n.get('pad.impexp.importfailed')}: `)) + .append(document.createTextNode(msg)); + popup[(fade ? 'fadeIn' : 'show')](); }; if ($('#importexport .importmessage').is(':visible')) { From 008209b0e0434da9832768aefbed66d6c7205736 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 18:22:54 -0500 Subject: [PATCH 339/357] ImportHandler: Delete redundant variable --- src/node/handler/ImportHandler.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index d2f98f47d..82a611398 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -152,7 +152,6 @@ const doImport = async (req, res, padId) => { throw 'padHasData'; } - const fsp_readFile = util.promisify(fs.readFile); const _text = await fsp_readFile(srcFile, 'utf8'); req.directDatabaseAccess = true; await importEtherpad.setPadRaw(padId, _text); From c7b1abebe4eefa7cb21f7d9699126294ec606d92 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 18:35:00 -0500 Subject: [PATCH 340/357] ImportHandler: Avoid deprecated `fs.exists()` function --- src/node/handler/ImportHandler.js | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index 82a611398..f5956edcb 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -34,11 +34,18 @@ const log4js = require('log4js'); const hooks = require('../../static/js/pluginfw/hooks.js'); const util = require('util'); -const fsp_exists = util.promisify(fs.exists); const fsp_rename = util.promisify(fs.rename); const fsp_readFile = util.promisify(fs.readFile); const fsp_unlink = util.promisify(fs.unlink); +const rm = async (path) => { + try { + await fsp_unlink(path); + } catch (err) { + if (err.code !== 'ENOENT') throw err; + } +}; + let convertor = null; let exportExtension = 'htm'; @@ -239,19 +246,8 @@ const doImport = async (req, res, padId) => { await padMessageHandler.updatePadClients(pad); // clean up temporary files - - /* - * TODO: directly delete the file and handle the eventual error. Checking - * before for existence is prone to race conditions, and does not handle any - * errors anyway. - */ - if (await fsp_exists(srcFile)) { - fsp_unlink(srcFile); - } - - if (await fsp_exists(destFile)) { - fsp_unlink(destFile); - } + rm(srcFile); + rm(destFile); }; exports.doImport = (req, res, padId) => { From 5b1b0309062b8c5cdd74036a87f11595a8afd135 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 18:38:49 -0500 Subject: [PATCH 341/357] ImportHandler: Use asynchronous rename instead of `fs.renameSync()` --- src/node/handler/ImportHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index f5956edcb..d05ea858f 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -174,7 +174,7 @@ const doImport = async (req, res, padId) => { // See https://github.com/ether/etherpad-lite/issues/2572 if (fileIsHTML || !useConvertor) { // if no convertor only rename - fs.renameSync(srcFile, destFile); + await fsp_rename(srcFile, destFile); } else { // @TODO - no Promise interface for convertors (yet) await new Promise((resolve, reject) => { From e01059dce5cb66e3b92558d039f2874638d4ad30 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 18:39:36 -0500 Subject: [PATCH 342/357] ImportHandler: Switch to `fs/promises` API --- src/node/handler/ImportHandler.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index d05ea858f..399192761 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -23,7 +23,7 @@ const padManager = require('../db/PadManager'); const padMessageHandler = require('./PadMessageHandler'); -const fs = require('fs'); +const fs = require('fs').promises; const path = require('path'); const settings = require('../utils/Settings'); const formidable = require('formidable'); @@ -32,15 +32,10 @@ const importHtml = require('../utils/ImportHtml'); const importEtherpad = require('../utils/ImportEtherpad'); const log4js = require('log4js'); const hooks = require('../../static/js/pluginfw/hooks.js'); -const util = require('util'); - -const fsp_rename = util.promisify(fs.rename); -const fsp_readFile = util.promisify(fs.readFile); -const fsp_unlink = util.promisify(fs.unlink); const rm = async (path) => { try { - await fsp_unlink(path); + await fs.unlink(path); } catch (err) { if (err.code !== 'ENOENT') throw err; } @@ -132,7 +127,7 @@ const doImport = async (req, res, padId) => { const oldSrcFile = srcFile; srcFile = path.join(path.dirname(srcFile), `${path.basename(srcFile, fileEnding)}.txt`); - await fsp_rename(oldSrcFile, srcFile); + await fs.rename(oldSrcFile, srcFile); } else { console.warn('Not allowing unknown file type to be imported', fileEnding); throw 'uploadFailed'; @@ -159,7 +154,7 @@ const doImport = async (req, res, padId) => { throw 'padHasData'; } - const _text = await fsp_readFile(srcFile, 'utf8'); + const _text = await fs.readFile(srcFile, 'utf8'); req.directDatabaseAccess = true; await importEtherpad.setPadRaw(padId, _text); } @@ -174,7 +169,7 @@ const doImport = async (req, res, padId) => { // See https://github.com/ether/etherpad-lite/issues/2572 if (fileIsHTML || !useConvertor) { // if no convertor only rename - await fsp_rename(srcFile, destFile); + await fs.rename(srcFile, destFile); } else { // @TODO - no Promise interface for convertors (yet) await new Promise((resolve, reject) => { @@ -192,7 +187,7 @@ const doImport = async (req, res, padId) => { if (!useConvertor && !req.directDatabaseAccess) { // Read the file with no encoding for raw buffer access. - const buf = await fsp_readFile(destFile); + const buf = await fs.readFile(destFile); // Check if there are only ascii chars in the uploaded file const isAscii = !Array.prototype.some.call(buf, (c) => (c > 240)); @@ -209,7 +204,7 @@ const doImport = async (req, res, padId) => { let text; if (!req.directDatabaseAccess) { - text = await fsp_readFile(destFile, 'utf8'); + text = await fs.readFile(destFile, 'utf8'); // node on windows has a delay on releasing of the file lock. // We add a 100ms delay to work around this From 908635a1deb89e11b86d746a297364f313a22618 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 18:57:25 -0500 Subject: [PATCH 343/357] ImportHandler: Use `return reject(...)` to avoid double settle --- src/node/handler/ImportHandler.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index 399192761..a542a6d30 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -99,16 +99,15 @@ const doImport = async (req, res, padId) => { // I hate doing indexOf here but I can't see anything to use... if (err && err.stack && err.stack.indexOf('maxFileSize') !== -1) { - reject('maxFileSize'); + return reject('maxFileSize'); } - reject('uploadFailed'); + return reject('uploadFailed'); } if (!files.file) { // might not be a graceful fix but it works - reject('uploadFailed'); - } else { - resolve(files.file.path); + return reject('uploadFailed'); } + resolve(files.file.path); }); }); @@ -177,7 +176,7 @@ const doImport = async (req, res, padId) => { // catch convert errors if (err) { console.warn('Converting Error:', err); - reject('convertFailed'); + return reject('convertFailed'); } resolve(); }); From 0ff131bbbbb1546f13ef20d46850b03a401d5f3d Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 19:21:53 -0500 Subject: [PATCH 344/357] ImportHandler: Throw Errors, not strings --- src/node/handler/ImportHandler.js | 37 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index a542a6d30..b561aa834 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -33,6 +33,18 @@ const importEtherpad = require('../utils/ImportEtherpad'); const log4js = require('log4js'); const hooks = require('../../static/js/pluginfw/hooks.js'); +// `status` must be a string supported by `importErrorMessage()` in `src/static/js/pad_impexp.js`. +class ImportError extends Error { + constructor(status, ...args) { + super(...args); + if (Error.captureStackTrace) Error.captureStackTrace(this, ImportError); + this.name = 'ImportError'; + this.status = status; + const msg = this.message == null ? '' : String(this.message); + if (status !== '') this.message = msg === '' ? status : `${status}: ${msg}`; + } +} + const rm = async (path) => { try { await fs.unlink(path); @@ -99,13 +111,13 @@ const doImport = async (req, res, padId) => { // I hate doing indexOf here but I can't see anything to use... if (err && err.stack && err.stack.indexOf('maxFileSize') !== -1) { - return reject('maxFileSize'); + return reject(new ImportError('maxFileSize')); } - return reject('uploadFailed'); + return reject(new ImportError('uploadFailed')); } if (!files.file) { // might not be a graceful fix but it works - return reject('uploadFailed'); + return reject(new ImportError('uploadFailed')); } resolve(files.file.path); }); @@ -129,7 +141,7 @@ const doImport = async (req, res, padId) => { await fs.rename(oldSrcFile, srcFile); } else { console.warn('Not allowing unknown file type to be imported', fileEnding); - throw 'uploadFailed'; + throw new ImportError('uploadFailed'); } } @@ -150,7 +162,7 @@ const doImport = async (req, res, padId) => { if (headCount >= 10) { apiLogger.warn('Aborting direct database import attempt of a pad that already has content'); - throw 'padHasData'; + throw new ImportError('padHasData'); } const _text = await fs.readFile(srcFile, 'utf8'); @@ -176,7 +188,7 @@ const doImport = async (req, res, padId) => { // catch convert errors if (err) { console.warn('Converting Error:', err); - return reject('convertFailed'); + return reject(new ImportError('convertFailed')); } resolve(); }); @@ -192,7 +204,7 @@ const doImport = async (req, res, padId) => { const isAscii = !Array.prototype.some.call(buf, (c) => (c > 240)); if (!isAscii) { - throw 'uploadFailed'; + throw new ImportError('uploadFailed'); } } @@ -256,15 +268,8 @@ exports.doImport = (req, res, padId) => { */ let status = 'ok'; doImport(req, res, padId).catch((err) => { - // check for known errors and replace the status - if (err === 'uploadFailed' || - err === 'convertFailed' || - err === 'padHasData' || - err === 'maxFileSize') { - status = err; - } else { - throw err; - } + if (!(err instanceof ImportError) || !err.status) throw err; + status = err.status; }).then(() => { // close the connection res.send(``); From ed80883709a60a57e3860e297b9f183be0b75895 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 19:36:29 -0500 Subject: [PATCH 345/357] ImportHandler: Lint the response script sent to the browser --- src/node/handler/ImportHandler.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index b561aa834..e1ac59ccb 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -272,6 +272,13 @@ exports.doImport = (req, res, padId) => { status = err.status; }).then(() => { // close the connection - res.send(``); + res.send([ + '', + ].join('\n')); }); }; From 26b5a69cccf623545334e95d3b2005b713c6b65a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 19:38:14 -0500 Subject: [PATCH 346/357] ImportHandler: Use `JSON.stringify()` to properly escape characters --- src/node/handler/ImportHandler.js | 2 +- src/static/js/pad_impexp.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index e1ac59ccb..9653bc929 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -276,7 +276,7 @@ exports.doImport = (req, res, padId) => { '', ].join('\n')); diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.js index ea93de7c9..07848d141 100644 --- a/src/static/js/pad_impexp.js +++ b/src/static/js/pad_impexp.js @@ -171,7 +171,6 @@ const padimpexp = (() => { $('.disabledexport').click(cantExport); }, handleFrameCall: (directDatabaseAccess, status) => { - if (directDatabaseAccess === 'undefined') directDatabaseAccess = false; if (status !== 'ok') { importErrorMessage(status); } else { From 28b28866a266912dd311532c26b1005ec2a84d8a Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Sun, 7 Feb 2021 21:13:52 -0500 Subject: [PATCH 347/357] ImportHandler: Move the logger up Also change the name to something shorter. --- src/node/handler/ImportHandler.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index 9653bc929..62f21f3b2 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -33,6 +33,8 @@ const importEtherpad = require('../utils/ImportEtherpad'); const log4js = require('log4js'); const hooks = require('../../static/js/pluginfw/hooks.js'); +const logger = log4js.getLogger('ImportHandler'); + // `status` must be a string supported by `importErrorMessage()` in `src/static/js/pad_impexp.js`. class ImportError extends Error { constructor(status, ...args) { @@ -73,8 +75,6 @@ const tmpDirectory = os.tmpdir(); * do a requested import */ const doImport = async (req, res, padId) => { - const apiLogger = log4js.getLogger('ImportHandler'); - // pipe to a file // convert file to html via abiword or soffice // set html in the pad @@ -161,7 +161,7 @@ const doImport = async (req, res, padId) => { const headCount = _pad.head; if (headCount >= 10) { - apiLogger.warn('Aborting direct database import attempt of a pad that already has content'); + logger.warn('Aborting direct database import attempt of a pad that already has content'); throw new ImportError('padHasData'); } @@ -230,7 +230,7 @@ const doImport = async (req, res, padId) => { try { await importHtml.setPadHTML(pad, text); } catch (e) { - apiLogger.warn('Error importing, possibly caused by malformed HTML'); + logger.warn('Error importing, possibly caused by malformed HTML'); } } else { await pad.setText(text); From fba55fa6cf2d2728bcbc9aef4d3cc71389e591b1 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 5 Oct 2020 17:47:50 -0400 Subject: [PATCH 348/357] ImportHandler: Refactor `doImport()` for readability --- src/node/handler/ImportHandler.js | 54 ++++++++++++++----------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index 62f21f3b2..5732beada 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -155,6 +155,7 @@ const doImport = async (req, res, padId) => { const fileIsHTML = (fileEnding === '.html' || fileEnding === '.htm'); const fileIsTXT = (fileEnding === '.txt'); + let directDatabaseAccess = false; if (fileIsEtherpad) { // we do this here so we can see if the pad has quite a few edits const _pad = await padManager.getPad(padId); @@ -166,12 +167,12 @@ const doImport = async (req, res, padId) => { } const _text = await fs.readFile(srcFile, 'utf8'); - req.directDatabaseAccess = true; + directDatabaseAccess = true; await importEtherpad.setPadRaw(padId, _text); } // convert file to html if necessary - if (!importHandledByPlugin && !req.directDatabaseAccess) { + if (!importHandledByPlugin && !directDatabaseAccess) { if (fileIsTXT) { // Don't use convertor for text files useConvertor = false; @@ -196,7 +197,7 @@ const doImport = async (req, res, padId) => { } } - if (!useConvertor && !req.directDatabaseAccess) { + if (!useConvertor && !directDatabaseAccess) { // Read the file with no encoding for raw buffer access. const buf = await fs.readFile(destFile); @@ -214,7 +215,7 @@ const doImport = async (req, res, padId) => { // read the text let text; - if (!req.directDatabaseAccess) { + if (!directDatabaseAccess) { text = await fs.readFile(destFile, 'utf8'); // node on windows has a delay on releasing of the file lock. @@ -225,7 +226,7 @@ const doImport = async (req, res, padId) => { } // change text of the pad and broadcast the changeset - if (!req.directDatabaseAccess) { + if (!directDatabaseAccess) { if (importHandledByPlugin || useConvertor || fileIsHTML) { try { await importHtml.setPadHTML(pad, text); @@ -244,9 +245,7 @@ const doImport = async (req, res, padId) => { // direct Database Access means a pad user should perform a switchToPad // and not attempt to receive updated pad data - if (req.directDatabaseAccess) { - return; - } + if (directDatabaseAccess) return true; // tell clients to update await padMessageHandler.updatePadClients(pad); @@ -254,31 +253,26 @@ const doImport = async (req, res, padId) => { // clean up temporary files rm(srcFile); rm(destFile); + + return false; }; -exports.doImport = (req, res, padId) => { - /** - * NB: abuse the 'req' object by storing an additional - * 'directDatabaseAccess' property on it so that it can - * be passed back in the HTML below. - * - * this is necessary because in the 'throw' paths of - * the function above there's no other way to return - * a value to the caller. - */ +exports.doImport = async (req, res, padId) => { let status = 'ok'; - doImport(req, res, padId).catch((err) => { + let directDatabaseAccess; + try { + directDatabaseAccess = await doImport(req, res, padId); + } catch (err) { if (!(err instanceof ImportError) || !err.status) throw err; status = err.status; - }).then(() => { - // close the connection - res.send([ - '', - ].join('\n')); - }); + } + // close the connection + res.send([ + '', + ].join('\n')); }; From b711ff6acf976a3f8808325411cf244d55c5a0e7 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Mon, 5 Oct 2020 22:22:44 -0400 Subject: [PATCH 349/357] import: Ajaxify pad import This eliminates an inline script (good for Content Security Policy) and improves the user experience. --- src/node/handler/ImportHandler.js | 21 +++-- src/static/js/pad_impexp.js | 77 +++++++------------ .../backend/specs/api/importexportGetPost.js | 45 +++++++++-- 3 files changed, 76 insertions(+), 67 deletions(-) diff --git a/src/node/handler/ImportHandler.js b/src/node/handler/ImportHandler.js index 5732beada..67bef01ce 100644 --- a/src/node/handler/ImportHandler.js +++ b/src/node/handler/ImportHandler.js @@ -258,21 +258,18 @@ const doImport = async (req, res, padId) => { }; exports.doImport = async (req, res, padId) => { - let status = 'ok'; + let httpStatus = 200; + let code = 0; + let message = 'ok'; let directDatabaseAccess; try { directDatabaseAccess = await doImport(req, res, padId); } catch (err) { - if (!(err instanceof ImportError) || !err.status) throw err; - status = err.status; + const known = err instanceof ImportError && err.status; + if (!known) logger.error(`Internal error during import: ${err.stack || err}`); + httpStatus = known ? 400 : 500; + code = known ? 1 : 2; + message = known ? err.status : 'internalError'; } - // close the connection - res.send([ - '', - ].join('\n')); + res.status(httpStatus).json({code, message, data: {directDatabaseAccess}}); }; diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.js index 07848d141..60a51354f 100644 --- a/src/static/js/pad_impexp.js +++ b/src/static/js/pad_impexp.js @@ -23,9 +23,9 @@ */ const padimpexp = (() => { - // /// import - let currentImportTimer = null; + let pad; + // /// import const addImportFrames = () => { $('#import .importframe').remove(); const iframe = $('