diff --git a/README.md b/README.md index 9615b73..b95cfe9 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,20 @@ ![Node.js CI](https://github.com/oyve/barometer-trend/workflows/Node.js%20CI/badge.svg) # barometer-trend -Calculate the trend of a barometer over a three hour period. +Calculate the tendency and trend of a barometer for a one to three hour period with barometric weather predictions. ## Features -Gets the suggested: -- Tendency and trend of the barometer for the *last hour* or *three hours* (`FALLNG|SLOWLY`) -- Front system tendency, passage and wind analyze based on the latest three hours. (`Falling before a lesser rise` | `Cold front passage` | `Strong and gusty, then veers`) -- Force wind expectation in Beaufort scale based on the ratio of pressure increase/decrease (`F8-9`) -- Prediction of weather by ratio of increase/decrease trend (`Expect gale force weather'`) -- Prediction of weather by pressure tendency, threshold and wind direction (`Increasing rain, clearing within 12 hours.`) -- Prediction of weather by pressure threshold for winter|summer (`Cloudy and humid, thunderstorms`) +- Tendency and trend of the barometer for the *last hour* or *three hours* (`FALLNG|SLOWLY`)- +- Prediction of weather and systems: + - By pressure tendency only (`Expect gale force weather'`) + - By pressure tendency, thresholds and wind direction (`Increasing rain, clearing within 12 hours.`) + - By seasonal pressure thresholds for winter and summer (`Cloudy and humid, thunderstorms`) + - Front system tendency for the last three hours (`Falling before a lesser rise` | `Cold front passage` | `Strong and gusty, then veers`) + - Force wind expectation in Beaufort scale based on the pressure tendency (`F8-9`) +- Detects current pressure system `Low`, `Normal`, `High` + +Note - All calculations corrected to sea level pressure by optional `altitude` and `temperature` +- 48 hour history ## Install & Use ``` @@ -22,27 +26,28 @@ const barometer = require('barometer-trend'); barometer.addPressure(datetime1, 101500); barometer.addPressure(datetime2, 101505); -barometer.addPressure(datetime3, 101512, 100, 20, 225); //100 = altitude, 20 = C degrees, 225 = wind direction - enables more calculations (or give NULL for each seperately) +barometer.addPressure(datetime3, 101512, 100, 20, 225); //100 = altitude, 20 = C degrees, 225 = wind direction -let forecast = barometer.getPredictions(); +//barometer.addPressure(...) is more presice when pressure is corrected by altitude and temperature. + +let forecast = barometer.getPredictions(); //returns JSON ``` ## Note -- Pressure must be in Pascals, 1015 mBar/hPa = 101500 Pascal. -- Pressure readings older than *three hours* are automatically removed. +- Pressure must be input in Pascals, 1015 mBar/hPa = 101500 Pascal. - `getPredictions()` investigate the trend for the latest *one hour* and *three hours* - If run less than *one hour* or *three hours*, the latest timing up until now is picked. -- The most recent trend with the highest severity is chosen. +- The most recent trend with the highest severity is chosen (*One hour* or *Three hour* reading) ## Contribute -Feel free to create a Pull Request including test code. +Feel free to contribute; create an Issue, and Pull Request including test code. ## Disclaimer - All calculations is done by online research; the author of this library does not have a background in metereology. All sources listed below. - A barometer is only *one source of weather information* and may give a general trend and indication, but not "see" the overall picture. (There's a reason satelittes exists and being a metereologist is a paid job.) - All calculations presumes being located at sea with no disturbances. - Near land, winds may be one-two Beaufort scale numbers lower and the wind might be coming from "the wrong direction". -- In subtropic and tropical regions some of the calculations may not be valid at all; i.e. the tradewind system is different from northern hemishpere west->east low pressure systems. +- In subtropic and tropical regions some of the calculations may not be valid at all; i.e. the trade winds (easterlies) is different from northern hemishpere west->east (westerlies) low pressure systems. - In trade wind zones observe the daily variations; any change to this pattern could possibly indicate gale weather. ## Sources / References diff --git a/index.js b/index.js index 517eb4e..1d1462a 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,8 @@ const byPressureTrendAndSeason = require('./predictions/byPressureTrendAndSeason const beaufort = require('./predictions/beaufort') const trend = require('./trend'); const utils = require('./utils'); +const history = require('./predictions/history'); +const system = require('./predictions/system'); let pressures = []; @@ -29,24 +31,26 @@ function hasPressures() { * @param {number} trueWindDirection True wind direction in degrees */ function addPressure(datetime, pressure, altitude = null, temperature = null, trueWindDirection = null) { - if (trueWindDirection !== null && trueWindDirection === 360) trueWindDirection = 0; if (altitude === null) altitude = 0; - if (temperature === null) temperature = 15 + utils.KELVIN; + if (temperature === null) temperature = utils.toKelvinFromCelcius(15); + if (trueWindDirection !== null && trueWindDirection === 360) trueWindDirection = 0; - if (altitude > 0) { - pressure = utils.adjustPressureToSeaLevel(pressure, altitude, temperature); - } + let pressureASL = utils.adjustPressureToSeaLevel(pressure, altitude, temperature); pressures.push({ datetime: datetime, - value: pressure, - twd: trueWindDirection + value: pressureASL, + meta: { + value: pressure, + altitude: altitude, + temperature: temperature, + twd: trueWindDirection + } }); removeOldPressures(); } - /** * Get the count of pressure entries. (Mainly for testing purposes) * @returns {number} Number of pressure entries @@ -55,8 +59,11 @@ function getPressureCount() { return pressures.length; } -function removeOldPressures(threshold) { - var threshold = utils.minutesFromNow(-utils.MINUTES.THREE_HOURS); +function removeOldPressures(threshold = null) { + if (threshold === null) { + threshold = utils.minutesFromNow(-utils.MINUTES.FORTYEIGHT_HOURS); + } + pressures = pressures.filter((p) => p.datetime.getTime() >= threshold.getTime()); } @@ -64,6 +71,8 @@ function getLastPressure() { return pressures[pressures.length - 1]; } + + /** * Get the trend of the barometer * @param {boolean} isNorthernHemisphere Located north of equator? Default true. @@ -74,16 +83,20 @@ function getPredictions(isNorthernHemisphere = true) { let lastPressure = getLastPressure(); - var pressureTrend = trend.getTrend(pressures); + let pressureTrend = trend.getTrend(pressures); + let pressureSystem = system.getSystemByPressure(lastPressure.value); + let pressureHistory = history.getHistoricPressures(pressures); let predictionPressureOnly = byPressureTrend.getPrediction(pressureTrend.tendency, pressureTrend.trend); let predictionFront = front.getFront(pressures); let predictionBeaufort = beaufort.getByPressureVariationRatio(pressureTrend.ratio); let predictionSeason = byPressureTrendAndSeason.getPrediction(lastPressure.value, pressureTrend.tendency, pressureTrend.trend, utils.isSummer(isNorthernHemisphere)) - let predictionPressureTendencyThresholdAndQuadrant = byPressureTendencyAndWind.getPrediction(lastPressure.value, lastPressure.twd, pressureTrend.tendency, pressureTrend.trend, isNorthernHemisphere); + let predictionPressureTendencyThresholdAndQuadrant = byPressureTendencyAndWind.getPrediction(lastPressure.value, lastPressure.meta.twd, pressureTrend.tendency, pressureTrend.trend, isNorthernHemisphere); let forecast = { + lastPressure: lastPressure, + history: pressureHistory, trend: pressureTrend, - indicator: "Please update JSON, see latest documentation", + system: pressureSystem, predictions: { pressureOnly: predictionPressureOnly, quadrant: predictionPressureTendencyThresholdAndQuadrant, diff --git a/package-lock.json b/package-lock.json index da3a4af..9bef600 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "barometer-trend", - "version": "2.1.2", + "version": "2.1.5", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -32,9 +32,9 @@ } }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -42,24 +42,21 @@ } }, "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, "brace-expansion": { @@ -88,30 +85,41 @@ "dev": true }, "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true }, "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "chokidar": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", - "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, "requires": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.1.2", + "fsevents": "~2.3.1", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -120,40 +128,46 @@ } }, "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" }, "dependencies": { "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^5.0.0" } } } @@ -180,30 +194,44 @@ "dev": true }, "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } } }, "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, "escape-string-regexp": { @@ -212,12 +240,6 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -250,9 +272,9 @@ "dev": true }, "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "optional": true }, @@ -277,9 +299,9 @@ } }, "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" @@ -368,13 +390,12 @@ "dev": true }, "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" } }, "locate-path": { @@ -405,48 +426,48 @@ } }, "mocha": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", - "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", + "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.4.3", - "debug": "4.2.0", - "diff": "4.0.2", + "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": "3.14.0", + "js-yaml": "4.0.0", "log-symbols": "4.0.0", "minimatch": "3.0.4", - "ms": "2.1.2", - "nanoid": "3.1.12", + "ms": "2.1.3", + "nanoid": "3.1.20", "serialize-javascript": "5.0.1", "strip-json-comments": "3.1.1", - "supports-color": "7.2.0", + "supports-color": "8.1.1", "which": "2.0.2", "wide-align": "1.1.3", - "workerpool": "6.0.2", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "nanoid": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", - "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", "dev": true }, "normalize-path": { @@ -465,12 +486,12 @@ } }, "p-limit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", - "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" } }, "p-locate": { @@ -482,12 +503,6 @@ "p-limit": "^3.0.2" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -501,9 +516,9 @@ "dev": true }, "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", + "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", "dev": true }, "randombytes": { @@ -530,12 +545,6 @@ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -551,18 +560,6 @@ "randombytes": "^2.1.0" } }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -589,9 +586,9 @@ "dev": true }, "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -615,12 +612,6 @@ "isexe": "^2.0.0" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -631,70 +622,52 @@ } }, "workerpool": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", - "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", "dev": true }, "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "dependencies": { "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^5.0.0" } } } @@ -706,109 +679,65 @@ "dev": true }, "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" }, "dependencies": { "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { + "is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^5.0.0" } } } }, "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true }, "yargs-unparser": { "version": "2.0.0", @@ -820,21 +749,13 @@ "decamelize": "^4.0.0", "flat": "^5.0.2", "is-plain-obj": "^2.1.0" - }, - "dependencies": { - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, - "decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true - } } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/package.json b/package.json index 3cf2f96..2bf4968 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "barometer-trend", - "version": "2.1.4", - "description": "Calculate the trend of a barometer", + "version": "2.2.0", + "description": "Calculate the tendency, trend and weather predictions of barometric pressure", "main": "index.js", "directories": { "test": "test" @@ -15,17 +15,17 @@ }, "keywords": [ "barometer", - "weather", - "trend", "pressure", - "calculate", "trend", "tendency", + "history", + "calculate", + "weather", "front", "hemisphere", "prediction", - "weather", - "forecast" + "forecast", + "barometric" ], "author": "github.com/oyve", "license": "Apache-2.0", @@ -34,6 +34,6 @@ }, "homepage": "https://github.com/oyve/barometer-trend#readme", "devDependencies": { - "mocha": "^8.2.1" + "mocha": "^8.4.0" } } diff --git a/predictions/beaufort.js b/predictions/beaufort.js index cfa563d..b7a918e 100644 --- a/predictions/beaufort.js +++ b/predictions/beaufort.js @@ -1,14 +1,16 @@ const BEAUFORT_RATIOS = [ - { low: 2.67, high: 3.33, number: "F6-7" }, - { low: 3.33, high: 5.5, number: "F8-9" }, - { low: 5.5, high: 9999, number: "F10+" }, - { low: -3.33, high: -1.67, number: "F6-7" }, - { low: -9999, high: -3.33, number: "F8-12" }, + { low: 2.67, high: 3.33, force: "F6-7", min: 6, max: 7, description: "Strong breeze to near gale" }, + { low: 3.33, high: 5.5, force: "F8-9", min: 8, max: 9, description: "Gale to strong gale" }, + { low: 5.5, high: 9999, force: "F10+", min: 10, max: 12, description: "Storm or more" }, + { low: -3.33, high: -1.67, force: "F6-7", min: 6, max: 7, description: "Strong breeze to near gale" }, + { low: -9999, high: -3.33, force: "F8-12", min:8, max: 12, description: "Gale or violent storm conditions" }, ]; function getByPressureVariationRatio(ratio) { let beaufort = BEAUFORT_RATIOS.find((b) => ratio <= b.high && ratio >= b.low); - return beaufort !== undefined ? beaufort.number : "Less than F6"; + return beaufort !== undefined ? + { force: beaufort.force, min: beaufort.min, max: beaufort.max, description: beaufort.description } : + { force: "Less than F6", min: 0, max: 6, description: "Less than a strong breeze" }; } module.exports = { diff --git a/predictions/byPressureTrend.js b/predictions/byPressureTrend.js index 724477a..5a19b07 100644 --- a/predictions/byPressureTrend.js +++ b/predictions/byPressureTrend.js @@ -4,7 +4,7 @@ const PREDICTIONS = [ //rising: general rule: more fair, dry, stable, colder - the faster the more wind { tendency: trend.TENDENCY.RISING.key, trend: trend.TREND.STEADY.key, prediction: 'Continued current weather' }, { tendency: trend.TENDENCY.RISING.key, trend: trend.TREND.SLOWLY.key, prediction: 'Expect more fair, dry, cool weather' }, - { tendency: trend.TENDENCY.RISING.key, trend: trend.TREND.CHANGING.key, prediction: 'Expect fair, dry, cool weather with and a strong breeze' }, + { tendency: trend.TENDENCY.RISING.key, trend: trend.TREND.CHANGING.key, prediction: 'Expect fair, dry, cool weather and a strong breeze' }, { tendency: trend.TENDENCY.RISING.key, trend: trend.TREND.QUICKLY.key, prediction: 'Expect more dry and cold weather and a strong breeze to gale winds' }, { tendency: trend.TENDENCY.RISING.key, trend: trend.TREND.RAPIDLY.key, prediction: 'Expect shortly fair weather and gale or storm winds' }, //falling: general rule: more wet, unstable - the faster the more wind diff --git a/predictions/front.js b/predictions/front.js index 3b73085..f4923f2 100644 --- a/predictions/front.js +++ b/predictions/front.js @@ -26,10 +26,12 @@ function analyzePressures(hourThreePressure, hourTwoPressure, hourOnePressure, n return front !== undefined ? front : fronts.find((f) => f.key === "N/A"); } +const Pascal10 = 10; + function getTendency(earlier, later) { let difference = earlier - later; - if(Math.abs(difference) < 10) return "S"; //STEADY + if(Math.abs(difference) < Pascal10) return "S"; //STEADY if(difference > 0) return "F"; //FALLING if(difference < 0) return "R"; //RISING } diff --git a/predictions/history.js b/predictions/history.js new file mode 100644 index 0000000..5b1447c --- /dev/null +++ b/predictions/history.js @@ -0,0 +1,40 @@ +const utils = require('../utils'); + +/** + * + * @param {Array} pressures Pressures + * @param {number} limit Number of historic values to return + * @returns [{hour: hour, pressure: pressure}] + */ +function getHistoricPressures(pressures, limit = 48) { + let historicPressures = []; + + for (hour = 1; hour <= limit; hour++) { + let threshold = utils.minutesFromNow(-hour * 60); + + let pressure = utils.getPressureClosestTo(pressures, threshold); + + if (pressure !== null && isLessThanOld(pressure.datetime, threshold, 30)) { + historicPressures.push({ hour: hour, pressure: pressure }); + } else { + historicPressures.push({ hour: hour, pressure: null }); + } + } + + return historicPressures; +} + +/** + * + * @param {Date} actual The actual pressure time + * @param {Date} threshold Threshold time + * @param {number} minutes Max number of minutes difference + * @returns + */ +function isLessThanOld(actual, threshold, minutes) { + return (actual.getTime() - threshold.getTime()) < minutes * 60 * 1000; +} + +module.exports = { + getHistoricPressures +} \ No newline at end of file diff --git a/predictions/system.js b/predictions/system.js new file mode 100644 index 0000000..1ecd757 --- /dev/null +++ b/predictions/system.js @@ -0,0 +1,21 @@ +/** + * + * @param {number} pressure in Pascal + * @returns LOW | NORMAL | HIGH pressure system + */ +function getSystemByPressure(pressure) { + const lowThreshold = 100914.4; + const highTreshold = 102268.9; + + const systems = [ + { key: 0, name: "Low", threshold: lowThreshold + 0.1 }, + { key: 1, name: "Normal", threshold: highTreshold }, + { key: 2, name: "High", threshold: 999999 } + ]; + + return systems.find((s) => pressure < s.threshold); +} + +module.exports = { + getSystemByPressure +} \ No newline at end of file diff --git a/test/beaufort.test.js b/test/beaufort.test.js index c797e75..907c6cc 100644 --- a/test/beaufort.test.js +++ b/test/beaufort.test.js @@ -9,7 +9,7 @@ describe("Beaufort Tests", function () { //arrange const expected = "F6-7"; //act - var actual = beaufort.getByPressureVariationRatio(+3); + var actual = beaufort.getByPressureVariationRatio(+3).force; //assert assert.strictEqual(actual, expected); }); @@ -18,7 +18,7 @@ describe("Beaufort Tests", function () { //arrange const expected = "F8-9"; //act - var actual = beaufort.getByPressureVariationRatio(+4); + var actual = beaufort.getByPressureVariationRatio(+4).force; //assert assert.strictEqual(actual, expected); }); @@ -27,7 +27,7 @@ describe("Beaufort Tests", function () { //arrange const expected = "F10+"; //act - var actual = beaufort.getByPressureVariationRatio(+6); + var actual = beaufort.getByPressureVariationRatio(+6).force; //assert assert.strictEqual(actual, expected); }); @@ -36,7 +36,7 @@ describe("Beaufort Tests", function () { //arrange const expected = "F6-7"; //act - var actual = beaufort.getByPressureVariationRatio(-2); + var actual = beaufort.getByPressureVariationRatio(-2).force; //assert assert.strictEqual(actual, expected); }); @@ -45,7 +45,7 @@ describe("Beaufort Tests", function () { //arrange const expected = "F8-12"; //act - var actual = beaufort.getByPressureVariationRatio(-4); + var actual = beaufort.getByPressureVariationRatio(-4).force; //assert assert.strictEqual(actual, expected); }); @@ -54,7 +54,7 @@ describe("Beaufort Tests", function () { //arrange const expected = "Less than F6"; //act - var actual = beaufort.getByPressureVariationRatio(1); + var actual = beaufort.getByPressureVariationRatio(1).force; //assert assert.strictEqual(actual, expected); }); diff --git a/test/history.test.js b/test/history.test.js new file mode 100644 index 0000000..bd5c909 --- /dev/null +++ b/test/history.test.js @@ -0,0 +1,77 @@ +const assert = require('assert'); +const history = require('../predictions/history'); +const barometer = require('../index'); +const utils = require('../utils'); + +describe("History Tests", function () { + describe("Unit tests", function () { + it("it should equal 5 hours", function () { + //arrange + //+1 = time will run past one hour mark - with only a reading per hour thus picking the hour before + const pressures = [ + { datetime: utils.minutesFromNow(-5 * 60), value: 101505 }, + { datetime: utils.minutesFromNow(-4 * 60), value: 101504 }, + { datetime: utils.minutesFromNow(-3 * 60), value: 101503 }, + { datetime: utils.minutesFromNow(-2 * 60), value: 101502 }, + { datetime: utils.minutesFromNow(-1 * 60), value: 101501 }, + { datetime: new Date(), value: 101500 } + ]; + + let actual = history.getHistoricPressures(pressures); + + //assert + assert.strictEqual(actual.length, 48); + + assert.strictEqual(actual.find((h) => h.hour == 1).pressure.value, 101501); + assert.strictEqual(actual.find((h) => h.hour == 2).pressure.value, 101502); + assert.strictEqual(actual.find((h) => h.hour == 3).pressure.value, 101503); + assert.strictEqual(actual.find((h) => h.hour == 4).pressure.value, 101504); + assert.strictEqual(actual.find((h) => h.hour == 5).pressure.value, 101505); + }); + + it("it should equal 3 hours", function () { + //arrange + const pressures = [ + { datetime: utils.minutesFromNow(-5 * 60), value: 101505 }, + { datetime: utils.minutesFromNow(-4 * 60), value: 101504 }, + { datetime: utils.minutesFromNow(-3 * 60), value: 101503 }, + { datetime: utils.minutesFromNow(-2 * 60), value: 101502 }, + { datetime: utils.minutesFromNow(-1 * 60), value: 101501 }, + { datetime: new Date(), value: 101500 } + ]; + + let actual = history.getHistoricPressures(pressures, 3); + + //assert + assert.strictEqual(actual.length, 3); + }); + }); + + describe("Function tests", function () { + + it("it should equal 5 hours", function () { + //arrange + barometer.clear(); + + //+1 = time will run past one hour mark - with only a reading per hour thus picking the hour before + barometer.addPressure(utils.minutesFromNow(-5 * 60), 101505); + barometer.addPressure(utils.minutesFromNow(-4 * 60), 101504); + barometer.addPressure(utils.minutesFromNow(-3 * 60), 101503); + barometer.addPressure(utils.minutesFromNow(-2 * 60), 101502); + barometer.addPressure(utils.minutesFromNow(-1 * 60), 101501); + barometer.addPressure(utils.minutesFromNow(0), 101500); + + //act + let actual = barometer.getPredictions().history; + + //assert + assert.strictEqual(actual.length, 48); + + assert.strictEqual(actual.find((h) => h.hour == 1).pressure.value, 101501); + assert.strictEqual(actual.find((h) => h.hour == 2).pressure.value, 101502); + assert.strictEqual(actual.find((h) => h.hour == 3).pressure.value, 101503); + assert.strictEqual(actual.find((h) => h.hour == 4).pressure.value, 101504); + assert.strictEqual(actual.find((h) => h.hour == 5).pressure.value, 101505); + }); + }); +}); diff --git a/test/index.test.js b/test/index.test.js index 990bf43..3b91c35 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -83,8 +83,8 @@ describe("Function Tests - Trend chooser", function () { //arrange barometer.clear(); //THREE HOUR+: this should result in FALLING.STEADY - barometer.addPressure(utils.minutesFromNow(-190), 101500 + 80); - barometer.addPressure(utils.minutesFromNow(-185), 101500 + 75); + barometer.addPressure(utils.minutesFromNow(-49*60), 101500 + 80); + barometer.addPressure(utils.minutesFromNow(-48.5*60), 101500 + 75); //IF THE OLD READINGS WHERE INCLUDED THIS SHOULD RESULT IN FALLING.RAPIDLY - NOT FALLING.STEADY barometer.addPressure(utils.minutesFromNow(-20), 101500 - 1); diff --git a/test/system.test.js b/test/system.test.js new file mode 100644 index 0000000..2254a37 --- /dev/null +++ b/test/system.test.js @@ -0,0 +1,52 @@ +const assert = require('assert'); +const system = require('../predictions/system'); + +describe("Prediction System Tests", function () { + describe("getHighLow", function () { + + it("it should be NORMAL", function () { + //arrange + //act + var actual = system.getSystemByPressure(101500); + //assert + assert.strictEqual(actual.key, 1); + assert.strictEqual(actual.name, "Normal"); + }); + + it("it should be LOW", function () { + //arrange + //act + var actual = system.getSystemByPressure(100500); + //assert + assert.strictEqual(actual.key, 0); + assert.strictEqual(actual.name, "Low"); + }); + + it("it should be LOW", function () { + //arrange + //act + var actual = system.getSystemByPressure(100914.4); //<- TEST MARGIN + //assert + assert.strictEqual(actual.key, 0); + assert.strictEqual(actual.name, "Low"); + }); + + it("it should be HIGH", function () { + //arrange + //act + var actual = system.getSystemByPressure(102500); + //assert + assert.strictEqual(actual.key, 2); + assert.strictEqual(actual.name, "High"); + }); + + it("it should be HIGH", function () { + //arrange + //act + var actual = system.getSystemByPressure(102268.9); //<-TEST MARGIN + //assert + assert.strictEqual(actual.key, 2); + assert.strictEqual(actual.name, "High"); + }); + }); +}); \ No newline at end of file diff --git a/test/utils.test.js b/test/utils.test.js index 6d8c5df..6a31647 100644 --- a/test/utils.test.js +++ b/test/utils.test.js @@ -31,5 +31,75 @@ describe("Utils Tests", function () { //assert assert.strictEqual(actual, 99110); }); + + it("Correct pressure to sea level with only temperature", function () { + //arrange + //act + var actual = utils.adjustPressureToSeaLevel(98000, 0, 30 + utils.KELVIN); + //assert + assert.strictEqual(actual, 98000); + //https://www.easycalculation.com/weather/temperature-barometer-correction.php + }); + }); + + + describe("Find pressure closest to", function () { + + it("it should pick the previous", function () { + //arrange + const expected = 101400; + const pressures = [ + { datetime: utils.minutesFromNow(-61), value: expected }, + { datetime: utils.minutesFromNow(-58), value: 101600 }, + ]; + //act + var actual = utils.getPressureClosestTo(pressures, utils.minutesFromNow(-60)); + //assert + assert.strictEqual(actual.value, expected); + }); + + it("it should pick the next", function () { + //arrange + const expected = 101600; + const pressures = [ + { datetime: utils.minutesFromNow(-62), value: 101400 }, + { datetime: utils.minutesFromNow(-59), value: expected }, + ]; + //act + var actual = utils.getPressureClosestTo(pressures, utils.minutesFromNow(-60)); + //assert + assert.strictEqual(actual.value, expected); + }); + + it("it should pick the middle", function () { + //arrange + const expected = 101500; + const pressures = [ + { datetime: utils.minutesFromNow(-61), value: 101400 }, + { datetime: utils.minutesFromNow(-60), value: expected }, + { datetime: utils.minutesFromNow(-59), value: 101600 }, + ]; + //act + var actual = utils.getPressureClosestTo(pressures, utils.minutesFromNow(-60)); + //assert + assert.strictEqual(actual.value, expected); + }); + + it("it should pick hour", function () { + //arrange + const expected = 101400; + const pressures = [ + { datetime: utils.minutesFromNow(-60*5), value: 101100 }, + { datetime: utils.minutesFromNow(-60*4), value: 101200 }, + { datetime: utils.minutesFromNow(-60*3), value: 101300 }, + { datetime: utils.minutesFromNow(-60*2), value: expected }, + { datetime: utils.minutesFromNow(-60*1), value: 101500 }, + { datetime: new Date(), value: 101600 }, + ]; + //act + var actual = utils.getPressureClosestTo(pressures, utils.minutesFromNow(-60*2)); + //assert + assert.strictEqual(actual.value, expected); + }); }); }); \ No newline at end of file diff --git a/trend.js b/trend.js index 5ca2751..a8ac69d 100644 --- a/trend.js +++ b/trend.js @@ -56,7 +56,7 @@ function calculate(pressures, from) { to: later.value, difference: difference, ratio: Math.abs(ratio), - period: from, + period: Math.abs(from), severity: getSeverityNotion(threshold.trend.severity, tendency), } } diff --git a/utils.js b/utils.js index f37f7ca..2a83ec9 100644 --- a/utils.js +++ b/utils.js @@ -1,6 +1,7 @@ const MINUTES = { ONE_HOUR: 60, - THREE_HOURS: 60*3 + THREE_HOURS: 60*3, + FORTYEIGHT_HOURS: 60*48 } const KELVIN = 273.15; @@ -16,26 +17,52 @@ function minutesFromNow(minutes) { return new Date(now); } -function adjustPressureToSeaLevel(pressure, height, temperature = 288.15) { //15C - temperature = temperature - 273.15; - let seaLevelPressure = pressure * Math.pow(1 - ((0.0065 * height) / (temperature + 0.0065 * height + 273.15)), -5.257); +function adjustPressureToSeaLevel(pressure, height, temperature = toKelvinFromCelcius(15)) { + temperature = temperature - KELVIN; + let seaLevelPressure = pressure * Math.pow(1 - ((0.0065 * height) / (temperature + 0.0065 * height + KELVIN)), -5.257); return Math.round(seaLevelPressure); } /** * - * @param {*} pressures The pressures to filter - * @param {*} minutes Since X minutes + * @param {Array} pressures The pressures to filter + * @param {number} minutes Since X minutes * @returns {Array} Subset of pressures */ function getPressuresSince(pressures, minutes) { + let earlier = minutesFromNow(-Math.abs(minutes)); + let subPressures = pressures.filter((p) => { return p.datetime.getTime() >= earlier.getTime(); }); return subPressures; } +function isNullOrUndefined(value) { + return (value === null || value === undefined); +} + +/** + * + * @param {Array} pressures Array of pressurs + * @param {Date} datetime + * @returns The pressure closest to the given datetime, null if not found + */ +function getPressureClosestTo(pressures, datetime) { + let previous = [...pressures].reverse().find((p) => p.datetime.getTime() <= datetime.getTime()); + let next = pressures.find((p) => p.datetime.getTime() >= datetime.getTime()); + + if(isNullOrUndefined(next) && isNullOrUndefined(previous)) return null; + if(isNullOrUndefined(next)) return previous; + if(isNullOrUndefined(previous)) return next; + + let diffNext = Math.abs(next.datetime.getTime() - datetime.getTime()); + let diffPrevious = Math.abs(previous.datetime.getTime() - datetime.getTime()); + + return (diffNext < diffPrevious) ? next : previous; +} + function isSummer(isNorthernHemisphere = true) { let month = new Date().getMonth() + 1; let summer = month >= 4 && month <= 9; //April to September @@ -43,11 +70,22 @@ function isSummer(isNorthernHemisphere = true) { return isNorthernHemisphere ? summer : !summer; } +/** + * + * @param {number} celcius Celcius degrees + * @returns Kelvin degrees + */ +function toKelvinFromCelcius(celcius) { + return celcius + 273.15; +} + module.exports = { minutesFromNow, getPressuresSince, isSummer, adjustPressureToSeaLevel, + toKelvinFromCelcius, + getPressureClosestTo, MINUTES, KELVIN } \ No newline at end of file