diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..510ebaa --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +**/.DS_Store diff --git a/index.js b/index.ts similarity index 60% rename from index.js rename to index.ts index 54e5dae..aba4b72 100644 --- a/index.js +++ b/index.ts @@ -1,29 +1,39 @@ const { spawn, exec } = require('child_process') const fs = require('fs') const youtubedl = require('youtube-dl-exec') -const tmp = require('tmp') +import {FileResult, fileSync} from 'tmp' +// const {fileSync} = require('tmp') const playlist = require('./playlist-search') const scraper = require('./playlist-scrape') +const {getNewPoll} = require('./poll-master') const STREAM_URL = "https://www.youtube.com/henrikomagnifico/live" const DURATION_REGEX = /(\d{1,2}:\d{2})\/(\d{1,2}:\d{2})/ const EXPIRATION_REGEX = /.+\/expire\/([0-9]{10})\/.+/ +const thresholdMap = [20, 17.5, 15, 12.5, 10, 7.5, 5].reduce((obj, t) => { + obj[t] = {trackVotes: {correct: 0, skipped: 0}, positionVotes: {correct: 0, skipped: 0}} + return obj +}, {}) const thresholds = [20, 17.5, 15, 12.5, 10, 7.5, 5].map(t => ({threshold: t, trackVotes: {correct: 0, skipped: 0}, positionVotes: {correct: 0, skipped: 0}})) -let albumWhitelistCharacters = playlist.getValidAlbumCharacters() -let trackWhitelistCharacters = playlist.getValidTrackCharacters() let currentTrack = {} +let currentTrackPosition = { + position: 0, + positionTimestamp: 0 +} +let trackIdentified = false let nextTrackTimestamp = 0 let readAttempts = 0 let totalVotes = 0 +let preemptiveCallback = null let resolvedUrl = { url: '', expires: 0 } -var trackStatsFileStream = fs.createWriteStream("track.csv", {flags: 'a'}); -var positionStatsFileStream = fs.createWriteStream("position.csv", {flags: 'a'}); +const trackStatsFileStream = fs.createWriteStream("track.csv", {flags: 'a'}); +const positionStatsFileStream = fs.createWriteStream("position.csv", {flags: 'a'}); thresholds.forEach(t => { trackStatsFileStream.write(t.threshold + ",") positionStatsFileStream.write(t.threshold + ",") @@ -31,14 +41,14 @@ thresholds.forEach(t => { trackStatsFileStream.write('\n') positionStatsFileStream.write('\n') -function readText(tmpfile, charWhitelist) { +function readText(tmpFile: FileResult, charWhitelist: string = null) { return new Promise((resolve, reject) => { - let command = `tesseract ${tmpfile.name} -` + let command = `tesseract ${tmpFile.name} -` if (charWhitelist) { command += ` -c tessedit_char_whitelist="${charWhitelist}"` } exec(command, ((error, stdout, stderr) => { - tmpfile.removeCallback() + tmpFile.removeCallback() if (error) { console.error(stderr) reject(error) @@ -49,9 +59,9 @@ function readText(tmpfile, charWhitelist) { }) } -function thresholdImage(image, threshold) { +function thresholdImage(image, threshold): Promise { return new Promise(((resolve, reject) => { - const tmpFile = tmp.fileSync() + const tmpFile = fileSync() exec(`convert png:${image.name} -white-threshold ${threshold}% -colorspace HSB -channel B -separate ${tmpFile.name}`, ((error, stdout, stderr) => { if (error) { console.error(stderr) @@ -65,9 +75,9 @@ function thresholdImage(image, threshold) { } -function getRegion(frame, x, y, width, height) { // Returns Promise +function getRegion(frame, x, y, width, height): Promise { // Returns Promise return new Promise(function (resolve, reject) { - const tmpFile = tmp.fileSync() + const tmpFile = fileSync() exec(`convert png:${frame.name} -negate -crop ${width}x${height}+${x}+${y} png:${tmpFile.name}`, ((error, stdout, stderr) => { if (error) { console.error(stderr) @@ -80,9 +90,9 @@ function getRegion(frame, x, y, width, height) { // Returns Promise }); } -function getFrame(url) { +function getFrame(url): Promise { return new Promise((resolve, reject) => { - const frameFile = tmp.fileSync() + const frameFile = fileSync() exec(`ffmpeg -i ${url} -y -f image2 -c:v png -frames:v 1 ${frameFile.name}`, (err, stdout, stderr) => { if (err) { console.log(stderr) @@ -245,13 +255,20 @@ function tallyVotes(votes) { } } -function timeUntilNextTrack(position, duration) { +function millisToNextTrack(position, duration) { + const durationInSeconds = timeToSeconds(duration) + const positionInSeconds = timeToSeconds(position) + return (durationInSeconds - positionInSeconds) * 1000 +} + +function timeToSeconds(time) { const timeRegex = /(\d{1,2}):(\d{2})/ - const durationValues = duration.match(timeRegex) - const positionValues = position.match(timeRegex) - const durationInSeconds = (parseInt(durationValues[1]) * 60) + parseInt(durationValues[2]) - const positionInSeconds = (parseInt(positionValues[1]) * 60) + parseInt(positionValues[2]) - return durationInSeconds - positionInSeconds + if (timeRegex.test(time)) { + const timeValues = time.match(timeRegex) + return (parseInt(timeValues[1]) * 60) + parseInt(timeValues[2]) + } else { + throw `${time} does not match MM:SS format` + } } function performQuorumProcessing(titleCroppedPromise, albumCroppedPromise, durationCroppedPromise) { @@ -283,6 +300,69 @@ function performQuorumProcessing(titleCroppedPromise, albumCroppedPromise, durat })) } +function logVotingResults(votes, result, voteType) { + Object.keys(votes).forEach(t => { + const tracker = thresholdMap[t] + if (votes[t] != null) { + if (votes[t] === result) { + tracker[voteType].correct++ + } + } else { + tracker[voteType].skipped++ + } + }) +} + + +async function positionDurationVoting(croppedDuration, voteOnDuration) { + const matchIndex = voteOnDuration ? 2 : 1 + return new Promise((async (resolve) => { + const positionPollName = `position-${matchIndex}-${Date.now()}` + poll.registerNewPoll(positionPollName, thresholds.length, (result, votes) => { + // Log votes + logVotingResults(votes, result, 'positionVotes') + // Return result + resolve(result) + }, (votes) => { + // Log votes + logVotingResults(votes, null, 'positionVotes') + resolve(null) + }) + for (const t of Object.keys(thresholdMap)) { + const thresholded = await thresholdImage(croppedDuration, t) + const ocrText = await readText(thresholded, "1234567890:/") + let capturedDuration = DURATION_REGEX.test(ocrText) ? ocrText.match(DURATION_REGEX) : [null, null, null] + poll.voteInPoll(positionPollName, t, capturedDuration[matchIndex]) + } + })) +} + +async function trackVoting(croppedTitle, croppedAlbum, duration) { + return new Promise(async (resolve) => { + const poll = getNewPoll(Object.keys(thresholdMap).length, (result, votes) => { + logVotingResults(votes, result, 'trackVotes') + console.log('trackVoteResult', result) + resolve(playlist.getAt(result)) + }, (votes) => { + logVotingResults(votes, null, 'trackVotes') + resolve(null) + }) + for (const t of Object.keys(thresholdMap)) { + Promise.all([thresholdImage(croppedTitle, t).then(text => readText(text)), thresholdImage(croppedAlbum, t).then(text => readText(text))]).then(trackOcr => { + const title = trackOcr[0].trim() + const album = trackOcr[1].trim() + const track = playlist.search(album, title, duration) + if (track != null) { + poll.vote(t, track.refIndex) + } else { + poll.skipVote(t) + } + }) + } + + }) +} + async function updateStreamUrl() { const ytStreamUrl = await getYoutubeStream() resolvedUrl.url = ytStreamUrl @@ -328,11 +408,11 @@ async function updateTrackInfo() { console.log(`${t.threshold}%:\tTrack: [Skip: ${skippedTrackRatio.toFixed(2)}% Correct: ${accurateTrackRatio.toFixed(2)}% Eff.: ${trackEffectiveness.toFixed(2)}%]\tPos: [Skip: ${skippedPositionRatio.toFixed(2)}% Correct: ${accuratePositionRatio.toFixed(2)}% Eff.: ${positionEffectiveness.toFixed(2)}%]`) }) if (result.position) { - const secondsUntilNext = timeUntilNextTrack(result.position, currentTrack.duration) + const secondsUntilNext = millisToNextTrack(result.position, currentTrack.duration) const processingTime = Date.now() - processingStart const timeInFrame = Date.now() - frameTime nextTrackTimestamp = frameTime + (secondsUntilNext * 1000) - setTimeout(() => updateTrackInfo(), (secondsUntilNext * 1000) - timeInFrame + 750) + setTimeout(() => updateTrackInfo(), secondsUntilNext - timeInFrame + 750) console.log(`\nFrame processing took ${processingTime}ms (Frame retrieval: ${frameTime - processingStart}ms) (Processing & voting: ${votingTime - frameTime}ms)`) console.log(`${currentTrack.album}: ${currentTrack.track} (${result.position}/${currentTrack.duration})`) console.log(`Next track should be at: ${new Date(nextTrackTimestamp).toLocaleString()}`) @@ -343,6 +423,108 @@ async function updateTrackInfo() { } } +async function updateTrackInfo_v2() { + trackIdentified = false + const urlExpiresSoon = resolvedUrl.expires < (Date.now() + (30 * 60 * 1000)) + if (resolvedUrl.expires < Date.now()) { + await updateStreamUrl() + console.log("Got stream info") + } else if (urlExpiresSoon) { + updateStreamUrl().then(() => console.log("Updated stream info in the background")) + } + console.log(`V2: Now: ${Date.now()}\tExpiration: ${resolvedUrl.expires}\tAttempt: ${readAttempts}`) + const processingStart = Date.now() + const frame = await getFrame(resolvedUrl.url) + const frameTime = Date.now() + const croppedDuration = await getRegion(frame, 0, 1028, 235, 34) + + positionDurationVoting(croppedDuration, true).then(async duration => { + // Not checking if duration was null is fine because we can search without it + const title = await getRegion(frame, 432, 850, 1487, 100) + const album = await getRegion(frame, 440, 957, 1487, 32) + const trackResult = await trackVoting(title, album, duration) + if (trackResult == null) { + // Didn't get a result, try again + setTimeout(updateTrackInfo_v2, 0) + } else { + console.log("identified track") + currentTrack = trackResult + trackIdentified = true + if (positionIsIdentified()) { + registerNextTrackCheck(currentTrackPosition.position, currentTrack.duration, frameTime) + printTrack(frameTime, processingStart) + } else { + // We know that a song will only run as long as the duration, + // so we can at least register a callback for that far out + const timeoutLength = millisToNextTrack("0:00", currentTrack.duration) - (Date.now() - frameTime) + 750 + preemptiveCallback = setTimeout(updateTrackInfo_v2, timeoutLength) + } + } + + }).catch(err => { + // + }) + + if (!positionIsIdentified()) { + updatePosition(frame, frameTime).then(position => { + if (position != null) { + console.log("identified position") + if (trackIdentified) { + registerNextTrackCheck(position, currentTrack.duration, frameTime) + printTrack(frameTime, processingStart) + if (preemptiveCallback != null) { + clearTimeout(preemptiveCallback) + preemptiveCallback = null + } + } else { + currentTrackPosition.position = position + currentTrackPosition.positionTimestamp = frameTime + } + } else { + console.log("position missing") + setTimeout(updatePosition, 0) + } + }) + } +} + +function printTrack(frameTime, processingStart) { + const now = Date.now() + const processingTime = now - processingStart + const votingTime = now - frameTime + const frameRetrievalTime = frameTime - processingStart + console.log(`\nFrame processing took ${processingTime}ms (Frame retrieval: ${frameRetrievalTime}ms) (Processing & voting: ${votingTime}ms)`) + console.log(`${currentTrack.album}: ${currentTrack.track} (${currentTrackPosition.position}/${currentTrack.duration})`) + console.log(`Next track should be at: ${new Date(nextTrackTimestamp).toLocaleString()}`) +} + +function registerNextTrackCheck(position, duration, frameTime) { + nextTrackTimestamp = millisToNextTrack(position, duration) - (Date.now() - frameTime) + setTimeout(updateTrackInfo_v2, nextTrackTimestamp + 750) +} + +function positionIsIdentified() { + return nextTrackTimestamp < currentTrackPosition.positionTimestamp +} + +async function updatePosition(frame, frameTime) { + // Stream URL update shouldn't need to be called from here + if (frame == null) { + frame = await getFrame(resolvedUrl.url) + frameTime = Date.now() + } + const durationFrame = await getRegion(frame, 0, 1028, 235, 34) + const position = await positionDurationVoting(durationFrame, false) + return position + if (position == null) { + // Position wasn't found, and we need it. Try again. + setTimeout(updatePosition, 0) + } else { + currentTrackPosition.position = position + currentTrackPosition.positionTimestamp = frameTime + } +} + // getYoutubeStream() // .then(url => getFrame(url)) // .then(frame => { @@ -359,4 +541,4 @@ async function updateTrackInfo() { // }) // .catch(err => console.error(err)) -updateTrackInfo() +updateTrackInfo_v2() diff --git a/package-lock.json b/package-lock.json index 0709edf..6e3f4d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,79 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha1-9K1DWqJj25NbjxDyxVLSP7cWpj8=", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha1-yaHwIZF9y1zPDU5FPjmQIpgfye0=", + "dev": true + }, + "@babel/highlight": { + "version": "7.13.10", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/@babel/highlight/-/highlight-7.13.10.tgz", + "integrity": "sha1-qLKmYUj1sn1maxXYF3Q0enMdUtE=", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha1-zUJUFnelQzPPVBpJEIwUMrRMlCQ=", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, + "@eslint/eslintrc": { + "version": "0.4.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", + "integrity": "sha1-mcwKBYTXLx3zi5APsGK6mV85VUc=", + "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", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/debug/-/debug-4.3.1.tgz", + "integrity": "sha1-8NIpxQXgxtjEmsVT0bE9wYP2su4=", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/ms/-/ms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + } + } + }, "@sindresorhus/is": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz", @@ -54,6 +127,18 @@ "@types/node": "*" } }, + "@types/tmp": { + "version": "0.2.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/@types/tmp/-/tmp-0.2.0.tgz", + "integrity": "sha1-4/UrTXOX6qkZNZLvP91E3Ar0KYw=", + "dev": true + }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha1-qlgEJxHW4ydd033Fl+XTHowpCkQ=", + "dev": true + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -63,6 +148,70 @@ "negotiator": "0.6.2" } }, + "acorn": { + "version": "7.4.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha1-/q7SVZc9LndVW4PbwIhRpsY1IPo=", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha1-/IZh4Rt6wVOcR9v+oucrOvNNJns=", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha1-uvWmLoArB9l3A0WG+MO69a3ybfQ=", + "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" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha1-y7muJWv3UK8eqzRPIpqif+lLo0g=", + "dev": true + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha1-OIU59VF5vzkznIGvMKZU1p+Hy3U=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha1-xV7PAhheJGklk5kxDBc84xIzsUI=", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "array-find-index": { "version": "1.0.2", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/array-find-index/-/array-find-index-1.0.2.tgz", @@ -78,6 +227,12 @@ "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/arrify/-/arrify-1.0.1.tgz", "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha1-SDFDxWeu7UeFdZwIZXhtx319LjE=", + "dev": true + }, "balanced-match": { "version": "1.0.0", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/balanced-match/-/balanced-match-1.0.0.tgz", @@ -136,6 +291,12 @@ "meow": "^5.0.0" } }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha1-dfUC7q+f/eQvyYgpZFvk6na9ni0=", + "dev": true + }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -167,6 +328,21 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/braces/-/braces-3.0.2.tgz", + "integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha1-uqVZ7hTO1zRSIputcyZGfGH6vWA=", + "dev": true + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -191,6 +367,12 @@ "responselike": "^2.0.0" } }, + "callsites": { + "version": "3.1.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha1-s2MKvYlDQy9Us/BRkjjjPNffL3M=", + "dev": true + }, "camelcase": { "version": "4.1.0", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/camelcase/-/camelcase-4.1.0.tgz", @@ -213,6 +395,57 @@ } } }, + "chalk": { + "version": "4.1.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha1-ThSHCmGNni7dl92DRf2dncMVZGo=", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "cheerio": { "version": "1.0.0-rc.5", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.5.tgz", @@ -239,6 +472,33 @@ "domutils": "^2.4.4" } }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha1-7pznu+vSt59J8wR5nVRo4x4U5oo=", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha1-oCZe5lVHb8gHrqnfPfjfd4OAi08=", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", @@ -247,6 +507,21 @@ "mimic-response": "^1.0.0" } }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha1-u3GFBpDh8TZWfeYp0tVHHe2kweg=", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/concat-map/-/concat-map-0.0.1.tgz", @@ -361,6 +636,12 @@ } } }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, "defer-to-connect": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", @@ -376,6 +657,21 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "diff": { + "version": "5.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/diff/-/diff-5.0.0.tgz", + "integrity": "sha1-ftatdthZ0DB4fsNYVfWx2vMdhSs=", + "dev": true + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha1-rd6+rXKmV023g2OdyHoSF3OXOWE=", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "dom-serializer": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.2.0.tgz", @@ -414,6 +710,12 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha1-6Bj9ac5cz8tARZT4QpY79TFkzDc=", + "dev": true + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -427,6 +729,15 @@ "once": "^1.4.0" } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha1-Kn/l3WNKHkElqXXsmU/1RW3Dc00=", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, "entities": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", @@ -440,11 +751,239 @@ "is-arrayish": "^0.2.1" } }, + "escalade": { + "version": "3.1.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha1-2M/ccACWXFoBdLSoLqpcBVJ0LkA=", + "dev": true + }, "escape-html": { "version": "1.0.3", "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://artifactory.cwantools.io/api/npm/npm-group/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "7.21.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/eslint/-/eslint-7.21.0.tgz", + "integrity": "sha1-Ts1bjFtE9d7cm4oRCwG7/rFdHIM=", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.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.4.0", + "esutils": "^2.0.2", + "file-entry-cache": "^6.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", + "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" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha1-9zqFudXUHQRVUcF34ogtSshXKKY=", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/debug/-/debug-4.3.1.tgz", + "integrity": "sha1-8NIpxQXgxtjEmsVT0bE9wYP2su4=", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/ms/-/ms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha1-WB9q3mWMu6ZaDTOA3ndTKVBU83U=", + "dev": true + }, + "semver": { + "version": "7.3.4", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/semver/-/semver-7.3.4.tgz", + "integrity": "sha1-J6qn0uTKdkUvmNOt0JOnLJQ+3Jc=", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha1-zNCvT4g1+9wmW4JGGq8MNmY/NOo=", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha1-rhbxZE2HPsrYQ7AwexQzYtTEIXI=", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/which/-/which-2.0.2.tgz", + "integrity": "sha1-fGqN0KY2oDJ+ELWckobu6T8/UbE=", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha1-54blmmbLkrP2wfsNUIqrF0hI9Iw=", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha1-0t5eA0JOcH3BDHQGjd7a5wh0Gyc=", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha1-MOvR73wv3/AcOk8VEESvJfqwUj4=", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha1-If3I+82ceVzAMh8FY3AglXUVEag=", + "dev": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/espree/-/espree-7.3.1.tgz", + "integrity": "sha1-8t8zC3Usb1UBn4vYm3ZgA5wbu7Y=", + "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://artifactory.cwantools.io/api/npm/npm-group/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha1-MOvR73wv3/AcOk8VEESvJfqwUj4=", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha1-IUj/w4uC6McFff7UhCWz5h8PJKU=", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha1-MH30JUfmzHMk088DwVXVzbjFOIA=", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha1-eteWTWeauyi+5yzsY3WLHF0smSE=", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha1-MH30JUfmzHMk088DwVXVzbjFOIA=", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha1-OYrT88WiSUi+dyXoPRGn3ijNvR0=", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=", + "dev": true + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -559,6 +1098,42 @@ "vary": "~1.1.2" } }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha1-IRst2WWcsDlLBz5zI6w8kz1SICc=", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -589,6 +1164,28 @@ "semver-regex": "^2.0.0" } }, + "flat": { + "version": "5.0.2", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/flat/-/flat-5.0.2.tgz", + "integrity": "sha1-jKb+MyBp/6nTJMMnGYxZglnOskE=", + "dev": true + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha1-YbAzgwKy/p+Vfcwy/CqH8cMEixE=", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.1.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha1-xLSJ6ACW2d8d/JfHmHGup8YXxGk=", + "dev": true + }, "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", @@ -604,16 +1201,35 @@ "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha1-ilJveLj99GI7cJ4Ll1xSwkwC/Ro=", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=" }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "fuse.js": { "version": "6.4.6", "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.4.6.tgz", "integrity": "sha512-/gYxR/0VpXmWSfZOIPS3rWwU8SHgsRTwWuXhyb2O6s7aRuVtHtxCkR33bNYu3wyLyNx/Wpv0vU7FZy8Vj53VNw==" }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha1-T5RBKoLbMvNuOwuXQfipf+sDH34=", + "dev": true + }, "get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -635,6 +1251,24 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha1-hpgyxYA0/mikCTwX3BXoNA2EAcQ=", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/globals/-/globals-12.4.0.tgz", + "integrity": "sha1-oYgTV2pBsAokqX5/gVkYwuGZJfg=", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, "got": { "version": "11.8.1", "resolved": "https://registry.npmjs.org/got/-/got-11.8.1.tgz", @@ -658,6 +1292,12 @@ "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/graceful-fs/-/graceful-fs-4.2.6.tgz", "integrity": "sha1-/wQLKwhTsjw9MQJ1I3BvGIXXa+4=" }, + "growl": { + "version": "1.10.5", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/growl/-/growl-1.10.5.tgz", + "integrity": "sha1-8nNdwig2dPpnR4sQGBBZNVw2nl4=", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/has/-/has-1.0.3.tgz", @@ -666,6 +1306,18 @@ "function-bind": "^1.1.1" } }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/he/-/he-1.2.0.tgz", + "integrity": "sha1-hK5l+n6vsWX922FWauFLrwVmTw8=", + "dev": true + }, "hosted-git-info": { "version": "2.8.8", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/hosted-git-info/-/hosted-git-info-2.8.8.tgz", @@ -721,6 +1373,28 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "ignore": { + "version": "4.0.6", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha1-dQ49tYYgh7RzfrrIIH/9HvJ7Jfw=", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha1-NxYsJfy566oublPVtNiM4X2eDCs=", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, "indent-string": { "version": "3.2.0", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/indent-string/-/indent-string-3.2.0.tgz", @@ -750,6 +1424,15 @@ "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha1-6h9/O4DwZCNug0cPhsCcJU+0Wwk=", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, "is-core-module": { "version": "2.2.0", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/is-core-module/-/is-core-module-2.2.0.tgz", @@ -758,6 +1441,33 @@ "has": "^1.0.3" } }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha1-8Rb4Bk/pCz94RKOJl8C3UFEmnx0=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha1-dWfb6fL14kZ7x3q4PEopSCQHpdw=", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=", + "dev": true + }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -778,6 +1488,22 @@ "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha1-2ugS/bOCX6MGYJqHFzg8UMNqBTc=", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -788,6 +1514,18 @@ "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha1-u4Z8+zRQ5pEHwTHRxRS6s9yLyqk=" }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "keyv": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", @@ -796,6 +1534,16 @@ "json-buffer": "3.0.1" } }, + "levn": { + "version": "0.4.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/levn/-/levn-0.4.1.tgz", + "integrity": "sha1-rkViwAdHO5MqYgDUAyaN0v/8at4=", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, "load-json-file": { "version": "4.0.0", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/load-json-file/-/load-json-file-4.0.0.tgz", @@ -816,6 +1564,21 @@ "path-exists": "^3.0.0" } }, + "lodash": { + "version": "4.17.21", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha1-Z5WRxWTDv/quhFTPCz3zcMPWkRw=", + "dev": true + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha1-abPMRtIPRI7M23XqH6cz2eghySA=", + "dev": true, + "requires": { + "chalk": "^4.0.0" + } + }, "loud-rejection": { "version": "1.6.0", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/loud-rejection/-/loud-rejection-1.6.0.tgz", @@ -830,6 +1593,15 @@ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha1-bW/mVw69lqr5D8rR2vo7JWbbOpQ=", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "map-obj": { "version": "2.0.0", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/map-obj/-/map-obj-2.0.0.tgz", @@ -921,11 +1693,175 @@ "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha1-PrXtYmInVteaXw4qIh3+utdcL34=" }, + "mocha": { + "version": "8.3.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/mocha/-/mocha-8.3.1.tgz", + "integrity": "sha1-ue2m2h64y40phgqcIgXeW4oHZWA=", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.0.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha1-JG9Q88p4oyQPbJl+ipvR6sSeSzg=", + "dev": true + }, + "debug": { + "version": "4.3.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/debug/-/debug-4.3.1.tgz", + "integrity": "sha1-8NIpxQXgxtjEmsVT0bE9wYP2su4=", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/ms/-/ms-2.1.2.tgz", + "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "dev": true + } + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha1-FLqDpdNz49MR5a/KKc9b+tllvzQ=", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha1-TJKBnstwg1YeT0okCoa+UZj1Nvw=", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=", + "dev": true + }, + "js-yaml": { + "version": "4.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha1-9Ca8D/S0BRkmzViMcRExg0CaEh8=", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha1-VTIeswn+u8WcSAHZMackUqaB0oY=", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/ms/-/ms-2.1.3.tgz", + "integrity": "sha1-V0yBOM4dK1hh8LRFedut1gxmFbI=", + "dev": true + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha1-4drMvnjQ0TiMoYxk/qOOPlfjcGs=", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha1-g8gxXGeFAF470CGDlBHJ4RDm2DQ=", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha1-UTvb4tO5XXdi6METfvoZXGxhtbM=", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha1-zW/BfihQDP9WwbhsCn/UpUpzAFw=", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/which/-/which-2.0.2.tgz", + "integrity": "sha1-fGqN0KY2oDJ+ELWckobu6T8/UbE=", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha1-tCiQ8UVmeW+Fro46JSkNIF8VSlQ=", + "dev": true + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "nanoid": { + "version": "3.1.20", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha1-utwmPGsdzxS3HvqoX2q0wdbPx4g=", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -947,6 +1883,12 @@ "validate-npm-package-license": "^3.0.1" } }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=", + "dev": true + }, "normalize-url": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", @@ -992,6 +1934,20 @@ "mimic-fn": "^2.1.0" } }, + "optionator": { + "version": "0.9.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha1-TyNqY3Pa4FZqbUPhMmZ09QwpFJk=", + "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" + } + }, "p-cancelable": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", @@ -1028,6 +1984,15 @@ "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/p-try/-/p-try-1.0.0.tgz", "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha1-aR0nCeeMefrjoVZiJFLQB2LKqqI=", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parse-json": { "version": "4.0.0", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/parse-json/-/parse-json-4.0.0.tgz", @@ -1088,11 +2053,29 @@ "pify": "^3.0.0" } }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha1-IfMz6ba46v8CRo9RRupAbTRfTa0=", + "dev": true + }, "pify": { "version": "3.0.0", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha1-3rxkidem5rDnYRiIzsiAM30xY5Y=", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/progress/-/progress-2.0.3.tgz", + "integrity": "sha1-foz42PW48jnBvGi+tOt4Vn1XLvg=", + "dev": true + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -1111,6 +2094,12 @@ "once": "^1.3.1" } }, + "punycode": { + "version": "2.1.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", + "dev": true + }, "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", @@ -1121,6 +2110,15 @@ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha1-32+ENy8CcNxlzfYpE0mrekc9Tyo=", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -1156,6 +2154,15 @@ "read-pkg": "^3.0.0" } }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha1-m6dMAZsV02UnjS6Ru4xI17TULJ4=", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, "redent": { "version": "2.0.0", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/redent/-/redent-2.0.0.tgz", @@ -1165,6 +2172,24 @@ "strip-indent": "^2.0.0" } }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha1-IG0K0KVkjP+9uK5GQ489xRyfeOI=", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha1-iaf92TgmEmcxjq/hT5wy5ZjDaQk=", + "dev": true + }, "resolve": { "version": "1.20.0", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/resolve/-/resolve-1.20.0.tgz", @@ -1179,6 +2204,12 @@ "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz", "integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==" }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha1-SrzYUq0y3Xuqv+m0DgCjbbXzkuY=", + "dev": true + }, "responselike": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", @@ -1250,6 +2281,15 @@ } } }, + "serialize-javascript": { + "version": "5.0.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha1-eIbshIBJpGJGepfT2Rjrsqr5NPQ=", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "serve-static": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", @@ -1284,6 +2324,43 @@ "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/signal-exit/-/signal-exit-3.0.3.tgz", "integrity": "sha1-oUEMLt2PB3sItOJTyOrPyvBXRhw=" }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha1-UA6N0P1VsFgVCGJVsxla3ypF/ms=", + "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://artifactory.cwantools.io/api/npm/npm-group/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=", + "dev": true + } + } + }, "spdx-correct": { "version": "3.1.1", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -1312,11 +2389,37 @@ "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", "integrity": "sha1-6cGKQQ5e1+EkQqVJ+9ivp2cDjWU=" }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "string-width": { + "version": "4.2.2", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha1-2v1PlVmnWFz7pSnGoKT3NIjr1MU=", + "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://artifactory.cwantools.io/api/npm/npm-group/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha1-CxVx3XZpzNTz4G4U7x7tJiJa5TI=", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, "strip-bom": { "version": "3.0.0", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/strip-bom/-/strip-bom-3.0.0.tgz", @@ -1337,6 +2440,59 @@ "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/strip-indent/-/strip-indent-2.0.0.tgz", "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=" }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha1-MfEoGzgyYwQ0gxwxDAHMzajL4AY=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "6.0.7", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/table/-/table-6.0.7.tgz", + "integrity": "sha1-5FiX/7zBvPnoqHv0IPLJ5aelKjQ=", + "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.2.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/ajv/-/ajv-7.2.1.tgz", + "integrity": "sha1-pawiYXGRJEdoNST6LxJI/Pi6yD0=", + "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://artifactory.cwantools.io/api/npm/npm-group/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha1-rnvLNlard6c7pcSb9lTzjmtoYOI=", + "dev": true + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "tmp": { "version": "0.2.1", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/tmp/-/tmp-0.2.1.tgz", @@ -1345,6 +2501,15 @@ "rimraf": "^3.0.0" } }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", @@ -1355,6 +2520,21 @@ "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/trim-newlines/-/trim-newlines-2.0.0.tgz", "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=" }, + "type-check": { + "version": "0.4.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha1-B7ggO/pwVsBlcFDjzNLDdzC6uPE=", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha1-CeJJ696FHTseSNJ8EFREZn8XuD0=", + "dev": true + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1364,16 +2544,37 @@ "mime-types": "~2.1.24" } }, + "typescript": { + "version": "4.2.3", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/typescript/-/typescript-4.2.3.tgz", + "integrity": "sha1-OQYtgBmRLUNyYpjwlJPVmASMHOM=", + "dev": true + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha1-mxpSWVIlhZ5V9mnZKPiMbFfyp34=", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha1-LeGWGMZtwkfc+2+ZM4A12CRaLO4=", + "dev": true + }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -1396,11 +2597,137 @@ "isexe": "^2.0.0" } }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha1-rgdOa9wMFKQx6ATmJFScYzsABFc=", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha1-YQY29rH3A4kb00dxzLF/uTtHB5w=", + "dev": true + }, + "workerpool": { + "version": "6.1.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha1-qOA4tMlFaVloUt56jqQiju/es3s=", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha1-Z+FFz/UQpqaYS98RUpEdadLrnkM=", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha1-7dgDYornHATIWuegkG7a00tkiTc=", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=", + "dev": true + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "y18n": { + "version": "5.0.5", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/y18n/-/y18n-5.0.5.tgz", + "integrity": "sha1-h2nsCNA7HqLfJQCs71YXQ7u5qxg=", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha1-HIK/D2tqZur85+8w43b0mhJHf2Y=", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "dependencies": { + "yargs-parser": { + "version": "20.2.7", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/yargs-parser/-/yargs-parser-20.2.7.tgz", + "integrity": "sha1-Yd+FwRPt+1p6TjbriqYO9CPLyQo=", + "dev": true + } + } + }, "yargs-parser": { "version": "10.1.0", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/yargs-parser/-/yargs-parser-10.1.0.tgz", @@ -1409,6 +2736,44 @@ "camelcase": "^4.1.0" } }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha1-8TH5ImkRrl2a04xDL+gJNmwjJes=", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "dependencies": { + "camelcase": { + "version": "6.2.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha1-kkr4gcnVJaydh/QNlk5c6pgqGAk=", + "dev": true + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha1-qkcte/Zg6xXzSU79UxyrfypwmDc=", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha1-ReQuN/zPH0Dajl927iFRWEDAkoc=", + "dev": true + } + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha1-ApTrPe4FAo0x7hpfosVWpqrxChs=", + "dev": true + }, "youtube-dl-exec": { "version": "1.0.0", "resolved": "https://artifactory.cwantools.io/api/npm/npm-group/youtube-dl-exec/-/youtube-dl-exec-1.0.0.tgz", diff --git a/package.json b/package.json index 086cac6..9ade1da 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "radioscraper", "version": "1.0.0", "description": "", - "main": "index.js", + "main": "index.ts", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, @@ -15,5 +15,11 @@ "got": "^11.8.1", "tmp": "^0.2.1", "youtube-dl-exec": "^1.0.0" + }, + "devDependencies": { + "@types/tmp": "^0.2.0", + "eslint": "^7.21.0", + "mocha": "^8.3.1", + "typescript": "^4.2.3" } } diff --git a/playlist-search.js b/playlist-search.ts similarity index 80% rename from playlist-search.js rename to playlist-search.ts index 83e0272..cc74fd7 100644 --- a/playlist-search.js +++ b/playlist-search.ts @@ -1,8 +1,11 @@ +import Fuse from 'fuse.js'; +import FuseResult = Fuse.FuseResult; + const Fuse = require('fuse.js') const fs = require('fs') const scraper = require('./playlist-scrape') const PLAYLIST_URL = 'https://nintendoradioplaylist2.000webhostapp.com/Livestream%20Playlist.html' -const playlist = require('./playlist.json') +const playlist: Track[] = require('./playlist.json') const albumValidCharacters = new Set() const trackValidCharacters = new Set() @@ -41,15 +44,15 @@ const fuseByTrack = new Fuse(playlist, { keys: ['track'] }) -function searchAlbums(query) { +function searchAlbums(query): FuseResult[] { return fuseByAlbum.search(query) } -function searchTracks(query) { +function searchTracks(query): FuseResult[] { return fuseByTrack.search(query) } -function fullSearch(album, track, duration) { +function fullSearch(album, track, duration): FuseResult { const inAlbumSearch = new Fuse(searchAlbums(album), {keys: ['item.track']}) const trackResults = inAlbumSearch.search(track).map(result => result.item) if (duration) { @@ -60,17 +63,17 @@ function fullSearch(album, track, duration) { } } -function albumDurationSearch(album, duration) { +function albumDurationSearch(album, duration): FuseResult { const albumResults = searchAlbums(album) return filterResultsByDuration(albumResults, duration, true) } -function trackDurationSearch(track, duration) { +function trackDurationSearch(track, duration): FuseResult { const trackResults = searchTracks(track) return filterResultsByDuration(trackResults, duration) } -function filterResultsByDuration(results, duration, failSearch = false) { +function filterResultsByDuration(results, duration, failSearch = false): FuseResult { const durationResults = results.filter(result => result.item.duration === duration) if (durationResults.length > 0) { return durationResults[0] @@ -79,7 +82,7 @@ function filterResultsByDuration(results, duration, failSearch = false) { } } -function search(albumQuery, trackQuery, duration) { +export function search(albumQuery, trackQuery, duration): FuseResult { if (albumQuery && trackQuery) { return fullSearch(albumQuery, trackQuery, duration) } else if (albumQuery && duration) { @@ -93,16 +96,27 @@ function search(albumQuery, trackQuery, duration) { } } +export function getAt(index): Track { + return playlist[index] +} + function trySearch() { console.log(JSON.stringify(search("Final Fantasy VI", "Dancing Mad", "\f"))) } +class Track { + public album: string + public track: string + public duration: string +} + // These shouldn't be called asynchronously, manually call it ourselves buildTrackValidCharacters() buildAlbumValidCharacters() module.exports = { search, + getAt, getValidAlbumCharacters, getValidTrackCharacters } diff --git a/poll-master.js b/poll-master.js deleted file mode 100644 index e69de29..0000000 diff --git a/poll-master.spec.ts b/poll-master.spec.ts new file mode 100644 index 0000000..d8c98a5 --- /dev/null +++ b/poll-master.spec.ts @@ -0,0 +1,7 @@ +const {describe} = require("mocha"); +const poll = require('poll-master') +describe('Poll master', () => { + it('can create poll', () => { + poll.registerNewPoll() + }) +}) diff --git a/poll-master.ts b/poll-master.ts new file mode 100644 index 0000000..32364cb --- /dev/null +++ b/poll-master.ts @@ -0,0 +1,89 @@ +const polls: Map = new Map() + +export function registerNewPoll(key, maxVotes, onComplete, onNoResult): void { + polls.set(key, new Poll(maxVotes, onComplete, onNoResult)) + console.log("created poll", key) +} + +export function getNewPoll(maxVotes: number, onComplete: (result: T, votes: Map) => void, onNoResult: (votes: Map) => void) { + return new Poll(maxVotes, onComplete, onNoResult) +} + +export function voteInPoll(key, voterId, vote): void { + if (polls.has(key)) { + const poll = polls.get(key) + poll.vote(`${voterId}`, vote) + const result = poll.isMajorityReached() + if (result) { + poll.onComplete(result, poll.votes) + delete polls[key] + console.log("vote finished", key, result) + } else if (poll.currentVotes === poll.maxVotes) { + poll.onNoResult(poll.votes) + delete polls[key] + console.log("vote inconclusive", key) + } + } +} + +function isMajorityReached_dep(poll): any { + const minimumConsensus = Math.ceil(poll.maxVotes * 0.5) + const voteTally = {} + for (const vote in poll.votes) { + if (vote != null) { + if (voteTally.hasOwnProperty(poll.votes[vote])) { + voteTally[poll.votes[vote]]++ + } else { + voteTally[poll.votes[vote]] = 1 + } + } + if (voteTally[poll.votes[vote]] >= minimumConsensus) { + return vote + } + } + return null +} + +class Poll { + public currentVotes = 0; + public votes = new Map() + constructor(public readonly maxVotes: number, public onComplete: (result: any, votes: Map) => void, public onNoResult: (votes: Map) => void) { + + } + + vote(voterId: string, vote: T): void { + this.currentVotes++ + this.votes.set(voterId, vote) + this.checkVotes() + } + + skipVote(voterId: string): void { + this.currentVotes++ + this.checkVotes() + } + + private checkVotes(): void { + const minimumConsensus = Math.ceil(this.maxVotes * 0.5) + const voteTally = new Map() + this.votes.forEach((vote, voter) => { + // Allow for null votes, can't have a null key in a map + const normalizedVote = vote == null ? new T() : vote + if (voteTally.has(normalizedVote)) { + voteTally.set(normalizedVote, voteTally.get(normalizedVote) + 1) + } else { + voteTally.set(normalizedVote, 1) + } + if (voteTally.get(normalizedVote) >= minimumConsensus) { + this.onComplete(vote, this.votes) + } + }) + if (this.currentVotes === this.maxVotes) { + this.onNoResult(this.votes) + } + } +} + +module.exports = { + registerNewPoll, + voteInPoll +}