diff --git a/packages/api/app.js b/packages/api/app.js index c2e2adc6..2ab35628 100644 --- a/packages/api/app.js +++ b/packages/api/app.js @@ -17,9 +17,10 @@ const config = require('config'), { preRequest, preCors, Honeybadger, getVersion, postToMattermost } = require('./lib/helpers/apiUtils'), routes = require('./lib/routes'), - bunyan = require('bunyan'); + pino = require('pino'); -const log = bunyan.createLogger({ name: 'opensensemap-api', serializers: bunyan.stdSerializers }); +// const log = bunyan.createLogger({ name: 'opensensemap-api', serializers: bunyan.stdSerializers }); +const log = pino({ name: 'opensensemap-api', sserializers: pino.stdSerializers }); const server = restify.createServer({ name: `opensensemap-api (${getVersion})`, diff --git a/packages/api/lib/controllers/boxesController.js b/packages/api/lib/controllers/boxesController.js index 4d1e9516..00b9cc4f 100644 --- a/packages/api/lib/controllers/boxesController.js +++ b/packages/api/lib/controllers/boxesController.js @@ -132,7 +132,7 @@ const * @apiUse ContentTypeJSON * */ -const updateBox = async function updateBox (req, res, next) { +const updateBox = async function updateBox (req, res) { try { let box = await Box.findBoxById(req._userParams.boxId, { lean: false, populate: false }); box = await box.updateBox(req._userParams); @@ -143,7 +143,7 @@ const updateBox = async function updateBox (req, res, next) { res.send({ code: 'Ok', data: box.toJSON({ includeSecrets: true }) }); clearCache(['getBoxes']); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -167,12 +167,12 @@ const updateBox = async function updateBox (req, res, next) { * { "coordinates": [7.68323, 51.9423], "type": "Point", "timestamp": "2017-07-27T12:02:00Z"} * ] */ -const getBoxLocations = async function getBoxLocations (req, res, next) { +const getBoxLocations = async function getBoxLocations (req, res) { try { const box = await Box.findBoxById(req._userParams.boxId, { onlyLocations: true, lean: false }); res.send(await box.getLocations(req._userParams)); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -211,7 +211,7 @@ const geoJsonStringifyReplacer = function geoJsonStringifyReplacer (key, box) { * @apiSampleRequest https://api.opensensemap.org/boxes?date=2015-03-07T02:50Z&phenomenon=Temperatur * @apiSampleRequest https://api.opensensemap.org/boxes?date=2015-03-07T02:50Z,2015-04-07T02:50Z&phenomenon=Temperatur */ -const getBoxes = async function getBoxes (req, res, next) { +const getBoxes = async function getBoxes (req, res) { // content-type is always application/json for this route res.header('Content-Type', 'application/json; charset=utf-8'); @@ -252,7 +252,7 @@ const getBoxes = async function getBoxes (req, res, next) { }) .pipe(res); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -357,7 +357,7 @@ const getBoxes = async function getBoxes (req, res, next) { } */ -const getBox = async function getBox (req, res, next) { +const getBox = async function getBox (req, res) { const { format, boxId } = req._userParams; try { @@ -372,7 +372,7 @@ const getBox = async function getBox (req, res, next) { } res.send(box); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -406,7 +406,7 @@ const getBox = async function getBox (req, res, next) { * @apiUse ContentTypeJSON * @apiUse JWTokenAuth */ -const postNewBox = async function postNewBox (req, res, next) { +const postNewBox = async function postNewBox (req, res) { try { let newBox = await req.user.addBox(req._userParams); newBox = await Box.populate(newBox, Box.BOX_SUB_PROPS_FOR_POPULATION); @@ -422,7 +422,7 @@ const postNewBox = async function postNewBox (req, res, next) { }](https://opensensemap.org/explore/${newBox._id})` ); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -443,7 +443,7 @@ const postNewBox = async function postNewBox (req, res, next) { * @apiUse JWTokenAuth * @apiUse BoxIdParam */ -const getSketch = async function getSketch (req, res, next) { +const getSketch = async function getSketch (req, res) { res.header('Content-Type', 'text/plain; charset=utf-8'); try { const box = await Box.findBoxById(req._userParams.boxId, { populate: false, lean: false }); @@ -468,7 +468,7 @@ const getSketch = async function getSketch (req, res, next) { res.send(box.getSketch(params)); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -482,7 +482,7 @@ const getSketch = async function getSketch (req, res, next) { * @apiUse JWTokenAuth * @apiUse BoxIdParam */ -const deleteBox = async function deleteBox (req, res, next) { +const deleteBox = async function deleteBox (req, res) { const { password, boxId } = req._userParams; try { @@ -493,7 +493,7 @@ const deleteBox = async function deleteBox (req, res, next) { postToMattermost(`Box deleted: ${req.user.name} (${redactEmail(req.user.email)}) just deleted "${box.name}" (${boxId})`); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -505,7 +505,7 @@ const deleteBox = async function deleteBox (req, res, next) { * @apiUse JWTokenAuth * @apiUse BoxIdParam */ -const getTransfer = async function getTransfer (req, res, next) { +const getTransfer = async function getTransfer (req, res) { const { boxId } = req._userParams; try { const transfer = await Claim.findClaimByDeviceID(boxId); @@ -513,7 +513,7 @@ const getTransfer = async function getTransfer (req, res, next) { data: transfer, }); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -526,7 +526,7 @@ const getTransfer = async function getTransfer (req, res, next) { * @apiParam (RequestBody) {RFC3339Date} expiresAt Expiration date for transfer token (default: 24 hours from now). * @apiUse JWTokenAuth */ -const createTransfer = async function createTransfer (req, res, next) { +const createTransfer = async function createTransfer (req, res) { const { boxId, date } = req._userParams; try { const transferCode = await req.user.transferBox(boxId, date); @@ -535,7 +535,7 @@ const createTransfer = async function createTransfer (req, res, next) { data: transferCode, }); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -549,7 +549,7 @@ const createTransfer = async function createTransfer (req, res, next) { * @apiUse JWTokenAuth * @apiUse BoxIdParam */ -const updateTransfer = async function updateTransfer (req, res, next) { +const updateTransfer = async function updateTransfer (req, res) { const { boxId, token, date } = req._userParams; try { const transfer = await req.user.updateTransfer(boxId, token, date); @@ -558,7 +558,7 @@ const updateTransfer = async function updateTransfer (req, res, next) { data: transfer, }); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -571,13 +571,13 @@ const updateTransfer = async function updateTransfer (req, res, next) { * @apiParam (RequestBody) {String} token Transfer token you want to revoke. * @apiUse JWTokenAuth */ -const removeTransfer = async function removeTransfer (req, res, next) { +const removeTransfer = async function removeTransfer (req, res) { const { boxId, token } = req._userParams; try { await req.user.removeTransfer(boxId, token); res.send(204); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -590,7 +590,7 @@ const removeTransfer = async function removeTransfer (req, res, next) { * @apiParam (RequestBody) {String} token the token to claim a senseBox * @apiUse JWTokenAuth */ -const claimBox = async function claimBox (req, res, next) { +const claimBox = async function claimBox (req, res) { const { token } = req._userParams; try { @@ -601,7 +601,7 @@ const claimBox = async function claimBox (req, res, next) { res.send(200, { message: 'Device successfully claimed!' }); } catch (err) { - handleError(err, next); + return handleError(err); } }; diff --git a/packages/api/lib/controllers/managementController.js b/packages/api/lib/controllers/managementController.js index ea377c35..84e217af 100644 --- a/packages/api/lib/controllers/managementController.js +++ b/packages/api/lib/controllers/managementController.js @@ -10,7 +10,7 @@ const { Box, User } = require('@sensebox/opensensemap-api-models'), jsonstringify = require('stringify-stream'); -const listBoxes = async function listBoxes (req, res, next) { +const listBoxes = async function listBoxes (req, res) { // default format const stringifier = jsonstringify({ open: '[', close: ']' }); @@ -23,11 +23,11 @@ const listBoxes = async function listBoxes (req, res, next) { }) .pipe(res); } catch (err) { - handleError(err, next); + return handleError(err); } }; -const listUsers = async function listUsers (req, res, next) { +const listUsers = async function listUsers (req, res) { try { const users = await User.find() .then(function (users) { @@ -40,11 +40,11 @@ const listUsers = async function listUsers (req, res, next) { res.send({ code: 'Ok', users }); } catch (err) { - handleError(err, next); + return handleError(err); } }; -const getUser = async function getUser (req, res, next) { +const getUser = async function getUser (req, res) { const { userId } = req._userParams; try { @@ -57,11 +57,11 @@ const getUser = async function getUser (req, res, next) { }); res.send(user.toJSON({ includeSecrets: true })); } catch (err) { - handleError(err, next); + return handleError(err); } }; -const getBox = async function getBox (req, res, next) { +const getBox = async function getBox (req, res) { const { boxId } = req._userParams; try { @@ -74,11 +74,11 @@ const getBox = async function getBox (req, res, next) { res.send(box); } catch (err) { - handleError(err, next); + return handleError(err); } }; -const deleteBoxes = async function deleteBoxes (req, res, next) { +const deleteBoxes = async function deleteBoxes (req, res) { const { boxIds } = req._userParams; try { @@ -90,11 +90,11 @@ const deleteBoxes = async function deleteBoxes (req, res, next) { } res.send({ boxIds }); } catch (err) { - handleError(err, next); + return handleError(err); } }; -const updateBox = async function updateBox (req, res, next) { +const updateBox = async function updateBox (req, res) { try { const { owner, boxId } = req._userParams; // update owner @@ -117,17 +117,17 @@ const updateBox = async function updateBox (req, res, next) { res.send({ code: 'Ok', data: box }); clearCache(['getBoxes']); } catch (err) { - handleError(err, next); + return handleError(err); } }; -const updateUser = async function updateUser (req, res, next) { +const updateUser = async function updateUser (req, res) { try { const { userId } = req._userParams; const user = await User.findById(userId); if (!user) { - throw new NotFoundError('Box not found'); + return Promise.reject(new NotFoundError('Box not found')); } for (const param of ['email', 'name', 'password', 'role', 'language']) { @@ -141,11 +141,11 @@ const updateUser = async function updateUser (req, res, next) { postToMattermost(`Management Action: User updated: ${req.user.name} (${req.user.email}) just updated "${user.name}" (${user.email})`); res.send({ code: 'Ok', data: user }); } catch (err) { - handleError(err, next); + return handleError(err); } }; -const deleteUsers = async function deleteUsers (req, res, next) { +const deleteUsers = async function deleteUsers (req, res) { try { const { userIds } = req._userParams; @@ -160,16 +160,16 @@ const deleteUsers = async function deleteUsers (req, res, next) { } res.send({ userIds }); } catch (err) { - handleError(err, next); + return handleError(err); } }; -const execUserAction = async function execUserAction (req, res, next) { +const execUserAction = async function execUserAction (req, res) { try { const { userId, boxId, action } = req._userParams; if (action === 'resendBoxMail' && !boxId) { - throw new BadRequestError('Action \'resendBoxMail\' requires parameter boxId.'); + return Promise.reject(new BadRequestError('Action \'resendBoxMail\' requires parameter boxId.')); } const user = await User.findById(userId) @@ -179,7 +179,7 @@ const execUserAction = async function execUserAction (req, res, next) { if (action === 'resendBoxMail') { box = user.boxes.find(id => id.equals(boxId)); if (!box) { - throw new BadRequestError(`Box with id ${boxId} not in this user.`); + return Promise.reject(new BadRequestError(`Box with id ${boxId} not in this user.`)); } } @@ -203,7 +203,7 @@ const execUserAction = async function execUserAction (req, res, next) { res.send({ code: 'Ok' }); } catch (err) { - handleError(err, next); + return handleError(err); } }; diff --git a/packages/api/lib/controllers/measurementsController.js b/packages/api/lib/controllers/measurementsController.js index a5cdd7a0..e7e72ab3 100644 --- a/packages/api/lib/controllers/measurementsController.js +++ b/packages/api/lib/controllers/measurementsController.js @@ -1,12 +1,18 @@ 'use strict'; -const - { BadRequestError, UnsupportedMediaTypeError } = require('restify-errors'), +const { + BadRequestError, + UnsupportedMediaTypeError, + } = require('restify-errors'), { Measurement, Box } = require('@sensebox/opensensemap-api-models'), - { checkContentType, createDownloadFilename, csvStringifier } = require('../helpers/apiUtils'), + { + checkContentType, + createDownloadFilename, + csvStringifier, + } = require('../helpers/apiUtils'), { retrieveParameters, - validateFromToTimeParams + validateFromToTimeParams, } = require('../helpers/userParamHelpers'), handleError = require('../helpers/errorHandler'), OutlierTransformer = require('../transformers/outlierTransformer'), @@ -30,7 +36,7 @@ const * @apiUse SensorIdParam * @apiParam {Boolean="true","false"} [onlyValue] If set to true only returns the measured value without information about the sensor. Requires a sensorId. */ -const getLatestMeasurements = async function getLatestMeasurements (req, res, next) { +const getLatestMeasurements = async function getLatestMeasurements (req, res) { const { _userParams: params } = req; let box; @@ -61,9 +67,7 @@ const getLatestMeasurements = async function getLatestMeasurements (req, res, ne }); } } catch (err) { - handleError(err, next); - - return; + return handleError(err); } if (params.sensorId) { @@ -122,7 +126,7 @@ const jsonLocationReplacer = function jsonLocationReplacer (k, v) { * @apiParam {Boolean="true","false"} [download] if specified, the api will set the `content-disposition` header thus forcing browsers to download instead of displaying. Is always true for format csv. * @apiUse SeparatorParam */ -const getData = function getData (req, res, next) { +const getData = async function getData (req, res) { const { sensorId, format, download, outliers, outlierWindow, delimiter } = req._userParams; let stringifier; @@ -141,7 +145,7 @@ const getData = function getData (req, res, next) { let measurementsStream = Measurement.getMeasurementsStream(req._userParams) .on('error', function (err) { - return handleError(err, next); + return handleError(err); }); if (outliers) { @@ -152,6 +156,9 @@ const getData = function getData (req, res, next) { })); } + // A last time flush headers :) + res.flushHeaders(); + measurementsStream .pipe(stringifier) .pipe(res); @@ -173,7 +180,7 @@ const getData = function getData (req, res, next) { * @apiParam {String=createdAt,value,lat,lon,height,boxId,boxName,exposure,sensorId,phenomenon,unit,sensorType} [columns=sensorId,createdAt,value,lat,lon] Comma separated list of columns to export. * @apiParam {Boolean=true,false} [download=true] Set the `content-disposition` header to force browsers to download instead of displaying. */ -const getDataMulti = async function getDataMulti (req, res, next) { +const getDataMulti = async function getDataMulti (req, res) { const { boxId, bbox, exposure, delimiter, columns, fromDate, toDate, phenomenon, download, format } = req._userParams; // build query @@ -182,9 +189,9 @@ const getDataMulti = async function getDataMulti (req, res, next) { }; if (boxId && bbox) { - return next(new BadRequestError('please specify only boxId or bbox')); + return Promise.reject(new BadRequestError('please specify only boxId or bbox')); } else if (!boxId && !bbox) { - return next(new BadRequestError('please specify either boxId or bbox')); + return Promise.reject(new BadRequestError('please specify either boxId or bbox')); } if (boxId) { @@ -206,30 +213,32 @@ const getDataMulti = async function getDataMulti (req, res, next) { }); stream = stream .on('error', function (err) { - return handleError(err, next); + return handleError(err); }); switch (format) { case 'csv': res.header('Content-Type', 'text/csv'); - stream = stream - .pipe(csvStringifier(columns, delimiter)); + stream = stream.pipe(csvStringifier(columns, delimiter)); break; case 'json': res.header('Content-Type', 'application/json'); - stream = stream - .pipe(jsonstringify({ open: '[', close: ']' })); + // stringifier = jsonstringify({ open: '[', close: ']' }); + stream = stream.pipe(jsonstringify({ open: '[', close: ']' })); break; } if (download === 'true') { - res.header('Content-Disposition', `attachment; filename=${createDownloadFilename(req.date(), 'download', [phenomenon, ...columns], format)}`); + res.setHeader('Content-Disposition', `attachment; filename=${createDownloadFilename(req.date(), 'download', [phenomenon, ...columns], format)}`); } + // flushHeaders is fixing csv-stringify + res.flushHeaders(); + stream .pipe(res); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -241,7 +250,7 @@ const getDataMulti = async function getDataMulti (req, res, next) { * @apiName getDataByGroupTag * @apiParam {String} grouptag The grouptag to search by. */ -const getDataByGroupTag = async function getDataByGroupTag (req, res, next) { +const getDataByGroupTag = async function getDataByGroupTag (req, res) { const { grouptag, format } = req._userParams; const queryTags = grouptag.split(','); // build query @@ -256,7 +265,7 @@ const getDataByGroupTag = async function getDataByGroupTag (req, res, next) { }); stream = stream .on('error', function (err) { - return handleError(err, next); + return handleError(err); }); switch (format) { case 'json': @@ -269,7 +278,7 @@ const getDataByGroupTag = async function getDataByGroupTag (req, res, next) { stream .pipe(res); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -287,13 +296,13 @@ const getDataByGroupTag = async function getDataByGroupTag (req, res, next) { * @apiParam (RequestBody) {Location} [location] the WGS84-coordinates of the measurement. * @apiHeader {String} Authorization Box' unique access_token. Will be used as authorization token if box has auth enabled (e.g. useAuth: true) */ -const postNewMeasurement = async function postNewMeasurement (req, res, next) { +const postNewMeasurement = async function postNewMeasurement (req, res) { const { boxId, sensorId, value, createdAt, location } = req._userParams; try { const box = await Box.findBoxById(boxId, { populate: false, lean: false }); if (box.useAuth && box.access_token && box.access_token !== req.headers.authorization) { - throw new UnauthorizedError('Box access token not valid!'); + return Promise.reject(new UnauthorizedError('Box access token not valid!')); } const [measurement] = await Measurement.decodeMeasurements([{ @@ -305,7 +314,7 @@ const postNewMeasurement = async function postNewMeasurement (req, res, next) { await box.saveMeasurement(measurement); res.send(201, 'Measurement saved in box'); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -388,7 +397,7 @@ const postNewMeasurement = async function postNewMeasurement (req, res, next) { * "error": "4" * } */ -const postNewMeasurements = async function postNewMeasurements (req, res, next) { +const postNewMeasurements = async function postNewMeasurements (req, res) { const { boxId, luftdaten, hackair } = req._userParams; let contentType = req.getContentType(); @@ -408,17 +417,17 @@ const postNewMeasurements = async function postNewMeasurements (req, res, next) // authorization for all boxes that have not opt out if ((box.useAuth || contentType === 'hackair') && box.access_token && box.access_token !== req.headers.authorization) { - throw new UnauthorizedError('Box access token not valid!'); + return Promise.reject(new UnauthorizedError('Box access token not valid!')); } const measurements = await Measurement.decodeMeasurements(req.body, { contentType, sensors: box.sensors }); await box.saveMeasurementsArray(measurements); res.send(201, 'Measurements saved in box'); } catch (err) { - handleError(err, next); + return handleError(err); } } else { - return next(new UnsupportedMediaTypeError('Unsupported content-type.')); + return Promise.reject(new UnsupportedMediaTypeError('Unsupported content-type.')); } }; diff --git a/packages/api/lib/controllers/sensorsController.js b/packages/api/lib/controllers/sensorsController.js index 65feaf50..554d30fe 100644 --- a/packages/api/lib/controllers/sensorsController.js +++ b/packages/api/lib/controllers/sensorsController.js @@ -40,13 +40,13 @@ const { Box } = require('@sensebox/opensensemap-api-models'), * @apiParam (RequestBody) {RFC3339Date[]} [timestamps] Allows to specify timestamps which should be deleted * @apiParam (RequestBody) {Boolean=true,false} [deleteAllMeasurements=false] Specify `deleteAllMeasurements` with a value of `true` to delete all measurements of this sensor */ -const deleteSensorData = async function deleteSensorData (req, res, next) { +const deleteSensorData = async function deleteSensorData (req, res) { try { const box = await Box.findBoxById(req._userParams.boxId, { lean: false }); const message = await box.deleteMeasurementsOfSensor(req._userParams); res.send({ code: 'Ok', message }); } catch (err) { - handleError(err, next); + return handleError(err); } }; diff --git a/packages/api/lib/controllers/statisticsController.js b/packages/api/lib/controllers/statisticsController.js index 8aa74a36..a5e6e81e 100644 --- a/packages/api/lib/controllers/statisticsController.js +++ b/packages/api/lib/controllers/statisticsController.js @@ -24,7 +24,7 @@ const { Box, Measurement } = require('@sensebox/opensensemap-api-models'), * @apiSuccessExample {json} [human=true] * ["318","118M","393"] */ -const getStatistics = async function getStatistics (req, res, next) { +const getStatistics = async function getStatistics (req, res) { const { human } = req._userParams; try { let results = await Promise.all([ @@ -43,7 +43,7 @@ const getStatistics = async function getStatistics (req, res, next) { res.send(200, results); } catch (err) { - return next(err); + return err; } }; @@ -72,14 +72,14 @@ const getStatistics = async function getStatistics (req, res, next) { const idwColumns = ['sensorId', 'value', 'lat', 'lon']; -const idwHandler = async function (req, res, next) { +const idwHandler = async function (req, res) { const { phenomenon, bbox, exposure, cellWidth, gridType, power, numTimeSteps, numClasses, fromDate, toDate } = req._userParams; // validate bbox param, we don't want too much load on our server! const areaSqKm = area(bbox) / 10e6; if (areaSqKm / cellWidth > 2500) { - return next(new UnprocessableEntityError('planned computation too expensive ((area in square kilometers / cellWidth) > 2500)')); + return Promise.reject(new UnprocessableEntityError('planned computation too expensive ((area in square kilometers / cellWidth) > 2500)')); } // build query @@ -102,9 +102,12 @@ const idwHandler = async function (req, res, next) { }); res.header('Content-Type', 'application/json; charset=utf-8'); + // Flush again + res.flushHeaders(); + cursor .on('error', function (err) { - return handleError(err, next); + return handleError(err); }) .pipe(idwTransformer({ numTimeSteps, @@ -121,7 +124,7 @@ const idwHandler = async function (req, res, next) { }) .pipe(res); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -186,13 +189,13 @@ const idwHandler = async function (req, res, next) { * 5a8e8c6c8432c3001bfe4156,2018-02-05T00:00:00.000Z,17 */ const minWindowLengthMs = ms('1m'); -const descriptiveStatisticsHandler = async function descriptiveStatisticsHandler (req, res, next) { +const descriptiveStatisticsHandler = async function descriptiveStatisticsHandler (req, res) { const { boxId, bbox, exposure, delimiter, columns, phenomenon, operation, download, format, window } = req._userParams; let { fromDate, toDate } = req._userParams; const windowMs = Math.round(ms(window) / minWindowLengthMs) * minWindowLengthMs; if (!windowMs || windowMs < minWindowLengthMs) { - return next(new BadRequestError(`Invalid window length. Smallest window size is ${ms(minWindowLengthMs, { long: true })}.`)); + return Promise.reject(new BadRequestError(`Invalid window length. Smallest window size is ${ms(minWindowLengthMs, { long: true })}.`)); } // compute start and end times in milliseconds @@ -212,9 +215,9 @@ const descriptiveStatisticsHandler = async function descriptiveStatisticsHandler toDate = new Date(toDate + windowMs); if (boxId && bbox) { - return next(new BadRequestError('please specify only boxId or bbox')); + return Promise.reject(new BadRequestError('please specify only boxId or bbox')); } else if (!boxId && !bbox) { - return next(new BadRequestError('please specify either boxId or bbox')); + return Promise.reject(new BadRequestError('please specify either boxId or bbox')); } const opts = { @@ -299,10 +302,13 @@ const descriptiveStatisticsHandler = async function descriptiveStatisticsHandler res.header('Content-Disposition', `attachment; filename=${createDownloadFilename(req.date(), operation, [phenomenon, ...columns], fileExtension)}`); } + // Flush again to stream + res.flushHeaders(); + // stream response to client cursor .on('error', function (err) { - return handleError(err, next); + return handleError(err); }) .pipe(new DescriptiveStatisticsTransformer({ operation, @@ -310,15 +316,15 @@ const descriptiveStatisticsHandler = async function descriptiveStatisticsHandler tidy: (format === 'tidy') })) .on('error', function (err) { - return handleError(err, next); + return handleError(err); }) .pipe(stringifier) .on('error', function (err) { - return handleError(err, next); + return handleError(err); }) .pipe(res); } catch (err) { - handleError(err, next); + return handleError(err); } }; diff --git a/packages/api/lib/controllers/usersController.js b/packages/api/lib/controllers/usersController.js index 3995cf6d..7e2028f7 100644 --- a/packages/api/lib/controllers/usersController.js +++ b/packages/api/lib/controllers/usersController.js @@ -43,7 +43,7 @@ const { User } = require('@sensebox/opensensemap-api-models'), * @apiSuccess (Created 201) {String} refreshToken valid refresh token * @apiSuccess (Created 201) {Object} data `{ "user": {"name":"fullname","email":"test@test.de","role":"user","language":"en_US","boxes":[],"emailIsConfirmed":false} }` */ -const registerUser = async function registerUser (req, res, next) { +const registerUser = async function registerUser (req, res) { const { email, password, language, name } = req._userParams; try { @@ -56,10 +56,10 @@ const registerUser = async function registerUser (req, res, next) { return res.send(201, { code: 'Created', message: 'Successfully registered new user', data: { user: newUser }, token, refreshToken }); } catch (err) { - return next(new InternalServerError(`User successfully created but unable to create jwt token: ${err.message}`)); + return Promise.reject(new InternalServerError(`User successfully created but unable to create jwt token: ${err.message}`)); } } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -77,7 +77,7 @@ const registerUser = async function registerUser (req, res, next) { * @apiSuccess {Object} data `{ "user": {"name":"fullname","email":"test@test.de","role":"user","language":"en_US","boxes":[],"emailIsConfirmed":false} }` * @apiError {String} 403 Unauthorized */ -const signIn = async function signIn (req, res, next) { +const signIn = async function signIn (req, res) { const { email: emailOrName, password } = req._userParams; try { @@ -87,7 +87,7 @@ const signIn = async function signIn (req, res, next) { .exec(); if (!user) { - throw new ForbiddenError('User and or password not valid!'); + return Promise.reject(new ForbiddenError('User and or password not valid!')); } if (await user.checkPassword(password)) { @@ -97,9 +97,10 @@ const signIn = async function signIn (req, res, next) { } } catch (err) { if (err.name === 'ModelError' && err.message === 'Password incorrect') { - return handleError(new ForbiddenError('User and or password not valid!'), next); + return handleError(new ForbiddenError('User and or password not valid!')); } - handleError(err, next); + + return handleError(err); } }; @@ -116,12 +117,12 @@ const signIn = async function signIn (req, res, next) { * @apiSuccess {Object} data `{ "user": {"name":"fullname","email":"test@test.de","role":"user","language":"en_US","boxes":[],"emailIsConfirmed":false} }` * @apiError {Object} Forbidden `{"code":"ForbiddenError","message":"Refresh token invalid or too old. Please sign in with your username and password."}` */ -const refreshJWT = async function refreshJWT (req, res, next) { +const refreshJWT = async function refreshJWT (req, res) { try { const { token, refreshToken, user } = await refreshJwt(req._userParams.token); res.send(200, { code: 'Authorized', message: 'Successfully refreshed auth', data: { user }, token, refreshToken }); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -134,7 +135,7 @@ const refreshJWT = async function refreshJWT (req, res, next) { * @apiSuccess {String} code `Ok` * @apiSuccess {String} message `Successfully signed out` */ -const signOut = function signOut (req, res) { +const signOut = async function signOut (req, res) { invalidateToken(req); return res.send(200, { code: 'Ok', message: 'Successfully signed out' }); @@ -150,12 +151,12 @@ const signOut = function signOut (req, res) { * @apiSuccess {String} message `Password reset initiated` */ // generate new password reset token and send the token to the user -const requestResetPassword = async function requestResetPassword (req, res, next) { +const requestResetPassword = async function requestResetPassword (req, res) { try { await User.initPasswordReset(req._userParams); res.send(200, { code: 'Ok', message: 'Password reset initiated' }); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -170,12 +171,12 @@ const requestResetPassword = async function requestResetPassword (req, res, next * @apiSuccess {String} message `Password successfully changed. You can now login with your new password` */ // set new password with reset token as auth -const resetPassword = async function resetPassword (req, res, next) { +const resetPassword = async function resetPassword (req, res) { try { await User.resetPassword(req._userParams); res.send(200, { code: 'Ok', message: 'Password successfully changed. You can now login with your new password' }); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -189,12 +190,12 @@ const resetPassword = async function resetPassword (req, res, next) { * @apiSuccess {String} code `Ok` * @apiSuccess {String} message `E-Mail successfully confirmed. Thank you` */ -const confirmEmailAddress = async function confirmEmailAddress (req, res, next) { +const confirmEmailAddress = async function confirmEmailAddress (req, res) { try { await User.confirmEmail(req._userParams); res.send(200, { code: 'Ok', message: 'E-Mail successfully confirmed. Thank you' }); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -206,13 +207,13 @@ const confirmEmailAddress = async function confirmEmailAddress (req, res, next) * @apiSuccess {String} code `Ok` * @apiSuccess {String} data A json object with a single `boxes` array field */ -const getUserBoxes = async function getUserBoxes (req, res, next) { +const getUserBoxes = async function getUserBoxes (req, res) { try { const boxes = await req.user.getBoxes(); const sharedBoxes = await req.user.getSharedBoxes(); res.send(200, { code: 'Ok', data: { boxes: boxes, sharedBoxes: sharedBoxes } }); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -223,7 +224,7 @@ const getUserBoxes = async function getUserBoxes (req, res, next) { * @apiGroup Users * @apiUse JWTokenAuth */ -const getUser = function getUser (req, res) { +const getUser = async function getUser (req, res) { res.send(200, { code: 'Ok', data: { me: req.user } }); }; @@ -239,7 +240,7 @@ const getUser = function getUser (req, res) { * @apiParam {String} [newPassword] the new password for this user. Should be at least 8 characters long. * @apiParam {String} currentPassword the current password for this user. */ -const updateUser = async function updateUser (req, res, next) { +const updateUser = async function updateUser (req, res) { try { const { updated, signOut, messages, updatedUser } = await req.user.updateUser(req._userParams); if (updated === false) { @@ -251,7 +252,7 @@ const updateUser = async function updateUser (req, res, next) { } res.send(200, { code: 'Ok', message: `User successfully saved.${messages.join('.')}`, data: { me: updatedUser } }); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -264,7 +265,7 @@ const updateUser = async function updateUser (req, res, next) { * @apiParam {String} password the current password for this user. */ -const deleteUser = async function deleteUser (req, res, next) { +const deleteUser = async function deleteUser (req, res) { const { password } = req._userParams; try { @@ -276,7 +277,7 @@ const deleteUser = async function deleteUser (req, res, next) { clearCache(['getBoxes', 'getStats']); postToMattermost(`User deleted: ${req.user.name} (${redactEmail(req.user.email)})`); } catch (err) { - handleError(err, next); + return handleError(err); } }; @@ -289,7 +290,7 @@ const deleteUser = async function deleteUser (req, res, next) { * @apiSuccess {String} code `Ok` * @apiSuccess {String} message `Email confirmation has been sent to ` */ -const requestEmailConfirmation = async function requestEmailConfirmation (req, res, next) { +const requestEmailConfirmation = async function requestEmailConfirmation (req, res) { try { const result = await req.user.resendEmailConfirmation(); let usedAddress = result.email; @@ -298,7 +299,7 @@ const requestEmailConfirmation = async function requestEmailConfirmation (req, r } res.send(200, { code: 'Ok', message: `Email confirmation has been sent to ${usedAddress}` }); } catch (err) { - handleError(err, next); + return handleError(err); } }; diff --git a/packages/api/lib/helpers/apiUtils.js b/packages/api/lib/helpers/apiUtils.js index 30e7dd0d..6095754d 100644 --- a/packages/api/lib/helpers/apiUtils.js +++ b/packages/api/lib/helpers/apiUtils.js @@ -6,7 +6,7 @@ const { NotAuthorizedError, UnsupportedMediaTypeError } = require('restify-error config = require('config'), apicache = require('apicache'), got = require('got'), - csvstringify = require('csv-stringify'), + { stringify } = require('csv-stringify'), hostname = require('os').hostname(); const addCache = function addCache (duration, group) { @@ -198,7 +198,7 @@ const computeTimestampTruncationLength = function computeTimestampTruncationLeng }; const csvStringifier = function csvStringifier (columns, delimiter) { - return csvstringify({ + return stringify({ columns, delimiter, header: 1, cast: { date: d => d.toISOString() } diff --git a/packages/api/lib/helpers/errorHandler.js b/packages/api/lib/helpers/errorHandler.js index 37d90e48..41a7ed93 100644 --- a/packages/api/lib/helpers/errorHandler.js +++ b/packages/api/lib/helpers/errorHandler.js @@ -4,13 +4,13 @@ const restifyErrors = require('restify-errors'); const restifyErrorNames = Object.keys(restifyErrors).filter(e => e.includes('Error') && e !== 'codeToHttpError'); -const handleError = function (err, next) { +const handleError = function (err) { if (err.name === 'ModelError') { if (err.data && err.data.type) { - return next(new restifyErrors[err.data.type](err.message)); + return Promise.reject(new restifyErrors[err.data.type](err.message)); } - return next(new restifyErrors.BadRequestError(err.message)); + return Promise.reject(new restifyErrors.BadRequestError(err.message)); } if (err.name === 'ValidationError') { @@ -21,11 +21,11 @@ const handleError = function (err, next) { } } - return next(new restifyErrors.UnprocessableEntityError(`Validation failed: ${msgs.join(', ')}`)); + return Promise.reject(new restifyErrors.UnprocessableEntityError(`Validation failed: ${msgs.join(', ')}`)); } if (restifyErrorNames.includes(err.name)) { - return next(err); + return Promise.reject(err); } if (err.errors) { @@ -33,11 +33,10 @@ const handleError = function (err, next) { .map(f => `${err.errors[f].message}`) .join(', '); - return next(new restifyErrors.UnprocessableEntityError(msg)); + return Promise.reject(new restifyErrors.UnprocessableEntityError(msg)); } - return next(new restifyErrors.InternalServerError(err.message)); + return Promise.reject(new restifyErrors.InternalServerError(err.message)); }; module.exports = handleError; - diff --git a/packages/api/lib/helpers/userParamHelpers.js b/packages/api/lib/helpers/userParamHelpers.js index 4eddac6b..3ccd80fe 100644 --- a/packages/api/lib/helpers/userParamHelpers.js +++ b/packages/api/lib/helpers/userParamHelpers.js @@ -206,12 +206,10 @@ const castParam = function castParam (param, paramDataType, dataTypeIsArray) { return param; }; -const initUserParams = function initUserParams (req, res, next) { +const initUserParams = async function initUserParams (req) { if (!req._userParams) { req._userParams = Object.create(null); } - - next(); }; const setUserParam = function setUserParam (req, paramName, paramValue) { @@ -423,7 +421,7 @@ const retrieveParametersPredefs = { } }; -const handleAndSetParameterRequest = function handleAndSetParameterRequest (req, next, { name, aliases, dataType = 'String', allowedValues, mapping, required = false, defaultValue, min, max, paramValidatorAndParser = validateAndCastParam }) { +const handleAndSetParameterRequest = async function handleAndSetParameterRequest (req, { name, aliases, dataType = 'String', allowedValues, mapping, required = false, defaultValue, min, max, paramValidatorAndParser = validateAndCastParam }) { // extract param from request const { value, nameUsed } = extractParam({ req, name, aliases }); // there was no user supplied value but a default value @@ -433,7 +431,7 @@ const handleAndSetParameterRequest = function handleAndSetParameterRequest (req, return; } else if (typeof value === 'undefined' && required === true) { // no user supplied value and required -> error - return next(new BadRequestError(`missing required parameter ${name}`)); + return Promise.reject(new BadRequestError(`missing required parameter ${name}`)); } else if (typeof value === 'undefined' && required === false) { // no value and not required, skip to the next parameter return; @@ -445,15 +443,15 @@ const handleAndSetParameterRequest = function handleAndSetParameterRequest (req, const { castedValue, error, message } = paramValidatorAndParser({ value, dataType, allowedValues, mapping, dataTypeIsArray, min, max }); switch (error) { case ARRAY_NOT_ALLOWED: - return next(new BadRequestError(`Parameter ${nameUsed} must only be specified once`)); + return Promise.reject(new BadRequestError(`Parameter ${nameUsed} must only be specified once`)); case CAST_FAILED: /* eslint-disable prefer-template */ - return next(new UnprocessableEntityError(`Parameter ${nameUsed} is not parseable as datatype ${(dataTypeIsArray ? 'array of ' + dataType[0] : dataType)}`)); + return Promise.reject(new UnprocessableEntityError(`Parameter ${nameUsed} is not parseable as datatype ${(dataTypeIsArray ? 'array of ' + dataType[0] : dataType)}`)); /* eslint-enable prefer-template */ case ILLEGAL_VALUE: - return next(new UnprocessableEntityError(`Illegal value for parameter ${nameUsed}. allowed values: ${allowedValues.join(', ')}`)); + return Promise.reject(new UnprocessableEntityError(`Illegal value for parameter ${nameUsed}. allowed values: ${allowedValues.join(', ')}`)); case ERROR_CUSTOM_MESSAGE: - return next(new UnprocessableEntityError(`Illegal value for parameter ${nameUsed}. ${message}`)); + return Promise.reject(new UnprocessableEntityError(`Illegal value for parameter ${nameUsed}. ${message}`)); } // no error matched @@ -472,7 +470,7 @@ const handleAndSetParameterRequest = function handleAndSetParameterRequest (req, // min: minimal value for Numbers and Integers. Compared with >= // max: maximal value for Numbers and Integers. Compared with < const retrieveParameters = function retrieveParameters (parameters = []) { - return function (req, res, next) { + return async function (req) { //for (let { name, aliases, dataType, allowedValues, mapping, required, defaultValue, predef } of parameters) { for (const parameter of parameters) { // predef has precedence over all other keys @@ -484,86 +482,81 @@ const retrieveParameters = function retrieveParameters (parameters = []) { Object.assign(parameterPredef, parameter); // handle paramter request - handleAndSetParameterRequest(req, next, parameterPredef); + await handleAndSetParameterRequest(req, parameterPredef); continue; } if (parameter.name) { - handleAndSetParameterRequest(req, next, parameter); + await handleAndSetParameterRequest(req, parameter); } } - - return next(); }; }; const fromToTimeParamsSanityCheck = function fromToTimeParamsSanityCheck (fromDate, toDate) { if ((fromDate && !toDate) || (toDate && !fromDate)) { - return new BadRequestError('fromDate and toDate need to be specified simultaneously'); + return Promise.reject(BadRequestError('fromDate and toDate need to be specified simultaneously')); } if (fromDate && toDate) { if (fromDate.isAfter(toDate)) { - return new InvalidArgumentError(`Invalid time frame specified: fromDate (${fromDate.toISOString()}) is after toDate (${toDate.toISOString()})`); + return Promise.reject(new InvalidArgumentError(`Invalid time frame specified: fromDate (${fromDate.toISOString()}) is after toDate (${toDate.toISOString()})`)); } } }; -const validateFromToTimeParams = function validateFromToTimeParams (req, res, next) { - next(fromToTimeParamsSanityCheck(req._userParams.fromDate, req._userParams.toDate)); +const validateFromToTimeParams = async function validateFromToTimeParams (req) { + return (fromToTimeParamsSanityCheck(req._userParams.fromDate, req._userParams.toDate)); }; -const parseAndValidateTimeParamsForFindAllBoxes = function parseAndValidateTimeParamsForFindAllBoxes (req, res, next) { +const parseAndValidateTimeParamsForFindAllBoxes = async function parseAndValidateTimeParamsForFindAllBoxes (req) { if (req._userParams.date) { const [fromDate, toDate, ...rest] = req._userParams.date; if (rest.length !== 0) { - return next(new UnprocessableEntityError('invalid number of dates for date parameter supplied')); + return Promise.reject(new UnprocessableEntityError('invalid number of dates for date parameter supplied')); } else if (!toDate) { setUserParam(req, 'fromDate', fromDate.clone().subtract(4, 'hours')); // sub 4 setUserParam(req, 'toDate', fromDate.clone().add(4, 'hours')); // then add 4 to get into the future } else { const timesSane = fromToTimeParamsSanityCheck(fromDate, toDate); if (typeof timesSane !== 'undefined') { - return next(timesSane); + return timesSane; } setUserParam(req, 'fromDate', fromDate); setUserParam(req, 'toDate', toDate); } } - - next(); }; -const validateDateNotPast = function validateDateNotPast (req, res, next) { +const validateDateNotPast = async function validateDateNotPast (req) { if (req._userParams.date) { const { date } = req._userParams; if (date.isBefore(moment.utc().toDate())) { - return next(new InvalidArgumentError( + return Promise.reject(new InvalidArgumentError( `Invalid date specified: date (${date.toISOString()}) must be in the future.` )); } } - next(); }; -const checkPrivilege = function checkPrivilege (req, res, next) { +const checkPrivilege = async function checkPrivilege (req) { if (req.user && req.user.role === config.get('management_role')) { - return next(); + return; } if (req._userParams.boxId) { try { req.user.checkBoxOwner(req._userParams.boxId); - return next(); + return; } catch (err) { - return handleModelError(err, next); + return handleModelError(err); } } - return next(new ForbiddenError('Not signed in or not authorized to access.')); + return Promise.reject(new ForbiddenError('Not signed in or not authorized to access.')); }; module.exports = { diff --git a/packages/api/lib/routes.js b/packages/api/lib/routes.js index 8a249535..48d6cdd2 100644 --- a/packages/api/lib/routes.js +++ b/packages/api/lib/routes.js @@ -26,7 +26,7 @@ const spaces = function spaces (num) { * @apiDescription Returns all routes of this API in human readable format * @apiGroup Misc */ -const printRoutes = function printRoutes (req, res) { +const printRoutes = async function printRoutes (req, res) { res.header('Content-Type', 'text/plain; charset=utf-8'); const lines = [ @@ -122,7 +122,6 @@ const routes = { { path: `${managementPath}/users/:userId`, method: 'put', handler: managementController.updateUser, reference: 'api-Admin-updateUser' }, { path: `${managementPath}/users/delete`, method: 'post', handler: managementController.deleteUsers, reference: 'api-Admin-deleteUsers' }, { path: `${managementPath}/users/:userId/exec`, method: 'post', handler: managementController.execUserAction, reference: 'api-Admin-execUserAction' }, - ] }; diff --git a/packages/api/package.json b/packages/api/package.json index 8689d385..1d1e8292 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -22,9 +22,8 @@ "@turf/square-grid": "^6.3.0", "@turf/triangle-grid": "^6.3.0", "apicache": "^1.6.2", - "bunyan": "^1.8.15", "config": "^3.3.6", - "csv-stringify": "^5.6.2", + "csv-stringify": "^6.2.3", "dashify": "^2.0.0", "got": "^11.8.2", "honeybadger": "^1.4.0", @@ -33,11 +32,15 @@ "millify": "^5.0.1", "moment": "^2.29.4", "ms": "^2.1.3", - "restify": "8.6.1", + "pino": "^8.8.0", + "restify": "9.0.0", "restify-errors": "^8.0.2", "simple-statistics": "^7.7.0", "stringify-stream": "^1.0.5", "uuid": "^8.3.2" }, - "private": true + "private": true, + "devDependencies": { + "csv-parse": "^4.15.4" + } } diff --git a/packages/models/package.json b/packages/models/package.json index 22ad371c..cc0fe68e 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -10,7 +10,6 @@ "@sensebox/osem-protos": "^1.1.0", "@sensebox/sketch-templater": "1.12.1", "bcrypt": "^5.1.0", - "bunyan": "^1.8.15", "config": "^3.3.6", "got": "^11.8.2", "isemail": "^3.0.0", @@ -19,6 +18,7 @@ "moment": "^2.29.4", "mongoose": "^4.13.21", "mongoose-timestamp": "^0.6", + "pino": "^8.8.0", "uuid": "^8.3.2" }, "scripts": { diff --git a/packages/models/src/box/box.js b/packages/models/src/box/box.js index 279799ae..0ec698eb 100644 --- a/packages/models/src/box/box.js +++ b/packages/models/src/box/box.js @@ -717,6 +717,8 @@ boxSchema.statics.findMeasurementsOfBoxesStream = function findMeasurementsOfBox return Promise.reject(new Error('missing sensor query')); } + // return this.find(query, BOX_PROPS_FOR_POPULATION).cursor({ lean: true }); + return this.find(query, BOX_PROPS_FOR_POPULATION) .lean() .then(function (boxData) { @@ -772,7 +774,7 @@ boxSchema.statics.findMeasurementsOfBoxesStream = function findMeasurementsOfBox boxSchema.statics.findMeasurementsOfBoxesByTagStream = function findMeasurementsOfBoxesByTagStream (opts) { const { query } = opts; - return this.find(query, BOX_PROPS_FOR_POPULATION) + return Promise.resolve(this.find(query, BOX_PROPS_FOR_POPULATION) .lean() .then(function (boxData) { if (boxData.length === 0) { @@ -805,7 +807,7 @@ boxSchema.statics.findMeasurementsOfBoxesByTagStream = function findMeasurements return Measurement.find(measureQuery, { 'createdAt': 1, 'value': 1, 'location': 1, '_id': 0, 'sensor_id': 1 }) .cursor({ lean: true, order: 1 }) .map(transformer); - }); + })); }; diff --git a/packages/models/src/log.js b/packages/models/src/log.js index 5fe8a140..c513e095 100644 --- a/packages/models/src/log.js +++ b/packages/models/src/log.js @@ -1,5 +1,8 @@ 'use strict'; -const bunyan = require('bunyan'); +const pino = require('pino'); -module.exports = bunyan.createLogger({ name: 'opensensemap-api-models', serializers: bunyan.stdSerializers }); +module.exports = pino({ + name: 'opensensemap-api-models', + serializers: pino.stdSerializers +}); diff --git a/yarn.lock b/yarn.lock index 6ff464d7..7daf4003 100644 --- a/yarn.lock +++ b/yarn.lock @@ -392,6 +392,13 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + acorn-jsx@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" @@ -541,6 +548,11 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= +atomic-sleep@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" + integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -648,15 +660,13 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -bunyan@^1.8.12, bunyan@^1.8.15: - version "1.8.15" - resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.15.tgz#8ce34ca908a17d0776576ca1b2f6cbd916e93b46" - integrity sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig== - optionalDependencies: - dtrace-provider "~0.8" - moment "^2.19.3" - mv "~2" - safe-json-stringify "~1" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" cacheable-lookup@^5.0.3: version "5.0.3" @@ -971,26 +981,21 @@ csv-generate@^3.4.3: resolved "https://registry.yarnpkg.com/csv-generate/-/csv-generate-3.4.3.tgz#bc42d943b45aea52afa896874291da4b9108ffff" integrity sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw== -csv-parse@^4.15.4: - version "4.15.4" - resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-4.15.4.tgz#ad1ec62aaf71a642982dfcb81f1848184d691db5" - integrity sha512-OdBbFc0yZhOm17lSxqkirrHlFFVpKRT0wp4DAGoJelsP3LbGzV9LNr7XmM/lrr0uGkCtaqac9UhP8PDHXOAbMg== - -csv-parse@^4.16.3: +csv-parse@^4.15.4, csv-parse@^4.16.3: version "4.16.3" resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-4.16.3.tgz#7ca624d517212ebc520a36873c3478fa66efbaf7" integrity sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg== -csv-stringify@^5.6.2: - version "5.6.2" - resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-5.6.2.tgz#e653783e2189c4c797fbb12abf7f4943c787caa9" - integrity sha512-n3rIVbX6ylm1YsX2NEug9IaPV8xRnT+9/NNZbrA/bcHgOSSeqtWla6XnI/xmyu57wIw+ASCAoX1oM6EZtqJV0A== - csv-stringify@^5.6.5: version "5.6.5" resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-5.6.5.tgz#c6d74badda4b49a79bf4e72f91cce1e33b94de00" integrity sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A== +csv-stringify@^6.2.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-6.2.3.tgz#fefd25e66fd48f8f42f43b85a66a4663a2c3e796" + integrity sha512-4qGjUMwnlaRc00gc2jrIYh2w/h1fo25B0mTuY9K8fBiIgtmCX3LcgUbrEGViL98Ci4Se/F5LFEtu8k+dItJVZQ== + csv@^5.1.1: version "5.5.3" resolved "https://registry.yarnpkg.com/csv/-/csv-5.5.3.tgz#cd26c1e45eae00ce6a9b7b27dcb94955ec95207d" @@ -1166,7 +1171,7 @@ domutils@^2.5.1, domutils@^2.5.2: domelementtype "^2.2.0" domhandler "^4.1.0" -dtrace-provider@^0.8.1, dtrace-provider@~0.8: +dtrace-provider@^0.8.1: version "0.8.8" resolved "https://registry.yarnpkg.com/dtrace-provider/-/dtrace-provider-0.8.8.tgz#2996d5490c37e1347be263b423ed7b297fb0d97e" integrity sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg== @@ -1407,6 +1412,16 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +events@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + ewma@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/ewma/-/ewma-2.0.1.tgz#9876c1c491ac5733c8666001a3961a04c97cf1e8" @@ -1454,6 +1469,16 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-redact@^3.0.0, fast-redact@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.1.2.tgz#d58e69e9084ce9fa4c1a6fa98a3e1ecf5d7839aa" + integrity sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw== + +fast-safe-stringify@^2.0.8: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -1498,6 +1523,11 @@ flat@^5.0.2: resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== +flatstr@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/flatstr/-/flatstr-1.0.12.tgz#c2ba6a08173edbb6c9640e3055b95e287ceb5931" + integrity sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw== + flatted@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.0.tgz#a5d06b4a8b01e3a63771daa5cb7a1903e2e57067" @@ -1640,17 +1670,6 @@ glob@7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^6.0.1: - version "6.0.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" - integrity sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI= - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@^7.1.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -1812,17 +1831,18 @@ http-deceiver@^1.2.7: resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== +http-errors@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== dependencies: depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.1" -http-signature@^1.2.0: +http-signature@^1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9" integrity sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw== @@ -1863,7 +1883,7 @@ iconv-lite@~0.4.13: dependencies: safer-buffer ">= 2.1.2 < 3" -ieee754@^1.1.13: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -1894,16 +1914,11 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== - ip-regex@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -2227,12 +2242,7 @@ mime-types@^2.1.12, mime-types@~2.1.19: dependencies: mime-db "1.44.0" -mime@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" - integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== - -mime@^1.4.1: +mime@1.6.0, mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== @@ -2265,7 +2275,7 @@ minimalistic-assert@^1.0.0: resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -"minimatch@2 || 3", minimatch@3.0.4: +minimatch@3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -2309,13 +2319,6 @@ mkdirp@^1.0.3: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@~0.5.1: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - mocha@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.3.2.tgz#53406f195fa86fbdebe71f8b1c6fb23221d69fcc" @@ -2347,7 +2350,7 @@ mocha@^8.3.2: yargs-parser "20.2.4" yargs-unparser "2.0.0" -moment@^2.19.3, moment@^2.29.4: +moment@^2.29.4: version "2.29.4" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== @@ -2464,15 +2467,6 @@ muri@1.3.0: resolved "https://registry.yarnpkg.com/muri/-/muri-1.3.0.tgz#aeccf3db64c56aa7c5b34e00f95b7878527a4721" integrity sha512-FiaFwKl864onHFFUV/a2szAl7X0fxVlSKNdhTf+BM8i8goEgYut8u5P9MqQqIYwvaMxjzVESsoEm/2kfkFH1rg== -mv@~2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2" - integrity sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI= - dependencies: - mkdirp "~0.5.1" - ncp "~2.0.0" - rimraf "~2.4.0" - nan@^2.14.0: version "2.14.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" @@ -2488,11 +2482,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -ncp@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" - integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= - negotiator@^0.6.2: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" @@ -2564,6 +2553,11 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== +on-exit-leak-free@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz#5c703c968f7e7f851885f6459bf8a8a57edc9cc4" + integrity sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w== + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -2677,6 +2671,54 @@ pidusage@^2.0.17: dependencies: safe-buffer "^5.2.1" +pino-abstract-transport@v1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz#cc0d6955fffcadb91b7b49ef220a6cc111d48bb3" + integrity sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA== + dependencies: + readable-stream "^4.0.0" + split2 "^4.0.0" + +pino-std-serializers@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz#b56487c402d882eb96cd67c257868016b61ad671" + integrity sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg== + +pino-std-serializers@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-6.1.0.tgz#307490fd426eefc95e06067e85d8558603e8e844" + integrity sha512-KO0m2f1HkrPe9S0ldjx7za9BJjeHqBku5Ch8JyxETxT8dEFGz1PwgrHaOQupVYitpzbFSYm7nnljxD8dik2c+g== + +pino@^6.3.2: + version "6.14.0" + resolved "https://registry.yarnpkg.com/pino/-/pino-6.14.0.tgz#b745ea87a99a6c4c9b374e4f29ca7910d4c69f78" + integrity sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg== + dependencies: + fast-redact "^3.0.0" + fast-safe-stringify "^2.0.8" + flatstr "^1.0.12" + pino-std-serializers "^3.1.0" + process-warning "^1.0.0" + quick-format-unescaped "^4.0.3" + sonic-boom "^1.0.2" + +pino@^8.8.0: + version "8.8.0" + resolved "https://registry.yarnpkg.com/pino/-/pino-8.8.0.tgz#1f0d6695a224aa06afc7ad60f2ccc4772d3b9233" + integrity sha512-cF8iGYeu2ODg2gIwgAHcPrtR63ILJz3f7gkogaHC/TXVVXxZgInmNYiIpDYEwgEkxZti2Se6P2W2DxlBIZe6eQ== + dependencies: + atomic-sleep "^1.0.0" + fast-redact "^3.1.1" + on-exit-leak-free "^2.1.0" + pino-abstract-transport v1.0.0 + pino-std-serializers "^6.0.0" + process-warning "^2.0.0" + quick-format-unescaped "^4.0.3" + real-require "^0.2.0" + safe-stable-stringify "^2.3.1" + sonic-boom "^3.1.0" + thread-stream "^2.0.0" + polygon-clipping@^0.15.2: version "0.15.2" resolved "https://registry.yarnpkg.com/polygon-clipping/-/polygon-clipping-0.15.2.tgz#076199182bf23a4a6cf6c7003f3b81ecf30b2cb8" @@ -2704,6 +2746,21 @@ process-nextick-args@~1.0.6: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= +process-warning@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616" + integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== + +process-warning@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-2.1.0.tgz#1e60e3bfe8183033bbc1e702c2da74f099422d1a" + integrity sha512-9C20RLxrZU/rFnxWncDkuF6O999NdIf3E1ws4B0ZeY3sRVPzWBMsYDE2lxjxhiXxg464cQTgKUGm8/i6y2YGXg== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" @@ -2770,6 +2827,11 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== +quick-format-unescaped@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" + integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== + quick-lru@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" @@ -2792,7 +2854,7 @@ randomgeojson@^1.0.0: resolved "https://registry.yarnpkg.com/randomgeojson/-/randomgeojson-1.0.0.tgz#2446f5a5cd88365365a10ebdda9fe76656fa6b1f" integrity sha1-JEb1pc2INlNloQ692p/nZlb6ax8= -range-parser@~1.2.0: +range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== @@ -2839,6 +2901,16 @@ readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.3.0.tgz#0914d0c72db03b316c9733bb3461d64a3cc50cba" + integrity sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + readable-stream@~2.1.0: version "2.1.5" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" @@ -2859,6 +2931,11 @@ readdirp@~3.5.0: dependencies: picomatch "^2.2.1" +real-require@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78" + integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg== + regexp-clone@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/regexp-clone/-/regexp-clone-0.0.1.tgz#a7c2e09891fdbf38fbb10d376fb73003e68ac589" @@ -2958,29 +3035,29 @@ restify-errors@^8.0.2: optionalDependencies: safe-json-stringify "^1.0.4" -restify@8.6.1: - version "8.6.1" - resolved "https://registry.yarnpkg.com/restify/-/restify-8.6.1.tgz#728d391797e2fe20d6a748737da156a66575796f" - integrity sha512-I54/Geo2qN4K/2Ers+zNAU/A/nwPrcoTVBVeamw/sROv/kLLuMAzidLmO3f6842tKFxxQvcNhOMYoWZAhYr3vQ== +restify@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/restify/-/restify-9.0.0.tgz#9f75680ed7f740e2058141639c89b16eeb597a03" + integrity sha512-3bl0+3GdwCGvHKRf95Zhem/kc/zzWtcxl1kKmv3uDLN64WsXImRcMFR7ryqKVuv5RGIkOU7kVSbCbyxrLhHWdg== dependencies: assert-plus "^1.0.0" - bunyan "^1.8.12" csv "^5.1.1" escape-regexp-component "^1.0.2" ewma "^2.0.1" find-my-way "^2.0.1" formidable "^1.2.1" - http-signature "^1.2.0" + http-signature "^1.3.6" lodash "^4.17.11" lru-cache "^5.1.1" mime "^2.4.3" negotiator "^0.6.2" once "^1.4.0" pidusage "^2.0.17" + pino "^6.3.2" qs "^6.7.0" restify-errors "^8.0.2" semver "^6.1.1" - send "^0.16.2" + send "^0.17.1" spdy "^4.0.0" uuid "^3.3.2" vasync "^2.2.0" @@ -2999,13 +3076,6 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" -rimraf@~2.4.0: - version "2.4.5" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" - integrity sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto= - dependencies: - glob "^6.0.1" - safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -3016,7 +3086,7 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-json-stringify@^1.0.4, safe-json-stringify@~1: +safe-json-stringify@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd" integrity sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg== @@ -3028,6 +3098,11 @@ safe-regex2@^2.0.0: dependencies: ret "~0.2.0" +safe-stable-stringify@^2.3.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz#ec7b037768098bf65310d1d64370de0dc02353aa" + integrity sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA== + "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -3060,10 +3135,10 @@ semver@^7.2.1, semver@^7.3.5, semver@^7.3.8: dependencies: lru-cache "^6.0.0" -send@^0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" - integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw== +send@^0.17.1: + version "0.17.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" + integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== dependencies: debug "2.6.9" depd "~1.1.2" @@ -3072,12 +3147,12 @@ send@^0.16.2: escape-html "~1.0.3" etag "~1.8.1" fresh "0.5.2" - http-errors "~1.6.2" - mime "1.4.1" - ms "2.0.0" + http-errors "1.8.1" + mime "1.6.0" + ms "2.1.3" on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.4.0" + range-parser "~1.2.1" + statuses "~1.5.0" serialize-javascript@5.0.1: version "5.0.1" @@ -3091,10 +3166,10 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== shebang-command@^2.0.0: version "2.0.0" @@ -3146,6 +3221,21 @@ sliced@1.0.1: resolved "https://registry.yarnpkg.com/sliced/-/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41" integrity sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E= +sonic-boom@^1.0.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-1.4.1.tgz#d35d6a74076624f12e6f917ade7b9d75e918f53e" + integrity sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg== + dependencies: + atomic-sleep "^1.0.0" + flatstr "^1.0.12" + +sonic-boom@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.2.1.tgz#972ceab831b5840a08a002fa95a672008bda1c38" + integrity sha512-iITeTHxy3B9FGu8aVdiDXUVAcHMF9Ss0cCsAOo2HfCrmVGT3/DT5oYaeu0M/YKZDlKTvChEyPq0zI9Hf33EX6A== + dependencies: + atomic-sleep "^1.0.0" + source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -3186,6 +3276,11 @@ split2@^3.1.0: dependencies: readable-stream "^3.0.0" +split2@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-4.1.0.tgz#101907a24370f85bb782f08adaabe4e281ecf809" + integrity sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -3233,16 +3328,11 @@ static-eval@2.0.2: dependencies: escodegen "^1.8.1" -"statuses@>= 1.4.0 < 2": +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== -statuses@~1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" - integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== - stream-shift@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" @@ -3409,6 +3499,13 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= +thread-stream@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-2.2.0.tgz#310c03a253f729094ce5d4638ef5186dfa80a9e8" + integrity sha512-rUkv4/fnb4rqy/gGy7VuqK6wE1+1DOCOWy4RMeaV69ZHMP11tQKZvZSip1yTgrKCMZzEMcCL/bKfHvSfDHx+iQ== + dependencies: + real-require "^0.2.0" + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -3416,6 +3513,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"