From 87c907f543dd5c3a82c3bc1be1165aad0fda1bd7 Mon Sep 17 00:00:00 2001 From: Quan HL Date: Tue, 14 Feb 2023 08:46:39 +0700 Subject: [PATCH] feat: start using verb-specifications --- lib/routes/api/applications.js | 14 +- lib/utils/jambonz-app-json-validation.js | 112 ---- lib/utils/specs.json | 759 ----------------------- package-lock.json | 309 +++++++++ package.json | 1 + 5 files changed, 321 insertions(+), 874 deletions(-) delete mode 100644 lib/utils/jambonz-app-json-validation.js delete mode 100644 lib/utils/specs.json diff --git a/lib/routes/api/applications.js b/lib/routes/api/applications.js index c55c37e..66d05e0 100644 --- a/lib/routes/api/applications.js +++ b/lib/routes/api/applications.js @@ -6,7 +6,7 @@ const Webhook = require('../../models/webhook'); const {promisePool} = require('../../db'); const decorate = require('./decorate'); const sysError = require('../error'); -const jambonzAppJsonValidator = require('../../utils/jambonz-app-json-validation'); +const { validate } = require('verb-specifications/jambonz-app-json-validation'); const preconditions = { 'add': validateAdd, 'update': validateUpdate @@ -80,7 +80,11 @@ router.post('/', async(req, res) => { // validate app json if required if (obj['app_json']) { const app_json = JSON.parse(obj['app_json']); - jambonzAppJsonValidator(logger, app_json); + try { + validate(logger, app_json); + } catch (err) { + throw new DbErrorBadRequest(err); + } } const uuid = await Application.make(obj); @@ -189,7 +193,11 @@ router.put('/:sid', async(req, res) => { // validate app json if required if (obj['app_json']) { const app_json = JSON.parse(obj['app_json']); - jambonzAppJsonValidator(logger, app_json); + try { + validate(logger, app_json); + } catch (err) { + throw new DbErrorBadRequest(err); + } } const rowsAffected = await Application.update(sid, obj); diff --git a/lib/utils/jambonz-app-json-validation.js b/lib/utils/jambonz-app-json-validation.js deleted file mode 100644 index 4e22072..0000000 --- a/lib/utils/jambonz-app-json-validation.js +++ /dev/null @@ -1,112 +0,0 @@ -const {DbErrorBadRequest} = require('../utils/errors'); -const assert = require('assert'); -const _specData = require('./specs'); -const specs = new Map(); -for (const key in _specData) { specs.set(key, _specData[key]); } - -function normalizeJambones(logger, obj) { - if (!Array.isArray(obj)) throw new DbErrorBadRequest('malformed jambonz payload: must be array'); - const document = []; - for (const tdata of obj) { - if (typeof tdata !== 'object') throw new DbErrorBadRequest('malformed jambonz payload: must be array of objects'); - if ('verb' in tdata) { - // {verb: 'say', text: 'foo..bar'..} - const name = tdata.verb; - const o = {}; - Object.keys(tdata) - .filter((k) => k !== 'verb') - .forEach((k) => o[k] = tdata[k]); - const o2 = {}; - o2[name] = o; - document.push(o2); - } - else if (Object.keys(tdata).length === 1) { - // {'say': {..}} - document.push(tdata); - } - else { - logger.info(tdata, 'malformed jambonz payload: missing verb property'); - throw new DbErrorBadRequest('malformed jambonz payload: missing verb property'); - } - } - logger.debug({ document }, `normalizeJambones: returning document with ${document.length} tasks`); - return document; -} - -function validate(logger, obj) { - normalizeJambones(logger, obj).map((tdata) => { - const keys = Object.keys(tdata); - const name = keys[0]; - const data = tdata[name]; - validateVerb(name, data, logger); - }); -} - -function validateVerb(name, data, logger) { - logger.debug(`validating ${name} with data ${JSON.stringify(data)}`); - // validate the instruction is supported - if (!specs.has(name)) throw new DbErrorBadRequest(`invalid instruction: ${name}`); - - // check type of each element and make sure required elements are present - const specData = specs.get(name); - let required = specData.required || []; - for (const dKey in data) { - if (dKey in specData.properties) { - const dVal = data[dKey]; - const dSpec = specData.properties[dKey]; - logger.debug(`Task:validate validating property ${dKey} with value ${JSON.stringify(dVal)}`); - - if (typeof dSpec === 'string' && dSpec === 'array') { - if (!Array.isArray(dVal)) throw new DbErrorBadRequest(`${name}: property ${dKey} is not an array`); - } - else if (typeof dSpec === 'string' && dSpec.includes('|')) { - const types = dSpec.split('|').map((t) => t.trim()); - if (!types.includes(typeof dVal) && !(types.includes('array') && Array.isArray(dVal))) { - throw new DbErrorBadRequest(`${name}: property ${dKey} has invalid data type, must be one of ${types}`); - } - } - else if (typeof dSpec === 'string' && ['number', 'string', 'object', 'boolean'].includes(dSpec)) { - // simple types - if (typeof dVal !== specData.properties[dKey]) { - throw new DbErrorBadRequest(`${name}: property ${dKey} has invalid data type`); - } - } - else if (Array.isArray(dSpec) && dSpec[0].startsWith('#')) { - const name = dSpec[0].slice(1); - for (const item of dVal) { - validate(name, item); - } - } - else if (typeof dSpec === 'object') { - // complex types - const type = dSpec.type; - assert.ok(['number', 'string', 'object', 'boolean'].includes(type), - `invalid or missing type in spec ${JSON.stringify(dSpec)}`); - if (type === 'string' && dSpec.enum) { - assert.ok(Array.isArray(dSpec.enum), `enum must be an array ${JSON.stringify(dSpec.enum)}`); - if (!dSpec.enum.includes(dVal)) { - throw new DbErrorBadRequest(`invalid value ${dVal} must be one of ${dSpec.enum}`); - } - } - } - else if (typeof dSpec === 'string' && dSpec.startsWith('#')) { - // reference to another datatype (i.e. nested type) - const name = dSpec.slice(1); - //const obj = {}; - //obj[name] = dVal; - validate(name, dVal); - } - else { - assert.ok(0, `invalid spec ${JSON.stringify(dSpec)}`); - } - required = required.filter((item) => item !== dKey); - } - else if (dKey === '_') { - /* no op: allow arbitrary info to be carried here, used by conference e.g in transfer */ - } - else throw new DbErrorBadRequest(`${name}: unknown property ${dKey}`); - } - if (required.length > 0) throw new DbErrorBadRequest(`${name}: missing value for ${required}`); -} - -module.exports = validate; diff --git a/lib/utils/specs.json b/lib/utils/specs.json deleted file mode 100644 index df3c2b3..0000000 --- a/lib/utils/specs.json +++ /dev/null @@ -1,759 +0,0 @@ -{ - "sip:decline": { - "properties": { - "id": "string", - "status": "number", - "reason": "string", - "headers": "object" - }, - "required": [ - "status" - ] - }, - "sip:request": { - "properties": { - "id": "string", - "method": "string", - "body": "string", - "headers": "object", - "actionHook": "object|string" - }, - "required": [ - "method" - ] - }, - "sip:refer": { - "properties": { - "id": "string", - "referTo": "string", - "referredBy": "string", - "headers": "object", - "actionHook": "object|string", - "eventHook": "object|string" - }, - "required": [ - "referTo" - ] - }, - "config": { - "properties": { - "id": "string", - "synthesizer": "#synthesizer", - "recognizer": "#recognizer", - "bargeIn": "#bargeIn", - "record": "#recordOptions", - "amd": "#amd", - "notifyEvents": "boolean" - }, - "required": [] - }, - "bargeIn": { - "properties": { - "enable": "boolean", - "sticky": "boolean", - "actionHook": "object|string", - "input": "array", - "finishOnKey": "string", - "numDigits": "number", - "minDigits": "number", - "maxDigits": "number", - "interDigitTimeout": "number", - "dtmfBargein": "boolean", - "minBargeinWordCount": "number" - }, - "required": [ - "enable" - ] - }, - "dequeue": { - "properties": { - "id": "string", - "name": "string", - "actionHook": "object|string", - "timeout": "number", - "beep": "boolean" - }, - "required": [ - "name" - ] - }, - "enqueue": { - "properties": { - "id": "string", - "name": "string", - "actionHook": "object|string", - "waitHook": "object|string", - "_": "object" - }, - "required": [ - "name" - ] - }, - "leave": { - "properties": { - "id": "string" - } - }, - "hangup": { - "properties": { - "id": "string", - "headers": "object" - }, - "required": [ - ] - }, - "play": { - "properties": { - "id": "string", - "url": "string|array", - "loop": "number|string", - "earlyMedia": "boolean", - "seekOffset": "number|string", - "timeoutSecs": "number|string", - "actionHook": "object|string" - }, - "required": [ - "url" - ] - }, - "say": { - "properties": { - "id": "string", - "text": "string|array", - "loop": "number|string", - "synthesizer": "#synthesizer", - "earlyMedia": "boolean", - "disableTtsCache": "boolean" - }, - "required": [ - "text" - ] - }, - "gather": { - "properties": { - "id": "string", - "actionHook": "object|string", - "finishOnKey": "string", - "input": "array", - "numDigits": "number", - "minDigits": "number", - "maxDigits": "number", - "interDigitTimeout": "number", - "partialResultHook": "object|string", - "speechTimeout": "number", - "listenDuringPrompt": "boolean", - "dtmfBargein": "boolean", - "bargein": "boolean", - "minBargeinWordCount": "number", - "timeout": "number", - "recognizer": "#recognizer", - "play": "#play", - "say": "#say" - }, - "required": [ - ] - }, - "conference": { - "properties": { - "id": "string", - "name": "string", - "beep": "boolean", - "startConferenceOnEnter": "boolean", - "endConferenceOnExit": "boolean", - "maxParticipants": "number", - "joinMuted": "boolean", - "actionHook": "object|string", - "waitHook": "object|string", - "statusEvents": "array", - "statusHook": "object|string", - "enterHook": "object|string", - "record": "#record" - }, - "required": [ - "name" - ] - }, - "dial": { - "properties": { - "id": "string", - "actionHook": "object|string", - "answerOnBridge": "boolean", - "callerId": "string", - "confirmHook": "object|string", - "referHook": "object|string", - "dialMusic": "string", - "dtmfCapture": "object", - "dtmfHook": "object|string", - "headers": "object", - "listen": "#listen", - "target": ["#target"], - "timeLimit": "number", - "timeout": "number", - "proxy": "string", - "transcribe": "#transcribe", - "amd": "#amd" - }, - "required": [ - "target" - ] - }, - "dialogflow": { - "properties": { - "id": "string", - "credentials": "object|string", - "project": "string", - "environment": "string", - "region": { - "type": "string", - "enum": ["europe-west1", "europe-west2", "australia-southeast1", "asia-northeast1"] - }, - "lang": "string", - "actionHook": "object|string", - "eventHook": "object|string", - "events": "[string]", - "welcomeEvent": "string", - "welcomeEventParams": "object", - "noInputTimeout": "number", - "noInputEvent": "string", - "passDtmfAsTextInput": "boolean", - "thinkingMusic": "string", - "tts": "#synthesizer", - "bargein": "boolean" - }, - "required": [ - "project", - "credentials", - "lang" - ] - }, - "dtmf": { - "properties": { - "id": "string", - "dtmf": "string", - "duration": "number" - }, - "required": [ - "dtmf" - ] - }, - "lex": { - "properties": { - "id": "string", - "botId": "string", - "botAlias": "string", - "credentials": "object", - "region": "string", - "locale": "string", - "intent": "#lexIntent", - "welcomeMessage": "string", - "metadata": "object", - "bargein": "boolean", - "passDtmf": "boolean", - "actionHook": "object|string", - "eventHook": "object|string", - "noInputTimeout": "number", - "tts": "#synthesizer" - }, - "required": [ - "botId", - "botAlias", - "region", - "credentials" - ] - }, - "listen": { - "properties": { - "id": "string", - "actionHook": "object|string", - "auth": "#auth", - "finishOnKey": "string", - "maxLength": "number", - "metadata": "object", - "mixType": { - "type": "string", - "enum": ["mono", "stereo", "mixed"] - }, - "passDtmf": "boolean", - "playBeep": "boolean", - "sampleRate": "number", - "timeout": "number", - "transcribe": "#transcribe", - "url": "string", - "wsAuth": "#auth", - "earlyMedia": "boolean" - }, - "required": [ - "url" - ] - }, - "message": { - "properties": { - "id": "string", - "carrier": "string", - "account_sid": "string", - "message_sid": "string", - "to": "string", - "from": "string", - "text": "string", - "media": "string|array", - "actionHook": "object|string" - }, - "required": [ - "to", - "from" - ] - }, - "pause": { - "properties": { - "id": "string", - "length": "number" - }, - "required": [ - "length" - ] - }, - "rasa": { - "properties": { - "id": "string", - "url": "string", - "recognizer": "#recognizer", - "tts": "#synthesizer", - "prompt": "string", - "actionHook": "object|string", - "eventHook": "object|string" - }, - "required": [ - "url" - ] - }, - "record": { - "properties": { - "path": "string" - }, - "required": [ - "path" - ] - }, - "recordOptions": { - "properties": { - "action": { - "type": "string", - "enum": ["startCallRecording", "stopCallRecording", "pauseCallRecording", "resumeCallRecording"] - }, - "recordingID": "string", - "siprecServerURL": "string" - }, - "required": [ - "action" - ] - }, - "redirect": { - "properties": { - "id": "string", - "actionHook": "object|string" - }, - "required": [ - "actionHook" - ] - }, - "rest:dial": { - "properties": { - "id": "string", - "account_sid": "string", - "application_sid": "string", - "call_hook": "object|string", - "call_status_hook": "object|string", - "from": "string", - "fromHost": "string", - "speech_synthesis_vendor": "string", - "speech_synthesis_voice": "string", - "speech_synthesis_language": "string", - "speech_recognizer_vendor": "string", - "speech_recognizer_language": "string", - "tag": "object", - "to": "#target", - "headers": "object", - "timeout": "number" - }, - "required": [ - "call_hook", - "from", - "to" - ] - }, - "tag": { - "properties": { - "id": "string", - "data": "object" - }, - "required": [ - "data" - ] - }, - "transcribe": { - "properties": { - "id": "string", - "transcriptionHook": "string", - "recognizer": "#recognizer", - "earlyMedia": "boolean" - }, - "required": [ - "recognizer" - ] - }, - "target": { - "properties": { - "type": { - "type": "string", - "enum": ["phone", "sip", "user", "teams"] - }, - "confirmHook": "object|string", - "method": { - "type": "string", - "enum": ["GET", "POST"] - }, - "headers": "object", - "from": "#dialFrom", - "name": "string", - "number": "string", - "sipUri": "string", - "auth": "#auth", - "vmail": "boolean", - "tenant": "string", - "trunk": "string", - "overrideTo": "string" - }, - "required": [ - "type" - ] - }, - "dialFrom": { - "properties": { - "user": "string", - "host": "string" - }, - "required": [ - ] - }, - "auth": { - "properties": { - "username": "string", - "password": "string" - }, - "required": [ - "username", - "password" - ] - }, - "synthesizer": { - "properties": { - "vendor": { - "type": "string", - "enum": ["google", "aws", "polly", "microsoft", "nuance", "ibm", "default"] - }, - "language": "string", - "voice": "string", - "engine": { - "type": "string", - "enum": ["standard", "neural"] - }, - "gender": { - "type": "string", - "enum": ["MALE", "FEMALE", "NEUTRAL"] - } - }, - "required": [ - "vendor" - ] - }, - "recognizer": { - "properties": { - "vendor": { - "type": "string", - "enum": ["google", "aws", "microsoft", "nuance", "deepgram", "ibm", "default"] - }, - "language": "string", - "vad": "#vad", - "hints": "array", - "hintsBoost": "number", - "altLanguages": "array", - "profanityFilter": "boolean", - "interim": "boolean", - "singleUtterance": "boolean", - "dualChannel": "boolean", - "separateRecognitionPerChannel": "boolean", - "punctuation": "boolean", - "enhancedModel": "boolean", - "words": "boolean", - "diarization": "boolean", - "diarizationMinSpeakers": "number", - "diarizationMaxSpeakers": "number", - "interactionType": { - "type": "string", - "enum": [ - "unspecified", - "discussion", - "presentation", - "phone_call", - "voicemail", - "voice_search", - "voice_command", - "dictation" - ] - }, - "naicsCode": "number", - "identifyChannels": "boolean", - "vocabularyName": "string", - "vocabularyFilterName": "string", - "filterMethod": { - "type": "string", - "enum": [ - "remove", - "mask", - "tag" - ] - }, - "model": "string", - "outputFormat": { - "type": "string", - "enum": [ - "simple", - "detailed" - ] - }, - "profanityOption": { - "type": "string", - "enum": [ - "masked", - "removed", - "raw" - ] - }, - "requestSnr": "boolean", - "initialSpeechTimeoutMs": "number", - "azureServiceEndpoint": "string", - "azureSttEndpointId": "string", - "asrDtmfTerminationDigit": "string", - "asrTimeout": "number", - "nuanceOptions": "#nuanceOptions", - "deepgramOptions": "#deepgramOptions", - "ibmOptions": "#ibmOptions" - }, - "required": [ - "vendor" - ] - }, - "ibmOptions": { - "properties": { - "sttApiKey": "string", - "sttRegion": "string", - "ttsApiKey": "string", - "ttsRegion": "string", - "instanceId": "string", - "model": "string", - "languageCustomizationId": "string", - "acousticCustomizationId": "string", - "baseModelVersion": "string", - "watsonMetadata": "string", - "watsonLearningOptOut": "boolean" - }, - "required": [ - ] - }, - "deepgramOptions": { - "properties": { - "apiKey": "string", - "tier": { - "type": "string", - "enum": [ - "enhanced", - "base" - ] - }, - "model": { - "type": "string", - "enum": [ - "general", - "meeting", - "phonecall", - "voicemail", - "finance", - "conversationalai", - "video", - "custom" - ] - }, - "customModel": "string", - "version": "string", - "punctuate": "boolean", - "profanityFilter": "boolean", - "redact": { - "type": "string", - "enum": [ - "pci", - "numbers", - "true", - "ssn" - ] - }, - "diarize": "boolean", - "diarizeVersion": "string", - "ner": "boolean", - "multichannel": "boolean", - "alternatives": "number", - "numerals": "boolean", - "search": "array", - "replace": "array", - "keywords": "array", - "endpointing": "boolean", - "vadTurnoff": "number", - "tag": "string" - } - }, - "nuanceOptions": { - "properties": { - "clientId": "string", - "secret": "string", - "kryptonEndpoint": "string", - "topic": "string", - "utteranceDetectionMode": { - "type": "string", - "enum": [ - "single", - "multiple", - "disabled" - ] - }, - "punctuation": "boolean", - "profanityFilter": "boolean", - "includeTokenization": "boolean", - "discardSpeakerAdaptation": "boolean", - "suppressCallRecording": "boolean", - "maskLoadFailures": "boolean", - "suppressInitialCapitalization": "boolean", - "allowZeroBaseLmWeight": "boolean", - "filterWakeupWord": "boolean", - "resultType": { - "type": "string", - "enum": [ - "final", - "partial", - "immutable_partial" - ] - }, - "noInputTimeoutMs": "number", - "recognitionTimeoutMs": "number", - "utteranceEndSilenceMs": "number", - "maxHypotheses": "number", - "speechDomain": "string", - "formatting": "#formatting", - "clientData": "object", - "userId": "string", - "speechDetectionSensitivity": "number", - "resources": ["#resource"] - }, - "required": [ - ] - }, - "resource": { - "properties": { - "externalReference": "#resourceReference", - "inlineWordset": "string", - "builtin": "string", - "inlineGrammar": "string", - "wakeupWord": "[string]", - "weightName": { - "type": "string", - "enum": [ - "defaultWeight", - "lowest", - "low", - "medium", - "high", - "highest" - ] - }, - "weightValue": "number", - "reuse": { - "type": "string", - "enum": [ - "undefined_reuse", - "low_reuse", - "high_reuse" - ] - } - }, - "required": [ - ] - }, - "resourceReference": { - "properties": { - "type": { - "type": "string", - "enum": [ - "undefined_resource_type", - "wordset", - "compiled_wordset", - "domain_lm", - "speaker_profile", - "grammar", - "settings" - ] - }, - "uri": "string", - "maxLoadFailures": "boolean", - "requestTimeoutMs": "number", - "headers": "object" - }, - "required": [ - ] - }, - "formatting": { - "properties": { - "scheme": "string", - "options": "object" - }, - "required": [ - "scheme", - "options" - ] - }, - "lexIntent": { - "properties": { - "name": "string", - "slots": "object" - }, - "required": [ - "name" - ] - }, - "vad": { - "properties": { - "enable": "boolean", - "voiceMs": "number", - "mode": "number" - }, - "required": [ - "enable" - ] - }, - "amd": { - "properties": { - "actionHook": "object|string", - "thresholdWordCount": "number", - "timers": "#amdTimers", - "recognizer": "#recognizer" - }, - "required": [ - "actionHook" - ] - }, - "amdTimers": { - "properties": { - "noSpeechTimeoutMs": "number", - "decisionTimeoutMs": "number", - "toneTimeoutMs": "number", - "greetingCompletionTimeoutMs": "number" - } - } -} diff --git a/package-lock.json b/package-lock.json index 0eab409..3c41b61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "stripe": "^8.222.0", "swagger-ui-express": "^4.4.0", "uuid": "^8.3.2", + "verb-specifications": "^0.0.1", "yamljs": "^0.3.0" }, "devDependencies": { @@ -5273,6 +5274,11 @@ "node": ">=0.10.0" } }, + "node_modules/on-exit-leak-free": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", + "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==" + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -5526,6 +5532,79 @@ "pino": "bin.js" } }, + "node_modules/pino-abstract-transport": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz", + "integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==", + "dependencies": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "node_modules/pino-abstract-transport/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/pino-abstract-transport/node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/pino-abstract-transport/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/pino-abstract-transport/node_modules/readable-stream": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.3.0.tgz", + "integrity": "sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/pino-std-serializers": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-2.5.0.tgz", @@ -5566,6 +5645,14 @@ "node": ">= 10" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-on-spawn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", @@ -5578,6 +5665,11 @@ "node": ">=8" } }, + "node_modules/process-warning": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.1.0.tgz", + "integrity": "sha512-9C20RLxrZU/rFnxWncDkuF6O999NdIf3E1ws4B0ZeY3sRVPzWBMsYDE2lxjxhiXxg464cQTgKUGm8/i6y2YGXg==" + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -5884,6 +5976,14 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/redis": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", @@ -6219,6 +6319,14 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "node_modules/safe-stable-stringify": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz", + "integrity": "sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -6436,6 +6544,14 @@ "node": ">=8" } }, + "node_modules/split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -6883,6 +6999,14 @@ "url": "https://bevry.me/fund" } }, + "node_modules/thread-stream": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.3.0.tgz", + "integrity": "sha512-kaDqm1DET9pp3NXwR8382WHbnpXnRkN9xGN9dQt3B2+dmXiW8X1SOwmFOxAErEQ47ObhZ96J6yhZNXuyCOL7KA==", + "dependencies": { + "real-require": "^0.2.0" + } + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -7210,6 +7334,61 @@ "node": ">= 0.8" } }, + "node_modules/verb-specifications": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/verb-specifications/-/verb-specifications-0.0.1.tgz", + "integrity": "sha512-QKU+J6goicEaaBlr0ONSunUBMySrWWf9C96ZQCRu7kmfG5R5YXjKOX748bbbFZUFHUBDliBxPrDKtA6V+sf5pw==", + "dependencies": { + "pino": "^8.8.0" + } + }, + "node_modules/verb-specifications/node_modules/fast-redact": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.2.tgz", + "integrity": "sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/verb-specifications/node_modules/pino": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.10.0.tgz", + "integrity": "sha512-ODfIe+giJtQGsvNAEj5/sHHpL3TFBg161JBH4W62Hc0l0PJjsDFD1R7meLI4PZ2aoHDJznxFNShkJcaG/qJToQ==", + "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" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/verb-specifications/node_modules/pino-std-serializers": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.1.0.tgz", + "integrity": "sha512-KO0m2f1HkrPe9S0ldjx7za9BJjeHqBku5Ch8JyxETxT8dEFGz1PwgrHaOQupVYitpzbFSYm7nnljxD8dik2c+g==" + }, + "node_modules/verb-specifications/node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, + "node_modules/verb-specifications/node_modules/sonic-boom": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.2.1.tgz", + "integrity": "sha512-iITeTHxy3B9FGu8aVdiDXUVAcHMF9Ss0cCsAOo2HfCrmVGT3/DT5oYaeu0M/YKZDlKTvChEyPq0zI9Hf33EX6A==", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -11547,6 +11726,11 @@ "isobject": "^3.0.1" } }, + "on-exit-leak-free": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", + "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==" + }, "on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -11726,6 +11910,47 @@ "sonic-boom": "^0.7.5" } }, + "pino-abstract-transport": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz", + "integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==", + "requires": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + }, + "dependencies": { + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "readable-stream": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.3.0.tgz", + "integrity": "sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==", + "requires": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10" + } + } + } + }, "pino-std-serializers": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-2.5.0.tgz", @@ -11757,6 +11982,11 @@ "react-is": "^17.0.1" } }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, "process-on-spawn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", @@ -11766,6 +11996,11 @@ "fromentries": "^1.2.0" } }, + "process-warning": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.1.0.tgz", + "integrity": "sha512-9C20RLxrZU/rFnxWncDkuF6O999NdIf3E1ws4B0ZeY3sRVPzWBMsYDE2lxjxhiXxg464cQTgKUGm8/i6y2YGXg==" + }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -11991,6 +12226,11 @@ "readable-stream": "^3.6.0" } }, + "real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==" + }, "redis": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz", @@ -12244,6 +12484,11 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "safe-stable-stringify": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.2.tgz", + "integrity": "sha512-gMxvPJYhP0O9n2pvcfYfIuYgbledAOJFcqRThtPRmjscaipiwcwPPKLytpVzMkG2HAN87Qmo2d4PtGiri1dSLA==" + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -12426,6 +12671,11 @@ "which": "^2.0.1" } }, + "split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==" + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -12760,6 +13010,14 @@ "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.6.0.tgz", "integrity": "sha512-49WtAWS+tcsy93dRt6P0P3AMD2m5PvXRhuEA0kaXos5ZLlujtYmpmFsB+QvWUSxE1ZsstmYXfQ7L40+EcQgpAQ==" }, + "thread-stream": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.3.0.tgz", + "integrity": "sha512-kaDqm1DET9pp3NXwR8382WHbnpXnRkN9xGN9dQt3B2+dmXiW8X1SOwmFOxAErEQ47ObhZ96J6yhZNXuyCOL7KA==", + "requires": { + "real-require": "^0.2.0" + } + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -13009,6 +13267,57 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "verb-specifications": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/verb-specifications/-/verb-specifications-0.0.1.tgz", + "integrity": "sha512-QKU+J6goicEaaBlr0ONSunUBMySrWWf9C96ZQCRu7kmfG5R5YXjKOX748bbbFZUFHUBDliBxPrDKtA6V+sf5pw==", + "requires": { + "pino": "^8.8.0" + }, + "dependencies": { + "fast-redact": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.2.tgz", + "integrity": "sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==" + }, + "pino": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.10.0.tgz", + "integrity": "sha512-ODfIe+giJtQGsvNAEj5/sHHpL3TFBg161JBH4W62Hc0l0PJjsDFD1R7meLI4PZ2aoHDJznxFNShkJcaG/qJToQ==", + "requires": { + "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" + } + }, + "pino-std-serializers": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.1.0.tgz", + "integrity": "sha512-KO0m2f1HkrPe9S0ldjx7za9BJjeHqBku5Ch8JyxETxT8dEFGz1PwgrHaOQupVYitpzbFSYm7nnljxD8dik2c+g==" + }, + "quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, + "sonic-boom": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.2.1.tgz", + "integrity": "sha512-iITeTHxy3B9FGu8aVdiDXUVAcHMF9Ss0cCsAOo2HfCrmVGT3/DT5oYaeu0M/YKZDlKTvChEyPq0zI9Hf33EX6A==", + "requires": { + "atomic-sleep": "^1.0.0" + } + } + } + }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", diff --git a/package.json b/package.json index 34d9ca5..2ff1433 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@jambonz/db-helpers": "^0.7.3", "@jambonz/realtimedb-helpers": "^0.6.0", "@jambonz/time-series": "^0.2.5", + "verb-specifications": "^0.0.1", "argon2-ffi": "^2.0.0", "aws-sdk": "^2.1152.0", "bent": "^7.3.12",