From 98066245f7708b39793970b2085b3b4b42255964 Mon Sep 17 00:00:00 2001 From: Antony Sande Date: Thu, 20 May 2021 16:57:18 +0300 Subject: [PATCH 1/4] fix(parser): expand recurring events until end date is reached --- lib/Calendar.js | 10 +- lib/xml/parser.js | 88 +++++++------- test/Calendar.test.js | 108 ++++++++++++++++++ test/fixtures/index.js | 3 + .../recurringEventsCount.response.xml | 33 ++++++ .../recurringEventsEndNever.response.xml | 48 ++++++++ .../recurringEventsUntil.response.xml | 34 ++++++ 7 files changed, 271 insertions(+), 53 deletions(-) create mode 100644 test/fixtures/recurringEventsCount.response.xml create mode 100644 test/fixtures/recurringEventsEndNever.response.xml create mode 100644 test/fixtures/recurringEventsUntil.response.xml diff --git a/lib/Calendar.js b/lib/Calendar.js index 152b73c..68053aa 100644 --- a/lib/Calendar.js +++ b/lib/Calendar.js @@ -88,15 +88,7 @@ function createCalendar(request) { const xmlRequest = byTimeTemplate({ start, end }); return request(this.config, "REPORT", 1, xmlRequest) - .then(xmlParser.parseEvents) - .then(events => { - return events.filter(event => { - const isNotRecurring = !event.data.type.recurring; - const isSameDayOrAfter = moment(event.data.start).isSameOrAfter(start, "day"); - - return isNotRecurring || isSameDayOrAfter; - }); - }); + .then((xml) => xmlParser.parseEvents(xml, start, end)); } } diff --git a/lib/xml/parser.js b/lib/xml/parser.js index c7a3f57..79ced29 100644 --- a/lib/xml/parser.js +++ b/lib/xml/parser.js @@ -99,7 +99,7 @@ function getNormalizedEndDate(endDate, duration) { endDate.toISOString(); } -function getNormalOccurenceEventData(nextEvent, eventData, vevent) { +function getNormalOccurenceEventData(nextEvent, vevent) { const dtstart = nextEvent.startDate; const dtend = nextEvent.endDate; const { summary: title, uid, location, description } = nextEvent.item; @@ -118,7 +118,7 @@ function getNormalOccurenceEventData(nextEvent, eventData, vevent) { }; } -function getModifiedOccurenceEventData(vevent, eventData) { +function getModifiedOccurenceEventData(vevent) { const dtstart = vevent.getFirstPropertyValue("dtstart"); const dtend = vevent.getFirstPropertyValue("dtend"); const duration = getNormalizedDuration(dtstart, dtend); @@ -136,7 +136,7 @@ function getModifiedOccurenceEventData(vevent, eventData) { }; } -function parseEvents(xml) { +function parseEvents(xml, startDate = null, endDate = null) { let parsed; const formatted = []; @@ -152,8 +152,6 @@ function parseEvents(xml) { etag = stripDoubleQuotes(etag); - let eventData = {}; - if (!event.propstat[0].prop[0]["calendar-data"]) { return; } @@ -178,48 +176,51 @@ function parseEvents(xml) { const icalEvent = new ICAL.Event(vevent); if (icalEvent.isRecurring()) { - // This is a recurring event, expand the next few occurances const expand = new ICAL.RecurExpansion({ component: vevent, dtstart: vevent.getFirstPropertyValue("dtstart"), }); + const recur = vevent.getFirstPropertyValue("rrule"); + // Expand upto a maximum of 10 upcoming occurrences for events that never end + const MAX_INFINITE_OCCURRENCES_COUNT = 10; + const startRange = startDate ? ICAL.Time.fromDateTimeString(moment(startDate).toISOString()) : null; + const endRange = endDate ? ICAL.Time.fromDateTimeString(moment(endDate).toISOString()) : null; + let occurrencesCount = 0; + + for (let next = expand.next(); next; next = expand.next()) { + if (startRange && next.compare(startRange) < 0) { + continue; + } + + if (endRange && next.compare(endRange) > 0) { + break; + } - // Since there are infinite rules, its a good idea to limit the scope - // of the iteration then resume later on - // Expand upto a maximum of 10 upcoming occurances - for (let i = 0; i < 10; i++) { - const nextOccuranceTime = expand.next(); - - if (!expand.complete) { - // Handle this next expanded occurence - const nextEvent = icalEvent.getOccurrenceDetails(nextOccuranceTime); - - eventData = {}; - - if (modifiedOccurences.length === 0) { - // No events have been modified - formatted.push({ - ics: event.href[0], - etag, - data: getNormalOccurenceEventData(nextEvent, eventData, vevent), - }); - } else if (isModified(nextOccuranceTime, modifiedOccurences)) { - // This is the event that has been modied - const key = getModifiedOccuranceKey(nextOccuranceTime, modifiedOccurences) || 0; - - formatted.push({ - ics: event.href[0], - etag, - data: getModifiedOccurenceEventData(vevents[key], eventData), - }); - } else { - // Expand this event normally - formatted.push({ - ics: event.href[0], - etag, - data: getNormalOccurenceEventData(nextEvent, eventData, vevent), - }); - } + if (!endRange && !recur.isFinite() && occurrencesCount >= MAX_INFINITE_OCCURRENCES_COUNT) { + break; + } + + occurrencesCount += 1; + + // Handle this next expanded occurence + const nextEvent = icalEvent.getOccurrenceDetails(next); + + if (modifiedOccurences.length && isModified(next, modifiedOccurences)) { + // This event has been modified + const key = getModifiedOccuranceKey(next, modifiedOccurences); + + formatted.push({ + ics: event.href[0], + etag, + data: getModifiedOccurenceEventData(vevents[key]), + }); + } else { + // Expand this event normally + formatted.push({ + ics: event.href[0], + etag, + data: getNormalOccurenceEventData(nextEvent, vevent), + }); } } } else { @@ -228,8 +229,7 @@ function parseEvents(xml) { const dtstart = vevent.getFirstPropertyValue("dtstart"); const dtend = vevent.getFirstPropertyValue("dtend"); const duration = getNormalizedDuration(dtstart, dtend); - - eventData = { + const eventData = { title: vevent.getFirstPropertyValue("summary"), uid: vevent.getFirstPropertyValue("uid"), location: vevent.getFirstPropertyValue("location"), diff --git a/test/Calendar.test.js b/test/Calendar.test.js index f285f18..c97036f 100644 --- a/test/Calendar.test.js +++ b/test/Calendar.test.js @@ -314,5 +314,113 @@ describe("Calendar", () => { expect(response[0]).to.have.property("data"); }); }); + + describe("recurring events with count", () => { + it("should expand upto end date if provided", () => { + const response = fixtures.getRecurringEventsCountResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + const calendar = new Calendar(config); + + return calendar + .getEventsByTime("20210601T000000Z", "20210630T000000Z") + .then((response) => { + expect(response).to.be.an("array"); + expect(response).to.have.lengthOf(22); + expect(response[0]).to.have.property("ics"); + expect(response[0]).to.have.property("etag"); + expect(response[0]).to.have.property("data"); + }); + }); + + it("should expand all upcoming events if end date is not provided", () => { + const response = fixtures.getRecurringEventsCountResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + const calendar = new Calendar(config); + + return calendar + .getEventsByTime("20210601T000000Z") + .then((response) => { + expect(response).to.be.an("array"); + expect(response).to.have.lengthOf(29); + expect(response[0]).to.have.property("ics"); + expect(response[0]).to.have.property("etag"); + expect(response[0]).to.have.property("data"); + }); + }); + }); + + describe("recurring events with until date", () => { + it("should expand upto end date if provided", () => { + const response = fixtures.getRecurringEventsUntilResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + const calendar = new Calendar(config); + + return calendar + .getEventsByTime("20210601T000000Z", "20210630T000000Z") + .then((response) => { + expect(response).to.be.an("array"); + expect(response).to.have.lengthOf(22); + expect(response[0]).to.have.property("ics"); + expect(response[0]).to.have.property("etag"); + expect(response[0]).to.have.property("data"); + }); + }); + + it("should expand all upcoming events if end date is not provided", () => { + const response = fixtures.getRecurringEventsUntilResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + const calendar = new Calendar(config); + + return calendar + .getEventsByTime("20210601T000000Z") + .then((response) => { + expect(response).to.be.an("array"); + expect(response).to.have.lengthOf(44); + expect(response[0]).to.have.property("ics"); + expect(response[0]).to.have.property("etag"); + expect(response[0]).to.have.property("data"); + }); + }); + }); + + describe("recurring events that never end", () => { + it("should expand upto end date if provided", () => { + const response = fixtures.getRecurringEventsEndNeverResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + const calendar = new Calendar(config); + + return calendar + .getEventsByTime("20210601T000000Z", "20210630T000000Z") + .then((response) => { + expect(response).to.be.an("array"); + expect(response).to.have.lengthOf(22); + expect(response[0]).to.have.property("ics"); + expect(response[0]).to.have.property("etag"); + expect(response[0]).to.have.property("data"); + }); + }); + + it("should expand only 10 upcoming events if end date is not provided", () => { + const response = fixtures.getRecurringEventsEndNeverResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + const calendar = new Calendar(config); + + return calendar + .getEventsByTime("20210601T000000Z") + .then((response) => { + expect(response).to.be.an("array"); + expect(response).to.have.lengthOf(10); + expect(response[0]).to.have.property("ics"); + expect(response[0]).to.have.property("etag"); + expect(response[0]).to.have.property("data"); + }); + }); + }); }); }); diff --git a/test/fixtures/index.js b/test/fixtures/index.js index 6e178ed..8c2a893 100644 --- a/test/fixtures/index.js +++ b/test/fixtures/index.js @@ -12,6 +12,9 @@ const fixtures = { getAllEventsNoNamespaceResponse: fs.readFileSync(path.join(__dirname, "/eventsAllNoNamespace.response.xml"), "utf8"), getEventsByTimeResponse: fs.readFileSync(path.join(__dirname, "/eventsByTime.response.xml"), "utf8"), getFutureEventsResponse: fs.readFileSync(path.join(__dirname, "/eventsFuture.response.xml"), "utf8"), + getRecurringEventsCountResponse: fs.readFileSync(path.join(__dirname, "/recurringEventsCount.response.xml"), "utf8"), + getRecurringEventsUntilResponse: fs.readFileSync(path.join(__dirname, "/recurringEventsUntil.response.xml"), "utf8"), + getRecurringEventsEndNeverResponse: fs.readFileSync(path.join(__dirname, "/recurringEventsEndNever.response.xml"), "utf8"), }; module.exports = fixtures; diff --git a/test/fixtures/recurringEventsCount.response.xml b/test/fixtures/recurringEventsCount.response.xml new file mode 100644 index 0000000..f057faa --- /dev/null +++ b/test/fixtures/recurringEventsCount.response.xml @@ -0,0 +1,33 @@ + + + + /16292435102/calendars/92C07D77-CECD-44C6-8239-197FBFFFECC8/D7A8414B-A91A-4696-B8BC-A2EFD286B68A.ics + + + + "kowjd8ik" + + HTTP/1.1 200 OK + + + \ No newline at end of file diff --git a/test/fixtures/recurringEventsEndNever.response.xml b/test/fixtures/recurringEventsEndNever.response.xml new file mode 100644 index 0000000..401931b --- /dev/null +++ b/test/fixtures/recurringEventsEndNever.response.xml @@ -0,0 +1,48 @@ + + + + /16292435102/calendars/92C07D77-CECD-44C6-8239-197FBFFFECC8/D7A8414B-A91A-4696-B8BC-A2EFD286B68A.ics + + + + "kowjd8im" + + HTTP/1.1 200 OK + + + \ No newline at end of file diff --git a/test/fixtures/recurringEventsUntil.response.xml b/test/fixtures/recurringEventsUntil.response.xml new file mode 100644 index 0000000..8a596d0 --- /dev/null +++ b/test/fixtures/recurringEventsUntil.response.xml @@ -0,0 +1,34 @@ + + + + /16292435102/calendars/92C07D77-CECD-44C6-8239-197FBFFFECC8/D7A8414B-A91A-4696-B8BC-A2EFD286B68A.ics + + + + "kowjd8il" + + HTTP/1.1 200 OK + + + \ No newline at end of file From f83a58b05e29e6a3ce1c0425ab0f1916b96b160e Mon Sep 17 00:00:00 2001 From: Antony Sande Date: Thu, 27 May 2021 08:13:14 +0300 Subject: [PATCH 2/4] chore(core): update dependencies --- package-lock.json | 112 +++++++++++++++++++++++++--------------------- package.json | 8 ++-- 2 files changed, 64 insertions(+), 56 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5fa0677..67d96f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -323,9 +323,9 @@ } }, "@es-joy/jsdoccomment": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.4.4.tgz", - "integrity": "sha512-ua4qDt9dQb4qt5OI38eCZcQZYE5Bq3P0GzgvDARdT8Lt0mAUpxKTPy8JGGqEvF77tG1irKDZ3WreeezEa3P43w==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.7.2.tgz", + "integrity": "sha512-i5p0VgxeCXbf5aPLPY9s9Fz6K5BkzYdbRCisw/vEY/FXAxUJ8SiAifPwkFUm0CJrmZ8tFBGW8bUtM7wiE4KTIA==", "dev": true, "requires": { "comment-parser": "^1.1.5", @@ -485,18 +485,18 @@ } }, "@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==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.0.tgz", + "integrity": "sha512-hAEzXi6Wbvlb67NnGMGSNOeAflLVnMa4yliPU/ty1qjgW/vAletH15/v/esJwASSIA0YlIyjnloenFbEZc9q9A==", "dev": true, "requires": { "@sinonjs/commons": "^1.7.0" } }, "@sinonjs/samsam": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.1.tgz", - "integrity": "sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.2.tgz", + "integrity": "sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==", "dev": true, "requires": { "@sinonjs/commons": "^1.6.0", @@ -538,9 +538,9 @@ "dev": true }, "@types/node": { - "version": "14.14.44", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.44.tgz", - "integrity": "sha512-+gaugz6Oce6ZInfI/tK4Pq5wIIkJMEJUu92RB3Eu93mtj4wjjjz9EB5mLp5s1pSsLXdC/CPut/xF20ZzAQJbTA==", + "version": "14.17.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.1.tgz", + "integrity": "sha512-/tpUyFD7meeooTRwl3sYlihx2BrJE7q9XF71EguPFIySj9B7qgnRtHsHTho+0AUm4m1SvWGm6uSncrR94q6Vtw==", "dev": true }, "@types/normalize-package-data": { @@ -1976,9 +1976,9 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.26.0.tgz", - "integrity": "sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.27.0.tgz", + "integrity": "sha512-JZuR6La2ZF0UD384lcbnd0Cgg6QJjiCwhMD6eU4h/VGPcVGwawNNzKU41tgokGXnfjOOyI6QIffthhJTPzzuRA==", "dev": true, "requires": { "@babel/code-frame": "7.12.11", @@ -1989,12 +1989,14 @@ "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", @@ -2006,7 +2008,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.21", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -2015,7 +2017,7 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^6.0.4", + "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -2054,6 +2056,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2199,12 +2207,12 @@ } }, "eslint-plugin-jsdoc": { - "version": "33.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-33.1.1.tgz", - "integrity": "sha512-6Avc2czg/mh0zmuU3H4v2xTXOALl9OiMGpn55nBDydhU684cVgvn2VtXm1JgH+2TW5SxEDnX3o/FUgda7LgVYA==", + "version": "35.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-35.0.0.tgz", + "integrity": "sha512-n92EO6g84qzjF4Lyvg+hDouMQTRHCKvW0hRobGRza0aqbG9fmmlS4p1x8cvPPAc0P87TmahMZnrP0F7hPOcAoQ==", "dev": true, "requires": { - "@es-joy/jsdoccomment": "^0.4.4", + "@es-joy/jsdoccomment": "^0.7.2", "comment-parser": "1.1.5", "debug": "^4.3.1", "esquery": "^1.4.0", @@ -2924,9 +2932,9 @@ } }, "globals": { - "version": "13.8.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", - "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", + "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -3669,6 +3677,12 @@ "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", "dev": true }, + "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==", + "dev": true + }, "lodash.set": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", @@ -4204,13 +4218,13 @@ "dev": true }, "nise": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/nise/-/nise-4.1.0.tgz", - "integrity": "sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz", + "integrity": "sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==", "dev": true, "requires": { "@sinonjs/commons": "^1.7.0", - "@sinonjs/fake-timers": "^6.0.0", + "@sinonjs/fake-timers": "^7.0.4", "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", "path-to-regexp": "^1.7.0" @@ -5216,9 +5230,9 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "scriptlint": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/scriptlint/-/scriptlint-2.1.3.tgz", - "integrity": "sha512-q9m/eBwuHSyLuTKKorr6XV8IYKpzgaryZJaoBY8cLLWq0Zaybv81QAa2Y49EC1/+DaasvIeaYvF9+WvB9Cq6Og==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/scriptlint/-/scriptlint-2.1.4.tgz", + "integrity": "sha512-koAyDkdZYprpcKI0hizLYmAGbFmzWdfLKYjgyhQX7rZUVmXYfDupbZ7E7wg8WioK3qAsCvmEBvcRAiokGPgQeg==", "dev": true, "requires": { "@types/node": "^14.14.37", @@ -5359,25 +5373,19 @@ "dev": true }, "sinon": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-10.0.0.tgz", - "integrity": "sha512-XAn5DxtGVJBlBWYrcYKEhWCz7FLwZGdyvANRyK06419hyEpdT0dMc5A8Vcxg5SCGHc40CsqoKsc1bt1CbJPfNw==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-11.1.1.tgz", + "integrity": "sha512-ZSSmlkSyhUWbkF01Z9tEbxZLF/5tRC9eojCdFh33gtQaP7ITQVaMWQHGuFM7Cuf/KEfihuh1tTl3/ABju3AQMg==", "dev": true, "requires": { - "@sinonjs/commons": "^1.8.1", - "@sinonjs/fake-timers": "^6.0.1", - "@sinonjs/samsam": "^5.3.1", - "diff": "^4.0.2", - "nise": "^4.1.0", - "supports-color": "^7.1.0" + "@sinonjs/commons": "^1.8.3", + "@sinonjs/fake-timers": "^7.1.0", + "@sinonjs/samsam": "^6.0.2", + "diff": "^5.0.0", + "nise": "^5.1.0", + "supports-color": "^7.2.0" }, "dependencies": { - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -5678,9 +5686,9 @@ } }, "table": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.0.tgz", - "integrity": "sha512-SAM+5p6V99gYiiy2gT5ArdzgM1dLDed0nkrWmG6Fry/bUS/m9x83BwpJUOf1Qj/x2qJd+thL6IkIx7qPGRxqBw==", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", "dev": true, "requires": { "ajv": "^8.0.1", @@ -5692,9 +5700,9 @@ }, "dependencies": { "ajv": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.3.0.tgz", - "integrity": "sha512-RYE7B5An83d7eWnDR8kbdaIFqmKCNsP16ay1hDbJEU+sa0e3H9SebskCt0Uufem6cfAVu7Col6ubcn/W+Sm8/Q==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.5.0.tgz", + "integrity": "sha512-Y2l399Tt1AguU3BPRP9Fn4eN+Or+StUGWCUpbnFyXSo8NZ9S4uj+AG2pjs5apK+ZMOwYOz1+a+VKvKH7CudXgQ==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", diff --git a/package.json b/package.json index 8850d2b..f7d69b3 100644 --- a/package.json +++ b/package.json @@ -43,16 +43,16 @@ "homepage": "https://github.com/peerigon/scrapegoat", "devDependencies": { "chai": "^4.3.4", - "eslint": "^7.26.0", + "eslint": "^7.27.0", "eslint-config-peerigon": "^30.1.0", - "eslint-plugin-jsdoc": "^33.1.1", + "eslint-plugin-jsdoc": "^35.0.0", "mocha": "^8.4.0", "nock": "^13.0.11", "nodemon": "^2.0.7", "npm-run-all": "^4.1.5", "nyc": "^15.1.0", - "scriptlint": "^2.1.3", - "sinon": "^10.0.0", + "scriptlint": "^2.1.4", + "sinon": "^11.1.1", "standard-version": "^9.3.0" } } From 347a51f8a0ef4f85275f842bfc01d357413dafc4 Mon Sep 17 00:00:00 2001 From: Antony Sande Date: Fri, 9 Jul 2021 10:18:52 +0300 Subject: [PATCH 3/4] fix(parser): allow user to specify upper iteration bound --- README.md | 47 ++-- lib/Calendar.js | 19 +- lib/xml/parser.js | 16 +- test/Calendar.test.js | 270 +++++++++++++++++++++-- test/integration/request/request.test.js | 56 ++++- 5 files changed, 358 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index ed6c1b2..faac32d 100644 --- a/README.md +++ b/README.md @@ -11,24 +11,34 @@ Specify basic configuration: config = { auth: { user: "username", - pass: "password" + pass: "password", }, // example using baikal as CalDAV server - uri: "http://example.com/cal.php/calendars//" + uri: "http://example.com/cal.php/calendars//", + events: { + maxExpandCount: 10, + }, }; ``` -The request will timeout if it gets no reponse from the CalDav server after 10 seconds. +`config.events.maxExpandCount` is a required parameter that receives a positive number which defines how many upcoming +occurrences of each recurring event should be expanded. This prevents the possibility of an almost infinite loop. +However, if you don't care about running into an infinite loop, you can set it to `Infinity`. + +The request will timeout if it gets no response from the CalDav server after 10 seconds. An optional `timeout` parameter can be provided to override this default by passing an integer containing the number of milliseconds to wait for the server to send the response before aborting the request. ```javascript config = { auth: { user: "username", - pass: "password" + pass: "password", }, uri: "http://example.com/cal.php/calendars//", - timeout: 20000 + events: { + maxExpandCount: 10, + }, + timeout: 20000, }; ``` @@ -70,12 +80,12 @@ You'll get an array of objects, which looks like this: [ { ics: "/cal.php/calendars/test/holidays/6151613161614616.ics", - etag: "fc46dd304e83f572688c68ab63816c8f" + etag: "fc46dd304e83f572688c68ab63816c8f", }, { ics: "/cal.php/calendars/test/holidays/6816189165131651.ics", - etag: "8d59671ba294af1de0e0b154a8ea64c2" - } + etag: "8d59671ba294af1de0e0b154a8ea64c2", + }, ]; ``` @@ -112,12 +122,12 @@ Output should be something like this: hours: 0, minutes: 0, seconds: 0, - isNegative: false + isNegative: false, }, type: { recurring: false, edited: false }, - createdAt: "2017-01-24T15:33:04.000Z" - } - } + createdAt: "2017-01-24T15:33:04.000Z", + }, + }, ]; ``` @@ -132,17 +142,18 @@ If you leave `start` and `end` out, you'll get all upcoming events from today. Passing only one date as a parameter returns all upcoming events from that date. The end-date must be larger that the start-date. +NOTE: The provided `config.events.maxExpandCount` config property takes precedence over the `end` date when expanding +recurring events. The event expansion will be terminated once `config.events.maxExpandCount` is reached. If you wish to +expand all events until the end date you can set `config.events.maxExpandCount` to a really high number, like `3650` +(this would expand an event that recurs every day for 10 years) or set it to `Infinity`. + Example using [moment.js](http://momentjs.com/) for date formatting: ```javascript const moment = require("moment"); -const start = moment() - .startOf("month") - .format("YYYYMMDD[T]HHmmss[Z]"); -const end = moment() - .endOf("month") - .format("YYYYMMDD[T]HHmmss[Z]"); +const start = moment().startOf("month").format("YYYYMMDD[T]HHmmss[Z]"); +const end = moment().endOf("month").format("YYYYMMDD[T]HHmmss[Z]"); scrapegoat.getEventsByTime(start, end).then(console.log); ``` diff --git a/lib/Calendar.js b/lib/Calendar.js index 68053aa..429a622 100644 --- a/lib/Calendar.js +++ b/lib/Calendar.js @@ -12,6 +12,15 @@ function createCalendar(request) { if (!config) { throw new Error("Missing config object"); } + + if (!config.events || config.events.maxExpandCount === undefined) { + throw new Error("Missing config.events.maxExpandCount"); + } + + if (typeof config.events.maxExpandCount !== "number" || config.events.maxExpandCount <= 0) { + throw new TypeError("config.events.maxExpandCount should be a positive number or Infinity"); + } + this.config = config; } @@ -50,7 +59,7 @@ function createCalendar(request) { const filteredEvents = events.filter((event) => event.ics && event.ics.endsWith(".ics")); const multiget = multigetTemplate({ gets: filteredEvents }); - return request(this.config, "REPORT", 1, multiget).then(xmlParser.parseEvents); + return request(this.config, "REPORT", 1, multiget).then((xml) => xmlParser.parseEvents(xml, this.config.events.maxExpandCount)); } /** @@ -59,7 +68,7 @@ function createCalendar(request) { * @returns {Promise} */ getAllEvents() { - return request(this.config, "REPORT", 1, xml.calendarQuery).then(xmlParser.parseEvents); + return request(this.config, "REPORT", 1, xml.calendarQuery).then((xml) => xmlParser.parseEvents(xml, this.config.events.maxExpandCount)); } /** @@ -82,13 +91,15 @@ function createCalendar(request) { if (Boolean(end) && moment(end).isSameOrBefore(start)) { // CalDAV requires end-date to be larger than start-date - end = null; + throw new Error("End date must be after start date"); } + // The returned events from `byTimeTemplate` are already filtered out by start and end date (if provided) + // and only include recurring events that start (or have an occurrence) between the start and end date const xmlRequest = byTimeTemplate({ start, end }); return request(this.config, "REPORT", 1, xmlRequest) - .then((xml) => xmlParser.parseEvents(xml, start, end)); + .then((xml) => xmlParser.parseEvents(xml, this.config.events.maxExpandCount, start, end)); } } diff --git a/lib/xml/parser.js b/lib/xml/parser.js index 79ced29..3de7bf2 100644 --- a/lib/xml/parser.js +++ b/lib/xml/parser.js @@ -136,7 +136,7 @@ function getModifiedOccurenceEventData(vevent) { }; } -function parseEvents(xml, startDate = null, endDate = null) { +function parseEvents(xml, maxExpandCount, startDate = null, endDate = null) { let parsed; const formatted = []; @@ -180,24 +180,24 @@ function parseEvents(xml, startDate = null, endDate = null) { component: vevent, dtstart: vevent.getFirstPropertyValue("dtstart"), }); - const recur = vevent.getFirstPropertyValue("rrule"); - // Expand upto a maximum of 10 upcoming occurrences for events that never end - const MAX_INFINITE_OCCURRENCES_COUNT = 10; const startRange = startDate ? ICAL.Time.fromDateTimeString(moment(startDate).toISOString()) : null; const endRange = endDate ? ICAL.Time.fromDateTimeString(moment(endDate).toISOString()) : null; let occurrencesCount = 0; for (let next = expand.next(); next; next = expand.next()) { - if (startRange && next.compare(startRange) < 0) { - continue; + // If max iteration count is reached: break + if (occurrencesCount >= maxExpandCount) { + break; } + // If date is after end date: break if (endRange && next.compare(endRange) > 0) { break; } - if (!endRange && !recur.isFinite() && occurrencesCount >= MAX_INFINITE_OCCURRENCES_COUNT) { - break; + // If date is before start date: continue + if (startRange && next.compare(startRange) < 0) { + continue; } occurrencesCount += 1; diff --git a/test/Calendar.test.js b/test/Calendar.test.js index c97036f..e73a543 100644 --- a/test/Calendar.test.js +++ b/test/Calendar.test.js @@ -11,6 +11,9 @@ const config = { pass: "password", }, uri: CALENDAR_DOMAIN + CALENDAR_PATH, + events: { + maxExpandCount: 10, + }, }; describe("Calendar", () => { @@ -22,6 +25,75 @@ describe("Calendar", () => { expect(() => new Calendar()).to.throw(Error, /Missing config object/); }); + it("should throw if config.events object is not passed", () => { + const response = fixtures.getCtagResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + + const invalidConfig = { + auth: config.auth, + uri: config.uri, + }; + + expect(() => new Calendar(invalidConfig)).to.throw(Error, /Missing config\.events\.maxExpandCount/); + }); + + it("should throw if config.events.maxExpandCount is not passed", () => { + const response = fixtures.getCtagResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + + const invalidConfig = { + auth: config.auth, + uri: config.uri, + events: {}, + }; + + expect(() => new Calendar(invalidConfig)).to.throw(Error, /Missing config\.events\.maxExpandCount/); + }); + + it("should throw if config.events.maxExpandCount is not a number", () => { + const response = fixtures.getCtagResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + + const invalidConfig = { + auth: config.auth, + uri: config.uri, + events: { maxExpandCount: "10" }, + }; + + expect(() => new Calendar(invalidConfig)).to.throw(TypeError, /config\.events\.maxExpandCount should be a positive number or Infinity/); + }); + + it("should throw if config.events.maxExpandCount is zero", () => { + const response = fixtures.getCtagResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + + const invalidConfig = { + auth: config.auth, + uri: config.uri, + events: { maxExpandCount: 0 }, + }; + + expect(() => new Calendar(invalidConfig)).to.throw(TypeError, /config\.events\.maxExpandCount should be a positive number or Infinity/); + }); + + it("should throw if config.events.maxExpandCount is a number less than zero", () => { + const response = fixtures.getCtagResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + + const invalidConfig = { + auth: config.auth, + uri: config.uri, + events: { maxExpandCount: -10 }, + }; + + expect(() => new Calendar(invalidConfig)).to.throw(TypeError, /config\.events\.maxExpandCount should be a positive number or Infinity/); + }); + describe(".getCtag()", () => { it("should call request with the correct arguments in the correct order", () => { const response = fixtures.getCtagResponse; @@ -41,7 +113,7 @@ describe("Calendar", () => { expect(request.firstCall.args[2]).to.equal(0); }); - it("should return an object with information about the calendar", () => { + it("should return an object with information about the calendar", async () => { const response = fixtures.getCtagResponse; const request = Promise.resolve(response); const Calendar = createCalendar(() => request); @@ -56,7 +128,7 @@ describe("Calendar", () => { }); }); - it("should return an object with information about the calendar (no namespace)", () => { + it("should return an object with information about the calendar (no namespace)", async () => { const response = fixtures.getCtagNoNamespaceResponse; const request = Promise.resolve(response); const Calendar = createCalendar(() => request); @@ -91,7 +163,7 @@ describe("Calendar", () => { expect(request.firstCall.args[2]).to.equal(1); }); - it("should return an array of object with etags of all events", () => { + it("should return an array of object with etags of all events", async () => { const response = fixtures.getEtagsResponse; const request = Promise.resolve(response); const Calendar = createCalendar(() => request); @@ -107,7 +179,7 @@ describe("Calendar", () => { }); }); - it("should return an array of object with etags of all events (no namespace)", () => { + it("should return an array of object with etags of all events (no namespace)", async () => { const response = fixtures.getEtagsNoNamespaceResponse; const request = Promise.resolve(response); const Calendar = createCalendar(() => request); @@ -175,7 +247,7 @@ describe("Calendar", () => { expect(request.firstCall.args[2]).to.equal(1); }); - it("should return an array of objects with the passed events", () => { + it("should return an array of objects with the passed events", async () => { const response = fixtures.getEventsResponse; const request = Promise.resolve(response); const Calendar = createCalendar(() => request); @@ -227,7 +299,7 @@ describe("Calendar", () => { expect(request.firstCall.args[2]).to.equal(1); }); - it("should return an array of objects with all events in the calendar", () => { + it("should return an array of objects with all events in the calendar", async () => { const response = fixtures.getAllEventsResponse; const request = Promise.resolve(response); const Calendar = createCalendar(() => request); @@ -244,7 +316,7 @@ describe("Calendar", () => { }); }); - it("should return an array of objects with all events in the calendar (no namespace)", () => { + it("should return an array of objects with all events in the calendar (no namespace)", async () => { const response = fixtures.getAllEventsNoNamespaceResponse; const request = Promise.resolve(response); const Calendar = createCalendar(() => request); @@ -260,6 +332,30 @@ describe("Calendar", () => { expect(response[0]).to.have.property("data"); }); }); + + it("should return an array of objects with all events in the calendar, respecting maxExpandCount", async () => { + const response = fixtures.getAllEventsResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + const newConfig = { + ...config, + events: { + ...config.events, + maxExpandCount: 3, + }, + }; + const calendar = new Calendar(newConfig); + + return calendar + .getAllEvents() + .then((response) => { + expect(response).to.be.an("array"); + expect(response).to.have.lengthOf(7); + expect(response[0]).to.have.property("ics"); + expect(response[0]).to.have.property("etag"); + expect(response[0]).to.have.property("data"); + }); + }); }); describe(".getEventsByTime()", () => { @@ -281,7 +377,16 @@ describe("Calendar", () => { expect(request.firstCall.args[2]).to.equal(1); }); - it("should return an array of objects with all events that occur between start and end dates", () => { + it("should throw error if end date is smaller than start date", () => { + const response = fixtures.getEventsByTimeResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + const calendar = new Calendar(config); + + expect(() => calendar.getEventsByTime("20160101T000000Z", "20151231T235959Z")).to.throw(Error, /End date must be after start date/); + }); + + it("should return an array of objects with all events that occur between start and end dates", async () => { const response = fixtures.getEventsByTimeResponse; const request = Promise.resolve(response); const Calendar = createCalendar(() => request); @@ -298,7 +403,7 @@ describe("Calendar", () => { }); }); - it("should return an array of objects with all upcoming events from today if start and end are left out", () => { + it("should return an array of objects with all upcoming events from today if start and end are left out", async () => { const response = fixtures.getFutureEventsResponse; const request = Promise.resolve(response); const Calendar = createCalendar(() => request); @@ -316,7 +421,7 @@ describe("Calendar", () => { }); describe("recurring events with count", () => { - it("should expand upto end date if provided", () => { + it("should expand upto end date if provided", async () => { const response = fixtures.getRecurringEventsCountResponse; const request = Promise.resolve(response); const Calendar = createCalendar(() => request); @@ -326,19 +431,67 @@ describe("Calendar", () => { .getEventsByTime("20210601T000000Z", "20210630T000000Z") .then((response) => { expect(response).to.be.an("array"); - expect(response).to.have.lengthOf(22); + expect(response).to.have.lengthOf(10); expect(response[0]).to.have.property("ics"); expect(response[0]).to.have.property("etag"); expect(response[0]).to.have.property("data"); }); }); - it("should expand all upcoming events if end date is not provided", () => { + it("should expand all upcoming events if end date is not provided", async () => { const response = fixtures.getRecurringEventsCountResponse; const request = Promise.resolve(response); const Calendar = createCalendar(() => request); const calendar = new Calendar(config); + return calendar + .getEventsByTime("20210601T000000Z") + .then((response) => { + expect(response).to.be.an("array"); + expect(response).to.have.lengthOf(10); + expect(response[0]).to.have.property("ics"); + expect(response[0]).to.have.property("etag"); + expect(response[0]).to.have.property("data"); + }); + }); + + it("should expand upto end date if provided - maxExpandCount: Infinity", async () => { + const response = fixtures.getRecurringEventsCountResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + const newConfig = { + ...config, + events: { + ...config.events, + maxExpandCount: Infinity, + }, + }; + const calendar = new Calendar(newConfig); + + return calendar + .getEventsByTime("20210601T000000Z", "20210630T000000Z") + .then((response) => { + expect(response).to.be.an("array"); + expect(response).to.have.lengthOf(22); + expect(response[0]).to.have.property("ics"); + expect(response[0]).to.have.property("etag"); + expect(response[0]).to.have.property("data"); + }); + }); + + it("should expand all upcoming events if end date is not provided - maxExpandCount: Infinity", async () => { + const response = fixtures.getRecurringEventsCountResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + const newConfig = { + ...config, + events: { + ...config.events, + maxExpandCount: Infinity, + }, + }; + const calendar = new Calendar(newConfig); + return calendar .getEventsByTime("20210601T000000Z") .then((response) => { @@ -352,7 +505,7 @@ describe("Calendar", () => { }); describe("recurring events with until date", () => { - it("should expand upto end date if provided", () => { + it("should expand upto end date if provided", async () => { const response = fixtures.getRecurringEventsUntilResponse; const request = Promise.resolve(response); const Calendar = createCalendar(() => request); @@ -362,19 +515,67 @@ describe("Calendar", () => { .getEventsByTime("20210601T000000Z", "20210630T000000Z") .then((response) => { expect(response).to.be.an("array"); - expect(response).to.have.lengthOf(22); + expect(response).to.have.lengthOf(10); expect(response[0]).to.have.property("ics"); expect(response[0]).to.have.property("etag"); expect(response[0]).to.have.property("data"); }); }); - it("should expand all upcoming events if end date is not provided", () => { + it("should expand all upcoming events if end date is not provided", async () => { const response = fixtures.getRecurringEventsUntilResponse; const request = Promise.resolve(response); const Calendar = createCalendar(() => request); const calendar = new Calendar(config); + return calendar + .getEventsByTime("20210601T000000Z") + .then((response) => { + expect(response).to.be.an("array"); + expect(response).to.have.lengthOf(10); + expect(response[0]).to.have.property("ics"); + expect(response[0]).to.have.property("etag"); + expect(response[0]).to.have.property("data"); + }); + }); + + it("should expand upto end date if provided - maxExpandCount: Infinity", async () => { + const response = fixtures.getRecurringEventsUntilResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + const newConfig = { + ...config, + events: { + ...config.events, + maxExpandCount: Infinity, + }, + }; + const calendar = new Calendar(newConfig); + + return calendar + .getEventsByTime("20210601T000000Z", "20210630T000000Z") + .then((response) => { + expect(response).to.be.an("array"); + expect(response).to.have.lengthOf(22); + expect(response[0]).to.have.property("ics"); + expect(response[0]).to.have.property("etag"); + expect(response[0]).to.have.property("data"); + }); + }); + + it("should expand all upcoming events if end date is not provided - maxExpandCount: Infinity", async () => { + const response = fixtures.getRecurringEventsUntilResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + const newConfig = { + ...config, + events: { + ...config.events, + maxExpandCount: Infinity, + }, + }; + const calendar = new Calendar(newConfig); + return calendar .getEventsByTime("20210601T000000Z") .then((response) => { @@ -388,12 +589,36 @@ describe("Calendar", () => { }); describe("recurring events that never end", () => { - it("should expand upto end date if provided", () => { + it("should expand upto end date if provided", async () => { const response = fixtures.getRecurringEventsEndNeverResponse; const request = Promise.resolve(response); const Calendar = createCalendar(() => request); const calendar = new Calendar(config); + return calendar + .getEventsByTime("20210601T000000Z", "20210630T000000Z") + .then((response) => { + expect(response).to.be.an("array"); + expect(response).to.have.lengthOf(10); + expect(response[0]).to.have.property("ics"); + expect(response[0]).to.have.property("etag"); + expect(response[0]).to.have.property("data"); + }); + }); + + it("should expand upto end date if provided - maxExpandCount: Infinity", async () => { + const response = fixtures.getRecurringEventsEndNeverResponse; + const request = Promise.resolve(response); + const Calendar = createCalendar(() => request); + const newConfig = { + ...config, + events: { + ...config.events, + maxExpandCount: Infinity, + }, + }; + const calendar = new Calendar(newConfig); + return calendar .getEventsByTime("20210601T000000Z", "20210630T000000Z") .then((response) => { @@ -405,17 +630,24 @@ describe("Calendar", () => { }); }); - it("should expand only 10 upcoming events if end date is not provided", () => { + it("should expand only `maxExpandCount` upcoming events if end date is not provided", async () => { const response = fixtures.getRecurringEventsEndNeverResponse; const request = Promise.resolve(response); const Calendar = createCalendar(() => request); - const calendar = new Calendar(config); + const newConfig = { + ...config, + events: { + ...config.events, + maxExpandCount: 100, + }, + }; + const calendar = new Calendar(newConfig); return calendar .getEventsByTime("20210601T000000Z") .then((response) => { expect(response).to.be.an("array"); - expect(response).to.have.lengthOf(10); + expect(response).to.have.lengthOf(100); expect(response[0]).to.have.property("ics"); expect(response[0]).to.have.property("etag"); expect(response[0]).to.have.property("data"); diff --git a/test/integration/request/request.test.js b/test/integration/request/request.test.js index 134ffa4..70a8254 100644 --- a/test/integration/request/request.test.js +++ b/test/integration/request/request.test.js @@ -12,10 +12,13 @@ const config = { pass: "password", }, uri: CALENDAR_DOMAIN + CALENDAR_PATH, + events: { + maxExpandCount: 10, + }, }; describe("Request", () => { - it("should call request with the correct headers", () => { + it("should call request with the correct headers", async () => { const calendarRequest = nock(CALENDAR_DOMAIN, { reqheaders: { "Content-length": xml.calendarCtag.length, @@ -34,4 +37,55 @@ describe("Request", () => { expect(calendarRequest.isDone()).to.equal(true); }); }); + + it("should throw if fetch response error is not 200 OK", async () => { + nock(CALENDAR_DOMAIN, { + reqheaders: { + "Content-length": xml.calendarCtag.length, + Depth: 0, + host: "example.com", + }, + }) + .intercept(CALENDAR_PATH, "PROPFIND") + .reply(301, "301 Moved Permanently"); + + const Calendar = createCalendar(request); + const calendar = new Calendar({ + ...config, + timeout: 20000, + headers: { + "User-Agent": "scrapegoat/1.0.0", + authorization: `Basic 123456789`, + }, + }); + + return calendar.getCtag().catch((error) => { + expect(error).to.have.property( + "message", + "Response with status code: 301" + ); + }); + }); + + it("should throw if there is a fetch error", async () => { + nock(CALENDAR_DOMAIN, { + reqheaders: { + "Content-length": xml.calendarCtag.length, + Depth: 0, + host: "example.com", + }, + }) + .intercept(CALENDAR_PATH, "PROPFIND") + .replyWithError("something awful happened"); + + const Calendar = createCalendar(request); + const calendar = new Calendar(config); + + return calendar.getCtag().catch((error) => { + expect(error).to.have.property( + "message", + "request to http://example.com/cal.php/calendars/user/calendar_name/ failed, reason: something awful happened" + ); + }); + }); }); From 9dee7b02c8771293cbc2754e3f1b28bc8b8c72e2 Mon Sep 17 00:00:00 2001 From: Antony Sande Date: Fri, 9 Jul 2021 11:12:12 +0300 Subject: [PATCH 4/4] chore(core): update dependencies --- package-lock.json | 246 ++++++++++++++++++++++++++-------------------- package.json | 12 +-- 2 files changed, 147 insertions(+), 111 deletions(-) diff --git a/package-lock.json b/package-lock.json index 67d96f6..4462026 100644 --- a/package-lock.json +++ b/package-lock.json @@ -323,44 +323,50 @@ } }, "@es-joy/jsdoccomment": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.7.2.tgz", - "integrity": "sha512-i5p0VgxeCXbf5aPLPY9s9Fz6K5BkzYdbRCisw/vEY/FXAxUJ8SiAifPwkFUm0CJrmZ8tFBGW8bUtM7wiE4KTIA==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.8.0.tgz", + "integrity": "sha512-Xd3GzYsL2sz2pcdtYt5Q0Wz1ol/o9Nt2UQL4nFPDcaEomvPmwjJsbjkKx1SKhl2h3TgwazNBLdcNr2m0UiGiFA==", "dev": true, "requires": { "comment-parser": "^1.1.5", "esquery": "^1.4.0", - "jsdoctypeparser": "^9.0.0" + "jsdoc-type-pratt-parser": "1.0.4" } }, "@eslint/eslintrc": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.1.tgz", - "integrity": "sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", + "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.1.1", "espree": "^7.3.0", - "globals": "^12.1.0", + "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "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" - } - } } }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", + "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "dev": true + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -538,9 +544,9 @@ "dev": true }, "@types/node": { - "version": "14.17.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.1.tgz", - "integrity": "sha512-/tpUyFD7meeooTRwl3sYlihx2BrJE7q9XF71EguPFIySj9B7qgnRtHsHTho+0AUm4m1SvWGm6uSncrR94q6Vtw==", + "version": "14.17.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.5.tgz", + "integrity": "sha512-bjqH2cX/O33jXT/UmReo2pM7DIJREPMnarixbQ57DOOzzFaI6D2+IcwaJQaJpv0M1E9TIhPCYVxrkcityLjlqA==", "dev": true }, "@types/normalize-package-data": { @@ -667,9 +673,9 @@ "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==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true }, "add-stream": { @@ -1976,13 +1982,14 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.27.0.tgz", - "integrity": "sha512-JZuR6La2ZF0UD384lcbnd0Cgg6QJjiCwhMD6eU4h/VGPcVGwawNNzKU41tgokGXnfjOOyI6QIffthhJTPzzuRA==", + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.30.0.tgz", + "integrity": "sha512-VLqz80i3as3NdloY44BQSJpFw534L9Oh+6zJOUaViV4JPd+DaHwutqP7tcpkW3YiXbK6s05RZl7yl7cQn+lijg==", "dev": true, "requires": { "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.1", + "@eslint/eslintrc": "^0.4.2", + "@humanwhocodes/config-array": "^0.5.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -1999,7 +2006,7 @@ "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", + "glob-parent": "^5.1.2", "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", @@ -2207,20 +2214,31 @@ } }, "eslint-plugin-jsdoc": { - "version": "35.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-35.0.0.tgz", - "integrity": "sha512-n92EO6g84qzjF4Lyvg+hDouMQTRHCKvW0hRobGRza0aqbG9fmmlS4p1x8cvPPAc0P87TmahMZnrP0F7hPOcAoQ==", + "version": "35.4.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-35.4.2.tgz", + "integrity": "sha512-Ls6a1DDWS0XHx0ZH9iMaHNy/HltQseG2kO0S0xlaS1vataPSPjGpv2vSdQUAHnvwpH75VfNad6OvlLCfKalL+w==", "dev": true, "requires": { - "@es-joy/jsdoccomment": "^0.7.2", + "@es-joy/jsdoccomment": "^0.8.0", "comment-parser": "1.1.5", - "debug": "^4.3.1", + "debug": "^4.3.2", "esquery": "^1.4.0", - "jsdoctypeparser": "^9.0.0", + "jsdoc-type-pratt-parser": "^1.0.4", "lodash": "^4.17.21", - "regextras": "^0.7.1", + "regextras": "^0.8.0", "semver": "^7.3.5", "spdx-expression-parse": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + } } }, "eslint-plugin-no-unsafe-regex": { @@ -2536,9 +2554,9 @@ } }, "flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.1.tgz", + "integrity": "sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg==", "dev": true }, "foreground-child": { @@ -2932,9 +2950,9 @@ } }, "globals": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", - "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz", + "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -3352,6 +3370,12 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -3516,10 +3540,10 @@ "esprima": "^4.0.0" } }, - "jsdoctypeparser": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-9.0.0.tgz", - "integrity": "sha512-jrTA2jJIL6/DAEILBEh2/w9QxCuwmvNXIry39Ay/HVfhE3o2yVV0U44blYkqdHA/OKloJEqvJy0xU+GSdE2SIw==", + "jsdoc-type-pratt-parser": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-1.0.4.tgz", + "integrity": "sha512-jzmW9gokeq9+bHPDR1nCeidMyFUikdZlbOhKzh9+/nJqB75XhpNKec1/UuxW5c4+O+Pi31Gc/dCboyfSm/pSpQ==", "dev": true }, "jsesc": { @@ -3696,12 +3720,13 @@ "dev": true }, "log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "requires": { - "chalk": "^4.0.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "dependencies": { "ansi-styles": { @@ -4043,33 +4068,33 @@ } }, "mocha": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", - "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.0.2.tgz", + "integrity": "sha512-FpspiWU+UT9Sixx/wKimvnpkeW0mh6ROAKkIaPokj3xZgxeRhcna/k5X57jJghEr8X+Cgu/Vegf8zCX5ugSuTA==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.1", + "chokidar": "3.5.2", "debug": "4.3.1", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.6", + "glob": "7.1.7", "growl": "1.10.5", "he": "1.2.0", - "js-yaml": "4.0.0", - "log-symbols": "4.0.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", "minimatch": "3.0.4", "ms": "2.1.3", - "nanoid": "3.1.20", - "serialize-javascript": "5.0.1", + "nanoid": "3.1.23", + "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", "wide-align": "1.1.3", - "workerpool": "6.1.0", + "workerpool": "6.1.5", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -4081,6 +4106,22 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -4097,20 +4138,6 @@ "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" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -4118,9 +4145,9 @@ "dev": true }, "js-yaml": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", - "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -4165,6 +4192,15 @@ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -4194,9 +4230,9 @@ "dev": true }, "nanoid": { - "version": "3.1.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", - "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", "dev": true }, "natural-compare": { @@ -4231,9 +4267,9 @@ } }, "nock": { - "version": "13.0.11", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.11.tgz", - "integrity": "sha512-sKZltNkkWblkqqPAsjYW0bm3s9DcHRPiMOyKO/PkfJ+ANHZ2+LA2PLe22r4lLrKgXaiSaDQwW3qGsJFtIpQIeQ==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.1.1.tgz", + "integrity": "sha512-YKTR9MjfK3kS9/l4nuTxyYm30cgOExRHzkLNhL8nhEUyU4f8Za/dRxOqjhVT1vGs0svWo3dDnJTUX1qxYeWy5w==", "dev": true, "requires": { "debug": "^4.1.0", @@ -4263,9 +4299,9 @@ "dev": true }, "nodemon": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz", - "integrity": "sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.10.tgz", + "integrity": "sha512-369KB2EC1fLzz7hIuKSRSIVhh9PXqFAwh1stxlNX8DMyat9y/maswuRxRMttyelnduLDa04r4wgVZ4fgRwZWuQ==", "dev": true, "requires": { "chokidar": "^3.2.2", @@ -4333,9 +4369,9 @@ "dev": true }, "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", "dev": true }, "npm-run-all": { @@ -5101,9 +5137,9 @@ "dev": true }, "regextras": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.7.1.tgz", - "integrity": "sha512-9YXf6xtW+qzQ+hcMQXx95MOvfqXFgsKDZodX3qZB0x2n5Z94ioetIITsBtvJbiOyxa/6s9AtyweBLCdPmPko/w==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.8.0.tgz", + "integrity": "sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ==", "dev": true }, "registry-auth-token": { @@ -5230,9 +5266,9 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "scriptlint": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/scriptlint/-/scriptlint-2.1.4.tgz", - "integrity": "sha512-koAyDkdZYprpcKI0hizLYmAGbFmzWdfLKYjgyhQX7rZUVmXYfDupbZ7E7wg8WioK3qAsCvmEBvcRAiokGPgQeg==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/scriptlint/-/scriptlint-2.1.6.tgz", + "integrity": "sha512-vuZUNAxlt3yn+mhf/gLwRXsCpwMJh7t/1y9EMfH+IV2BGMCkkxG/s5zkCtyLIoVQ1mxRvM0crpW2mW0dvcUktw==", "dev": true, "requires": { "@types/node": "^14.14.37", @@ -5320,9 +5356,9 @@ } }, "serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -5700,9 +5736,9 @@ }, "dependencies": { "ajv": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.5.0.tgz", - "integrity": "sha512-Y2l399Tt1AguU3BPRP9Fn4eN+Or+StUGWCUpbnFyXSo8NZ9S4uj+AG2pjs5apK+ZMOwYOz1+a+VKvKH7CudXgQ==", + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.1.tgz", + "integrity": "sha512-42VLtQUOLefAvKFAQIxIZDaThq6om/PrfP0CYk3/vn+y4BMNkKnbli8ON2QCiHov4KkzOSJ/xSoBJdayiiYvVQ==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -6143,9 +6179,9 @@ "dev": true }, "workerpool": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", - "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", "dev": true }, "wrap-ansi": { diff --git a/package.json b/package.json index f7d69b3..1f8dcd7 100644 --- a/package.json +++ b/package.json @@ -43,15 +43,15 @@ "homepage": "https://github.com/peerigon/scrapegoat", "devDependencies": { "chai": "^4.3.4", - "eslint": "^7.27.0", + "eslint": "^7.30.0", "eslint-config-peerigon": "^30.1.0", - "eslint-plugin-jsdoc": "^35.0.0", - "mocha": "^8.4.0", - "nock": "^13.0.11", - "nodemon": "^2.0.7", + "eslint-plugin-jsdoc": "^35.4.2", + "mocha": "^9.0.2", + "nock": "^13.1.1", + "nodemon": "^2.0.10", "npm-run-all": "^4.1.5", "nyc": "^15.1.0", - "scriptlint": "^2.1.4", + "scriptlint": "^2.1.6", "sinon": "^11.1.1", "standard-version": "^9.3.0" }