diff --git a/.github/workflows/create_all_workflows.yml b/.github/workflows/create_all_workflows.yml new file mode 100644 index 0000000..4fd180d --- /dev/null +++ b/.github/workflows/create_all_workflows.yml @@ -0,0 +1,34 @@ +name: create-workflows +on: + push: +jobs: + create-all-workflows: + runs-on: ubuntu-20.04 + steps: + - name: checkout simulation data + uses: actions/checkout@v4 + with: + submodules: true + token: ${{ secrets.WRITE_WORKFLOW }} + - name: install prerequisites + run: | + npm install yaml --prefix ./workflow_utils + npm install ./workflow_utils/stmd-crud --prefix ./workflow_utils/stmd-crud + - name: create new workflow + env: + GithubBranch: ${{ github.ref_name }} + GithubRepoName: ${{ github.event.repository.name }} + GithubOwner: ${{ github.repository_owner }} + run: node ./workflow_utils/parse_to_yaml.js -f "./stmdtest.stmd" -o ./.github/workflows/ + - name: push workflows + run: | + git config --global user.name "Automated Test Pipeline" + git config --global user.email "setlabs@users.noreply.github.com" + + git add ./.github/workflows + git commit -m "Generated new automated all workflow, based on STMD [actions skip]" + git push -f + + - name: display result + run: | + echo "Successfully created new workflows" >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3cb00d9 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# Changelog + +- Changed CDK Schema: + - MetricType: Deleted attribute `packageUri`, added attributes `module`, `interpreter`, `interpreterVersion` + - SimpleProcessingType: Deleted attribute `packageUri`, added attributes `module`, `interpreter`, `interpreterVersion`. Changed `id` from optional to required. + - ComplexProcessingType: Changed attribute name `method` to `interpreter`. Added `python` to selection for `interpreter`. Added attribute `interpreterVersion`. Changed `id` from optional to required. + - Prerequisites: Deleted attribute name `method` --> only bash scripts allowed + +- json_to_yaml.js: + - added handling of python quality metrics and processing functions + - added version management for NodeJS + - added version management and virtual environments for python + - refactored and commented everything + +- wrappers: + - added python wrapper + - added internal function to allow correct allocation from function arguments (so they can occur in arbitrary order in the STMD) + +- parse_to_yaml.js: + - refactored \ No newline at end of file diff --git a/data/cert_ahmann.crt b/data/cert_ahmann.crt new file mode 100644 index 0000000..c708c2c --- /dev/null +++ b/data/cert_ahmann.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIENzCCAx+gAwIBAgIURfMlTJ+E8y+XBS3oudOk0z75+jQwDQYJKoZIhvcNAQEL +BQAwgaoxCzAJBgNVBAYTAkRFMRAwDgYDVQQIDAdCYXZhcmlhMQ8wDQYDVQQHDAZN +dW5pY2gxHjAcBgNVBAoMFVNFVExhYnMgUmVzZWFyY2ggR21iSDETMBEGA1UECwwK +QXV0b21vdGl2ZTEYMBYGA1UEAwwPTWF1cml6aW8gQWhtYW5uMSkwJwYJKoZIhvcN +AQkBFhptYXVyaXppby5haG1hbm5Ac2V0bGFicy5kZTAeFw0yMzAyMjMxNTMwNTla +Fw0yNDEyMzExNTMwNTlaMIGqMQswCQYDVQQGEwJERTEQMA4GA1UECAwHQmF2YXJp +YTEPMA0GA1UEBwwGTXVuaWNoMR4wHAYDVQQKDBVTRVRMYWJzIFJlc2VhcmNoIEdt +YkgxEzARBgNVBAsMCkF1dG9tb3RpdmUxGDAWBgNVBAMMD01hdXJpemlvIEFobWFu +bjEpMCcGCSqGSIb3DQEJARYabWF1cml6aW8uYWhtYW5uQHNldGxhYnMuZGUwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDSmUihmubtTl5DHeuo1xntCESm +hfZESRXVIgtKwQnHwcr4Y0KHB7No6MxEZN88f5Kdq9qa7yU1CmjlmBQvvFhLoyZE +Fciv2JH1lKPPY0hpa1aUMulygQrMBoWhI7C4DDg5GB8D2FmkMHKWvTzXqLIkQLpT +esKN64wTT44ElTjZaNLKpkH79sDbGzG8pxdtBE5abTainSBbpCFpt9or8Kc2PL74 +smd24IfAcfCq+71arCQXFaW0cIAiXHWbSBvQGmAN5s+hcZ97P6VEQV5MMjRPESF4 +toH1RahYLUYT18VICzWVnbo2eAxYZgAsgepCD/sAtAYBLbzxzip45oVIzUpfAgMB +AAGjUzBRMB0GA1UdDgQWBBQQjy3fPlVqebstqr2+s7s7MhorcjAfBgNVHSMEGDAW +gBQQjy3fPlVqebstqr2+s7s7MhorcjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQAShChBMOU89sy+cIElFgtd0gg33HgIWB3dTNqKS1tyKW95y0Hk +iTOSx31DVj+dT/bcqtrqNUqiad+iVp4QZLR5TlWTugxT958VTvm+KhPln7ovk+En +lvjWwFK3/wPEH9d+rTR7c20jlSl0MIfLXVTU7/8AkkbOZYHriH7AAoulldmc1s4n +L0e1sAhhL2AQBk6RR0FLLJTrXgKtdj3UxzYCnC/WTT6fj37KY9bVyXxQU46luumW +UMTNLhOOx1SILybhfkpMPff7VBP+9cN/qmk3dgT0LcBo9G/KyHpHs4TGWlpsPZTN +fsV1NFOtAz3c8ye83Pl98RkKn4uZi0Xdwy/D +-----END CERTIFICATE----- diff --git a/data/expertStatement_req_m_01.json b/data/expertStatement_req_m_01.json new file mode 100644 index 0000000..d0486d9 --- /dev/null +++ b/data/expertStatement_req_m_01.json @@ -0,0 +1 @@ +{"content":{"result":true,"log":"The requirement with ID #req_m_01 is necessary, unambiguous, complete, singular, achievable, and verifiable."},"signature":"cd61a6dd9c56d7e4a14fdd2f590b8f45cb8bd18c4ba9fd67570f35b7729b6fbdabaae2f27d953d372064e7e8dcc5c67825393bf129838c31e2841aeb18786d2ea58800ac1209bc55d66406cc7b437471809e32866ecece40f3fe44422234182fec38379f38adafe1217ed50655e63421bded321fcb2f4cfaff36cf84df2f74ddace9c0a8375a11bec6e4dbefa84c50be591b6353dbfc3120716d2aca95b7ad28669bf2c82c8b1f40634793a9c967e1f7c610e76131712eee17d15844731fa3e7e55221f13fbf54a1d187c044796e8e3a035fa3a1631b100c9e3ab28bee569bacea9bfba7fc304757ee5623c3141070bfd0e688cc0a2ae78fd7d20113c714e97e","hash_algorithm":"SHA256","signature_encoding":"hex"} \ No newline at end of file diff --git a/data/my_testdata.txt b/data/my_testdata.txt new file mode 100644 index 0000000..cdca4cc --- /dev/null +++ b/data/my_testdata.txt @@ -0,0 +1 @@ +[1, 4, 5, 7, 10] \ No newline at end of file diff --git a/data/sl-1-3-object-based-generic-perception-object-model.fmu b/data/sl-1-3-object-based-generic-perception-object-model.fmu new file mode 100644 index 0000000..95892e1 Binary files /dev/null and b/data/sl-1-3-object-based-generic-perception-object-model.fmu differ diff --git a/processing_functions/node_functions/util/util-common/README.md b/processing_functions/node_functions/util/util-common/README.md new file mode 100644 index 0000000..47a94fd --- /dev/null +++ b/processing_functions/node_functions/util/util-common/README.md @@ -0,0 +1 @@ +# UTIL-COMMON diff --git a/processing_functions/node_functions/util/util-common/index.js b/processing_functions/node_functions/util/util-common/index.js new file mode 100644 index 0000000..9fa7aae --- /dev/null +++ b/processing_functions/node_functions/util/util-common/index.js @@ -0,0 +1,301 @@ +const Ajv = require("ajv"); +const ajv = new Ajv({$data: true, allErrors: true}) +require("ajv-keywords")(ajv); +const { XMLParser, XMLValidator } = require('fast-xml-parser'); +const xsdValidator = require('libxmljs2-xsd'); +const fileSync = require("fs"); +const path = require('path'); + +exports.factorialize = factorialize; +exports.makeDeepCopy = makeDeepCopy; +exports.isStructureValid = isStructureValid +exports.roundToDigit = roundToDigit; +exports.getLsd = getLsd; +exports.roundToInterval = roundToInterval; +exports.floorToInterval = floorToInterval; +exports.ceilToInterval = ceilToInterval; +exports.mod = mod; + +exports.validateXMLAgainstXSD = validateXMLAgainstXSD; +exports.validateXML = validateXML; +exports.parseXMLToJson = parseXMLToJson; + + +const PRECISION = 15; + +/** + * Calculates the faculty of an integer + * + * If the number is smaller than 0 or not an integer, -1 will be returned + * + * @param {number} num + * @returns + */ +function factorialize(num) { + if (!Number.isInteger(num) || num < 0) + return -1; + else if (num == 0) + return 1; + else + return (num * factorialize(num - 1)); +} + +/** + * Makes a deep copy of the input object + * + * @param {any[], Object} obj + * @returns + */ +function makeDeepCopy(obj) { + return JSON.parse(JSON.stringify(obj)); +} + +/** + * Validates if the passed object fulfills the given JSON schema + * + * @author localhorst87 + * @function + * @param {Object} obj The object to be validated + * @param {Object} schema The schema the object is validated against + * @return {Boolean} returns true if the object fulfills the schema +*/ +function isStructureValid(obj, schema) { + const validate = ajv.compile(schema); + return validate(obj); +} + +/** + * Applies the given significant digits to the number + * + * @author localhorst87 + * @function + * @param {number} value value to apply precision to + * @param {number} precision significant digits + * @returns {number} value with given significant digits + */ +function precise(value, precision) { + return parseFloat(value.toPrecision(precision)); +} + +/** + * Rounds a value to a given digit. + * The function is valid for fast floating point arithmetic, + * up to a precision of 15 digits. + * + * digit > 0: digit after decimal point + * + * digit == 0: round to integer + * + * digit < 0: digit before decimal point + * + * @author localhorst87 + * @function + * @param {number} value value to round + * @param {number} digit the digit to round to + * @param {Function} [roundFn=Math.round] round function, Math.round by default + * @returns {number} rounded value + */ +function roundToDigit(value, digit, roundFn = Math.round) { + if (typeof(value) !== "number") + throw("value must be numerical"); + if (typeof(digit) !== "number") + throw("digit must be numerical"); + if (!Number.isInteger(digit)) + throw("digit must be an integer"); + if (roundFn !== Math.round && roundFn !== Math.ceil && roundFn !== Math.floor) + throw("only Math.round, Math.ceil and Math.floor allowed as roundFn"); + + const tenPower = Math.pow(10, digit); + const roundedValue = roundFn(value * tenPower) / tenPower; + + return precise(roundedValue, PRECISION); +} + +/** + * Returns the least significant digit of the number. + * + * This function is valid for numbers up to a precision + * of 15 (numbers with 15 digits without leading or trailing + * zeroes) + * + * lsd > 0: digit after decimal point + * + * lsd <= 0: digit before decimal point + * + * @author localhorst87 + * @function + * @param {number} value + * @returns {number} least significant digit + */ +function getLsd(value) { + if (typeof(value) !== "number") + throw("value must be numeric"); + + if (value === 0) + return 0; + + value = precise(value, PRECISION); + + let lsd = 0; + + if (Number.isInteger(value)) { + while(value - roundToDigit(value, --lsd, Math.round) == 0) continue; + lsd += 1; + } + else + while(value - roundToDigit(value, ++lsd, Math.round) !== 0) continue; + + return lsd; +} + +/** + * Rounds a number to the nearest number that + * can be divided by the given interval + * + * @author localhorst87 + * @function + * @param {number} value raw numeric value + * @param {number} interval interval to round to + * @returns {number} number with applied interval + */ +function roundToInterval(value, interval) { + if (typeof(value) !== "number") + throw("value must be a number"); + if (typeof(interval) !== "number") + throw("interval must be a number"); + if (interval <= 0) + throw("interval must be > 0"); + + const lsd = Math.max(getLsd(interval), getLsd(value)); + const remainder = roundToDigit(mod(value, interval), lsd); // use round to digit to avoid floating point errors + + if (value >= 0) + return remainder < interval/2 ? roundToDigit(value - remainder, lsd) : roundToDigit(value - remainder + interval, lsd); + else + return Math.abs(remainder) <= interval/2 ? roundToDigit(value - remainder, lsd) : roundToDigit(value - remainder - interval, lsd); +} + +/** + * Floors a number to the nearest number that + * can be divided by the given interval + * + * @author localhorst87 + * @function + * @param {number} value raw numeric value + * @param {number} interval interval to round to + * @returns {number} number with applied interval + */ +function floorToInterval(value, interval) { + if (typeof(value) !== "number") + throw("value must be a number"); + if (typeof(interval) !== "number") + throw("interval must be a number"); + if (interval <= 0) + throw("interval must be > 0"); + + const lsd = Math.max(getLsd(interval), getLsd(value)); + const remainder = roundToDigit(mod(value, interval), lsd); // use round to digit to avoid floating point errors + + if (value >= 0) + return roundToDigit(value - remainder, lsd); + else + return Math.abs(remainder) > 0 ? roundToDigit(value - remainder - interval, lsd) : value; +} + +/** + * Ceils a number to the nearest number that + * can be divided by the given interval + * + * @author localhorst87 + * @function + * @param {number} value raw numeric value + * @param {number} interval interval to round to + * @returns {number} number with applied interval + */ + function ceilToInterval(value, interval) { + if (typeof(value) !== "number") + throw("value must be a number"); + if (typeof(interval) !== "number") + throw("interval must be a number"); + if (interval <= 0) + throw("interval must be > 0"); + + const lsd = Math.max(getLsd(interval), getLsd(value)); + const remainder = roundToDigit(mod(value, interval), lsd); // use round to digit to avoid floating point errors + + if (value >= 0) + return remainder > 0 ? roundToDigit(value - remainder + interval, lsd) : value; + else + return roundToDigit(value - remainder, lsd); +} + +/** + * floating-point safe modulo operator + * + * @author localhorst87 + * @function + * @param {number} value + * @param {number} divisor + * @returns {number} remainder of value / divisor + */ +function mod(value, divisor) { + if (typeof(value) !== "number") + throw("value must be numeric"); + if (typeof(divisor) !== "number") + throw("divisor must be numeric"); + + const lsd = Math.max(getLsd(value), getLsd(divisor)); + const remainderPower = (value * Math.pow(10, lsd)) % (divisor * Math.pow(10, lsd)); + + return remainderPower / Math.pow(10, lsd); +} + +/** +* Checks if the XML model description file fulfills the given XSD scheme +* @author lvtan +* @function +* @param {String} xmlString XML-validated model description +* @param {String} xsdFilePath path of xsd file that is used to validate +* @return {Boolean} returns true/false upon valid/invalid scheme +*/ +function validateXMLAgainstXSD(xmlString, xsdFilePath) { + var xsdString = fileSync.readFileSync(xsdFilePath, 'utf8'); + var modelDescXsdBasePath = path.dirname(path.resolve(xsdFilePath))+"/" + let parsedXsd = xsdValidator.parse(xsdString, { baseUrl: modelDescXsdBasePath }); + let validationErrors = parsedXsd.validate(xmlString); + return validationErrors == null; +} + +/** +* Checks if the XML structure of the given XML string is valid +* @author lvtan +* @function +* @param {String} xmlString XML as plain text +* @return {Boolean} returns true/false upon valid/invalid XML format +*/ + +function validateXML(xmlString) { + const validationOptions = { + allowBooleanAttributes: true + }; + const validationResult = XMLValidator.validate(xmlString, validationOptions); + + return validationResult == true; +} + +/** + * Convert XML string to JSON + * + * @author lvtan3005 + * @function + * @param {string} xmlString the xml at the string format + * @returns {object} json object after parsing + */ + +function parseXMLToJson(xmlString) { + const parserOptions = { + ignoreAttributes : false + }; + const xmlParser = new XMLParser(parserOptions); + return xmlParser.parse(xmlString); +} \ No newline at end of file diff --git a/processing_functions/node_functions/util/util-common/package-lock.json b/processing_functions/node_functions/util/util-common/package-lock.json new file mode 100644 index 0000000..b257673 --- /dev/null +++ b/processing_functions/node_functions/util/util-common/package-lock.json @@ -0,0 +1,2293 @@ +{ + "name": "util-common", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "util-common", + "version": "1.0.0", + "license": "CC-BY-4.0", + "dependencies": { + "ajv": "^8.10.0", + "ajv-keywords": "^5.1.0", + "fast-xml-parser": "^4.1.3", + "libxmljs2-xsd": "0.30.1" + }, + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.0.0" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "dev": true, + "license": "ISC" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "8.11.0", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "dev": true, + "license": "ISC" + }, + "node_modules/camelcase": { + "version": "6.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "4.3.6", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-error": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "license": "MIT" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, + "node_modules/debug": { + "version": "4.3.4", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/decamelize": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "license": "MIT" + }, + "node_modules/fast-xml-parser": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.3.tgz", + "integrity": "sha512-coV/D1MhrShMvU6D0I+VAK3umz6hUaxxhL0yp/9RjfiYUfAv14rDhGQL+PLForhMdr0wq3PiV07WtkkNjJjNHg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "node_modules/fill-range": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "license": "ISC" + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "node_modules/he": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/libxmljs2": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/libxmljs2/-/libxmljs2-0.30.1.tgz", + "integrity": "sha512-Upgf6LnbDZV8oYrG2cONazKjnUPV2v8TbAKP951flkCsyyrEuMYBe8IgYtrcm5bXFOmQnKsbFeZwMwVvVNZLpg==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.9", + "bindings": "~1.5.0", + "nan": "~2.15.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/libxmljs2-xsd": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/libxmljs2-xsd/-/libxmljs2-xsd-0.30.1.tgz", + "integrity": "sha512-SdGtrW2nrLYUf2dPRQx6CDcAPLSUjlVzJRXq8aMMEn0FCmrGI1/u+12B9j8pIKQ6IWjlX8zYnlJrw5dCPfcmeA==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.9", + "bindings": "~1.5.0", + "libxmljs2": "^0.30.1", + "nan": "~2.15.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.0" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/minimatch": { + "version": "5.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha": { + "version": "10.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" + }, + "node_modules/nanoid": { + "version": "3.3.3", + "dev": true, + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + }, + "node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/workerpool": { + "version": "6.2.1", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "license": "ISC" + }, + "node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "16.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "requires": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + } + }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "ajv": { + "version": "8.11.0", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "ansi-colors": { + "version": "4.1.1", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1" + }, + "ansi-styles": { + "version": "4.3.0", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "argparse": { + "version": "2.0.1", + "dev": true + }, + "assertion-error": { + "version": "1.1.0", + "dev": true + }, + "balanced-match": { + "version": "1.0.2" + }, + "binary-extensions": { + "version": "2.2.0", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "brace-expansion": { + "version": "2.0.1", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "braces": { + "version": "3.0.2", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "dev": true + }, + "camelcase": { + "version": "6.3.0", + "dev": true + }, + "chai": { + "version": "4.3.6", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, + "chalk": { + "version": "4.1.2", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "check-error": { + "version": "1.0.2", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "cliui": { + "version": "7.0.4", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "dev": true + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + }, + "concat-map": { + "version": "0.0.1" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, + "debug": { + "version": "4.3.4", + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2" + } + } + }, + "decamelize": { + "version": "4.0.0", + "dev": true + }, + "deep-eql": { + "version": "3.0.1", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==" + }, + "diff": { + "version": "5.0.0", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0" + }, + "escalade": { + "version": "3.1.1", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3" + }, + "fast-xml-parser": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.3.tgz", + "integrity": "sha512-coV/D1MhrShMvU6D0I+VAK3umz6hUaxxhL0yp/9RjfiYUfAv14rDhGQL+PLForhMdr0wq3PiV07WtkkNjJjNHg==", + "requires": { + "strnum": "^1.0.5" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "fill-range": { + "version": "7.0.1", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "dev": true + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0" + }, + "gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, + "get-caller-file": { + "version": "2.0.5", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "dev": true + }, + "glob": { + "version": "7.2.0", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "minimatch": { + "version": "3.1.2", + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "glob-parent": { + "version": "5.1.2", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "he": { + "version": "1.2.0", + "dev": true + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "inflight": { + "version": "1.0.6", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4" + }, + "is-binary-path": { + "version": "2.1.0", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0" + }, + "is-glob": { + "version": "4.0.3", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-schema-traverse": { + "version": "1.0.0" + }, + "libxmljs2": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/libxmljs2/-/libxmljs2-0.30.1.tgz", + "integrity": "sha512-Upgf6LnbDZV8oYrG2cONazKjnUPV2v8TbAKP951flkCsyyrEuMYBe8IgYtrcm5bXFOmQnKsbFeZwMwVvVNZLpg==", + "requires": { + "@mapbox/node-pre-gyp": "^1.0.9", + "bindings": "~1.5.0", + "nan": "~2.15.0" + } + }, + "libxmljs2-xsd": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/libxmljs2-xsd/-/libxmljs2-xsd-0.30.1.tgz", + "integrity": "sha512-SdGtrW2nrLYUf2dPRQx6CDcAPLSUjlVzJRXq8aMMEn0FCmrGI1/u+12B9j8pIKQ6IWjlX8zYnlJrw5dCPfcmeA==", + "requires": { + "@mapbox/node-pre-gyp": "^1.0.9", + "bindings": "~1.5.0", + "libxmljs2": "^0.30.1", + "nan": "~2.15.0" + } + }, + "locate-path": { + "version": "6.0.0", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "log-symbols": { + "version": "4.1.0", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "loupe": { + "version": "2.3.4", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "minimatch": { + "version": "5.0.1", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "mocha": { + "version": "10.0.0", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + } + }, + "ms": { + "version": "2.1.3", + "dev": true + }, + "nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" + }, + "nanoid": { + "version": "3.3.3", + "dev": true + }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "requires": { + "abbrev": "1" + } + }, + "normalize-path": { + "version": "3.0.0", + "dev": true + }, + "npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "once": { + "version": "1.4.0", + "requires": { + "wrappy": "1" + } + }, + "p-limit": { + "version": "3.1.0", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1" + }, + "pathval": { + "version": "1.1.1", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "dev": true + }, + "punycode": { + "version": "2.1.1" + }, + "randombytes": { + "version": "2.1.0", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "require-directory": { + "version": "2.1.1", + "dev": true + }, + "require-from-string": { + "version": "2.0.2" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.2.1" + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "serialize-javascript": { + "version": "6.0.0", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-width": { + "version": "4.2.3", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "dev": true + }, + "strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + }, + "supports-color": { + "version": "8.1.1", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "type-detect": { + "version": "4.0.8", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "workerpool": { + "version": "6.2.1", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2" + }, + "y18n": { + "version": "5.0.8", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yargs": { + "version": "16.2.0", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yocto-queue": { + "version": "0.1.0", + "dev": true + } + } +} diff --git a/processing_functions/node_functions/util/util-common/package.json b/processing_functions/node_functions/util/util-common/package.json new file mode 100644 index 0000000..08674d2 --- /dev/null +++ b/processing_functions/node_functions/util/util-common/package.json @@ -0,0 +1,21 @@ +{ + "name": "util-common", + "version": "1.0.0", + "description": "common utility methods used throughout the CDK ", + "main": "index.js", + "scripts": { + "test": "mocha --reporter spec" + }, + "author": "localhorst87", + "license": "CC-BY-4.0", + "dependencies": { + "ajv": "^8.10.0", + "ajv-keywords": "^5.1.0", + "fast-xml-parser": "^4.1.3", + "libxmljs2-xsd": "0.30.1" + }, + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.0.0" + } +} \ No newline at end of file diff --git a/processing_functions/node_functions/util/util-common/test/unit.test.js b/processing_functions/node_functions/util/util-common/test/unit.test.js new file mode 100644 index 0000000..10ffffa --- /dev/null +++ b/processing_functions/node_functions/util/util-common/test/unit.test.js @@ -0,0 +1,400 @@ +const { describe, it } = require('mocha'); +const { expect } = require('chai'); +const util = require('..'); + +describe('roundToDigit', () => { + + describe('argument tests', () => { + + it('all values as expected, no roundFn specified => expect not to throw', () => { + expect(() => util.roundToDigit(1.477, 2)).to.not.throw(); + }); + + it('all values as expected, roundFn = Math.ceil => expect not to throw', () => { + expect(() => util.roundToDigit(789, -1, Math.ceil)).to.not.throw(); + }); + + it('all values as expected, roundFn = Math.floor => expect not to throw', () => { + expect(() => util.roundToDigit(789, -1, Math.floor)).to.not.throw(); + }); + + it('value not number => expect to throw', () => { + expect(() => util.roundToDigit([456.44], 1)).to.throw(); + }); + + it('digit not number => expect to throw', () => { + expect(() => util.roundToDigit(456.44, [1])).to.throw(); + }); + + it('digit not integer => expect to throw', () => { + expect(() => util.roundToDigit(456.44, 1.5)).to.throw(); + }); + + it('roundFn not supported => expect to throw', () => { + expect(() => util.roundToDigit(456.44, -2, Math.abs)).to.throw(); + }); + + }); + + describe('return test', () => { + + it('digit > 0 => expect to round to value after decimal point', () => { + const roundedValue = util.roundToDigit(2500.7137, 3); + expect(roundedValue).to.equal(2500.714); + }); + + it('digit = 0 => expect to round to integer', () => { + const roundedValue = util.roundToDigit(-440.10000000002, 0); + expect(roundedValue).to.equal(-440); + }); + + it('digit < 0 => expect to round to power of ten', () => { + const roundedValue = util.roundToDigit(2742684.7489, -3); + expect(roundedValue).to.equal(2743000); + }); + + it('roundFn = Math.floor => expect to round down', () => { + const roundedValue = util.roundToDigit(9.8447e-2, 2, Math.floor); + expect(roundedValue).to.equal(0.09); + }); + + it('roundFn = Math.ceil => expect to round up', () => { + const roundedValue = util.roundToDigit(147123301, -2, Math.ceil); + expect(roundedValue).to.equal(147123400); + }); + + it('floating point representation => expect to cut trailing digits', () => { + const roundedValue = util.roundToDigit(0.2-0.02, 2); + expect(roundedValue).to.equal(0.18); + }); + + it('big number => expect to return correct value', () => { + const roundedValue = util.roundToDigit(-78e+16, -4, Math.floor); + expect(roundedValue).to.equal(-78e16); + }); + + }); + +}); + +describe('getLsd', () => { + + describe('argument tests', () => { + + it('value numeric => expect to not throw', () => { + expect(() => util.getLsd(-1.2478)).to.not.throw(); + }); + + it('value not numeric => expect to throw', () => { + expect(() => util.getLsd([7.2557])).to.throw(); + }); + + }); + + describe('return tests', () => { + + it('positive value with decimal point => expect lsd > 0', () => { + const lsd = util.getLsd(1.005); + expect(lsd).to.equal(3); + }); + + it('negative value with decimal point => expect lsd > 0', () => { + const lsd = util.getLsd(-0.1110024); + expect(lsd).to.equal(7); + }); + + it('trailing zeroes after decimal point => expect to ignore trailing zeroes', () => { + const lsd = util.getLsd(70.79900); + expect(lsd).to.equal(3); + }); + + it('positive integer value => expect lsd == 0', () => { + const lsd = util.getLsd(6503); + expect(lsd).to.equal(0); + }); + + it('negative integer value => expect lsd == 0', () => { + const lsd = util.getLsd(-9654); + expect(lsd).to.equal(0); + }); + + it('positive integer with trailing zeroes => expect lsd < 0', () => { + const lsd = util.getLsd(6500); + expect(lsd).to.equal(-2); + }); + + it('small number => expect lsd > 0', () => { + const lsd = util.getLsd(1.43e-57); + expect(lsd).to.equal(59); + }); + + it('bigger number => expect lsd < 0', () => { + const lsd = util.getLsd(-7.8e+14); + expect(lsd).to.equal(-13); + }); + + it('value is 0 => expect 0', () => { + const lsd = util.getLsd(0); + expect(lsd).to.equal(0); + }); + + }); + +}); + +describe('roundToInterval', () => { + + describe('argument tests', () => { + + it('all args valid => expect to not throw', () => { + expect(() => util.roundToInterval(-24.786, 0.05)).to.not.throw(); + }); + + it('value not a number => expect to throw', () => { + expect(() => util.roundToInterval('1.4', 0.05)).to.throw(); + }); + + it('interval not a number => expect to throw', () => { + expect(() => util.roundToInterval(1.4, '0.05')).to.throw(); + }); + + it('interval 0 => expect to throw', () => { + expect(() => util.roundToInterval(1.4, 0)).to.throw(); + }); + + it('interval < 0 => expect to throw', () => { + expect(() => util.roundToInterval(741.4, -5)).to.throw(); + }); + + }); + + describe('return tests', () => { + + it('positive number, interval is integer => expect integer rounding', () => { + const rounded = util.roundToInterval(24.786, 2); + expect(rounded).to.equal(24); + }); + + it('negative number, interval is integer => expect integer rounding', () => { + const rounded = util.roundToInterval(-7424.786, 5); + expect(rounded).to.equal(-7425); + }); + + it('interval < 1 => round to decimal', () => { + const rounded = util.roundToInterval(254.71, 0.5); + expect(rounded).to.equal(254.5); + }); + + it('positive number nearer to smaller number => round down to next interval', () => { + const rounded = util.roundToInterval(2.2, 0.5); + expect(rounded).to.equal(2.0); + }); + + it('positive number exactly between two intervals => round up to next interval', () => { + const rounded = util.roundToInterval(2.25, 0.5); + expect(rounded).to.equal(2.5); + }); + + it('positive number nearer to higher number => round up to next interval', () => { + const rounded = util.roundToInterval(2.3, 0.5); + expect(rounded).to.equal(2.5); + }); + + it('number slightly smaller than being between two intervals => round down to next interval', () => { + const rounded = util.roundToInterval(17689.7499999, 0.1); + expect(rounded).to.equal(17689.7); + }); + + it('negative number nearer to higher number => round up to next interval', () => { + const rounded = util.roundToInterval(-2.2, 0.5); + expect(rounded).to.equal(-2.0); + }); + + it('negative number exactly between two intervals => round down to next interval', () => { + const rounded = util.roundToInterval(-2.25, 0.5); + expect(rounded).to.equal(-2.0); + }); + + it('negative number nearer to smaller number => round up down next interval', () => { + const rounded = util.roundToInterval(-2.3, 0.5); + expect(rounded).to.equal(-2.5); + }); + + it('positive number matches exactly the interval => expect no rounding', () => { + const rounded = util.roundToInterval(58, 1); + expect(rounded).to.equal(58); + }); + + it('negative number matches exactly the interval => expect no rounding', () => { + const rounded = util.roundToInterval(-58, 1); + expect(rounded).to.equal(-58); + }); + + }); + +}); + +describe('floorToInterval', () => { + + describe('return tests (other scope is the same as roundToInterval)', () => { + + it('positive number, interval is integer => expect integer flooring', () => { + const rounded = util.floorToInterval(25.786, 2); + expect(rounded).to.equal(24); + }); + + it('negative number, interval is integer => expect integer flooring', () => { + const rounded = util.floorToInterval(-7421.786, 5); + expect(rounded).to.equal(-7425); + }); + + it('interval < 1 => floor to decimal', () => { + const rounded = util.floorToInterval(254.76, 0.5); + expect(rounded).to.equal(254.5); + }); + + it('positive number exactly between two intervals => floor to next interval', () => { + const rounded = util.floorToInterval(2.25, 0.5); + expect(rounded).to.equal(2.0); + }); + + it('positive number nearer to higher number => floor to next interval', () => { + const rounded = util.floorToInterval(2.3, 0.5); + expect(rounded).to.equal(2.0); + }); + + it('negative number nearer to higher number => floor to next interval', () => { + const rounded = util.floorToInterval(-2.2, 0.5); + expect(rounded).to.equal(-2.5); + }); + + it('negative number exactly between two intervals => floor to next interval', () => { + const rounded = util.floorToInterval(-2.25, 0.5); + expect(rounded).to.equal(-2.5); + }); + + it('positive number matches exactly the interval => expect no flooring', () => { + const rounded = util.floorToInterval(58, 1); + expect(rounded).to.equal(58); + }); + + it('positive number matches exactly the interval => expect no flooring', () => { + const rounded = util.floorToInterval(-58, 1); + expect(rounded).to.equal(-58); + }); + + }); + +}); + +describe('ceilToInterval', () => { + + describe('return tests (other scope is the same as roundToInterval)', () => { + + it('positive number, interval is integer => expect integer ceiling', () => { + const rounded = util.ceilToInterval(24.786, 2); + expect(rounded).to.equal(26); + }); + + it('negative number, interval is integer => expect integer ceiling', () => { + const rounded = util.ceilToInterval(-7424.786, 5); + expect(rounded).to.equal(-7420); + }); + + it('interval < 1 => ceil to decimal', () => { + const rounded = util.ceilToInterval(254.74, 0.5); + expect(rounded).to.equal(255.0); + }); + + it('positive number, remainder cloaser to lower interval => ceil to next interval', () => { + const rounded = util.ceilToInterval(2.2, 0.5); + expect(rounded).to.equal(2.5); + }); + + it('negative number, remainder closer to lower interval => ceil to next interval', () => { + const rounded = util.ceilToInterval(-2.3, 0.5); + expect(rounded).to.equal(-2.0); + }); + + it('negative number exactly between two intervals => ceil to next interval', () => { + const rounded = util.ceilToInterval(-2.25, 0.5); + expect(rounded).to.equal(-2.0); + }); + + it('positive number matches exactly the interval => expect no ceiling', () => { + const rounded = util.ceilToInterval(58, 1); + expect(rounded).to.equal(58); + }); + + it('negative number matches exactly the interval => expect no ceiling', () => { + const rounded = util.ceilToInterval(58, 1); + expect(rounded).to.equal(58); + }); + + }); + +}); + +describe('mod', () => { + + describe('argument tests', () => { + + it('all args valid => expect to not throw', () => { + expect(() => util.mod(12.5, 0.2)).to.not.throw(); + }); + + it('value not a number => expect to throw', () => { + expect(() => util.mod('12.5', 0.2)).to.throw(); + }); + + it('divisor not a number => expect to throw', () => { + expect(() => util.mod(12.5, '0.2')).to.throw(); + }); + + }); + + describe('return tests', () => { + + it('positive number, positive integer', () => { + const rem = util.mod(7.4, 2); + expect(rem).to.equal(1.4); // 1.4000000000000004 for 7.4 % 2 + }); + + it('positive number, negative integer', () => { + const rem = util.mod(-45.1e-1, -3); + expect(rem).to.equal(-1.51); // -1.5099999999999998 for -45.1e-1 % -3 + }); + + it('negative number, positive integer', () => { + const rem = util.mod(-58.7, 3); + expect(rem).to.equal(-1.7); // -1.7000000000000028 for -58.7 % 3 + }); + + it('negative number, negative integer', () => { + const rem = util.mod(-147.7, -4); + expect(rem).to.equal(-3.7); // -3.6999999999999886 for -58.7 % -4 + }); + + it('positive number, positive decimal point value', () => { + const rem = util.mod(2.89, 0.1); + expect(rem).to.equal(0.09); // 0.08999999999999997 for 2.89 % 0.1 + }); + + it('positive number, negative decimal point value', () => { + const rem = util.mod(2.89, -0.1); + expect(rem).to.equal(0.09); // 0.08999999999999997 for 2.89 % -0.1 + }); + + it('negative number, positive decimal point value', () => { + const rem = util.mod(-28, 0.378); + expect(rem).to.equal(-0.028); // -0.027999999999999803 for -28 % 0.378 + }); + + it('negative number, negative decimal point value', () => { + const rem = util.mod(-74, -0.101); + expect(rem).to.equal(-0.068); // -0.06799999999999529 for -74 % -0.101 + }); + + }); + +}); \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/README.md b/processing_functions/node_functions/util/veri-sign/README.md new file mode 100644 index 0000000..0bdced9 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/README.md @@ -0,0 +1,156 @@ +# VERI-SIGN + +This utility can be used to sign and verify expert statements, based on a public key infrastructure. + +## USAGE + +Expert statements are part of Credibility Assessments to confirm non-formal checks throughout the Credible Simulation Process and Credible Modeling Process. They are usually referred to as being part of Credibility Level 1 metrics. + +## PRINCIPLE + +This module provides for a `sign` and a `verify` function: + +* `sign` takes any arbitrary formalized statement (consisting of a result, a log, and a status response code), creates a signature of it by using the experts private key and wraps the signature into a well-defined structure, next to meta-data of the signature, like the hash algorithm and encoding used. This is done typically on supplier side by experts that customer and supplier agreed upon. +* `verify` takes the signature-wrapped statement and verifies it against a given X509 certificate. This is typically done on customer side, with public certificates of supplier experts that customer and supplier agreed upon. + +![workflow using sign and verify](./docs/workflow.png "Workflow") + +## DETAIL + +Statements to sign do not need to follow any formality, except that a statement must be given as a string, as in the following simplyfied example: + +```javascript +statement = { + result: true, + log: "All model requirements have been checked semantically, according to ISO/IEC/IEEE 29148", + status_code: 200 +}; +``` + +To sign the statement, a private key must be handed over, as well as the specification about the private key. + +Currently, `pem`, `der` and `jwk` encodings are supported for private key represantation. If you decide to use a PEM encoded private key, it must be given as a string, as for example illustrated in the following excerpt: + +``` +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDX41C6lKMAfxME +3fIfph95ZrKRxWzPSoa7Erv7sqQuT3jgrPhyCA6YUTlq6dXpcAddSaJ+2lHm4L5m +... ++VuHyq4upQ3ivUxPXiPp7ouL5pmBIPHusLrbiMGf4ytHosIMhiNI9NyGgyABFPuL +IOj3A7f9/ALJTtHUU/AC4zg= +-----END PRIVATE KEY----- +``` + +JWK keys must be handed over as a JSON object (not stringified) and DER encoded keys must be given as a Buffer. For details, see the reference. + +The key specification must contain at least the information about the format the key is given in and if the key is encrypted. If the latter is the case, the passphrase for decoding must be given as well (see second example). If the DER encoding is used, the encryption standard must be indicated with the `type` property. Currently `pkcs1`, `pkcs8` and `sec1` (only for elliptic curves as hash algorithm) are supported. + +```javascript +{ + format: "pem", + isEncrypted: false, +}; +``` + +```javascript +{ + format: "der", + isEncrypted: true, + type: "pkcs8", + passphrase: "ThisIsMySecretPassphrase" +}; +``` +After correctly handing over all arguments to the `sign` function, a JSON object as depicted in the following will be returned (but stringified): + +```javascript +{ + "content": { + "result": true, + "log": "All model requirements have been checked semantically, according to ISO/IEC/IEEE 29148", + }, + "signature": "00514ff050a656...b624eb92338308702b", + "hash_algorithm": "SHA256", + "signature_encoding": "hex" +} +``` + +This wrapped statement can later be used to be verified against a public key of an expert that is typically contained in a public X509 certificate. For this purpose, a `pem` oder `der` represantation of an X509 certificate must be handed over to the `verify` function, as given for example in the following: + +``` +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIUdBgY79Rx8iKuwNuP6KXS6/xUoJ8wDQYJKoZIhvcNAQEL +BQAwgZUxCzAJBgNVBAYTAkRFMRAwDgYDVQQIDAdCYXZhcmlhMQ8wDQYDVQQHDAZN +... +dPcjLqX0NndfpzCLFlYCzB+EA61/rvxqKcck1ZHAKCvPCqDYQiYs/v6p/xwRTRne +ltREsqyrgXMibih7xDCXyZyInzl33U9pFd8hi0gi81MC7jU= +-----END CERTIFICATE----- +``` + +The result of the verify function will be a `ResultLog` object, giving logging information alongside the result, for example: + +```javascript +{ + result: true, + log: "signature is valid" +} +``` + +For further explanation, see the reference. + +## EXAMPLE + +Sign expert statement: + +```javascript +const fs = require("fs"); +const verisign = require("./"); + +// create inputs +const statement = { + result: true, + log: "All model requirements have been checked semantically, according to ISO/IEC/IEEE 29148", + status_code: 200 +}; +const privateKey = fs.readFileSync("./examples/keystore/pem_encrypted/private.pem", "utf8"); +const keySpecification = { + format: "pem", + isEncrypted: true, + passphrase: "ThisIsMyPassphrase" +}; + +// sign statement and store +const signedStatement = verisign.sign(statement, privateKey, keySpecification); +fs.writeFileSync("./examples/signed_statements/req.json", signedStatement); +``` + +Verify expert statement: + +```javascript +const fs = require("fs"); +const verisign = require("./"); + +// get inputs +const signedStatement = fs.readFileSync("./examples/signed_statements/req.json", "utf8"); +const certificate_1 = fs.readFileSync("./examples/keystore/pem_encrypted/cert.pem", "utf8"); +const certificate_2 = fs.readFileSync("./examples/keystore/der_pkcs8/cert.der"); + +// verify signed statement +console.log(verisign.verify(signedStatement, certificate_1)); // should be valid +console.log(verisign.verify(signedStatement, certificate_2)); // should be invalid +``` + +## INTERACTIVE EXAMPLES + +For interactive examples, use + +``` +npm run example_sign +``` + +from the console to start the interactive example to create signed statements, and + +``` +npm run example_verify +``` + +to run an interactive example to verify your created statements. \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/docs/workflow.png b/processing_functions/node_functions/util/veri-sign/docs/workflow.png new file mode 100644 index 0000000..d3acffd Binary files /dev/null and b/processing_functions/node_functions/util/veri-sign/docs/workflow.png differ diff --git a/processing_functions/node_functions/util/veri-sign/examples/certificates/cert_1.pem b/processing_functions/node_functions/util/veri-sign/examples/certificates/cert_1.pem new file mode 100644 index 0000000..ebc4513 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/examples/certificates/cert_1.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEJzCCAw+gAwIBAgIUe+jKmJu7BhEJbfmpXGEZX9pZl94wDQYJKoZIhvcNAQEL +BQAwgaExCzAJBgNVBAYTAkRFMQwwCgYDVQQIDANOUlcxEDAOBgNVBAcMB0NvbG9n +bmUxHjAcBgNVBAoMFVRyYWZmaWMgTW9kZWxpbmcgR21iSDEQMA4GA1UECwwHVmVo +aWNsZTERMA8GA1UEAwwISm9obiBEb2UxLTArBgkqhkiG9w0BCQEWHmpvaG5kb2VA +dHJhZmZpY21vZGVsaW5nZ21iaC5kZTAgFw0yMjA5MDUwODAxMTBaGA8yMTIyMDgx +MjA4MDExMFowgaExCzAJBgNVBAYTAkRFMQwwCgYDVQQIDANOUlcxEDAOBgNVBAcM +B0NvbG9nbmUxHjAcBgNVBAoMFVRyYWZmaWMgTW9kZWxpbmcgR21iSDEQMA4GA1UE +CwwHVmVoaWNsZTERMA8GA1UEAwwISm9obiBEb2UxLTArBgkqhkiG9w0BCQEWHmpv +aG5kb2VAdHJhZmZpY21vZGVsaW5nZ21iaC5kZTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMPheOrpdcPPbD2LApKkw9SnryXPRhH5x0kBYawrlm4ZJ8Uy +/dwvPf86qT8QgwAUY2wl07HwdLoPqRzT9GEDfOpzROuKcLODSjxaK6Hq04uxds75 +fJBB5A5Apgnoeipi3Xz3GEeGPqgkyKWieWNJG0kuokZHR8jbVKpK7sNHJ+CL4K4Z +NveNC1nWAO0NKo10M475YbXdyGP2NpUuaL8lFZVKlJz2aQ/sGFvUALWkqMiFY5vS +sEuCmG22x4GMvET+0wg8tUCwW9jJJOc082ZCdcne6hvwhITtwXl99tCySPrqbYIQ +lSGx92YbqtG16msMkcoxOj3gAU4FDDWXj7dfKlECAwEAAaNTMFEwHQYDVR0OBBYE +FAkVNbiavW0A4FYqosBefWntBJ9CMB8GA1UdIwQYMBaAFAkVNbiavW0A4FYqosBe +fWntBJ9CMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAKlCCNKP +gTNKcK2s+LTtt0YL6z7GhqEhqzcZfytZsPVArE9KECyvBMbsK8/q/aF2YHXpEwmJ +yhgiR20iWtf49BLwljI6s1VUj986WbMdlDms2cZvXb2uh2Xgocvs7QnHtB5GMdSl +wxYZSQKBJqvAioPOsBCgUhqQhzuJ1ckw6AoXFSSsAP/tG3CCFkoKpOHukWTdcGYW +AP68YzwHqwGySwMJmX+2dxOxmv2tnERfLDVK5NZx35TZxAbMdvYjJ2C3j0zoxVww +QOXwqPWgG1IX0XP31we02vYbO7bXcv6w1Up7vYanjo4WvMYj4lXEotGvlmaS0dG8 +5GSgsG/sOUoKoZY= +-----END CERTIFICATE----- diff --git a/processing_functions/node_functions/util/veri-sign/examples/certificates/cert_2.pem b/processing_functions/node_functions/util/veri-sign/examples/certificates/cert_2.pem new file mode 100644 index 0000000..d3ab9ec --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/examples/certificates/cert_2.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICiTCCAi+gAwIBAgIUZb3LYK/49RqkWeQ+PQ5kf5/eMQIwCgYIKoZIzj0EAwIw +gZgxCzAJBgNVBAYTAkRFMQwwCgYDVQQIDANOUlcxDzANBgNVBAcMBkFhY2hlbjEW +MBQGA1UECgwNU2ltV29ybGQgR21iSDEVMBMGA1UECwwMVmVyaWZpY2F0aW9uMREw +DwYDVQQDDAhKb2huIERvZTEoMCYGCSqGSIb3DQEJARYZam9obi5kb2VAc2ltd29y +bGQtZ21iaC5kZTAgFw0yMjA5MDUxNTI1MTlaGA8yMTIyMDgxMjE1MjUxOVowgZgx +CzAJBgNVBAYTAkRFMQwwCgYDVQQIDANOUlcxDzANBgNVBAcMBkFhY2hlbjEWMBQG +A1UECgwNU2ltV29ybGQgR21iSDEVMBMGA1UECwwMVmVyaWZpY2F0aW9uMREwDwYD +VQQDDAhKb2huIERvZTEoMCYGCSqGSIb3DQEJARYZam9obi5kb2VAc2ltd29ybGQt +Z21iaC5kZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDuPagV1GqJq158onfaI +XttPSiBIYKh+IVFtFUP+v32+xVKdemmBBnpp99RwyaKVrqKn8UUaZA00xOjE+Uy1 +QZOjUzBRMB0GA1UdDgQWBBSRP6g9BKGiY5wNoaydkRC+0w3j8jAfBgNVHSMEGDAW +gBSRP6g9BKGiY5wNoaydkRC+0w3j8jAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49 +BAMCA0gAMEUCIQCTtUO6AXBMi4yYf5Lz0ItJoFZ7eT5JtLiepL0zLTWbeAIgb8YW +H4vxjjJSQ7+On1zfJqvE+7KGcxJb7IqUN02y/8M= +-----END CERTIFICATE----- diff --git a/processing_functions/node_functions/util/veri-sign/examples/certificates/cert_3.der b/processing_functions/node_functions/util/veri-sign/examples/certificates/cert_3.der new file mode 100644 index 0000000..962d117 Binary files /dev/null and b/processing_functions/node_functions/util/veri-sign/examples/certificates/cert_3.der differ diff --git a/processing_functions/node_functions/util/veri-sign/examples/certificates/cert_expired.pem b/processing_functions/node_functions/util/veri-sign/examples/certificates/cert_expired.pem new file mode 100644 index 0000000..6b7e0e7 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/examples/certificates/cert_expired.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEJTCCAw2gAwIBAgIUVaAGmS8al7x9alHAF/9nhEKLDP0wDQYJKoZIhvcNAQEL +BQAwgaExCzAJBgNVBAYTAkRFMRAwDgYDVQQIDAdCYXZhcmlhMQ8wDQYDVQQHDAZN +dW5pY2gxHjAcBgNVBAoMFUFEQVMgU2ltdWxhdGlvbiBDb21wLjEUMBIGA1UECwwL +SW50ZWdyYXRpb24xETAPBgNVBAMMCEpvaG4gRG9lMSYwJAYJKoZIhvcNAQkBFhdq +b2huLmRvZUBhZGFzc2ltY29tcC5kZTAeFw0yMjA5MDUwNzU2NDRaFw0yMjA5MDYw +NzU2NDRaMIGhMQswCQYDVQQGEwJERTEQMA4GA1UECAwHQmF2YXJpYTEPMA0GA1UE +BwwGTXVuaWNoMR4wHAYDVQQKDBVBREFTIFNpbXVsYXRpb24gQ29tcC4xFDASBgNV +BAsMC0ludGVncmF0aW9uMREwDwYDVQQDDAhKb2huIERvZTEmMCQGCSqGSIb3DQEJ +ARYXam9obi5kb2VAYWRhc3NpbWNvbXAuZGUwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDp0G/R2O3MqbkHUj77AS0S5qZLmQoxh20W9akWdro3DH/8FN2z +W72Qk4ENZ49vTwAYKuvBvlGSViSKQiWTGLysYp+Ja2DCZpcjoOk6YQkzrxRSNniZ +SuvBGo2NLxzFuhML/AhU7BQSF2N1ZU57XYnEHATh5j/cJJhHaPiJV4FIR+e9d+ym +QAgjcuHcFATVnec1YojjwF8mxHiAaJjETv4FeBTwcNQxjFvfmPV16czvBzqHs3u+ +y2vBU6riaEB2iU0SkJFplnYooCf9Bwjh5cmaGmh3PPAGTxzpDX27DsZEaFJUMrgt +C10XWHDMkRiJgpkpHKfKgx32cYxyqRlkBSPzAgMBAAGjUzBRMB0GA1UdDgQWBBQz +1yhBtzESRNMPKap22MCsQmWDwTAfBgNVHSMEGDAWgBQz1yhBtzESRNMPKap22MCs +QmWDwTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAMgcniEFxi +fYbBeXqrbwbQHSPxv2wZ01Wn2tfdTT2LMADlq8tWsKeXvwJeEDQoC6PTm3+f/XaR +uR0L9T+snS8QRC7X2oE3nJisFCWldZbzSlEwa7Jy+va/Ca68ySHr2t3Sh87Xp4KJ +Wmj7a3WVNFP25MXQU8/czSXXSrRzX3P6EfDozjmA+aW1wzqt+shqLb7zZdop2sDB +Z4zh5ui9tl118TIAa3+CZ7XV1JsYC847L9n57JbWEu3O51aAzOMQDTOjFz/k9G8r +aDtOemUaEV5KkjYDCO2XNNtM5B8eDJXStf6DkQEzl2j+m9GIfsWddlvJI+rIDQJ+ +stEAxvDWkktb +-----END CERTIFICATE----- diff --git a/processing_functions/node_functions/util/veri-sign/examples/keystore/der_pkcs8/private.der b/processing_functions/node_functions/util/veri-sign/examples/keystore/der_pkcs8/private.der new file mode 100644 index 0000000..1253a8d Binary files /dev/null and b/processing_functions/node_functions/util/veri-sign/examples/keystore/der_pkcs8/private.der differ diff --git a/processing_functions/node_functions/util/veri-sign/examples/keystore/jwk/private.jwk b/processing_functions/node_functions/util/veri-sign/examples/keystore/jwk/private.jwk new file mode 100644 index 0000000..db14cfc --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/examples/keystore/jwk/private.jwk @@ -0,0 +1,7 @@ +{ + "kty": "EC", + "crv": "P-256", + "d": "HOVASeP1KfX-U2g5vbnS7X-K2kY6W7UW9r0Vs074r2Y", + "x": "O49qBXUaomrXnyid9ohe209KIEhgqH4hUW0VQ_6_fb4", + "y": "xVKdemmBBnpp99RwyaKVrqKn8UUaZA00xOjE-Uy1QZM" +} \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/examples/keystore/pem_encrypted/passphrase b/processing_functions/node_functions/util/veri-sign/examples/keystore/pem_encrypted/passphrase new file mode 100644 index 0000000..58d636f --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/examples/keystore/pem_encrypted/passphrase @@ -0,0 +1 @@ +ThisIsMyPassphrase \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/examples/keystore/pem_encrypted/private.pem b/processing_functions/node_functions/util/veri-sign/examples/keystore/pem_encrypted/private.pem new file mode 100644 index 0000000..737ca80 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/examples/keystore/pem_encrypted/private.pem @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIyQ7UZyBKFRkCAggA +MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECENGuHWPm4sIBIIEyMKWWfxFn+2A +zV9peRypI/6VX09vUJcM4533JD1KhwMn9rFcC6zVOIssXg2EHTXaziJzlbbV81bE +fsItTX7nsnHPSiLnCOtJFS6NqrwisrdioyymOVn/WywmDFwG5pI6FbUBqpn6tYvI +wwoCilpnYHgUjkXiRuN8D/K91TEg8LmaLBqtxNMpWOLBFYvXb+XtD6UQlKIYXlRh +7biwGmIgGH/JcLh0X2bUst0hkBXvYKHBQoXf5+XtfNdejfgIe/yaxU5J8FqnQ0u3 +HN7yZ7kZowar3ZYTUWKu5cZYAUFVPEgWlvshxYbd4SbMXpqIQG7Fnjdt4EWcGXz2 +Bq6fnArPRFROoW7JqUJNlur0rDraI4NFUMn3uww4ErFyYB3TsElClrF3FMfNWogl +ZKXBzr9WtDgYkeMmB+DFjl9Rl7uGVnEtNloVkmeOxFc5yyZvgB8jGBC7UlPsMjAh +zdrlRzgQIrGn5okTarm1ey/aCVMpcoSDBZuut8Kd0CGRgjZN+GYSDBMEGKjJV2Q7 +fNPADZUIYt07d0cKWqDxQgx72BaKYfE/qCnfFuaNwif8tR9xCptUmEFMfDPaLmpp +/NPzn5hXt6bHE/q0/szFcU/K0hvaf3S2p2aytvX8zFKEvskYYd+0GvmHKECzt9KP ++J4OGNRNqfTE8zJfzFDz6pevtNu8DannT7fismtQzshO72BLA51SYvsBjgkG866X +YZeztqDtXJV5W27qqG0zWAkXTEd/AiqQuJCtY9UjYVEiWs7+TNJYcqIQxB/9pP2r +k3ZwxJ7u0IemngWTXtmw5mwol/SuHvY7Gn/EkyiIljHCBxxWq8sO8LPtSTI1OvVv +hMArtxmsUZzJ2d/msLrCWG+vqo7CP1bDoiF5vsrBFY0IR7BfhAlP7q/e8jS2yApl +92Dnl7aPPxBySEcW1YYaQ8Hdcze9XZhPlHWUt+83g+bTA1LfRV71iTu1YUWJJMr4 ++s0QX99kW3bObKQ5633uZoJkw0XlLgip/2iuSf/DYFrE0GZUArnGbtmJaiiTO+IN +kcneD160JQyaNDGRLRam5yyNAefI0YeNy1BWYOMPtTVASDUrsAgMrJeibkKClYAT +bww05hY5JxszzuC9CO/RbvAI8DpRR72CDOp3GaKNphGejHwBWd9O8++ySEVpdQ8z +nFiGAzLQi66eY1SPUeHPLv0VTIi37zTpA6eHt7VnfdzbFHSHAtgcOyzQKKslFGqV +vCGuLKe7ln4LDwJLZVJyKxWb11LiztxchgjKRmxLiy0fh9DvV2cS09S7VEsTleeu +/O/eqSrkLAwODoYJPuqqUdWnU1HND2Mx87yT1FOZxXrOUPiTbFXWzNmu9PtHCmpY +ZS6T59iye9Dn9l1q7jrEigMTVokHllp6pr9yb8QEzsRoHUCsDmWtplKlb+6JHa3Q +Pv00EU1PPxDSNs+kSlVPJN/Q+MBVT0J/kuISalsXi0r74R81zAsJo5vKhwPY8b3+ +apAYaPAUv74Bq/L5zZlyyWKk516BO0tZ9nc6GTIdPyipC3P9Tx2qqq4DuTqYZ+K0 +lZbMEsKcHAgEw1VhRWBEky9Nu44zkiQ9D3JeL8BX057ppljxikoKpr6tPO7ZbKfC +kGjPoenKRWqOARS2dPTXGA== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/processing_functions/node_functions/util/veri-sign/examples/sign_interactive.js b/processing_functions/node_functions/util/veri-sign/examples/sign_interactive.js new file mode 100644 index 0000000..e2a46cb --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/examples/sign_interactive.js @@ -0,0 +1,68 @@ +const fs = require("fs"); +const prompt = require("prompt-sync")(); +const verisign = require("./.."); + +// ------------ Create input -------------- +const result = prompt("Enter the result of the expert check (true/false):"); +const log = prompt("Enter the log of the expert check (any arbitrary text):"); +const status_code = prompt("Enter the status response code of the expert check (100...999):"); + +const expertStatement = { + result: result == "true" ? true : false, + log: log, + status_code: Number(status_code) +}; + +console.log(`\n expertStatement = ${expertStatement} \n`); + +// ------------ Choose private key -------------- +let privateKeyChoice; +while (privateKeyChoice != "0" && privateKeyChoice != "1" && privateKeyChoice != "2" && privateKeyChoice != "3") { + console.log("Please choose a private key type you want to use for signing the statement: \n") + privateKeyChoice = prompt("1 -> PEM | 2 -> JWK | 3 -> DER | (0 -> cancel): "); +} + +let privateKey, keySpecification; +if (privateKeyChoice == "0") + return; +else if (privateKeyChoice == "1") { + privateKey = fs.readFileSync("./examples/keystore/pem_encrypted/private.pem", "utf8"); + keySpecification = { + format: "pem", + isEncrypted: true, + passphrase: "ThisIsMyPassphrase" + }; +} +else if (privateKeyChoice == "2") { + privateKey = fs.readFileSync("./examples/keystore/jwk/private.jwk", "utf8"); + privateKey = JSON.parse(privateKey); + keySpecification = { + format: "jwk", + isEncrypted: false, + }; +} +else if (privateKeyChoice == "3") { + privateKey = fs.readFileSync("./examples/keystore/der_pkcs8/private.der"); + keySpecification = { + format: "der", + type: "pkcs8", + isEncrypted: false + }; +} + +console.log("\nYour chosen private key: \n"); +console.log(privateKey); +console.log(`keySpecification = ${JSON.stringify(keySpecification)}\n`); + +// ------------ sign statement -------------- + +signedStatement = verisign.sign(expertStatement, privateKey, keySpecification); +console.log(`signedStatement = ${signedStatement} \n`); + +// ------------ store statement ------------ + +const filename = prompt("Enter a filename for saving the statement (without extension): "); +const path = "./examples/signed_statements/" + filename + ".json"; +fs.writeFileSync(path, signedStatement); + +console.log(`statement saved under ${path}`); \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/examples/verify_interactive.js b/processing_functions/node_functions/util/veri-sign/examples/verify_interactive.js new file mode 100644 index 0000000..9a87342 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/examples/verify_interactive.js @@ -0,0 +1,56 @@ +const fs = require("fs"); +const prompt = require("prompt-sync")(); +const verisign = require("./.."); + +// ------------ Choose statement -------------- + +let files = fs.readdirSync('./examples/signed_statements/'); + +if (files.length == 0) { + console.log("no expert statements available. Create one using 'npm run example_sign'"); + return; +} + +console.log("choose an expert statement:"); +for (let i = 0; i < files.length; i++) { + console.log(i + " --> " + files[i]); +} +console.log("x --> cancel \n") +const chosenFileNo = prompt(""); + +if(chosenFileNo == "x") + return; + +const chosenStatement = fs.readFileSync('./examples/signed_statements/' + files[Number(chosenFileNo)], "utf8"); + +console.log("\nchosen statement:\n") +console.log(chosenStatement); + +// ------------ Choose Certificate -------------- + +let certs = fs.readdirSync('./examples/certificates/'); + +console.log("\nchoose a certificate to verify the statement:"); +for (let i = 0; i < certs.length; i++) { + console.log(i + " --> " + certs[i]); +} +console.log("x --> cancel \n") +const chosenCertNo = prompt(""); + +if(chosenCertNo == "x") + return; + +let certName = certs[Number(chosenCertNo)]; + +let encoding; +if (certName.slice(-3) != "der") { + encoding = "utf8" +} + +const chosenCert = fs.readFileSync('./examples/certificates/' + certName, encoding); + +// ------------ Verify statement -------------- + +let verification = verisign.verify(chosenStatement, chosenCert); +verification = JSON.stringify(verification); +console.log(`\nverification = ${verification}\n`); \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/helper.js b/processing_functions/node_functions/util/veri-sign/helper.js new file mode 100644 index 0000000..194d23b --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/helper.js @@ -0,0 +1,243 @@ +const crypto = require('crypto'); +const Ajv = require("ajv"); +const ajv = new Ajv(); +require("ajv-keywords")(ajv); + +/** + * @typedef {import('../../types').ResultLog} ResultLog + * @typedef {import('../../types').KeyObject} KeyObject + * @typedef {import('../../types').KeySpec} KeySpec +*/ + +exports.createPrivateKeyObject = createPrivateKeyObject; +exports.isKeySpecValid = isKeySpecValid; +exports.isDocumentStructureValid = isDocumentStructureValid; +exports.isCertificateValid = isCertificateValid; + +/** + * creates a Private Key Object from the node crypto module, that must be used in the signing function + * @author localhorst87 + * @private + * @param {String|Object|Buffer} privateKey Private key as PEM, JWK or DER. If PEM is used, a utf-8 encoded string must be passed; + * if JWK is used the JSON object must be passed; and if DER is used a Buffer must be passed. + * @param {KeySpec} keySpecification specification object, that is used as input for signDocument + * @return {KeyObject} returns a private key object +*/ + function createPrivateKeyObject(privateKey, keySpecification) { + const checkedKeySpec = isKeySpecValid(keySpecification); + if (checkedKeySpec.result == false) { + throw("Can't create private key object: " + checkedKeySpec.log); + } + + return crypto.createPrivateKey({ + key: privateKey, + format: keySpecification.format, + type: keySpecification.type, + passphrase: keySpecification.passphrase ? keySpecification.passphrase : "", + encoding: "utf-8" + }); + } + +/** + * Performs check if the specification object used in createPrivateKeyObject is valid. In specific, it checks if type is given, in case the + * format is DER (Distinguished Encoding Rules), if a passphrase is given, in case the key is encrypted and if the passphrase sticks to the + * maximum length of 1024 bytes. + * + * @author localhorst87 + * @private + * @param {KeySpec} keySpecification Specification object, that is used as input for createPrivateKeyObject + * @return {ResultLog} The result property will be true/false if keySpecification is valid/invalid +*/ + function isKeySpecValid(keySpecification) { + if (typeof(keySpecification) != "object") { + return { + result: false, + log: "keySpecification must be an object" + } + } + + const fields = Object.keys(keySpecification); + if (!fields.includes("format") || !fields.includes("isEncrypted")) { + return { + result: false, + log: "keySpecification must contain at least the following properties: 'format', 'isEncrypted'" + } + } + + if (keySpecification.format.toLowerCase() != "pem" && keySpecification.format.toLowerCase() != "der" && keySpecification.format.toLowerCase() != "jwk") { + return { + result: false, + log: "Unsupported private key format. Format must be 'pem', 'der' or 'jwk'" + } + } + + if (keySpecification.format == "der" && !keySpecification.type) { + return { + result: false, + log: "If private key is encrypted via DER (Distinguished Encoding Rules), a type must be specified ('pkcs1', 'pkcs8' or 'sec1')" + } + } + + if (keySpecification.format == "der" && keySpecification.type.toLowerCase() != "pkcs1" && keySpecification.type.toLowerCase() != "pkcs8" && keySpecification.type.toLowerCase() != "sec1") { + return { + result: false, + log: "Unsupported type. Must be 'pkcs1', 'pkcs8' or 'sec1'" + } + } + + if (keySpecification.isEncrypted && !keySpecification.passphrase) { + return { + result: false, + log: "A passphrase for decryption must be given, if private key is encrypted" + } + } + + if (keySpecification.isEncrypted && (keySpecification.passphrase.length < 4 || keySpecification.passphrase.length > 1024)) { + return { + result: false, + log: "Passphrase must be between 4 and 1024 characters" + } + } + + return { + result: true, + log: "Key specification is valid" + } + } + +/** +* Checks if the structure of the signed document is valid. In specific, it will be checked if all required properties are available + * (that is content, signature, hash_algorithm and signature_encoding) and if their values are of allowed values + * + * @author localhorst87 + * @private + * @param {String} signedDocument stringified signed document, as returned by the sign function of this module + * @return {Boolean} returns true/false upon valid/invalid structure +*/ +function isDocumentStructureValid(signedDocument) { + const signedDocumentJson = JSON.parse(signedDocument); + + const jsonSchema = { + type: "object", + properties: { + content: { + type: "string", + transform: ["trim"], + minLength: 1 }, + signature: { + type: "string", + minLength: 32 }, + hash_algorithm: { + type: "string", + transform: ["trim", "toEnumCase"], + enum: [ + "RSA-MD4", + "RSA-MD5", + "RSA-RIPEMD160", + "RSA-SHA1", + "RSA-SHA1-2", + "RSA-SHA224", + "RSA-SHA256", + "RSA-SHA3-224", + "RSA-SHA3-256", + "RSA-SHA3-384", + "RSA-SHA3-512", + "RSA-SHA384", + "RSA-SHA512", + "RSA-SHA512/224", + "RSA-SHA512/256", + "RSA-SM3", + "blake2b512", + "blake2s256", + "id-rsassa-pkcs1-v1_5-with-sha3-224", + "id-rsassa-pkcs1-v1_5-with-sha3-256", + "id-rsassa-pkcs1-v1_5-with-sha3-384", + "id-rsassa-pkcs1-v1_5-with-sha3-512", + "md4", + "md4WithRSAEncryption", + "md5", + "md5-sha1", + "md5WithRSAEncryption", + "ripemd", + "ripemd160", + "ripemd160WithRSA", + "rmd160", + "sha1", + "sha1WithRSAEncryption", + "sha224", + "sha224WithRSAEncryption", + "sha256", + "sha256WithRSAEncryption", + "sha3-224", + "sha3-256", + "sha3-384", + "sha3-512", + "sha384", + "sha384WithRSAEncryption", + "sha512", + "sha512-224", + "sha512-224WithRSAEncryption", + "sha512-256", + "sha512-256WithRSAEncryption", + "sha512WithRSAEncryption", + "shake128", + "shake256", + "sm3", + "sm3WithRSAEncryption", + "ssl3-md5", + "ssl3-sha1", + "whirlpool" ] }, + signature_encoding: { + enum: ["hex"] } + }, + "required": ["content", "signature", "hash_algorithm", "signature_encoding"] + }; + + const validate = ajv.compile(jsonSchema); + + return validate(signedDocumentJson); +} + +/** + * Checks if the X509 certificate is valid + * + * @author localhorst87 + * @private + * @param {String|Buffer} x509Certificate A PEM or DER encoded X509 Certificate. If PEM is used, a utf-8 encoded string must be passed, + * if DER is used a Buffer must be passed. + * @return {ResultLog} The result of the validity check of the certificate +*/ +function isCertificateValid(x509Certificate) { + try { + x509Certificate = new crypto.X509Certificate(x509Certificate); + } + catch (err) { + return { + result: false, + log: "X509 certificate not valid: " + err + } + } + + const validFrom = new Date(x509Certificate.validFrom); + const validTo = new Date(x509Certificate.validTo); + const currentDate = new Date(); + + if (currentDate > validTo) { + return { + result: false, + log: "X509 certificate expired" + } + } + + if (currentDate < validFrom) { + return { + result: false, + log: "X509 certificate not yet valid" + } + } + + return { + result: true, + log: "X509 certificate valid" + } +} \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/index.d.ts b/processing_functions/node_functions/util/veri-sign/index.d.ts new file mode 100644 index 0000000..de484ad --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/index.d.ts @@ -0,0 +1,28 @@ +export interface ExpertStatement { + result: boolean; + log: string; + status_code: number; +} + +export interface KeySpec { + format: string; + type?: string; + isEncrypted: boolean; + passphrase?: string +} + +export interface ResultLog { + result: boolean; + log: string; +} + +export interface SignedStatement { + content: ExpertStatement; + signature: string; + hash_algorithm: string; + signature_encoding: string; +} + +export declare function checkExpertStatement(signedExpertStatement: SignedStatement, x509Certificate: string | Buffer): ResultLog; +export declare function sign(expertStatement: ExpertStatement, privateKey: string | object | Buffer, keySpecification: KeySpec): string; +export declare function verify(signedStatement: string, x509Certificate: string | Buffer): ResultLog; \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/index.js b/processing_functions/node_functions/util/veri-sign/index.js new file mode 100644 index 0000000..b9546e0 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/index.js @@ -0,0 +1,169 @@ +const crypto = require('crypto'); +const helper = require('./helper'); +const common = require('util-common'); +const schemas = require('./types/schemas'); + + /** + * @module util/sign-data + */ + + /** + * @typedef {import('./types/types').ResultLog} ResultLog + * @typedef {import('./types/types').KeySpec} KeySpec + * @typedef {import('./types/types').ExpertStatement} ExpertStatement + */ + +exports.checkExpertStatement = checkExpertStatement; +exports.sign = sign; +exports.verify = verify; +exports.SIGNED_STATEMENT = schemas.SIGNED_STATEMENT; +exports.EXPERT_STATEMENT = schemas.EXPERT_STATEMENT; + +/** + * This metric takes a signed expert statement, checks if the signature of the expert is valid (against a given + * X509 certificate) and processes the expert statement. + * + * @author localhorst87 + * @license BSD-2-Clause + * @kind function + * @version 1.0 + * @param {String} signedExpertStatement stringified JSON that implements the {@link schemas.SIGNED_STATEMENT} schema + * @param {String|Buffer} x509Certificate PEM- or DER-encoded X509 certificate. If PEM is used, a string is expected, + * if DER is used, a Buffer is expected + * @returns {ResultLog} result and logging information + */ +function checkExpertStatement(signedExpertStatement, x509Certificate) { + // the verify method checks validity of inputs, therefore no need to check it again + const isSignatureValid = verify(signedExpertStatement, x509Certificate); + + if (isSignatureValid.result === false) { + return { + result: false, + log: "The expert statement could not be verified (" + isSignatureValid.log + ")" + }; + } + else { + const parsedStatement = JSON.parse(signedExpertStatement); + + return { + result: parsedStatement.content.result, + log: parsedStatement.content.log + }; + } +} + +/** + * Adds a signature to the given content, according to the given private key and returns a JSON, consisting of the following properties: content (the content that is signed), + * signature (the signature as encoded String), hash_algorithm (the used hash algo. SHA256 by default), signature_encoding (the binary-to-text encoding, used for creating + * the signature, hex by default) + * + * @author localhorst87 + * @license BSD-2-Clause + * @kind function + * @version 1.0 + * @param {ExpertStatement} expertStatement The expert statement that needs to be signed + * @param {String|Object|Buffer} privateKey Private key as PEM, JWK or DER. If PEM is used, a utf-8 encoded string must be passed; + * if JWK is used the JSON object must be passed; and if DER is used a Buffer must be passed. + * @param {KeySpec} keySpecification The specification of the given private key (cf. KeySpec definition) + * @return {String} Stringified JSON, consisting of the expert statement, signature and the meta-data (cf. description above) +*/ +function sign(expertStatement, privateKey, keySpecification) { + if(!common.isStructureValid(expertStatement, schemas.EXPERT_STATEMENT)) { + throw("expertStatement structure is not valid"); + } + + var privateKeyObject; + const HASH_FUNCTION = "SHA256"; + const SIGNATURE_ENCODING = "hex"; + + try { + privateKeyObject = helper.createPrivateKeyObject(privateKey, keySpecification); + } + catch (err) { + if (err.code == "ERR_OSSL_EVP_BAD_DECRYPT") + throw("passphrase is wrong!") + else + throw(err); + } + + const signObject = crypto.createSign(HASH_FUNCTION); + signObject.update(JSON.stringify(expertStatement)); + signObject.end(); + const signature = signObject.sign(privateKeyObject, SIGNATURE_ENCODING); + + const signedStatement = { + content: expertStatement, + signature: signature, + hash_algorithm: HASH_FUNCTION, + signature_encoding: SIGNATURE_ENCODING + }; + + return JSON.stringify(signedStatement); +} + +/** + * Checks, if the document that has been signed with the sign function has been signed by the + * holder of the given certificate + * + * @author localhorst87 + * @license BSD-2-Clause + * @kind function + * @version 1.0 + * @param {String} signedStatement The signed statement as a string, as it was returned by the "sign" function + * @param {String|Buffer} x509Certificate A PEM or DER encoded X509 Certificate. If PEM is used, a utf-8 encoded string must be passed, + * if DER is used a Buffer must be passed. + * @return {ResultLog} returns true/false w.r.t. the validity of the signature for the data and public key +*/ +function verify(signedStatement, x509Certificate) { + if(typeof(signedStatement) != "string") { + return { + result: false, + log: "signedStatement must be a stringified ExpertStatement JSON" + }; + } + + try { + signedStatement = JSON.parse(signedStatement); + } + catch (err) { + return { + result: false, + log: "Could not parse the signed statement (" + err + ")" + }; + } + + if(!common.isStructureValid(signedStatement, schemas.SIGNED_STATEMENT)) { + return { + result: false, + log: "signedStatement structure is not valid" + }; + } + + const certCheck = helper.isCertificateValid(x509Certificate) + if(certCheck.result == false) { + return { + result: false, + log: certCheck.log + } + } + + const x509Object = new crypto.X509Certificate(x509Certificate); + const publicKey = x509Object.publicKey; + const verifyObject = crypto.createVerify(signedStatement.hash_algorithm); + verifyObject.update(JSON.stringify(signedStatement.content)); + verifyObject.end(); + const verification = verifyObject.verify(publicKey, signedStatement.signature, signedStatement.signature_encoding); + + if(verification == true) { + return { + result: true, + log: "signature is valid" + }; + } + else { + return { + result: false, + log: "signature is not valid" + }; + } +} \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/package-lock.json b/processing_functions/node_functions/util/veri-sign/package-lock.json new file mode 100644 index 0000000..0c355a4 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/package-lock.json @@ -0,0 +1,2702 @@ +{ + "name": "veri-sign", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "veri-sign", + "version": "0.1.0", + "license": "CC-BY-4.0", + "dependencies": { + "ajv": "^8.10.0", + "ajv-keywords": "^5.1.0", + "crypto": "^1.0.1", + "crypto-browserify": "^3.12.0", + "fs": "^0.0.1-security", + "util-common": "../util-common" + }, + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.0.0", + "prompt-sync": "^4.2.0" + } + }, + "../util-common": { + "version": "1.0.0", + "license": "CC-BY-4.0", + "dependencies": { + "ajv": "^8.10.0", + "ajv-keywords": "^5.1.0", + "fast-xml-parser": "^4.1.3", + "libxmljs2-xsd": "0.30.1" + }, + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.0.0" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.1.tgz", + "integrity": "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==", + "dependencies": { + "bn.js": "^5.2.1", + "randombytes": "^2.1.0", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", + "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", + "dependencies": { + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.5", + "hash-base": "~3.0", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/elliptic": { + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", + "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-uri": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.2.tgz", + "integrity": "sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row==" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", + "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "dependencies": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/prompt-sync": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz", + "integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==", + "dev": true, + "dependencies": { + "strip-ansi": "^5.0.0" + } + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/util-common": { + "resolved": "../util-common", + "link": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true + }, + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true + }, + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "requires": { + "fill-range": "^7.1.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.1.tgz", + "integrity": "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==", + "requires": { + "bn.js": "^5.2.1", + "randombytes": "^2.1.0", + "safe-buffer": "^5.2.1" + } + }, + "browserify-sign": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", + "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", + "requires": { + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.5", + "hash-base": "~3.0", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.7", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + } + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "requires": { + "get-func-name": "^2.0.2" + } + }, + "chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, + "deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "elliptic": { + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", + "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-uri": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.2.tgz", + "integrity": "sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row==" + }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true + }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "requires": { + "get-func-name": "^2.0.1" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "mocha": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", + "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "parse-asn1": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "requires": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "prompt-sync": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz", + "integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==", + "dev": true, + "requires": { + "strip-ansi": "^5.0.0" + } + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true + }, + "util-common": { + "version": "file:../util-common", + "requires": { + "ajv": "^8.10.0", + "ajv-keywords": "^5.1.0", + "chai": "^4.3.6", + "fast-xml-parser": "^4.1.3", + "libxmljs2-xsd": "0.30.1", + "mocha": "^10.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/processing_functions/node_functions/util/veri-sign/package.json b/processing_functions/node_functions/util/veri-sign/package.json new file mode 100644 index 0000000..4b10ae6 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/package.json @@ -0,0 +1,27 @@ +{ + "name": "veri-sign", + "version": "0.1.0", + "description": "use this module to sign and verify your data, based on X509 certificates", + "main": "index.js", + "scripts": { + "test": "mocha --reporter spec", + "example_sign": "node ./examples/sign_interactive.js", + "example_verify": "node ./examples/verify_interactive.js" + }, + "types": "./types/veri-sign.d.ts", + "author": "localhorst87", + "license": "CC-BY-4.0", + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.0.0", + "prompt-sync": "^4.2.0" + }, + "dependencies": { + "ajv": "^8.10.0", + "ajv-keywords": "^5.1.0", + "crypto": "^1.0.1", + "crypto-browserify": "^3.12.0", + "fs": "^0.0.1-security", + "util-common": "../util-common" + } +} diff --git a/processing_functions/node_functions/util/veri-sign/test/api.test.js b/processing_functions/node_functions/util/veri-sign/test/api.test.js new file mode 100644 index 0000000..f4747f5 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/api.test.js @@ -0,0 +1,292 @@ +const { it, describe } = require("mocha"); +const { expect } = require("chai"); +const FileSync = require("fs"); +const verisign = require("../."); + +describe("sign", () => { + + describe("valid inputs", () => { + + it("use PEM utf8-string as private key => expect to successfully sign", () => { + const privateKey = FileSync.readFileSync("./test/test_data/pem/private.pem", "utf8"); + const keySpec = { + format: "pem", + isEncrypted: false + }; + const expertStatement = { + result: true, + log: "arbitrary content", + status_code: 200 + }; + + const signedDoc = verisign.sign(expertStatement, privateKey, keySpec); + + expect(signedDoc).to.be.ok; + }); + + it("use DER Buffer as private key => expect to successfully sign", () => { + const privateKey = FileSync.readFileSync("./test/test_data/der_pkcs8/private.der"); + const keySpec = { + format: "der", + type: "pkcs8", + isEncrypted: false + }; + const expertStatement = { + result: true, + log: "arbitrary content", + status_code: 200 + }; + + let signedDoc = verisign.sign(expertStatement, privateKey, keySpec); + + expect(signedDoc).to.be.ok; + }); + + it("use JWK object as private key => expect to successfully sign", () => { + let privateKey = FileSync.readFileSync("./test/test_data/jwk/private.jwk", "utf8"); + privateKey = JSON.parse(privateKey); + const keySpec = { + format: "jwk", + isEncrypted: false + }; + const expertStatement = { + result: true, + log: "arbitrary content", + status_code: 200 + }; + + let signedDoc = verisign.sign(expertStatement, privateKey, keySpec); + + expect(signedDoc).to.be.ok; + }); + + it("sign document => expect to be wrapped into information structure", () => { + const privateKey = FileSync.readFileSync("./test/test_data/pem/private.pem", "utf8"); + const keySpec = { + format: "pem", + isEncrypted: false + }; + const expertStatement = { + result: true, + log: "arbitrary content", + status_code: 200 + }; + + let signedDoc = verisign.sign(expertStatement, privateKey, keySpec); + + signedDoc = JSON.parse(signedDoc); + + expect(signedDoc.content).to.deep.equal(expertStatement); + expect(signedDoc.signature.length).to.equal(512); + }); + + }); + + describe("invalid inputs", () => { + + it("expertStatement does not fulfill schema => expect to throw", () => { + const privateKey = FileSync.readFileSync("./test/test_data/pem/private.pem", "utf8"); + const keySpec = { + format: "pem", + isEncrypted: false + }; + const expertStatement = { + result: true, + status_code: 200 + }; + + expect(() => verisign.sign(expertStatement, privateKey, {})).to.throw(); + }); + + it("log of expertStatement is empty string => expect to throw", () => { + const privateKey = FileSync.readFileSync("./test/test_data/pem/private.pem", "utf8"); + const keySpec = { + format: "pem", + isEncrypted: false + }; + const expertStatement = { + result: true, + log: "", + status_code: 200 + }; + + expect(() => verisign.sign(expertStatement, privateKey, keySpec)).to.throw(); + }); + + it("keySpec is empty => expect to throw", () => { + const privateKey = FileSync.readFileSync("./test/test_data/pem/private.pem", "utf8"); + const expertStatement = { + result: true, + log: "everything good", + status_code: 200 + }; + + expect(() => verisign.sign(expertStatement, privateKey, {})).to.throw(); + }); + + it("privateKey is empty => expect to throw", () => { + const privateKey = ` + -----BEGIN PRIVATE KEY----- + -----END PRIVATE KEY----- + `; + + const keySpec = { + format: "pem", + isEncrypted: false + }; + + const expertStatement = { + result: true, + log: "everything good", + status_code: 200 + }; + + expect(() => verisign.sign(expertStatement, privateKey, keySpec)).to.throw(); + }); + + it("privateKey is not String, Buffer and Object => expect to throw", () => { + const keySpec = { + format: "pem", + isEncrypted: false + }; + + const expertStatement = { + result: true, + log: "everything good", + status_code: 200 + }; + + expect(() => verisign.sign(expertStatement, 2564468, keySpec)).to.throw(); + }); + + it("keySpec not as expected => expect to throw", () => { + const privateKey = FileSync.readFileSync("./test/test_data/pem/private.pem", "utf8"); + + const expertStatement = { + result: true, + log: "everything good", + status_code: 200 + }; + + expect(() => verisign.sign(expertStatement, privateKey, 1)).to.throw(); + }); + + }); + +}); + +describe("verify", () => { + + describe("valid inputs", () => { + + it("PEM certificate, valid signature => expect true", () => { + const signedStatement = FileSync.readFileSync("./test/test_data/pem/signed_statement_example.json", "utf8"); + const certificate = FileSync.readFileSync("./test/test_data/pem/cert.pem", "utf8"); + + const resLog = verisign.verify(signedStatement, certificate); + + expect(resLog.result).to.be.true; + }); + + it("DER certificate, valid signature => expect true", () => { + const signedStatement = FileSync.readFileSync("./test/test_data/der_pkcs8/signed_statement_example.json", "utf8"); + const certificate = FileSync.readFileSync("./test/test_data/der_pkcs8/cert.der"); + + const resLog = verisign.verify(signedStatement, certificate); + + expect(resLog.result).to.be.true; + }); + + it("valid signature, wrong certificate => expect false", () => { + const signedStatement = FileSync.readFileSync("./test/test_data/pem/signed_statement_example.json", "utf8"); + const certificate = FileSync.readFileSync("./test/test_data/der_pkcs8/cert.der"); + + const resLog = verisign.verify(signedStatement, certificate); + + expect(resLog.result).to.be.false; + }); + + it("content of signature changed afterwards => expect false", () => { + const signedStatement = FileSync.readFileSync("./test/test_data/pem/signed_statement_example.json", "utf8"); + let tweakedDocument = JSON.parse(signedStatement); + tweakedDocument.content.result = false; + tweakedDocument = JSON.stringify(tweakedDocument); + const certificate = FileSync.readFileSync("./test/test_data/pem/cert.pem", "utf8"); + + const resLog = verisign.verify(tweakedDocument, certificate); + + expect(resLog.result).to.be.false; + }); + + it("certificate expired => expect false", () => { + const signedStatement = FileSync.readFileSync("./test/test_data/pem_expired/signed_statement_example.json", "utf8"); + const certificate = FileSync.readFileSync("./test/test_data/pem_expired/cert.pem", "utf8"); + + const resLog = verisign.verify(signedStatement, certificate); + + expect(resLog).to.deep.equal({ + result: false, + log: "X509 certificate expired" + }) + }); + + }); + + describe("invalid inputs", () => { + + it("signed document has invalid structure => expect to throw", () => { + let signedStatement = { + doc: "arbitrary content", + sign: "12fgpjgop" + }; + signedStatement = JSON.stringify(signedStatement); + const certificate = FileSync.readFileSync("./test/test_data/pem/cert.pem", "utf8"); + + const resLog = verisign.verify(signedStatement, certificate); + + expect(resLog).to.deep.equal({ + result: false, + log: "signedStatement structure is not valid" + }) + }); + + it("signed document has invalid type => expect to throw", () => { + let signedStatement = FileSync.readFileSync("./test/test_data/pem/signed_statement_example.json", "utf8"); + signedStatement = JSON.parse(signedStatement); // object instead of string + const certificate = FileSync.readFileSync("./test/test_data/pem/cert.pem", "utf8"); + + const resLog = verisign.verify(signedStatement, certificate); + + expect(resLog).to.deep.equal({ + result: false, + log: "signedStatement must be a stringified ExpertStatement JSON" + }) + }); + + it("PEM certificate has invalid structure => expect to throw", () => { + const signedStatement = FileSync.readFileSync("./test/test_data/pem/signed_statement_example.json", "utf8"); + const certificate = "-----BEGIN CERTIFICATE-----"; + + const resLog = verisign.verify(signedStatement, certificate); + + expect(resLog).to.deep.equal({ + result: false, + log: "X509 certificate not valid: Error: error:0908F066:PEM routines:get_header_and_data:bad end line" + }) + }); + + it("certificate has invalid data type => expect to throw", () => { + const signedStatement = FileSync.readFileSync("./test/test_data/pem/signed_statement_example.json", "utf8"); + const certificate = 12; + + const resLog = verisign.verify(signedStatement, certificate); + + expect(resLog).to.deep.equal({ + result: false, + log: "X509 certificate not valid: TypeError [ERR_INVALID_ARG_TYPE]: The \"buffer\" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received type number (12)" + }) + }); + + }); + +}); \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/test/helper.test.js b/processing_functions/node_functions/util/veri-sign/test/helper.test.js new file mode 100644 index 0000000..0908326 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/helper.test.js @@ -0,0 +1,341 @@ +const { it, describe } = require("mocha"); +const { expect } = require("chai"); +const FileSync = require("fs"); +const helper = require("../helper"); + +describe("createPrivateKeyObject", () => { + + describe("valid keys", () => { + + it("PEM => expect KeyObject to be created", () => { + const privateKey = FileSync.readFileSync("./test/test_data/pem/private.pem", "utf8"); + const keySpec = { + format: "pem", + isEncrypted: false + }; + + let keyObj = helper.createPrivateKeyObject(privateKey, keySpec); + + expect(keyObj).to.be.ok; + }); + + it("PEM, with encryption => expect KeyObject to be created", () => { + const privateKey = FileSync.readFileSync("./test/test_data/pem/private.pem", "utf8"); + const passphrase = FileSync.readFileSync("./test/test_data/pem_encrypted/passphrase", "utf8"); + const keySpec = { + format: "pem", + isEncrypted: true, + passphrase: passphrase + }; + + let keyObj = helper.createPrivateKeyObject(privateKey, keySpec); + + expect(keyObj).to.be.ok; + }); + + it("JWK => expect KeyObject to be created", () => { + let privateKey = FileSync.readFileSync("./test/test_data/jwk/private.jwk", "utf8"); + privateKey = JSON.parse(privateKey); + const keySpec = { + format: "jwk", + isEncrypted: false + }; + + let keyObj = helper.createPrivateKeyObject(privateKey, keySpec); + + expect(keyObj).to.be.ok; + }); + + it("DER PKCS1 => expect KeyObject to be created", () => { + const privateKey = FileSync.readFileSync("./test/test_data/der_pkcs1/private.der"); + const keySpec = { + format: "der", + type: "pkcs1", + isEncrypted: false + }; + + let keyObj = helper.createPrivateKeyObject(privateKey, keySpec); + + expect(keyObj).to.be.ok; + }); + + it("DER PKCS8 => expect KeyObject to be created", () => { + const privateKey = FileSync.readFileSync("./test/test_data/der_pkcs8/private.der"); + const keySpec = { + format: "der", + type: "pkcs8", + isEncrypted: false + }; + + let keyObj = helper.createPrivateKeyObject(privateKey, keySpec); + + expect(keyObj).to.be.ok; + }); + + it("DER SEC1 => expect KeyObject to be created", () => { + const privateKey = FileSync.readFileSync("./test/test_data/der_sec1/private.der"); + const keySpec = { + format: "der", + type: "sec1", + isEncrypted: false + }; + + let keyObj = helper.createPrivateKeyObject(privateKey, keySpec); + + expect(keyObj).to.be.ok; + }); + + }); + + describe("invalid key spec", () => { + + it("KeySpec object invalid => expect to throw", () => { + const keySpec = { + format: "pem", + isEncrypted: true, + // passphrase: "abcd" + }; + + expect(() => helper.createPrivateKeyObject(privateKey, keySpec)).to.throw(); + }); + + it("PEM, with encryption, passphrase not correct => expect to throw", () => { + const privateKey = FileSync.readFileSync("./test/test_data/pem_encrypted/private.pem", "utf8"); + const keySpec = { + format: "pem", + isEncrypted: true, + passphrase: "wrong passphrase" + }; + + expect(() => helper.createPrivateKeyObject(privateKey, keySpec)).to.throw(); + }); + + it("Private key and KeySpec do not match => expect to throw", () => { + const privateKey = FileSync.readFileSync("./test/test_data/der_pkcs1/private.der"); + const keySpec = { + format: "der", + type: "pkcs8", + isEncrypted: false + }; + + expect(() => helper.createPrivateKeyObject(privateKey, keySpec)).to.throw(); + }); + + }); + +}); + +describe("isKeySpecValid", () => { + + describe("valid keySpec", () => { + + it("valid format given in lower case => expect true", () => { + const keySpec = { + format: "pem", + isEncrypted: false + }; + + const resultLog = helper.isKeySpecValid(keySpec); + + expect(resultLog.result).to.be.true; + }); + + it("format given in upper case => expect true", () => { + const keySpec = { + format: "JWK", + isEncrypted: false + }; + + const resultLog = helper.isKeySpecValid(keySpec); + + expect(resultLog.result).to.be.true; + }); + + it("encryption used => expect true", () => { + const keySpec = { + format: "pem", + isEncrypted: true, + passphrase: "12345" + }; + + const resultLog = helper.isKeySpecValid(keySpec); + + expect(resultLog.result).to.be.true; + }); + + }); + + describe("invalid keySpec", () => { + + it("not an object => expect false", () => { + const keySpec = 12; + + const resultLog = helper.isKeySpecValid(keySpec); + + expect(resultLog.result).to.be.false; + }); + + it("missing property 'isEncrypted' => expect false", () => { + const keySpec = { + format: "pem" + }; + + const resultLog = helper.isKeySpecValid(keySpec); + + expect(resultLog.result).to.be.false; + }); + + it("missing property 'format' => expect false", () => { + const keySpec = { + isEncrypted: false + }; + + const resultLog = helper.isKeySpecValid(keySpec); + + expect(resultLog.result).to.be.false; + }); + + it("other type given than PEM, DER or JWK => expect false", () => { + const keySpec = { + format: "xxx", + isEncrypted: true, + passphrase: "12345" + }; + + const resultLog = helper.isKeySpecValid(keySpec); + + expect(resultLog.result).to.be.false; + }); + + it("no type given on DER format => expect false", () => { + const keySpec = { + format: "der", + isEncrypted: false, + passphrase: "12345" + }; + + const resultLog = helper.isKeySpecValid(keySpec); + + expect(resultLog.result).to.be.false; + }); + + it("unsupported type given for DER format => expect false", () => { + const keySpec = { + format: "der", + isEncrypted: true, + type: "pkcs2", + passphrase: "12345" + }; + + const resultLog = helper.isKeySpecValid(keySpec); + + expect(resultLog.result).to.be.false; + }); + + it("passphrase not given for encrypted key => expect false", () => { + const keySpec = { + format: "der", + isEncrypted: true, + type: "pkcs1" + }; + + const resultLog = helper.isKeySpecValid(keySpec); + + expect(resultLog.result).to.be.false; + }); + + it("passphrase too short for encrypted key => expect false", () => { + const keySpec = { + format: "der", + isEncrypted: true, + type: "pkcs1", + passphrase: "123" + }; + + const resultLog = helper.isKeySpecValid(keySpec); + + expect(resultLog.result).to.be.false; + }); + + it("passphrase too long for encrypted key => expect false", () => { + const tooLongPassphrase = ` + 0100000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999 + 0200000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999 + 0300000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999 + 0400000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999 + 0500000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999 + 0600000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999 + 0700000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999 + 0800000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999 + 0900000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999 + 1000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999 + 1100000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999 + `; + + const keySpec = { + format: "der", + isEncrypted: true, + type: "pkcs1", + passphrase: tooLongPassphrase + }; + + const resultLog = helper.isKeySpecValid(keySpec); + + expect(resultLog.result).to.be.false; + }); + + }); + +}); + +describe("isCertificateValid", () => { + + describe("valid inputs", () => { + + it("valid PEM encoded certificate => expect true", () => { + const certificate = FileSync.readFileSync("./test/test_data/pem/cert.pem", "utf8"); + const resLog = helper.isCertificateValid(certificate); + + expect(resLog.result).to.be.true; + }); + + it("valid DER encoded certificate => expect true", () => { + const certificate = FileSync.readFileSync("./test/test_data/der_pkcs8/cert.der"); + const resLog = helper.isCertificateValid(certificate); + + expect(resLog.result).to.be.true; + }); + + it("expired PEM encoded certificate => expect false", () => { + const certificate = FileSync.readFileSync("./test/test_data/pem_expired/cert.pem", "utf8"); + const resLog = helper.isCertificateValid(certificate); + + expect(resLog).to.deep.equal({ + result: false, + log: "X509 certificate expired" + }); + }); + + }); + + describe("invalid inputs", () => { + + it("PEM encoded certificate has invalid structure => expect false", () => { + const certificate = "-----BEGIN CERTIFICATE-----"; + + const resLog = helper.isCertificateValid(certificate); + + expect(resLog.result).to.be.false; + }); + + it("certificate has invalid data type => expect false", () => { + const certificate = 12; + + const resLog = helper.isCertificateValid(certificate); + + expect(resLog.result).to.be.false; + }); + + }); + +}); \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs1/cert.der b/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs1/cert.der new file mode 100644 index 0000000..3340f5b Binary files /dev/null and b/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs1/cert.der differ diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs1/cert.pem b/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs1/cert.pem new file mode 100644 index 0000000..d596159 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs1/cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEJTCCAw2gAwIBAgIUJdOTojBDUh51FoVom/jDzYzXug8wDQYJKoZIhvcNAQEL +BQAwgaAxCzAJBgNVBAYTAkRFMQwwCgYDVQQIDANOUlcxFDASBgNVBAcMC0R1ZXNz +ZWxkb3JmMRwwGgYDVQQKDBNDcmF6eSBNb2RlbGluZyBHbWJIMRMwEQYDVQQLDApW +YWxpZGF0aW9uMREwDwYDVQQDDAhKb2huIERvZTEnMCUGCSqGSIb3DQEJARYYam9o +bi5kb2VAY3Jhenltb2RnbWJoLmRlMCAXDTIyMDkwNTE1MzExOVoYDzIxMjIwODEy +MTUzMTE5WjCBoDELMAkGA1UEBhMCREUxDDAKBgNVBAgMA05SVzEUMBIGA1UEBwwL +RHVlc3NlbGRvcmYxHDAaBgNVBAoME0NyYXp5IE1vZGVsaW5nIEdtYkgxEzARBgNV +BAsMClZhbGlkYXRpb24xETAPBgNVBAMMCEpvaG4gRG9lMScwJQYJKoZIhvcNAQkB +Fhhqb2huLmRvZUBjcmF6eW1vZGdtYmguZGUwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDD4Xjq6XXDz2w9iwKSpMPUp68lz0YR+cdJAWGsK5ZuGSfFMv3c +Lz3/Oqk/EIMAFGNsJdOx8HS6D6kc0/RhA3zqc0TrinCzg0o8Wiuh6tOLsXbO+XyQ +QeQOQKYJ6HoqYt189xhHhj6oJMilonljSRtJLqJGR0fI21SqSu7DRyfgi+CuGTb3 +jQtZ1gDtDSqNdDOO+WG13chj9jaVLmi/JRWVSpSc9mkP7Bhb1AC1pKjIhWOb0rBL +gphttseBjLxE/tMIPLVAsFvYySTnNPNmQnXJ3uob8ISE7cF5ffbQskj66m2CEJUh +sfdmG6rRteprDJHKMTo94AFOBQw1l4+3XypRAgMBAAGjUzBRMB0GA1UdDgQWBBQJ +FTW4mr1tAOBWKqLAXn1p7QSfQjAfBgNVHSMEGDAWgBQJFTW4mr1tAOBWKqLAXn1p +7QSfQjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAjdVRSLemc +q+orKzJN7wUmSUlEIHJExx5rG7lFQ+eBNJe15Pejk9VuDec5hnMshfSO+vsvUrlx +WdGe469J5Mg1PRgIxMWXN1e6dZ/ZmRAYd5/vomFCas7sy3PN+vWWqdg3MypmdZGd +8V4AUa1sdw9Cb8QkViQy96/l6UJZpNFZA4jRL6SQ+S2F0Ir4gqPiXV+vgb944MFL +rUeIp7LPcJY/OXZRPiZdnehefPJPBmcw5meK/hRCrdYnJjFNVuWBsL99962O4gZv +JXp45KivPnKfO9GQcu5FuZb3SPnxzHMnHRhwWYdxYay+njjJQULPiaB6oLEqzUN5 +6QhawZVvjf8x +-----END CERTIFICATE----- diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs1/private.der b/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs1/private.der new file mode 100644 index 0000000..dcacc0b Binary files /dev/null and b/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs1/private.der differ diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs8/cert.der b/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs8/cert.der new file mode 100644 index 0000000..962d117 Binary files /dev/null and b/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs8/cert.der differ diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs8/cert.pem b/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs8/cert.pem new file mode 100644 index 0000000..7934f17 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs8/cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIELTCCAxWgAwIBAgIUJ9OSRiSO0EPQpwh90TpQRmcmf64wDQYJKoZIhvcNAQEL +BQAwgaQxCzAJBgNVBAYTAkRFMQwwCgYDVQQIDANOUlcxDzANBgNVBAcMBkFhY2hl +bjEsMCoGA1UECgwjSW5zdGl0dXRlIGZvciBBdXRvbW90aXZlIFNpbXVsYXRpb24x +DTALBgNVBAsMBFZlcmkxETAPBgNVBAMMCEpvaG4gRG9lMSYwJAYJKoZIhvcNAQkB +Fhdqb2huLmRvZUBpZmFzLWFhY2hlbi5kZTAgFw0yMjA5MDUxNTMwMTVaGA8yMTIy +MDgxMjE1MzAxNVowgaQxCzAJBgNVBAYTAkRFMQwwCgYDVQQIDANOUlcxDzANBgNV +BAcMBkFhY2hlbjEsMCoGA1UECgwjSW5zdGl0dXRlIGZvciBBdXRvbW90aXZlIFNp +bXVsYXRpb24xDTALBgNVBAsMBFZlcmkxETAPBgNVBAMMCEpvaG4gRG9lMSYwJAYJ +KoZIhvcNAQkBFhdqb2huLmRvZUBpZmFzLWFhY2hlbi5kZTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAPVSB0OfBDOIdXOgSNyaiytPP+vqRW3zcLn0Oy50 +lVyPDinoMIzTeN0SbVrzypC2VpwzXqSIT5b6JVExR9MyNouVN2OTIqR/E1K7ggGa +mD0v7XbBdUMfZyGikXPMwSkXhbbTO9PZtlXVwA2xqFZGDWBvYGzm71iJuXJMujxx +yvWBR+uwhQANVHwQNhVb5e6h1V/Pf9gX4ojvr0pjPDSDQzJzv//cRnfRaZb3I3fQ +SjCaVOvUcPxTYB2cebTIvFyRNOhhANj/jbxw1UaeewBMATqrOxOpLvJqA6ausQ58 +CfnmAPH0n7Mc1Z0FvACD1+8A9YShoNEppH03IbaHBdgrtSECAwEAAaNTMFEwHQYD +VR0OBBYEFMTCDziKWg20v1+H50RStNRI7AfxMB8GA1UdIwQYMBaAFMTCDziKWg20 +v1+H50RStNRI7AfxMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB +AEi8pb8Z4A5ppw24Q28eUhepcksvj+dHU0Q3V6WqRNX/Azn6xHMGjjDHGsB7NcAE +g4sZsYBeOOo5HvTmwm0TYoyxy9OqW+bE0s6RImuW8aah7LjR1ImjZIKf5e+ovhcP +Y4jMlWbjG3fwn/JxBAEvdFGCKgFSgp4EuO2VXZVqcFSbCvBkDOZNOqnA42H+/A3A +FlamBiDhpnrHUNLSR4GrOr+K1fipM30PYXfwObN/oENd9UhDo9SWbJUyo+0wgcnD +P3xt+G8H1JEN1+kfzLyQC5iO1JL1J1JUs2G8b1TsIsTHhrZCn+USSLldXYX3Nutn +bWqG3cWVqopYJvTjfCE6Cf0= +-----END CERTIFICATE----- diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs8/private.der b/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs8/private.der new file mode 100644 index 0000000..1253a8d Binary files /dev/null and b/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs8/private.der differ diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs8/signed_statement_example.json b/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs8/signed_statement_example.json new file mode 100644 index 0000000..563baaa --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/test_data/der_pkcs8/signed_statement_example.json @@ -0,0 +1 @@ +{"content":{"result":true,"log":"such great quality","status_code":200},"signature":"795f2d671ab274cc0e5057dfe5f0de10a27b60494f111c8f4feec1da68db559fc67a2627fdad17f7599a49dd9a47c6b7a8bb29d228c2174fbfd2167f7301d908bdbc557a5e6c4fa815f12cde68383f5c6ec704eecdfecc6677e5775f8e4c4063cab11207a42e190db193c45c9ce0c5678ce9cf1ce3d1e30d4e6ffca92eda76c1903b2866f6b191233fb0bf3603cb38f4ed2394ddf0ba87eb5ce81ecb4b2281040a01db98622f7183532d038aaae285ccac312ec7e9165d24825f6019f691281a3b0ef87703611dca8bd8621e93c44ab85f076eeca2c962a30de997b58e3342f21c8e846db392acd791c52a932c67870676da8ce34af226172c2f834a1925e23d","hash_algorithm":"SHA256","signature_encoding":"hex"} \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/der_sec1/cert.der b/processing_functions/node_functions/util/veri-sign/test/test_data/der_sec1/cert.der new file mode 100644 index 0000000..38a58e0 Binary files /dev/null and b/processing_functions/node_functions/util/veri-sign/test/test_data/der_sec1/cert.der differ diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/der_sec1/cert.pem b/processing_functions/node_functions/util/veri-sign/test/test_data/der_sec1/cert.pem new file mode 100644 index 0000000..878f82e --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/test_data/der_sec1/cert.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIIChzCCAjKgAwIBAgIURPGniq/YtgqHVkxJwOtUCgGVolowCgYIKoZIzj0EAwIw +gZ0xCzAJBgNVBAYTAkRFMRAwDgYDVQQIDAdCYXZhcmlhMQ8wDQYDVQQHDAZNdW5p +Y2gxFzAVBgNVBAoMDk1vdGlvblNpbSBHbWJIMRUwEwYDVQQLDAxWZXJpZmljYXRp +b24xETAPBgNVBAMMCEpvaG4gRG9lMSgwJgYJKoZIhvcNAQkBFhlKb2huLkRvZUBt +b3Rpb25zaW1nbWJoLmRlMCAXDTIyMDkwNTE1MjkxNVoYDzIxMjIwODEyMTUyOTE1 +WjCBnTELMAkGA1UEBhMCREUxEDAOBgNVBAgMB0JhdmFyaWExDzANBgNVBAcMBk11 +bmljaDEXMBUGA1UECgwOTW90aW9uU2ltIEdtYkgxFTATBgNVBAsMDFZlcmlmaWNh +dGlvbjERMA8GA1UEAwwISm9obiBEb2UxKDAmBgkqhkiG9w0BCQEWGUpvaG4uRG9l +QG1vdGlvbnNpbWdtYmguZGUwUjAQBgcqhkjOPQIBBgUrgQQAAwM+AAQreuNhtXfj +hqkBn3zujYfyDrV8v+z1xX2rmtIYO109QW5a++cXPV8ibDRzCBCdRqIY5jJJpfsL +hNgxu4yjUzBRMB0GA1UdDgQWBBSzX18zuSGQsLqueZj8RO4bM3stMTAfBgNVHSME +GDAWgBSzX18zuSGQsLqueZj8RO4bM3stMTAPBgNVHRMBAf8EBTADAQH/MAoGCCqG +SM49BAMCA0MAMEACHhk1V12zQxdnBbvQw/i6dLi4dPZOkugWboUvlMsk0gIeFG4i +EbMREvFWyXTOlQwEE3nHFRJGXsCtoGsGa4cl +-----END CERTIFICATE----- diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/der_sec1/private.der b/processing_functions/node_functions/util/veri-sign/test/test_data/der_sec1/private.der new file mode 100644 index 0000000..c6657df Binary files /dev/null and b/processing_functions/node_functions/util/veri-sign/test/test_data/der_sec1/private.der differ diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/jwk/cert.pem b/processing_functions/node_functions/util/veri-sign/test/test_data/jwk/cert.pem new file mode 100644 index 0000000..d3ab9ec --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/test_data/jwk/cert.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICiTCCAi+gAwIBAgIUZb3LYK/49RqkWeQ+PQ5kf5/eMQIwCgYIKoZIzj0EAwIw +gZgxCzAJBgNVBAYTAkRFMQwwCgYDVQQIDANOUlcxDzANBgNVBAcMBkFhY2hlbjEW +MBQGA1UECgwNU2ltV29ybGQgR21iSDEVMBMGA1UECwwMVmVyaWZpY2F0aW9uMREw +DwYDVQQDDAhKb2huIERvZTEoMCYGCSqGSIb3DQEJARYZam9obi5kb2VAc2ltd29y +bGQtZ21iaC5kZTAgFw0yMjA5MDUxNTI1MTlaGA8yMTIyMDgxMjE1MjUxOVowgZgx +CzAJBgNVBAYTAkRFMQwwCgYDVQQIDANOUlcxDzANBgNVBAcMBkFhY2hlbjEWMBQG +A1UECgwNU2ltV29ybGQgR21iSDEVMBMGA1UECwwMVmVyaWZpY2F0aW9uMREwDwYD +VQQDDAhKb2huIERvZTEoMCYGCSqGSIb3DQEJARYZam9obi5kb2VAc2ltd29ybGQt +Z21iaC5kZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDuPagV1GqJq158onfaI +XttPSiBIYKh+IVFtFUP+v32+xVKdemmBBnpp99RwyaKVrqKn8UUaZA00xOjE+Uy1 +QZOjUzBRMB0GA1UdDgQWBBSRP6g9BKGiY5wNoaydkRC+0w3j8jAfBgNVHSMEGDAW +gBSRP6g9BKGiY5wNoaydkRC+0w3j8jAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49 +BAMCA0gAMEUCIQCTtUO6AXBMi4yYf5Lz0ItJoFZ7eT5JtLiepL0zLTWbeAIgb8YW +H4vxjjJSQ7+On1zfJqvE+7KGcxJb7IqUN02y/8M= +-----END CERTIFICATE----- diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/jwk/private.jwk b/processing_functions/node_functions/util/veri-sign/test/test_data/jwk/private.jwk new file mode 100644 index 0000000..db14cfc --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/test_data/jwk/private.jwk @@ -0,0 +1,7 @@ +{ + "kty": "EC", + "crv": "P-256", + "d": "HOVASeP1KfX-U2g5vbnS7X-K2kY6W7UW9r0Vs074r2Y", + "x": "O49qBXUaomrXnyid9ohe209KIEhgqH4hUW0VQ_6_fb4", + "y": "xVKdemmBBnpp99RwyaKVrqKn8UUaZA00xOjE-Uy1QZM" +} \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/pem/cert.pem b/processing_functions/node_functions/util/veri-sign/test/test_data/pem/cert.pem new file mode 100644 index 0000000..8bf7d94 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/test_data/pem/cert.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIUdBgY79Rx8iKuwNuP6KXS6/xUoJ8wDQYJKoZIhvcNAQEL +BQAwgZUxCzAJBgNVBAYTAkRFMRAwDgYDVQQIDAdCYXZhcmlhMQ8wDQYDVQQHDAZN +dW5pY2gxFTATBgNVBAoMDFJvYm9TaW0gR21iSDERMA8GA1UECwwITW9kZWxpbmcx +ETAPBgNVBAMMCEpvaG4gRG9lMSYwJAYJKoZIhvcNAQkBFhdqb2huLmRvZUByb2Jv +c2ltZ21iaC5kZTAgFw0yMjA5MDUwNzUyNTRaGA8yMTIyMDgxMjA3NTI1NFowgZUx +CzAJBgNVBAYTAkRFMRAwDgYDVQQIDAdCYXZhcmlhMQ8wDQYDVQQHDAZNdW5pY2gx +FTATBgNVBAoMDFJvYm9TaW0gR21iSDERMA8GA1UECwwITW9kZWxpbmcxETAPBgNV +BAMMCEpvaG4gRG9lMSYwJAYJKoZIhvcNAQkBFhdqb2huLmRvZUByb2Jvc2ltZ21i +aC5kZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANfjULqUowB/EwTd +8h+mH3lmspHFbM9KhrsSu/uypC5PeOCs+HIIDphROWrp1elwB11Jon7aUebgvmZQ +VI7dlMvLHxlvdoGMLc325dsYE5dmv49R6Tq5QV3GARAxvxtiogYwP7R6WoMelIe2 +YrsmB+AJPbluMpb6QSCuBtXdCx/NaKBpAm7XqzODOcc2gjmYxO7BoylWviyeoArD +eOIPEtP/XAmsB32KBPT1lmh44padmHhU8GkKz0SgJOdqvCodaZb9zYWuFur/0Vkb +wyFqHigNz+k0UEIA5RyF/hNrDI2gWTqY5FfyeK6R0T3jB6tYmP58kMtt/+1+sBTx +al/YAyECAwEAAaNTMFEwHQYDVR0OBBYEFAFV1fB99c/i2QL27caWVUUaLULiMB8G +A1UdIwQYMBaAFAFV1fB99c/i2QL27caWVUUaLULiMA8GA1UdEwEB/wQFMAMBAf8w +DQYJKoZIhvcNAQELBQADggEBACS/4anqg6825QyWl2gJbUAt0V28xrkV4fy/KpWC +fM+1QjSBakfLn0NuyH7NVC7/ZBMLcHiOpobVMBKI/jhi+flp7v7SzD1d7CkvXu0z +XvRFuX18JqPxhuyo5KyddKiLS1G9LeoQbjBPQ5E70QyORuxYRT0PSc7xIVavQPZO +1Joj1dimtS8X0WwuW6/owEdoowg5fLuIFOrYse8kqIlxXTU2O5ahWKSIvez7quQ9 +dPcjLqX0NndfpzCLFlYCzB+EA61/rvxqKcck1ZHAKCvPCqDYQiYs/v6p/xwRTRne +ltREsqyrgXMibih7xDCXyZyInzl33U9pFd8hi0gi81MC7jU= +-----END CERTIFICATE----- diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/pem/private.pem b/processing_functions/node_functions/util/veri-sign/test/test_data/pem/private.pem new file mode 100644 index 0000000..069b452 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/test_data/pem/private.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDX41C6lKMAfxME +3fIfph95ZrKRxWzPSoa7Erv7sqQuT3jgrPhyCA6YUTlq6dXpcAddSaJ+2lHm4L5m +UFSO3ZTLyx8Zb3aBjC3N9uXbGBOXZr+PUek6uUFdxgEQMb8bYqIGMD+0elqDHpSH +tmK7JgfgCT25bjKW+kEgrgbV3QsfzWigaQJu16szgznHNoI5mMTuwaMpVr4snqAK +w3jiDxLT/1wJrAd9igT09ZZoeOKWnZh4VPBpCs9EoCTnarwqHWmW/c2Frhbq/9FZ +G8Mhah4oDc/pNFBCAOUchf4TawyNoFk6mORX8niukdE94werWJj+fJDLbf/tfrAU +8Wpf2AMhAgMBAAECggEAQR5X42+iGyw5WGZ5+gHpzHylsEKUp5IbQaJNAYbI7w8Z +rut9pSe4/0jAwPb3hwo7rm/fi9PekH2J8OKaHt3HXBFQuCNe44Sc7FtkyclhX3Gm +plxWtVfb16U5/OCEfIYrySIUlSOy2YoH00e4UCsQhQtEiwIqUE/8BRd5CFk8WcLV +Uoba+pWMf4B6CkRNHSxCeQs58m6fWQ/nEr/psP3nJNHrfIYB+zXdh9Uq4HgZjUEe +gZRbxw4MKv37KWdkelCIq9zEDJJZ+JPjAoTWJWkYUAMIQM/9AO5FDrQi4U3iCUGq +GW71/nLvf7m+Y7D9iImpbGdA6FBDuYYmvFMEzQeqAQKBgQD/mRjMjqGRyIISFW0K +fEKB2T2f0f26Dsy29wq5JVpJiEk8YPhgt+ULLjGi6PiziJKz/G54RnQsUx0cnYJJ +SBPPNzRn2X2ZYOODbLfMA7A/MxtDWpl7gGQ8DNOdvPtDCCBrpqNFCvgsy6E7/Sga +StIWXFEE/BFz+Bh90ENaLhGVKQKBgQDYOjs2KwFGNk91QbYaKwiQt3mdOF0Ik1Bo +DvgYgxveSqlgbiW8gzzYp1bSyl3DCE9TGIJSEa19oiADUuNmUzWYWUsBDtbKRT49 +Q6ALtCppdURJlJxPTig88LRTAZOxmcH7hiqsfd2gFIyHMjvlRr0vxvChrPNWryIF +5QhjsmQFOQKBgBnSgMPhwQb2Z9Wl3n9JsHpn1Sb5UxXh+uKdMLMlx+pX33Y/Ibi7 +u2MWxqL7wGx65r9SSGLUU1ZLC0zhyNDYfE/Z9uxq95pwRGv5ptPwp3SrGB0E4S6t ++PJsJVDxT70k7AmUKziFdC8qX16tI8kMOA9UQkcrpCPAM9E6IiPRNauJAoGBAM6V +Y/juwXrpbKTksSzzLG3HPaPeM9R7mniwxIv2x2tr/cMpuVgQMtPznNoricVKbS9A +3VG3nf/II7nJOYWLvkreFPDf0orR+RLjR0enTFYRObrrByQl0f/XIRGlEgy+kDzV +BHRklJfayiBo+Bynpxy8fy6YZKzPCjyDg65sw27RAoGAPBzhE1BSTmhodVvpPS+b +SUBqp+2JjN27k62f1yYqWAOh38VRgOuIPGHrocOh5D3FiBSWQek5dGxVRU+g+2sK ++VuHyq4upQ3ivUxPXiPp7ouL5pmBIPHusLrbiMGf4ytHosIMhiNI9NyGgyABFPuL +IOj3A7f9/ALJTtHUU/AC4zg= +-----END PRIVATE KEY----- diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/pem/signed_statement_example.json b/processing_functions/node_functions/util/veri-sign/test/test_data/pem/signed_statement_example.json new file mode 100644 index 0000000..d9ef2f9 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/test_data/pem/signed_statement_example.json @@ -0,0 +1 @@ +{"content":{"result":true,"log":"bla","status_code":400},"signature":"4a7c5873cfc49b61e938d4a6ed97c98fb8960b3c5f924b916987bac699cd5a144f1436570dd1601769a274dd947faf621472a6f1b042585a7f43f073199b31a7a2fe9fbdfb6b879352f3d935b9836479c76f88fe7f1a7674b197b6f0f3b35bf6044b92eb28c7e41a3ad27bd6bd596152479a806c2c20c7729c0fcbc68258010a90e6b00c5b666f73bce88e355a9475028c4b8ed37538137dd5a8d14dca5cfde9155b496f31207f2c1207114c92643b7df6d43fe24c19d6b82dff2083ea993eee3bc23ea34f103014b362bbab76799c7780c1ac11ea51c9d11ddc055ea88d185822f89943ec1725b0d60633a134c97cf3e1a22c6bfb7490478a0155bebe1fceac","hash_algorithm":"SHA256","signature_encoding":"hex"} \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/pem_encrypted/cert.pem b/processing_functions/node_functions/util/veri-sign/test/test_data/pem_encrypted/cert.pem new file mode 100644 index 0000000..ebc4513 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/test_data/pem_encrypted/cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEJzCCAw+gAwIBAgIUe+jKmJu7BhEJbfmpXGEZX9pZl94wDQYJKoZIhvcNAQEL +BQAwgaExCzAJBgNVBAYTAkRFMQwwCgYDVQQIDANOUlcxEDAOBgNVBAcMB0NvbG9n +bmUxHjAcBgNVBAoMFVRyYWZmaWMgTW9kZWxpbmcgR21iSDEQMA4GA1UECwwHVmVo +aWNsZTERMA8GA1UEAwwISm9obiBEb2UxLTArBgkqhkiG9w0BCQEWHmpvaG5kb2VA +dHJhZmZpY21vZGVsaW5nZ21iaC5kZTAgFw0yMjA5MDUwODAxMTBaGA8yMTIyMDgx +MjA4MDExMFowgaExCzAJBgNVBAYTAkRFMQwwCgYDVQQIDANOUlcxEDAOBgNVBAcM +B0NvbG9nbmUxHjAcBgNVBAoMFVRyYWZmaWMgTW9kZWxpbmcgR21iSDEQMA4GA1UE +CwwHVmVoaWNsZTERMA8GA1UEAwwISm9obiBEb2UxLTArBgkqhkiG9w0BCQEWHmpv +aG5kb2VAdHJhZmZpY21vZGVsaW5nZ21iaC5kZTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMPheOrpdcPPbD2LApKkw9SnryXPRhH5x0kBYawrlm4ZJ8Uy +/dwvPf86qT8QgwAUY2wl07HwdLoPqRzT9GEDfOpzROuKcLODSjxaK6Hq04uxds75 +fJBB5A5Apgnoeipi3Xz3GEeGPqgkyKWieWNJG0kuokZHR8jbVKpK7sNHJ+CL4K4Z +NveNC1nWAO0NKo10M475YbXdyGP2NpUuaL8lFZVKlJz2aQ/sGFvUALWkqMiFY5vS +sEuCmG22x4GMvET+0wg8tUCwW9jJJOc082ZCdcne6hvwhITtwXl99tCySPrqbYIQ +lSGx92YbqtG16msMkcoxOj3gAU4FDDWXj7dfKlECAwEAAaNTMFEwHQYDVR0OBBYE +FAkVNbiavW0A4FYqosBefWntBJ9CMB8GA1UdIwQYMBaAFAkVNbiavW0A4FYqosBe +fWntBJ9CMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAKlCCNKP +gTNKcK2s+LTtt0YL6z7GhqEhqzcZfytZsPVArE9KECyvBMbsK8/q/aF2YHXpEwmJ +yhgiR20iWtf49BLwljI6s1VUj986WbMdlDms2cZvXb2uh2Xgocvs7QnHtB5GMdSl +wxYZSQKBJqvAioPOsBCgUhqQhzuJ1ckw6AoXFSSsAP/tG3CCFkoKpOHukWTdcGYW +AP68YzwHqwGySwMJmX+2dxOxmv2tnERfLDVK5NZx35TZxAbMdvYjJ2C3j0zoxVww +QOXwqPWgG1IX0XP31we02vYbO7bXcv6w1Up7vYanjo4WvMYj4lXEotGvlmaS0dG8 +5GSgsG/sOUoKoZY= +-----END CERTIFICATE----- diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/pem_encrypted/passphrase b/processing_functions/node_functions/util/veri-sign/test/test_data/pem_encrypted/passphrase new file mode 100644 index 0000000..58d636f --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/test_data/pem_encrypted/passphrase @@ -0,0 +1 @@ +ThisIsMyPassphrase \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/pem_encrypted/private.pem b/processing_functions/node_functions/util/veri-sign/test/test_data/pem_encrypted/private.pem new file mode 100644 index 0000000..737ca80 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/test_data/pem_encrypted/private.pem @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIyQ7UZyBKFRkCAggA +MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECENGuHWPm4sIBIIEyMKWWfxFn+2A +zV9peRypI/6VX09vUJcM4533JD1KhwMn9rFcC6zVOIssXg2EHTXaziJzlbbV81bE +fsItTX7nsnHPSiLnCOtJFS6NqrwisrdioyymOVn/WywmDFwG5pI6FbUBqpn6tYvI +wwoCilpnYHgUjkXiRuN8D/K91TEg8LmaLBqtxNMpWOLBFYvXb+XtD6UQlKIYXlRh +7biwGmIgGH/JcLh0X2bUst0hkBXvYKHBQoXf5+XtfNdejfgIe/yaxU5J8FqnQ0u3 +HN7yZ7kZowar3ZYTUWKu5cZYAUFVPEgWlvshxYbd4SbMXpqIQG7Fnjdt4EWcGXz2 +Bq6fnArPRFROoW7JqUJNlur0rDraI4NFUMn3uww4ErFyYB3TsElClrF3FMfNWogl +ZKXBzr9WtDgYkeMmB+DFjl9Rl7uGVnEtNloVkmeOxFc5yyZvgB8jGBC7UlPsMjAh +zdrlRzgQIrGn5okTarm1ey/aCVMpcoSDBZuut8Kd0CGRgjZN+GYSDBMEGKjJV2Q7 +fNPADZUIYt07d0cKWqDxQgx72BaKYfE/qCnfFuaNwif8tR9xCptUmEFMfDPaLmpp +/NPzn5hXt6bHE/q0/szFcU/K0hvaf3S2p2aytvX8zFKEvskYYd+0GvmHKECzt9KP ++J4OGNRNqfTE8zJfzFDz6pevtNu8DannT7fismtQzshO72BLA51SYvsBjgkG866X +YZeztqDtXJV5W27qqG0zWAkXTEd/AiqQuJCtY9UjYVEiWs7+TNJYcqIQxB/9pP2r +k3ZwxJ7u0IemngWTXtmw5mwol/SuHvY7Gn/EkyiIljHCBxxWq8sO8LPtSTI1OvVv +hMArtxmsUZzJ2d/msLrCWG+vqo7CP1bDoiF5vsrBFY0IR7BfhAlP7q/e8jS2yApl +92Dnl7aPPxBySEcW1YYaQ8Hdcze9XZhPlHWUt+83g+bTA1LfRV71iTu1YUWJJMr4 ++s0QX99kW3bObKQ5633uZoJkw0XlLgip/2iuSf/DYFrE0GZUArnGbtmJaiiTO+IN +kcneD160JQyaNDGRLRam5yyNAefI0YeNy1BWYOMPtTVASDUrsAgMrJeibkKClYAT +bww05hY5JxszzuC9CO/RbvAI8DpRR72CDOp3GaKNphGejHwBWd9O8++ySEVpdQ8z +nFiGAzLQi66eY1SPUeHPLv0VTIi37zTpA6eHt7VnfdzbFHSHAtgcOyzQKKslFGqV +vCGuLKe7ln4LDwJLZVJyKxWb11LiztxchgjKRmxLiy0fh9DvV2cS09S7VEsTleeu +/O/eqSrkLAwODoYJPuqqUdWnU1HND2Mx87yT1FOZxXrOUPiTbFXWzNmu9PtHCmpY +ZS6T59iye9Dn9l1q7jrEigMTVokHllp6pr9yb8QEzsRoHUCsDmWtplKlb+6JHa3Q +Pv00EU1PPxDSNs+kSlVPJN/Q+MBVT0J/kuISalsXi0r74R81zAsJo5vKhwPY8b3+ +apAYaPAUv74Bq/L5zZlyyWKk516BO0tZ9nc6GTIdPyipC3P9Tx2qqq4DuTqYZ+K0 +lZbMEsKcHAgEw1VhRWBEky9Nu44zkiQ9D3JeL8BX057ppljxikoKpr6tPO7ZbKfC +kGjPoenKRWqOARS2dPTXGA== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/pem_encrypted/signed_statement_example.json b/processing_functions/node_functions/util/veri-sign/test/test_data/pem_encrypted/signed_statement_example.json new file mode 100644 index 0000000..246589a --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/test_data/pem_encrypted/signed_statement_example.json @@ -0,0 +1 @@ +{"content":{"result":false,"log":"not good :(","status_code":300},"signature":"a161287b90bedf4f873d3b419e2158d194312ab4c61abae48de95dbe26da51bf9ca738caaa76aecf507377111a9e97813b6785a744d2e46905c31eecaf5ec0d569014b34ad20a2573ee85b7402e4c4004c923b739b601fa28a08a4ec08ebea670f0df219073a0d1caa31bb8b7285215dc117ded40a4ad6b6fd832cd710269cdedcbc796c681d670bc9cc9559c518a8a7d337461e09c530d6386daff36a3e3db4aef9e4bd64aebbbdd899ceb2aea8e1fe090057cc983096a7fa3e5236906982592f23e620e67f5c9eaf3cabf54c04d728da475bf98a4b5ef4bbc6c60f84ad7b89aec150f6f7a2d0b24e0a007ca9111885716c3f064a1d1c672d698cf482afe6fd","hash_algorithm":"SHA256","signature_encoding":"hex"} \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/pem_expired/cert.pem b/processing_functions/node_functions/util/veri-sign/test/test_data/pem_expired/cert.pem new file mode 100644 index 0000000..6b7e0e7 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/test_data/pem_expired/cert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEJTCCAw2gAwIBAgIUVaAGmS8al7x9alHAF/9nhEKLDP0wDQYJKoZIhvcNAQEL +BQAwgaExCzAJBgNVBAYTAkRFMRAwDgYDVQQIDAdCYXZhcmlhMQ8wDQYDVQQHDAZN +dW5pY2gxHjAcBgNVBAoMFUFEQVMgU2ltdWxhdGlvbiBDb21wLjEUMBIGA1UECwwL +SW50ZWdyYXRpb24xETAPBgNVBAMMCEpvaG4gRG9lMSYwJAYJKoZIhvcNAQkBFhdq +b2huLmRvZUBhZGFzc2ltY29tcC5kZTAeFw0yMjA5MDUwNzU2NDRaFw0yMjA5MDYw +NzU2NDRaMIGhMQswCQYDVQQGEwJERTEQMA4GA1UECAwHQmF2YXJpYTEPMA0GA1UE +BwwGTXVuaWNoMR4wHAYDVQQKDBVBREFTIFNpbXVsYXRpb24gQ29tcC4xFDASBgNV +BAsMC0ludGVncmF0aW9uMREwDwYDVQQDDAhKb2huIERvZTEmMCQGCSqGSIb3DQEJ +ARYXam9obi5kb2VAYWRhc3NpbWNvbXAuZGUwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDp0G/R2O3MqbkHUj77AS0S5qZLmQoxh20W9akWdro3DH/8FN2z +W72Qk4ENZ49vTwAYKuvBvlGSViSKQiWTGLysYp+Ja2DCZpcjoOk6YQkzrxRSNniZ +SuvBGo2NLxzFuhML/AhU7BQSF2N1ZU57XYnEHATh5j/cJJhHaPiJV4FIR+e9d+ym +QAgjcuHcFATVnec1YojjwF8mxHiAaJjETv4FeBTwcNQxjFvfmPV16czvBzqHs3u+ +y2vBU6riaEB2iU0SkJFplnYooCf9Bwjh5cmaGmh3PPAGTxzpDX27DsZEaFJUMrgt +C10XWHDMkRiJgpkpHKfKgx32cYxyqRlkBSPzAgMBAAGjUzBRMB0GA1UdDgQWBBQz +1yhBtzESRNMPKap22MCsQmWDwTAfBgNVHSMEGDAWgBQz1yhBtzESRNMPKap22MCs +QmWDwTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAMgcniEFxi +fYbBeXqrbwbQHSPxv2wZ01Wn2tfdTT2LMADlq8tWsKeXvwJeEDQoC6PTm3+f/XaR +uR0L9T+snS8QRC7X2oE3nJisFCWldZbzSlEwa7Jy+va/Ca68ySHr2t3Sh87Xp4KJ +Wmj7a3WVNFP25MXQU8/czSXXSrRzX3P6EfDozjmA+aW1wzqt+shqLb7zZdop2sDB +Z4zh5ui9tl118TIAa3+CZ7XV1JsYC847L9n57JbWEu3O51aAzOMQDTOjFz/k9G8r +aDtOemUaEV5KkjYDCO2XNNtM5B8eDJXStf6DkQEzl2j+m9GIfsWddlvJI+rIDQJ+ +stEAxvDWkktb +-----END CERTIFICATE----- diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/pem_expired/private.pem b/processing_functions/node_functions/util/veri-sign/test/test_data/pem_expired/private.pem new file mode 100644 index 0000000..478c292 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/test_data/pem_expired/private.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDp0G/R2O3MqbkH +Uj77AS0S5qZLmQoxh20W9akWdro3DH/8FN2zW72Qk4ENZ49vTwAYKuvBvlGSViSK +QiWTGLysYp+Ja2DCZpcjoOk6YQkzrxRSNniZSuvBGo2NLxzFuhML/AhU7BQSF2N1 +ZU57XYnEHATh5j/cJJhHaPiJV4FIR+e9d+ymQAgjcuHcFATVnec1YojjwF8mxHiA +aJjETv4FeBTwcNQxjFvfmPV16czvBzqHs3u+y2vBU6riaEB2iU0SkJFplnYooCf9 +Bwjh5cmaGmh3PPAGTxzpDX27DsZEaFJUMrgtC10XWHDMkRiJgpkpHKfKgx32cYxy +qRlkBSPzAgMBAAECggEAUmx/Lsu5ML/YEUOmHhgRCquyjc3acRFbnI9nVxrtlEBc +0cGNUhASKGhHQzFxt/xyfRKMX5U27k5dvTuKQWuj/2UY9nma94zw1MeB0Gmqs8SR +dlXrd6aYq08v/UhRbUamNkBXs5AxlxsVnanj28tiqHZ0Jipj8rgX0nynuMrXMWIz +OAjbisv6uHp+zoyz8l8YnSTCbz+zkWs/t1v3y8D9trmwSBkn5Ig81RUFt0KusRJn +B3cRLe9C3lTIyyZNhakEvR67eQU0BQKT3hFJLh6iGg0d8FmkkJ+IMOpM8QNmI/j6 +FI1qmcylH9zz5zJ+1fXzt6JK8o2sPe+ENJ1rHlDJYQKBgQD7u37U/wgPyCDZt1UA +S5gGgy9egUNND1uFIyzcgSIJrHm5+tsbqCROxvTUNV2unyRGCXuKd/f6g9nBfHCE +m4JhjmLZ81o3NGYvXdFAkg73gcsxNNR5QvRvMCHfGIex6NDXSEnXcF9OOq1ljXJB +z///IOL2gFnftRe5Oo8/PQwfUQKBgQDtxy1o23KjJrp/oJ+9/3T5XtLd7VGMGda0 +X8Y7lYImUGx0RS8+33VmRWXsqNMO7GyT3q0JkNdEdL11fmZbcRxS3ZLHNd7YZhM+ +vscSQr5ikfDrEtqWDWf9HIU2Xz2+uJ0kcbFIo66hMIbg9eTf6qrAQ/kVwAJ+8DVx ++PSieV7mAwKBgHPzUiFz81epnBAMlReUukrNpEUIj2JwX3x8zk4nIPGJw2v583DS +yx6KH/2bMzKa1Q95kLEgP1Xh46xu0DkUlEKxXjD8GaLx/Nt2gEsYu9Ifs+NPbUsJ +nyOT7RYg62f0UDXJc1HLzepvtqzfMTDFSSpQEJrwVfVoKaxFH/RF/05xAoGBAMuv +L0r/kTgJWQEzSyOqzT1A6UmKnaxyBeYFlP49mHvRsNX43fvx19N/50vPYt0J0gjp +WzkyhfTyO8mGOZBjWGvzumRD1V1yM1Iwgr81xXkSo1n7kJuxaD7+S27EL1RNOO+b +JM6gyFr0AtZ6TVEy6IpCsADdtkWe52l2Zbk4Vv6ZAoGBAOWsVINjOXxoBTn6gw9h +gt35hmW5PD53k+O5hXWub3njCV8qE9I4+ACeMbPEQeY/sjHwkoSIkYAdNtiVFoOv +sCd8H8PwbDdwJRKlpCClbm/KjNi/wE0bx28EyWXgEoBGoS8dfpR2NybM86Iea3Ix +0VH/gmYyTlBiTbh/u2BP6Y63 +-----END PRIVATE KEY----- diff --git a/processing_functions/node_functions/util/veri-sign/test/test_data/pem_expired/signed_statement_example.json b/processing_functions/node_functions/util/veri-sign/test/test_data/pem_expired/signed_statement_example.json new file mode 100644 index 0000000..6c3744c --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/test/test_data/pem_expired/signed_statement_example.json @@ -0,0 +1 @@ +{"content":{"result":true,"log":"awesome!","status_code":200},"signature":"501a5e7f55793f677469979ddbeb854c196e86698e5c83242bd22010898da1d05ad86b2a7ef3a8a6f218d289a6764603a239655db1e7e8e0e81098a238c861c75480a3df84b4a4a261d9db0ea1710bb098838f7c839fa6869eadddbfbb1f2c978cfcbd134b0c5c9502039309fe35a9d54e5f4c2fc97806fcd82dec00b308d64a4759028d17c8a7e74ea1a353e46d8d4634da41fbc5229e65f721396442517293bec541e8858b4470c21f88dc00243a9e3a60e460d95d2cd8ad8af6aa9c010cb006c245e44916963987b0e38f42c93783478a1a1721082841118b347e834170efb9478ef58a77d1b5b1b528b71015dd7ffc9a1f6c9dd25daf3fc4449b0b9d6b54","hash_algorithm":"SHA256","signature_encoding":"hex"} \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/types/schemas.js b/processing_functions/node_functions/util/veri-sign/types/schemas.js new file mode 100644 index 0000000..20fc141 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/types/schemas.js @@ -0,0 +1,106 @@ +/** + * JSON schema for an expert statement + * + * @author localhorst87 + * @readonly + * @constant {Object} +*/ +const EXPERT_STATEMENT = { + "type": "object", + "properties": { + "result": { + "type": "boolean" + }, + "log": { + "type": "string", + "transform": ["trim"], + "minLength": 1 + } + } +}; + +/** + * JSON schema for a signed document + * + * @author localhorst87 + * @readonly + * @constant {Object} +*/ +const SIGNED_STATEMENT = { + "type": "object", + "properties": { + "content": EXPERT_STATEMENT, + "signature": { + "type": "string", + "minLength": 32 + }, + "hash_algorithm": { + "type": "string", + "transform": ["trim", "toEnumCase"], + "enum": [ + "RSA-MD4", + "RSA-MD5", + "RSA-RIPEMD160", + "RSA-SHA1", + "RSA-SHA1-2", + "RSA-SHA224", + "RSA-SHA256", + "RSA-SHA3-224", + "RSA-SHA3-256", + "RSA-SHA3-384", + "RSA-SHA3-512", + "RSA-SHA384", + "RSA-SHA512", + "RSA-SHA512/224", + "RSA-SHA512/256", + "RSA-SM3", + "blake2b512", + "blake2s256", + "id-rsassa-pkcs1-v1_5-with-sha3-224", + "id-rsassa-pkcs1-v1_5-with-sha3-256", + "id-rsassa-pkcs1-v1_5-with-sha3-384", + "id-rsassa-pkcs1-v1_5-with-sha3-512", + "md4", + "md4WithRSAEncryption", + "md5", + "md5-sha1", + "md5WithRSAEncryption", + "ripemd", + "ripemd160", + "ripemd160WithRSA", + "rmd160", + "sha1", + "sha1WithRSAEncryption", + "sha224", + "sha224WithRSAEncryption", + "sha256", + "sha256WithRSAEncryption", + "sha3-224", + "sha3-256", + "sha3-384", + "sha3-512", + "sha384", + "sha384WithRSAEncryption", + "sha512", + "sha512-224", + "sha512-224WithRSAEncryption", + "sha512-256", + "sha512-256WithRSAEncryption", + "sha512WithRSAEncryption", + "shake128", + "shake256", + "sm3", + "sm3WithRSAEncryption", + "ssl3-md5", + "ssl3-sha1", + "whirlpool" ] + }, + "signature_encoding": { + "enum": ["hex"] + } + }, + "required": ["content", "signature", "hash_algorithm", "signature_encoding"] +}; + +exports.SIGNED_STATEMENT = SIGNED_STATEMENT; +exports.EXPERT_STATEMENT = EXPERT_STATEMENT; \ No newline at end of file diff --git a/processing_functions/node_functions/util/veri-sign/types/types.js b/processing_functions/node_functions/util/veri-sign/types/types.js new file mode 100644 index 0000000..28d8345 --- /dev/null +++ b/processing_functions/node_functions/util/veri-sign/types/types.js @@ -0,0 +1,52 @@ +/** + * ResultLog is a mixed result type that gives a boolean return value and additional logging information + * + * @typedef {object} ResultLog + * @property {boolean} result The result of any operation + * @property {string} log Additional logging information of any operation + */ + +/** + * KeyObject from node crypto module, for documentation, see https://nodejs.org/api/crypto.html#class-keyobject + * + * @typedef {object} KeyObject + */ + +/** + * SignedStatement structure + * + * @typedef {object} SignedStatement + * @property {ExpertStatement} content The expert statement in a formal structure + * @property {string} signature The signature that has been generated from the stringified ExpertStatement + * @property {string} hash_algorithm The hash algorithm that has been used to generate the signature + * @property {string} signature_encoding The encoding of the signature (currently, only "hex" is allowed) + */ + +/** + * Formal structure to describe an expert statement + * + * @typedef {object} ExpertStatement + * @property {boolean} result result of the experts check (passed or not passed) + * @property {string} log additional information regarding the check for human interpretation + * @property {number} status_code additional information regarding the check for machine interpretation + */ + +/** + * Additional information required to specify a KeyObject + * + * @typedef {object} KeySpec + * @property {string} format Format of the given key. Must be "pem", "der" or "jwk". + * @property {string} [type] Specifies the cryptography standard that has been used. Must be "pkcs1", "pkcs8" or "sec1". This option is required only if the format is "der" and ignored otherwise. + * @property {boolean} isEncrypted Indicates whether encryption is used + * @property {string} [passphrase] The passphrase to use for decryption of an encrypted key + */ + +module.exports = { + /** + * @type {ResultLog} + * @type {KeyObject} + * @type {KeySpec} + * @type {ExpertStatement} + * @type {SignedStatement} + */ +} \ No newline at end of file diff --git a/processing_functions/prerequisites/install_node_utilities.sh b/processing_functions/prerequisites/install_node_utilities.sh new file mode 100644 index 0000000..39fd552 --- /dev/null +++ b/processing_functions/prerequisites/install_node_utilities.sh @@ -0,0 +1,2 @@ +npm install ./processing_functions/node_functions/util/veri-sign --prefix ./processing_functions/node_functions/util/veri-sign +npm install ./processing_functions/node_functions/util/util-common --prefix ./processing_functions/node_functions/util/util-common \ No newline at end of file diff --git a/processing_functions/python_functions/requirements.txt b/processing_functions/python_functions/requirements.txt new file mode 100644 index 0000000..296d654 --- /dev/null +++ b/processing_functions/python_functions/requirements.txt @@ -0,0 +1 @@ +numpy \ No newline at end of file diff --git a/processing_functions/python_functions/utilities.py b/processing_functions/python_functions/utilities.py new file mode 100644 index 0000000..0db9e24 --- /dev/null +++ b/processing_functions/python_functions/utilities.py @@ -0,0 +1,9 @@ +def join_sort_unique(array1, array2): + import numpy as np + import json + + arr1 = np.array(json.loads(array1)) + arr2 = np.array(json.loads(array2)) + arr12 = np.concatenate((arr1, arr2)) + + return np.array_str(np.unique(np.sort(arr12))) \ No newline at end of file diff --git a/quality_metrics/node_metrics/level_1/README.md b/quality_metrics/node_metrics/level_1/README.md new file mode 100644 index 0000000..81f36a8 --- /dev/null +++ b/quality_metrics/node_metrics/level_1/README.md @@ -0,0 +1,324 @@ +# REQUIREMENTS, LEVEL 1 + +A collection of metrics that can be used for assessing the credibility of simulation model implementation, according to Credibility Level 1: + +* [`checkSingleSemantic`](#checksinglesemantic): Checks if a single requirement fulfills quality aspects in a way that specifications can be built upon it and validation results can refer to it +* [`checkCollectionSemantic`](#checkcollectionsemantic): Checks if the collection of requirements fulfills quality aspects in a way that specifications can be built upon it and validation results can refer to it +--- +## `checkSingleSemantic` + +### I. METADATA +--------------- + +Metric properties || +------------------------|--------------- +Domains | Domain-independent +Model types | Model-independent +CSP phase | Requirements +CSP step | Models, Parameters, Environment, Test Cases, Integration +Level | 1 +Purpose | Checks if a single requirement fulfills quality aspects in a way that specifications can be built upon it and validation results can refer to it +Implements | Semantic Check +Acceptance criteria | Expert Statement + +### II. USAGE +----------------------------- + +This metric is to be considered as implicite metric, i.e. both the quality metric and the quality criterion is evaluated by an expert, who's giving a statement about the metric, the criterion and its results. Consider the following example: + +*Preparation step 1, expert statement:* + +A qualified expert performs semantical checks of a single requirement (*metric*) checks the following *criterions* of the requirement: + +* **necessary**: Every requirement generates extra effort in the form of processing, maintenance, and verification. Only necessary requirements should therefore be included. Unnecessary requirements are of two varieties: (i) unnecessary specification of environment, which should be left to the discretion of the engineer in the specification or implementation phase, and (ii) a redundant environment requirement covered in some other combination of model requirements +* **unambiguous**: Is the requirement clear and concise? Is it possible to interpret the requirement in multiple ways? Are the terms defined or referenced to the tool specification? Does the requirement conflict with or contradict another requirement? Each requirement statement should be written to address only one concept. Requirements with "and", "or", "commas", or other forms of redundancy can be difficult to verify and should be avoided as it can be difficult to ensure all personnel have a common understanding. The language used must be clear, exact, and in sufficient detail to meet all reasonable interpretations. +* **complete**: The stated requirement should be complete and measurable and not need further amplification. The stated requirement should provide sufficient capability of characteristics. +* **singular**: The requirement statement should be of only one requirement and should not be a combination of requirements or more than one function or constraint. +* **achievable**: The requirement must be technically achievable within the constraints of technical feasibility and costs. +* **verifiable**: Each requirement should be verifiable by a single method in the evaluation phase. A requirement requiring multiple methods to verify should be broken into multiple requirements. (There is no problem with one method verifying multiple requirements; however, it indicates a potential for consolidating requirements) + +The result of the first step is the expert statement. The content of the expert statement itself must contain the reduced result of the check (true/false) and the verbal statement, according to the following approach: + +```javascript +{ + result: true, + log: "The requirement with ID M_08 is necessary, unambiguous, complete, singular, achievable, and verifiable." +} +``` + +*Preparation step 2, sign expert statment:* + +The expert check must be wrapped into the content of the signedStatement object, as can be observed below. This object must be stringified to be used as input of `checkSingleSemantic`. For ease of use, the structure can be generated, using the [sign function of the veri-sign util](https://github.com/virtual-vehicle/Credibility-Assessment-Framework/tree/main/Credibility-Development-Kit/util/veri-sign). + +*Evaluate signed statment:* + +The `checkSingleSemantic` function takes the signed expert statement and the X509 certificate as inputs to check if the statement has been signed by the holder of the X509 certificate. + +The X509 certificate to be passed must be PEM- or DER-encoded. If PEM is used, a string is expected, if DER is used, a Buffer is expected. + +```javascript +signedStatement = '{"content":{"result":true,"log":"The requirement with ID M_08 is necessary, unambiguous, complete, singular, achievable, and verifiable."},"signature":"09f41fd02ab1a891cf92b7228d...997d93efa9b1b52712372","hash_algorithm":"SHA256","signature_encoding":"hex"}'; +x509Pem = `-----BEGIN CERTIFICATE----- +MIIENzCCAx+gAwIBAgIUIUdYlY0Da5/4K/kAvFGkcpO1j9kwDQYJKoZIhvcNAQEL +... +nsA7g4QT5oCHJnRtfLXMgMdW+/Q76CqJgYsT +-----END CERTIFICATE-----`; + +checkSingleSemantic(signedStatement, x509Pem); +``` + +The function will check... + +* ...the type and schema validity of the inputs +* ...the result of the expert statment itself +* ...the validity of the X509 certificate +* ...the validity of the signature + +The output will be a ResultLog, indicating the result and additional logging information, as can be observed in the example below. + +```javascript +{ + result: false, + log: "The expert statement could not be proven valid (X509 certificate expired)" +} +``` + +### III. INPUTS +--------------------------- + +Input for the expert || +------------------------|--------------- +Entity kind | Requirement +Entity type | Any requirement representation (text, images, etc.) +Must contain | At least a unique ID of the requirement and the requirement description + +### IV. OUTPUTS +----------------------------- + +|| Req. okay +------------------------|--------------- +Event | Requirement description fulfills semantic criteria +Result | true +Log | The requirement (requirement ID) is necessary, unambiguous, complete, singular, achievable, and verifiable. + +|| Req. not necessary +------------------------|--------------- +Event | Requirement is not necessary +Result | false +Log | The requirement description (requirement ID) is violating the necessity criterion: The requirement is (unnecessary/redundant), (explanation) + +|| Req. not unambiguous +------------------------|--------------- +Event | Requirement not unambiguous +Result | false +Log | The requirement description (requirement ID) is violating the unambiguity criterion: (explanation) + +|| Req. not complete +------------------------|--------------- +Event | Requirement is not complete +Result | false +Log | The requirement description (requirement ID) is violating the completeness criterion: (explanation) + +|| Req. not singular +------------------------|--------------- +Event | Requirement is not singular +Result | false +Log | The requirement description (requirement ID) is violating the singularity criterion: (explanation) + +|| Req. not achievable +------------------------|--------------- +Event | Requirement is not achievable +Result | false +Log | The requirement description (requirement ID) is violating the achievability criterion: (explanation) + +|| Req. not verfiable +------------------------|--------------- +Event | Requirement is not verfiable +Result | false +Log | The requirement description (requirement ID) is violating the verifiability criterion: (explanation) + +### V. EXAMPLES +----------------------------- + +```javascript +const fs = require("fs"); +const verisign = require("../../../../util/veri-sign"); + +// A. Preparation steps + +// Step 1 (expert makes statment) +expertStatement = { + result: true, + log: "The requirement with ID M_08 is necessary, unambiguous, complete, singular, achievable, and verifiable." +}; + +// Step 2 (expert signs statement) +privateKey = fs.readFileSync("./examples/keystore/pem_encrypted/private.pem", "utf8"); +keySpec = { + format: "pem", + isEncrypted: true, + passphrase: "ThisIsMyPassphrase" +}; + +signedStatement = verisign.sign(expertStatement, privateKey, keySpec); + +// B. Evaluation + +// read X509 certificate +x509Pem = fs.readFileSync("./examples/keystore/pem_encrypted/cert.pem", "utf8"); + +// evaluate +checkSingleSemantic(signedStatement, x509Pem); +``` + +## `checkCollectionSemantic` + +### I. METADATA +--------------- + +Metric properties || +------------------------|--------------- +Domains | Domain-independent +Model types | Model-independent +CSP phase | Requirements +CSP step | Models, Parameters, Environment, Test Cases, Integration +Level | 1 +Purpose | Checks if the collection of requirements fulfills quality aspects in a way that specifications can be built upon it and validation results can refer to it +Implements | Semantic Check +Acceptance criteria | Expert Statement + +### II. USAGE +----------------------------- + +*Preparation step 1, expert statement:* + +A qualified expert checks the following criterions of the collection of all requirement: + +* **complete**: The set of requirements contains everything pertinent to the definition of the simulation or model +* **consistent**: The set of requirements is consistent in that the requirements are not contradictory nor duplicated. Terms and abbreviations are used consistently in all requirements. +* **affordable**: The set of requirements can be satisfied by a solution that is obtainable within the planned cost, schedule, and technical constraints. +* **bounded**: The set of requirements defines the required scope for the solution to meet the stakeholder needs. Consequently, all necessary requirements must be included; irrelevant requirements must be excluded. + +The result of the first step is the expert statement. The content of the expert statement itself must contain the reduced result of the check (true/false) and the verbal statement, according to the following approach: + +```javascript +{ + result: false, + log: "The requirement collection is not affordable: The requirements towards the accuracy of the simulation results cannot be afforded with the given technical constraints." +} +``` + +*Preparation step 2, sign expert statment:* + +The expert check must be wrapped into the content of the signedStatement object, as can be observed below. This object must be stringified to be used as input of `checkSingleSemantic`. For ease of use, the structure can be generated, using the [sign function of the veri-sign util](https://github.com/virtual-vehicle/Credibility-Assessment-Framework/tree/main/Credibility-Development-Kit/util/veri-sign). + +*Evaluate signed statment:* + +The `checkSingleSemantic` function takes the signed expert statement and the X509 certificate as inputs to check if the statement has been signed by the holder of the X509 certificate. + +The X509 certificate to be passed must be PEM- or DER-encoded. If PEM is used, a string is expected, if DER is used, a Buffer is expected. + +```javascript +signedStatement = '{"content":{"result":false,"log":"The requirement collection is not affordable: The requirements towards the accuracy of the simulation results cannot be afforded with the given technical constraints."},"signature":"09f41fd02ab1a891cf92b7228d...997d93efa9b1b52712372","hash_algorithm":"SHA256","signature_encoding":"hex"}'; +x509Pem = `-----BEGIN CERTIFICATE----- +MIIENzCCAx+gAwIBAgIUIUdYlY0Da5/4K/kAvFGkcpO1j9kwDQYJKoZIhvcNAQEL +... +nsA7g4QT5oCHJnRtfLXMgMdW+/Q76CqJgYsT +-----END CERTIFICATE-----`; + +checkSingleSemantic(signedStatement, x509Pem); +``` + +The function will check... + +* ...the type and schema validity of the inputs +* ...the result of the expert statment itself +* ...the validity of the X509 certificate +* ...the validity of the signature + +The output will be a ResultLog, indicating the result and additional logging information, as can be observed in the example below. + +```javascript +{ + result: false, + log: "The requirement collection is not affordable: The requirements towards the accuracy of the simulation results cannot be afforded with the given technical constraints." +} +``` + + +### III. INPUTS +--------------------------- + +Input for the expert || +------------------------|--------------- +Entity kind | Requirement collection +Entity type | Any representation requirements (text, images, etc.) +Must contain | The collection of all single requirements + +### IV. OUTPUTS +----------------------------- + +|| Collection okay +------------------------|--------------- +Event | Requirement collection fulfills semantic criteria +Result | true +Log | The environment requirement collection is complete, consistent, affordable, and bounded. + +|| Collection not complete +------------------------|--------------- +Event | Requirement collection not complete +Result | false +Log | The requirement collection is violating the completeness criterion: (explanation) + +|| Collection not consistent +------------------------|--------------- +Event | Requirement collection not consistent +Result | false +Log | The requirement collection is violating the consistency criterion: (explanation) + +|| Collection not affordable +------------------------|--------------- +Event | Requirement collection is not affordable +Result | false +Log | The requirement collection is violating the affordability criterion: (explanation) + +|| Collection not bounded +------------------------|--------------- +Event | Requirement collection is not bounded +Result | false +Log | The requirement collection is violating the boundary criterion: (explanation) + + +### V. EXAMPLES +----------------------------- + +```javascript +const fs = require("fs"); +const verisign = require("../../../../util/veri-sign"); + +// A. Preparation steps + +// Step 1 (expert makes statment) +expertStatement = { + result: false, + log: "The requirement collection is not affordable: The requirements towards the accuracy of the simulation results cannot be afforded with the given technical constraints." +}; + +// Step 2 (expert signs statement) +privateKey = fs.readFileSync("./examples/keystore/pem_encrypted/private.pem", "utf8"); +keySpec = { + format: "pem", + isEncrypted: true, + passphrase: "ThisIsMyPassphrase" +}; + +signedStatement = verisign.sign(expertStatement, privateKey, keySpec); + +// B. Evaluation + +// read X509 certificate +x509Pem = fs.readFileSync("./examples/keystore/pem_encrypted/cert.pem", "utf8"); + +// evaluate +checkSingleSemantic(signedStatement, x509Pem); +``` \ No newline at end of file diff --git a/quality_metrics/node_metrics/level_1/index.js b/quality_metrics/node_metrics/level_1/index.js new file mode 100644 index 0000000..816c327 --- /dev/null +++ b/quality_metrics/node_metrics/level_1/index.js @@ -0,0 +1,50 @@ +const verisign = require("veri-sign"); + +/** + * @module metrics/requirements/level_1 + */ + +/** + * @typedef {import('./types/types').ResultLog} ResultLog + */ + +/** + * Checks if a single requirement fulfills quality aspects in a way that specifications can be built upon it and + * validation results can refer to it + * + * @author localhorst87 + * @license BSD-2-Clause + * @kind function + * @version 1.0 + * @domain domain-independent + * @modeltypes model type-independent + * @level 1 + * @phase requirements + * @step [models, parameters, environment, test Cases, integration] + * @param {String} signedExpertStatement stringified JSON implement the {@link verisign.SIGNED_STATEMENT} schema + * @param {String|Buffer} x509Certificate PEM- or DER-encoded X509 certificate. If PEM is used, a string is expected, if DER is used, a Buffer is expected + * @returns {ResultLog} result and logging information + */ +const checkSingleSemantic = verisign.checkExpertStatement; + +/** + * Checks if the collection of requirements fulfills quality aspects in a way that specifications can be built upon it + * and validation results can refer to it + * + * @author localhorst87 + * @license BSD-2-Clause + * @kind function + * @version 1.0 + * @domain domain-independent + * @modeltypes model type-independent + * @level 1 + * @phase requirements + * @step [models, parameters, environment, test Cases, integration] + * @param {String} signedExpertStatement stringified JSON implement the {@link SIGNED_STATEMENT} schema + * @param {String|Buffer} x509Certificate PEM- or DER-encoded X509 certificate. If PEM is used, a string is expected, if DER is used, a Buffer is expected + * @returns {ResultLog} result and logging information + */ +const checkCollectionSemantic = verisign.checkExpertStatement; + +exports.checkSingleSemantic = checkSingleSemantic; +exports.checkCollectionSemantic = checkCollectionSemantic; \ No newline at end of file diff --git a/quality_metrics/node_metrics/level_1/package-lock.json b/quality_metrics/node_metrics/level_1/package-lock.json new file mode 100644 index 0000000..727b21d --- /dev/null +++ b/quality_metrics/node_metrics/level_1/package-lock.json @@ -0,0 +1,1791 @@ +{ + "name": "cdk_requirements_level1", + "version": "0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "cdk_requirements_level1", + "version": "0.1", + "license": "BSD-2-Clause", + "dependencies": { + "veri-sign": "../../../processing_functions/node_functions/util/veri-sign" + }, + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.0.0", + "prompt-sync": "^4.2.0" + } + }, + "../../../processing_functions/node_functions/util/veri-sign": { + "version": "0.1.0", + "license": "CC-BY-4.0", + "dependencies": { + "ajv": "^8.10.0", + "ajv-keywords": "^5.1.0", + "crypto": "^1.0.1", + "crypto-browserify": "^3.12.0", + "fs": "^0.0.1-security", + "util-common": "../util-common" + }, + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.0.0", + "prompt-sync": "^4.2.0" + } + }, + "../../../util/veri-sign": { + "version": "0.1.0", + "extraneous": true, + "license": "CC-BY-4.0", + "dependencies": { + "ajv": "^8.10.0", + "ajv-keywords": "^5.1.0", + "crypto": "^1.0.1", + "crypto-browserify": "^3.12.0", + "fs": "^0.0.1-security" + }, + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.0.0", + "prompt-sync": "^4.2.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", + "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prompt-sync": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz", + "integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==", + "dev": true, + "dependencies": { + "strip-ansi": "^5.0.0" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "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/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/veri-sign": { + "resolved": "../../../processing_functions/node_functions/util/veri-sign", + "link": true + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true + }, + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "requires": { + "fill-range": "^7.1.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "requires": { + "get-func-name": "^2.0.2" + } + }, + "chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, + "deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true + }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "requires": { + "get-func-name": "^2.0.1" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "mocha": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", + "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "prompt-sync": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz", + "integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==", + "dev": true, + "requires": { + "strip-ansi": "^5.0.0" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true + }, + "veri-sign": { + "version": "file:../../../processing_functions/node_functions/util/veri-sign", + "requires": { + "ajv": "^8.10.0", + "ajv-keywords": "^5.1.0", + "chai": "^4.3.6", + "crypto": "^1.0.1", + "crypto-browserify": "^3.12.0", + "fs": "^0.0.1-security", + "mocha": "^10.0.0", + "prompt-sync": "^4.2.0", + "util-common": "../util-common" + } + }, + "workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/quality_metrics/node_metrics/level_1/package.json b/quality_metrics/node_metrics/level_1/package.json new file mode 100644 index 0000000..ec9587d --- /dev/null +++ b/quality_metrics/node_metrics/level_1/package.json @@ -0,0 +1,19 @@ +{ + "name": "cdk_requirements_level1", + "version": "0.1", + "description": "level 1 quality metrics for requirements", + "main": "index.js", + "scripts": { + "test": "mocha --reporter spec" + }, + "author": "localhorst87", + "license": "BSD-2-Clause", + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^10.0.0", + "prompt-sync": "^4.2.0" + }, + "dependencies": { + "veri-sign": "../../../processing_functions/node_functions/util/veri-sign" + } + } \ No newline at end of file diff --git a/quality_metrics/node_metrics/level_1/test/cert_ahmann.crt b/quality_metrics/node_metrics/level_1/test/cert_ahmann.crt new file mode 100644 index 0000000..c708c2c --- /dev/null +++ b/quality_metrics/node_metrics/level_1/test/cert_ahmann.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIENzCCAx+gAwIBAgIURfMlTJ+E8y+XBS3oudOk0z75+jQwDQYJKoZIhvcNAQEL +BQAwgaoxCzAJBgNVBAYTAkRFMRAwDgYDVQQIDAdCYXZhcmlhMQ8wDQYDVQQHDAZN +dW5pY2gxHjAcBgNVBAoMFVNFVExhYnMgUmVzZWFyY2ggR21iSDETMBEGA1UECwwK +QXV0b21vdGl2ZTEYMBYGA1UEAwwPTWF1cml6aW8gQWhtYW5uMSkwJwYJKoZIhvcN +AQkBFhptYXVyaXppby5haG1hbm5Ac2V0bGFicy5kZTAeFw0yMzAyMjMxNTMwNTla +Fw0yNDEyMzExNTMwNTlaMIGqMQswCQYDVQQGEwJERTEQMA4GA1UECAwHQmF2YXJp +YTEPMA0GA1UEBwwGTXVuaWNoMR4wHAYDVQQKDBVTRVRMYWJzIFJlc2VhcmNoIEdt +YkgxEzARBgNVBAsMCkF1dG9tb3RpdmUxGDAWBgNVBAMMD01hdXJpemlvIEFobWFu +bjEpMCcGCSqGSIb3DQEJARYabWF1cml6aW8uYWhtYW5uQHNldGxhYnMuZGUwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDSmUihmubtTl5DHeuo1xntCESm +hfZESRXVIgtKwQnHwcr4Y0KHB7No6MxEZN88f5Kdq9qa7yU1CmjlmBQvvFhLoyZE +Fciv2JH1lKPPY0hpa1aUMulygQrMBoWhI7C4DDg5GB8D2FmkMHKWvTzXqLIkQLpT +esKN64wTT44ElTjZaNLKpkH79sDbGzG8pxdtBE5abTainSBbpCFpt9or8Kc2PL74 +smd24IfAcfCq+71arCQXFaW0cIAiXHWbSBvQGmAN5s+hcZ97P6VEQV5MMjRPESF4 +toH1RahYLUYT18VICzWVnbo2eAxYZgAsgepCD/sAtAYBLbzxzip45oVIzUpfAgMB +AAGjUzBRMB0GA1UdDgQWBBQQjy3fPlVqebstqr2+s7s7MhorcjAfBgNVHSMEGDAW +gBQQjy3fPlVqebstqr2+s7s7MhorcjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQAShChBMOU89sy+cIElFgtd0gg33HgIWB3dTNqKS1tyKW95y0Hk +iTOSx31DVj+dT/bcqtrqNUqiad+iVp4QZLR5TlWTugxT958VTvm+KhPln7ovk+En +lvjWwFK3/wPEH9d+rTR7c20jlSl0MIfLXVTU7/8AkkbOZYHriH7AAoulldmc1s4n +L0e1sAhhL2AQBk6RR0FLLJTrXgKtdj3UxzYCnC/WTT6fj37KY9bVyXxQU46luumW +UMTNLhOOx1SILybhfkpMPff7VBP+9cN/qmk3dgT0LcBo9G/KyHpHs4TGWlpsPZTN +fsV1NFOtAz3c8ye83Pl98RkKn4uZi0Xdwy/D +-----END CERTIFICATE----- diff --git a/quality_metrics/node_metrics/level_1/test/expertStatement.json b/quality_metrics/node_metrics/level_1/test/expertStatement.json new file mode 100644 index 0000000..c65edfc --- /dev/null +++ b/quality_metrics/node_metrics/level_1/test/expertStatement.json @@ -0,0 +1 @@ +{"content":{"result":true,"log":"The requirement with ID #req_e_01 is necessary, unambiguous, complete, singular, achievable, and verifiable."},"signature":"0cf34a7a92d26b85410b5e2c28b8ea7262147261225b932152c2ea117db6b9349898d0a02e22cfe856339970fe8e2f9735ae1353f75777f94f554abc052334d011f40a96ba4f64f0ab9ecc663a52ec1fecd2011ca44c840ff6a07795fa33cbb3d5e4b5366a63e1e631b986e9ea247707da7162a9f5ed4de18ca1f0ecff389383afe43b1f00510532823b5bfdbe57e406eb94f8e99b09f20f319c232337d11561575f286bb68bda7a285a51fecfa5f8e94507526065eccc285889da26b5d044bd6ed71273f990b37518c6e7e90a4cf4fcbe5b462e499224dfad1c713cf47eaf4e4acdf7e093437e7e58f03061a56c405d90725410fbaf345c503765b749287458","hash_algorithm":"SHA256","signature_encoding":"hex"} \ No newline at end of file diff --git a/quality_metrics/node_metrics/level_1/test/test.js b/quality_metrics/node_metrics/level_1/test/test.js new file mode 100644 index 0000000..02d571f --- /dev/null +++ b/quality_metrics/node_metrics/level_1/test/test.js @@ -0,0 +1,9 @@ +const metrics = require(".."); +const fs = require("fs"); + +const es = fs.readFileSync("./expertStatement.json", "utf-8"); +const x509 = fs.readFileSync("./cert_ahmann.crt"); + +console.log(x509) + +console.log(metrics.checkSingleSemantic(es, x509)); \ No newline at end of file diff --git a/quality_metrics/node_metrics/level_1/types/types.js b/quality_metrics/node_metrics/level_1/types/types.js new file mode 100644 index 0000000..959de38 --- /dev/null +++ b/quality_metrics/node_metrics/level_1/types/types.js @@ -0,0 +1,34 @@ +/** + * ResultLog is a mixed result type that gives a boolean return value and additional logging information + * + * @typedef {object} ResultLog + * @property {boolean} result The result of any operation + * @property {string} log Additional logging information of any operation + */ + +/** + * SignedStatement structure + * + * @typedef {object} SignedStatement + * @property {ExpertStatement} content The expert statement in a formal structure + * @property {string} signature The signature that has been generated from the stringified ExpertStatement + * @property {string} hash_algorithm The hash algorithm that has been used to generate the signature + * @property {string} signature_encoding The encoding of the signature (currently, only "hex" is allowed) + */ + +/** + * Formal structure to describe an expert statement + * + * @typedef {object} ExpertStatement + * @property {boolean} result result of the experts check (passed or not passed) + * @property {string} log additional information regarding the check for human interpretation + */ + + +module.exports = { + /** + * @type {ResultLog} + * @type {SignedStatement} + * @type {ExpertStatement} + */ +} \ No newline at end of file diff --git a/quality_metrics/python_metrics/fmu_metrics.py b/quality_metrics/python_metrics/fmu_metrics.py new file mode 100644 index 0000000..c97f87c --- /dev/null +++ b/quality_metrics/python_metrics/fmu_metrics.py @@ -0,0 +1,13 @@ +def check_model_description(fmu_path): + from fmpy.validation import validate_fmu + import json + + problems = validate_fmu(fmu_path) + + if len(problems) == 0: + reslog = { "result": True, "log": "The modelDescription of the FMU is error-free." } + else: + logs = " // next error: ".join(problems) + reslog = { "result": False, "log": logs} + + return json.dumps(reslog) \ No newline at end of file diff --git a/quality_metrics/python_metrics/requirements.txt b/quality_metrics/python_metrics/requirements.txt new file mode 100644 index 0000000..69d9b66 --- /dev/null +++ b/quality_metrics/python_metrics/requirements.txt @@ -0,0 +1 @@ +fmpy \ No newline at end of file diff --git a/schemas/CDK__updated__.xsd b/schemas/CDK__updated__.xsd new file mode 100644 index 0000000..30a8556 --- /dev/null +++ b/schemas/CDK__updated__.xsd @@ -0,0 +1,561 @@ + + + + + + + + + + + + + + + + + + This element is used to collect metrics that serve as evidence for fulfilling a specific credibility + level. + + + + + + + This element is used to indicate that a metric from the Credibility Development Kit is used + as supporting evidence for the corresponding credibility level. + + + + + + + + This attribute is used to indicate the credibility level of the metric. Must be 1,2 or 3. + + + + + + + + + + + + + + + + This element is used to define a function of the Credibility Development Kit to be used. + The VerificationType defines the context (subset and level) to localize the correct module. + + + + + + + This element is used to define one execution of the given function. + + + + + + + + This attribute is used to indicate the URI of the module that exports the metric. + + + + + + + This attributes gives information which interpreter to use for the module. Currently the + Javascript RTE Node.js and the Python interpreter is supported. + + + + + + + + + + + + + This attributes is indicating the version identifier of the according interpreter, + for instance "16.14.0" to be used for Node.js version 16.14.0 or "3.8.10" for Python + version 3.8.10. + The supported syntax fore Node.js versions can be looked up here: + https://github.com/actions/setup-node?tab=readme-ov-file#supported-version-syntax + + + + + + + This attribute indicates the name of the function from the target module. + + + + + + + + + This element is used to define one execution of a function. A TestType must contain FunctionArgument + elements as child elements that map + + + + + + + This element is used to define an argument of the function defined in a parent element. + + + + + + + This element can optionally be defined to write the result of the test to a file. + + + + + + + + This mandatory attribute specifies a unique identification of the test execution. + + + + + + + + + This element is used to map a file to a function argument of the function that has been + defined in a parent element. + + + + + + This mandatory attribute specifies the name of the argument as used in the target function. + + + + + + + This mandatory attribute specifies if the input content it fed inline via the "content" attribute or by means of a specified file source. + - "inline": The content of the attribute "content" will be used as an input to the function argument. + - "path": The path, specified in the "source" argument must be passed as an input to the function argument. + - "file": The content of a file, specified in the "source" argument must be passed as an input to the function argument. + + + + + + + + + + + + + + This mandatory attribute specifies the MIME type of the file that shall be used as a function + argument. The file type must be convertible to a string, i.e. no binary data should be used. + + + + + + + This attribute must be used if method="file" or method="path" is used and indicates the source of the file that shall + be used as a function argument as an URI (cf. RFC 3986). + + + + + + + This attribute must be used if method="inline" is used and indicated the content of the input argument + to be used in the target function + + + + + + + + + + + + + + + + Defines the prerequisites to run before processing + + + + + + + This element lists all the inputs for the function + + + + + + + This element is used to specify the output of the processing step + + + + + + + + Can be used to add a description what is happening in this step + + + + + + + + + This element is used to define a simple processing step of data by using adapters or utils + + + + + + This attribute is used to indicate the package URI of the Credibility Development Kit, i.e. + an adapter package + + + + + + + This attributes gives information which interpreter to use for the module. Currently the + Javascript RTE Node.js and the Python interpreter is supported. + + + + + + + + + + + + + This attributes is indicating the version identifier of the according interpreter, + for instance "16.14.0" to be used for Node.js version 16.14.0 or "3.8.10" for Python + version 3.8.10. + The supported syntax fore Node.js versions can be looked up here: + https://github.com/actions/setup-node?tab=readme-ov-file#supported-version-syntax + + + + + + + This attribute indicates the name of the function from the Credibility Development Kit + within the specific package. + + + + + + + This optional attribute specifies a unique identification of the processing + + + + + + + + + This element is used to define complex processing steps of data using node.js scripts or a github action + + + + + + This mandatory attribute specifies the type of complex processing. Currently the following methods are supported "nodejs": Specify a script that runs + in a node.js environment; "github-action": Specify the identifier of a github action script that runs in a github actions pipeline + + + + + + + + + + + + No predefined solution, instead the definition is supplier- or customer-specific, e.g. + if a specifc proprietary simulatation tool is to be used, which is not accessible like + an open source simulation tool (e.g., openmcx or esmini) + + + + + + + + + + To be used if interpreter is selected to "nodejs" or "python". + This attributes is indicating the version identifier of the according interpreter, + for instance "16.14.0" to be used for Node.js version 16.14.0 or "3.8.10" for Python + version 3.8.10. + The supported syntax fore Node.js versions can be looked up here: + https://github.com/actions/setup-node?tab=readme-ov-file#supported-version-syntax + + + + + + + This attribute defines the the path to the node.js script to be used (in case method=="nodejs") or the identifier + for the github action (in case method=="github-action") + + + + + + + This attribute defines a non-predefined solution (if method=="specific") for the complex processing step, + e.g. "use the given FMU with ID #fmu1 and simulate 10 seconds with an ODE1 solver in Ubuntu 22.04 (64 bit). The stimuli are defined in the inputs." + + + + + + + This optional attribute specifies a unique identification of the processing + + + + + + + + + + This element is used to define the prerequisites prior to running a processing step, by indicating a shell script. + + + + + + This attribute is used to define the shell script that installs the prerequisites for a processing step + + + + + + + + + This element is used to collect single FunctionArguments as inputs for the specific processing function + + + + + + + This element is used to define an argument of the function defined in a parent element. + + + + + + + This element is used to define a command line argument + + + + + + + This element is used to define an argument of the function defined in a parent element. + + + + + + + + Can be used to add a description what is happening in this step + + + + + + + + + This element is used to collect outputs from a processing function or a complex processing step + + + + + + + This element is to be used in case of a single function return + + + + + + + This element is to be used if a complex processing step does not only yield a simple return, but + can output multiple returns that might need further description on how to interpret + + + + + + + + Can be used to add a description what is happening in this step + + + + + + + + + This element is used to specify the output of the processing step (file type and target path) + + + + + + This mandatory attribute specifies the MIME type of the file that is generated as output of + the function. The file type must be convertible to a string, i.e. no binary data should be used. + + + + + + + This mandatory attribute specifies the location where the output of the function shall be saved. + + + + + + + + + This element is used to specify the output of the processing step in case a complex processing step + does not only yield a simple return, but can output multiple returns that might need further description + on how to interpret, e.g., if a simulation returns multiple result files and a log file, etc. + + + + + + This mandatory attribute describes how the output is to be interpreted + + + + + + + This mandatory attribute specifies the MIME type of the file that is generated as output of + the function. The file type must be convertible to a string, i.e. no binary data should be used. + + + + + + + This mandatory attribute specifies the location where the output of the function shall be saved. + + + + + + + + + This element is used to specify inputs that are used in the command line + + + + + + This mandatory attribute specifies the command flag (like "-s"). + + + + + + + This optional attribute specifies the argument (like "./file.sh" after a flag like "-f"). + + + + + + + This optional attribute specifies the type of the argument, e.g. if the argument is a path + + + + + + + + + This element is used to specify an input to a complex processing step, that does not only expects to + have specific function argument fulfilled and need further description on how to interpret the input, + e.g. a configuration file of a proprietary simulation tool. + + + + + + This mandatory attribute describes how the input is to be interpreted + + + + + + + This mandatory attribute specifies the MIME type of the file that is used as input. + + + + + + + This mandatory attribute specifies the location where the input is located. + + + + + \ No newline at end of file diff --git a/stmdtest.stmd b/stmdtest.stmd new file mode 100644 index 0000000..d36ac3b --- /dev/null +++ b/stmdtest.stmd @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/workflow_utils/json_to_yaml.js b/workflow_utils/json_to_yaml.js new file mode 100644 index 0000000..02efba2 --- /dev/null +++ b/workflow_utils/json_to_yaml.js @@ -0,0 +1,833 @@ +/** + * creates a github-workflow (YAML file) + */ + +const yaml = require('yaml'); +const path = require('path'); +const fs = require('fs'); + +// helper methods for arrays to make array unique +Array.prototype.unique = function () { + return this.filter((val, i, self) => self.indexOf(val) === i); +} + +const WORKFLOW_TEMPLATE = { + "name": "process-phase-testing-pipelines", + "on": { + "workflow_run": { + "workflows": ['create-workflows'], + "types": ['completed'] + // "branches": ['thanh'] //delete it in main + } + }, + "jobs": { + "run-all-workflows": { + "runs-on": "ubuntu-20.04", + "needs": [], + "steps": [ + { + "name": "checkout simulation data", + "uses": "actions/checkout@v4", + "with": { + "submodules": true, + "token": "${{ secrets.WRITE_WORKFLOW }}" + } + }, + { + "name": "install prerequisites", + "run": "npm install yaml --prefix ./workflow_utils\nnpm install ./workflow_utils/stmd-crud --prefix ./workflow_utils/stmd-crud\n" + }, + { + "name": "make output folder", + "run": "mkdir -p ./.github/outputs" + } + ] + } + } +}; +const FIXED_STEPS = { + "checkout_repo": { + "name": "checkout repo", + "uses": "actions/checkout@v4", + "with": { + "submodules": true, + "token": "${{ secrets.WRITE_WORKFLOW }}" + } + }, + "install_nvm": { + "name": "install Node Version Manager nvm", + "run": "curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash" + }, + "load_nvm": { + "name": "load nvm", + "run": 'export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"' + }, + "install_pyenv": { + "name": "install Python Version Management pyenv", + "run": "curl https://pyenv.run | bash" + }, + "load_pyenv": { + "name": "load pyenv", + "run": 'export PYENV_ROOT="$HOME/.pyenv" && [[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH" && eval "$(pyenv init -)"' + } +}; + +// paths relative to STMD location +const PATH_FCN_WRAPPER_PYTHON = "./workflow_utils/wrapper/fcnWrapperPython"; +const PATH_FCN_WRAPPER_NODE = "./workflow_utils/wrapper/fcnWrapperNode"; +const PATH_RESULTS_FILE = "./workflow_utils/results.js"; + +function jsonToYaml(cdkElements, outputFile, basename, stmdFolderPath) { + let steps = []; + + // get required node and python versions to install + // const nodeVersions = identifyNodeVersions(cdkElements.Evidence, cdkElements.Processing); + // const pythonVersions = identifyPythonVersions(cdkElements.Evidence, cdkElements.Processing); + + // add steps to check out repo and set global vars + steps = addStepsBasic(stmdFolderPath, steps); + + // install node requirements + // steps = addStepsInstallNode(nodeVersions, steps); + + // install python version requirements + // steps = addStepsInstallPython(pythonVersions, steps); + + // add processing steps + steps = addStepsProcessing(cdkElements.Processing, stmdFolderPath, steps); + + // add evidence steps + steps = addStepsEvidence(cdkElements.Evidence, outputFile, stmdFolderPath, steps); + + // create the structure for translating to YAML + let yamlStruct = createStructForYaml("ubuntu-20.04", basename); + + // generate YAML string from the structure created above + let yamlString = generateYamlString(yamlStruct, steps, basename, outputFile) + + return yamlString; +} + +function allToYaml(workflowNameList) { + var allWorkflows = WORKFLOW_TEMPLATE; + + // add essential steps to the workflow + allWorkflows = addEnvStep(workflowNameList, allWorkflows); + allWorkflows = addPushStep(allWorkflows); + + let yamlString = yaml.stringify(allWorkflows); + + return yamlString; +} + +// -------------------- functions--------------------------------------------------------------------------------------- + +function addEnvStep(workflowNameList, workflow) { + var envStep = { + "env": { + "GithubBranch": "${{github.ref_name}}", + "GithubRepoName": "${{github.event.repository.name}}", + "GithubOwner": "${{github.repository_owner}}" + }, + "run": "node ./workflow_utils/results.js -s -o summary.md && cat summary.md >> $GITHUB_STEP_SUMMARY" + } + + var allActionList = ""; + for (var i in workflowNameList) { + if (typeof workflowNameList[i] === "string") { + var fileName = workflowNameList[i].replaceAll("_", "."); + + workflow["jobs"][workflowNameList[i]] = { + "uses": process.env.GithubOwner + "/" + process.env.GithubRepoName + "/.github/workflows/" + fileName + ".yaml@" + process.env.GithubBranch, + "secrets": { + "WRITE_WORKFLOW": "${{secrets.WRITE_WORKFLOW}}" + } + } + workflow["jobs"]["run-all-workflows"]["needs"].push(workflowNameList[i]); + + envStep["env"][workflowNameList[i]] = "${{needs." + workflowNameList[i] + ".outputs.summary}}" + allActionList += workflowNameList[i] + ","; + } + } + envStep["env"]["allActionList"] = allActionList; + workflow["jobs"]["run-all-workflows"]["steps"].push(envStep); + + return workflow; +} + +function addPushStep(workflow) { + workflow["jobs"]["run-all-workflows"]["steps"].push({ + "name": "push results", + "run": "git config --global user.name \"Add results\"\ngit config --global user.email \"setlabs@users.noreply.github.com\"\n\ngit add ./.github/outputs\ngit commit -m \"Add results [actions skip]\"\ngit push\n" + }); + + return workflow; +} + +function addStepsBasic(stmdFolderPath, stepsBefore) { + let stepsAfter = [...stepsBefore]; + + // add checking out the repository + stepsAfter.push(FIXED_STEPS.checkout_repo); + + // set the directory where the STMD is located + stepsAfter.push({ + "name": "set STMD Folder path", + "run": `echo 'STMDFOLDERPATH=${stmdFolderPath}' >> $GITHUB_ENV && echo $STMDFOLDERPATH` + }); + + return stepsAfter; +} + +function addStepsEvidence(evidenceElements, outputFilePath, stmdFolderPath, stepsBefore) { + let stepsAfter = [...stepsBefore]; + + for (let element of evidenceElements) { + let newSteps = translateEvidenceElement(element, outputFilePath, stmdFolderPath); + stepsAfter.push(...newSteps); + } + + return stepsAfter; +} + +function addStepsProcessing(processingElements, stmdFolderPath, stepsBefore) { + let stepsAfter = [...stepsBefore]; + + for (let element of processingElements) { + let newSteps = translateProcessingElement(element, stmdFolderPath); + stepsAfter.push(...newSteps); + } + + return stepsAfter; +} + +function addStepsInstallPython(pythonVersions, stepsBefore) { + let stepsAfter = [...stepsBefore]; + + if (pythonVersions.length > 0) { + // install python environment manager pyenv + stepsAfter.push(FIXED_STEPS.install_pyenv); + stepsAfter.push(FIXED_STEPS.load_pyenv); + + // install all required python versions and virtualenv via pyenv + for (let pyVersion of pythonVersions) { + stepsAfter.push({ + "name": "install python version " + pyVersion, + "run": "pyenv install " + pyVersion + }); + stepsAfter.push({ + "name": "install virtualenv for python version " + pyVersion, + "run": "pyenv global " + pyVersion + " && pip install virtualenv" + }); + } + } + + return stepsAfter; +} + +function addStepsInstallNode(nodeVersions, stepsBefore) { + let stepsAfter = [...stepsBefore]; + + if (nodeVersions.length > 0) { + + // install node version manager nvm + stepsAfter.push(FIXED_STEPS.install_nvm); + stepsAfter.push(FIXED_STEPS.load_nvm); + + // install all required node versions via nvm + for (let nodeVersion of nodeVersions) { + stepsAfter.push({ + "name": "install node version " + nodeVersion, + "run": "nvm install " + nodeVersion + }); + }; + } + + return stepsAfter; +} + +function createStructForYaml(ubuntuIdentifier, baseName) { + let yamlStruct = { + "name": baseName, + "on": { + "workflow_call": { + "outputs": { + "summary": { + "value": "${{jobs." + baseName + ".outputs.summary}}" + } + }, + "secrets": { + "WRITE_WORKFLOW": { + "required": true + } + } + } + }, + "jobs": { + }, + }; + + yamlStruct["jobs"][baseName] = { + "runs-on": ubuntuIdentifier, + "outputs": { + "summary": "${{steps.outputStep.outputs.summary}}" + }, + "steps": [] + }; + + return yamlStruct; +} + +function generateYamlString(yamlStruct, steps, basename, outputFilePath) { + // using "step__x" and replacing it later with the actual string is a workaround + // to compensate for bugs in the yaml module (Bug: yaml module wraps long strings + // to new lines) + for (let i = 0; i < steps.length; i++) { + yamlStruct.jobs[basename].steps.push({ ...steps[i] }); + + if (steps[i].run !== undefined) { + yamlStruct.jobs[basename].steps[i].run = "step__" + String(i); + } + } + + // add report and summary step + yamlStruct.jobs[basename].steps.push({ + name: "show report", + run: "cat " + outputFilePath + }); + yamlStruct.jobs[basename].steps.push({ + name: "send to outputs", + id: "outputStep", + run: "echo \"summary=$(cat " + outputFilePath + ")\" >> $GITHUB_OUTPUT" + }); + + let yamlString = yaml.stringify(yamlStruct); + + // replace step__x with the run (see above!) + for (let i = 0; i < steps.length; i++) { + if (steps[i].run !== undefined) { + yamlString = yamlString.replace('step__' + String(i), steps[i].run); + } + } + + return yamlString; +} + +function translateProcessingElement(element, stmdFolderPath) { + const wrapperPathNode = path.resolve(stmdFolderPath, PATH_FCN_WRAPPER_NODE); + const wrapperPathPython = path.resolve(stmdFolderPath, PATH_FCN_WRAPPER_PYTHON); + + let steps = []; + + for (let prerequisite of element.Prerequisites) { + var absolutePathSource = path.resolve(stmdFolderPath, prerequisite.attributes.source); + steps.push({ + name: "install prerequisites", + run: `sudo chmod +x ${absolutePathSource} && sudo ${absolutePathSource}` + }); + } + + if (element.SimpleProcessing !== undefined) { + var outputPath = path.resolve(stmdFolderPath, element.Outputs.Return.attributes.path); + var outputFolderPath = path.dirname(outputPath); + var metadata = { id: element.SimpleProcessing.attributes.id } + let functionArg = parseFcnArguments(element.Inputs.FunctionArgument, stmdFolderPath); + + if (element.SimpleProcessing.attributes.interpreter == "nodejs") { + // steps.push({ + // name: `activate NodeJS version ${element.SimpleProcessing.attributes.interpreterVersion} at processing element for metric ${element.SimpleProcessing.attributes.module}/${element.SimpleProcessing.attributes.function}`, + // run: `nvm use ${element.SimpleProcessing.attributes.interpreterVersion}` + // }); + + steps.push({ + name: `activate NodeJS version ${element.SimpleProcessing.attributes.interpreterVersion} at processing element for metric ${element.SimpleProcessing.attributes.module}/${element.SimpleProcessing.attributes.function}`, + uses: "actions/setup-node@v3", + with: { + "node-version": element.SimpleProcessing.attributes.interpreterVersion + } + },{ + name: "Remove node_modules, reset libs", + run: `| + npm ls --parseable --depth=0 | tail -n +2 | awk -F'/' '{print $NF}' | xargs -r npm uninstall + ` + }); + + let nodeModulePath = path.resolve(stmdFolderPath, element.SimpleProcessing.attributes.module); + steps.push({ + name: "install node module at processing element", + run: `npm install --prefix ${nodeModulePath} ${nodeModulePath}` + }); + + // compose node command + let nodeCmd = `const {wrapper} = require("${wrapperPathNode}"); let nodeResult = wrapper("${element.SimpleProcessing.attributes.module}", "${element.SimpleProcessing.attributes.function}", [${functionArg.names.join(",")}], [${functionArg.contents.join(",")}], [${functionArg.methods.join(",")}], "${stmdFolderPath}", ${JSON.stringify(metadata)}, false); process.stdout.write(JSON.stringify(nodeResult));`; + let nodeBashCmd = `res=$(node -e '${nodeCmd}') && echo $res && mkdir -p ${outputFolderPath} && echo $res > ${outputPath}`; + + // add SimpleProcessing command + steps.push({ + name: element.SimpleProcessing.attributes.id, + run: nodeBashCmd + }); + } + else if (element.SimpleProcessing.attributes.interpreter == "python") { + let pyModulePath = path.resolve(stmdFolderPath, element.SimpleProcessing.attributes.module) + ".py"; + let pyModuleDir = path.dirname(pyModulePath); + + //////////////////////////// + steps.push({ + name: "activate required python version at simple processing element", + uses: "actions/setup-python@v4", + with: { + "python-version": element.SimpleProcessing.attributes.interpreterVersion + } + },{ + name: "Create lib environment, reset all libs", + run: `| + pip freeze | xargs -r pip uninstall -y + ` + }); + + steps.push({ + name: "install python requirements, if necessary", + run: `| + [ -f ${pyModuleDir}/requirements.txt ] && pip install -r ${pyModuleDir}/requirements.txt + ` + }); + //////////////////////////// + + // steps.push({ + // name: "activate required python version at simple processing element", + // run: `pyenv global ${element.SimpleProcessing.attributes.interpreterVersion}` + // }); + // steps.push({ + // name: `create virtual python environment for preprocessing with id ${element.SimpleProcessing.attributes.id}`, + // run: `python -m virtualenv env_${element.SimpleProcessing.attributes.id}` + // }); + // steps.push({ + // name: "activate virtual python environment at processing element", + // run: `source env_${element.SimpleProcessing.attributes.id}/bin/activate` + // }); + // steps.push({ + // name: "install python requirements, if necessary", + // run: `[[ -e ${pyModuleDir}/requirements.txt ]] && pip install -r ${pyModuleDir}/requirements.txt` + // }); + + // compose python command + let pythonCmd = `import sys; sys.path.append("${path.dirname(wrapperPathPython)}"); from fcnWrapperPython import wrapper; result = wrapper("${element.SimpleProcessing.attributes.module}", "${element.SimpleProcessing.attributes.function}", [${functionArg.names.join(",")}], [${functionArg.contents.join(",")}], [${functionArg.methods.join(",")}], "${stmdFolderPath}", ${JSON.stringify(metadata)}, False); print(result)`; + let pythonBashCmd = `res=$(python -c '${pythonCmd}') && echo $res && mkdir -p ${outputFolderPath} && echo $res > ${outputPath}`; + + steps.push({ + name: element.SimpleProcessing.attributes.id, + run: pythonBashCmd + }); + } + } + else if (element.ComplexProcessing !== undefined) { + if (element.ComplexProcessing.attributes.method == "github-action") { + // parse arguments + let actionArgs = {}; + for (let input of element.Inputs.FunctionArgument) { + actionArgs[input.attributes.name] = input.attributes.method == "inline" ? input.attributes.content : input.attributes.method == "path" ? path.resolve(stmdFolderPath, input.attributes.content) : input.attributes.content; + } + + // compose step + let step = { + name: element.ComplexProcessing.attributes.id, + uses: element.ComplexProcessing.attributes.source + }; + + // add "with" only if arguments are given + if (element.Inputs.FunctionArgument.length > 0) { + step["with"] = actionArgs; + } + + steps.push(step); + } + + if (element.ComplexProcessing.attributes.method == "nodejs") { + var outputPath; + try { + var outputPath = path.resolve(stmdFolderPath, element.Outputs.Output[0].attributes.path) + } + catch (e) { + outputPath = ""; + } + + // parse arguments + let clArgs = parseCmdLineArguments(element.Inputs.CommandLineArgument, stmdFolderPath); + + let nodeCmd = `${path.resolve(stmdFolderPath, element.ComplexProcessing.attributes.source)} ${clArgs.join(" ")})`; + let nodeBashCmd; + + if (outputPath) { + var outputFolderPath = path.dirname(outputPath); + nodeBashCmd = `res=$(node ${nodeCmd}) && echo $res && mkdir -p ${outputFolderPath} && echo $res > ${outputPath}`; + } + else { + nodeBashCmd = `node ${nodeCmd}`; + } + + steps.push({ + name: element.attributes.id, + run: nodeBashCmd + }); + } + else if (element.ComplexProcessing.attributes.method == "python") { + var outputPath; + try { + var outputPath = path.resolve(stmdFolderPath, element.Outputs.Output[0].attributes.path) + } + catch (e) { + outputPath = ""; + } + + // parse arguments + let clArgs = parseCmdLineArguments(element.Inputs.CommandLineArgument, stmdFolderPath); + + let pythonCmd = `${path.resolve(stmdFolderPath, element.ComplexProcessing.attributes.source)} ${clArgs.join(" ")})`; + let pythonBashCmd; + + if (outputPath) { + var outputFolderPath = path.dirname(outputPath); + pythonBashCmd = `res=$(python ${pythonCmd}) && echo $res && mkdir -p ${outputFolderPath} && echo $res > ${outputPath}`; + } + else { + pythonBashCmd = `python ${pythonCmd}`; + } + + // steps.push({ + // name: "activate required python version at processing complex", + // run: `pyenv global ${element.ComplexProcessing.attributes.interpreterVersion}` + // }); + + steps.push({ + name: "activate required python version at processing complex", + uses: "actions/setup-python@v4", + with: { + "python-version": element.ComplexProcessing.attributes.interpreterVersion + } + },{ + name: "Create lib environment, reset all libs", + run: `| + pip freeze | xargs -r pip uninstall -y + ` + }); + + steps.push({ + name: element.attributes.id, + run: pythonBashCmd + }); + } + } + + return steps; +} + +function translateEvidenceElement(element, outputPath, stmdFolderPath) { + let steps = []; + + const nodeMetrics = element.Metric.filter(metric => metric.attributes.interpreter == "nodejs"); + const pyMetrics = element.Metric.filter(metric => metric.attributes.interpreter == "python"); + const wrapperPathNode = path.resolve(stmdFolderPath, PATH_FCN_WRAPPER_NODE); + const wrapperPathPython = path.resolve(stmdFolderPath, PATH_FCN_WRAPPER_PYTHON); + const resultsFilePath = path.resolve(stmdFolderPath, PATH_RESULTS_FILE); + + // install required node modules + // for (let metric of nodeMetrics) { + // let nodeModulePath = path.resolve(stmdFolderPath, metric.attributes.module); + // steps.push({ + // name: "install node module", + // run: "npm install --prefix " + nodeModulePath + " " + nodeModulePath + // }); + // } + + // install required python modules + // for (let metric of pyMetrics) { + // let pyModulePath = path.resolve(stmdFolderPath, metric.attributes.module) + ".py"; + // let pyModuleDir = path.dirname(pyModulePath); + + // steps.push({ + // name: "activate required python version at evidence", + // run: "pyenv global " + metric.attributes.interpreterVersion + // }); + // steps.push({ + // name: "create virtual python environment for metric", + // run: "python -m virtualenv " + metric.attributes.function + // }); + // steps.push({ + // name: "activate virtual python environment", + // run: "source " + metric.attributes.function + "/bin/activate" + // }); + // steps.push({ + // name: "install python requirements, if necessary", + // run: "[[ -e " + pyModuleDir + "/requirements.txt ]] && pip install -r " + pyModuleDir + "/requirements.txt" + // }); + // steps.push({ + // name: "deactivate virtual python environment", + // run: "[[ -n $VIRTUAL_ENV ]] && deactivate" + // }); + // } + + // process python metrics + for (let metric of pyMetrics) { + + // steps.push({ + // name: "activate required python version at evidence", + // run: "pyenv global " + metric.attributes.interpreterVersion + // }); + + let pyModulePath = path.resolve(stmdFolderPath, metric.attributes.module) + ".py"; + let pyModuleDir = path.dirname(pyModulePath); + + steps.push({ + name: `activate required python version ${metric.attributes.interpreterVersion} for metric ${metric.attributes.module}/${metric.attributes.function}`, + uses: "actions/setup-python@v4", + with: { + "python-version": metric.attributes.interpreterVersion + } + },{ + name: "Create lib environment, reset all libs", + run: `| + pip freeze | xargs -r pip uninstall -y + ` + }); + + steps.push({ + name: "install python requirements, if necessary", + run: `| + [ -f ${pyModuleDir}/requirements.txt ] && pip install -r ${pyModuleDir}/requirements.txt + ` + }); + + for (let test of metric.Test) { + let functionArg = parseFcnArguments(test.FunctionArgument, stmdFolderPath); + + // compose metadata + var metadata = { + level: element.attributes.level, + id: test.attributes.id + }; + + // compose python command + let pythonCmd = `import sys; sys.path.append("${path.dirname(wrapperPathPython)}"); from fcnWrapperPython import wrapper; result = wrapper("${metric.attributes.module}", "${metric.attributes.function}", [${functionArg.names.join(",")}], [${functionArg.contents.join(",")}], [${functionArg.methods.join(",")}], "${stmdFolderPath}", ${JSON.stringify(metadata)}); print(result)`; + let pythonBashCmd = `res=$(python -c '${pythonCmd}') && echo $res && node ${resultsFilePath} -p "$res" -o ${outputPath}`; + + // if result shall be written to an output file, add this to the command + if (test.Return) { + var returnPath = path.resolve(stmdFolderPath, test.Return.path); + pythonBashCmd += ` && echo $res > ${returnPath}`; + } + + // steps.push({ + // name: "activate virtual python environment for test " + test.attributes.id, + // run: "source " + metric.attributes.function + "/bin/activate" + // }) + steps.push({ + name: "execute test " + test.attributes.id, + run: pythonBashCmd + }); + // steps.push({ + // name: "deactivate virtual python environment for test " + test.attributes.id, + // run: "[[ -n $VIRTUAL_ENV ]] && deactivate" + // }); + } + } + + // process node metrics + for (let metric of nodeMetrics) { + // activate according node version + // steps.push({ + // name: `activate NodeJS version ${metric.attributes.interpreterVersion} for metric ${metric.attributes.module}/${metric.attributes.function}`, + // run: `nvm use ${metric.attributes.interpreterVersion}` + // }); + + steps.push({ + name: `activate NodeJS version ${metric.attributes.interpreterVersion} for metric ${metric.attributes.module}/${metric.attributes.function}`, + uses: "actions/setup-node@v3", + with: { + "node-version": metric.attributes.interpreterVersion + } + },{ + name: "Remove node_modules, reset libs", + run: `| + npm ls --parseable --depth=0 | tail -n +2 | awk -F'/' '{print $NF}' | xargs -r npm uninstall + ` + }); + + let nodeModulePath = path.resolve(metric.attributes.module); + + steps.push({ + name: "install node module", + run: "npm install --prefix " + nodeModulePath + " " + nodeModulePath + }); + + for (let test of metric.Test) { + let functionArg = parseFcnArguments(test.FunctionArgument, stmdFolderPath); + + var metadata = { + level: element.attributes.level, + id: test.attributes.id + }; + + // compose node command + let nodeCmd = `const {wrapper} = require("${wrapperPathNode}"); let nodeResult = wrapper("${metric.attributes.module}", "${metric.attributes.function}", [${functionArg.names.join(",")}], [${functionArg.contents.join(",")}], [${functionArg.methods.join(",")}], "${stmdFolderPath}", ${JSON.stringify(metadata)}); process.stdout.write(JSON.stringify(nodeResult));`; + let nodeBashCmd = `res=$(node -e '${nodeCmd}') && echo $res && node ${resultsFilePath} -p "$res" -o ${outputPath}`; + + // if result shall be written to an output file, add this to the command + if (test.Return) { + var returnPath = path.resolve(stmdFolderPath, test.Return.path); + nodeBashCmd += ` && echo $res > ${returnPath}`; + } + + steps.push({ + name: test.attributes.id, + run: nodeBashCmd + }); + } + } + + return steps; +} + +function identifyNodeVersions(evidenceElements, processingElements) { + let versions = []; + + versions.push(...identifyEvidenceNodeVersions(evidenceElements)); + versions.push(...identifyProcessingNodeVersions(processingElements)); + + return versions.unique(); +} + +function identifyEvidenceNodeVersions(evidenceElements) { + let versions = []; + + for (let evidence of evidenceElements) { + for (let metric of evidence.Metric) { + if (metric.attributes.interpreter == "nodejs") { + versions.push(metric.attributes.interpreterVersion); + } + } + } + + return versions.unique(); +} + +function identifyProcessingNodeVersions(processingElements) { + let versions = []; + + for (let processing of processingElements) { + if (processing.SimpleProcessing !== undefined) { + if (processing.SimpleProcessing.attributes.interpreter == "nodejs") { + versions.push(processing.SimpleProcessing.attributes.interpreterVersion); + } + } + else if (processing.ComplexProcessing !== undefined) { + if (processing.ComplexProcessing.attributes.interpreter == "nodejs") { + versions.push(processing.ComplexProcessing.attributes.interpreterVersion); + } + } + } + + return versions.unique(); +} + +function identifyPythonVersions(evidenceElements, processingElements) { + let versions = []; + + versions.push(...identifyEvidencePythonVersions(evidenceElements)); + versions.push(...identifyProcessingPythonVersions(processingElements)); + + return versions.unique(); +} + +function identifyEvidencePythonVersions(evidenceElements) { + let versions = []; + + for (let evidence of evidenceElements) { + for (let metric of evidence.Metric) { + if (metric.attributes.interpreter == "python") { + versions.push(metric.attributes.interpreterVersion); + } + } + } + + return versions.unique(); +} + +function identifyProcessingPythonVersions(processingElements) { + let versions = []; + + for (let processing of processingElements) { + if (processing.SimpleProcessing !== undefined) { + if (processing.SimpleProcessing.attributes.interpreter == "python") { + versions.push(processing.SimpleProcessing.attributes.interpreterVersion); + } + } + else if (processing.ComplexProcessing !== undefined) { + if (processing.ComplexProcessing.attributes.interpreter == "python") { + versions.push(processing.ComplexProcessing.attributes.interpreterVersion); + } + } + } + + return versions.unique(); +} + +function parseFcnArguments(cdkFunctionArguments, stmdFolderPath) { + let functionArgMethods = []; + let functionArgNames = []; + let functionArgContents = []; + + for (let arg of cdkFunctionArguments) { + functionArgNames.push('"' + arg.attributes.name + '"'); + functionArgMethods.push('"' + arg.attributes.method + '"'); + if (arg.attributes.method == "file") { + functionArgContents.push('"' + arg.attributes.content + '"'); + } else if (arg.attributes.method == "path") { + functionArgContents.push('"' + path.resolve(stmdFolderPath, arg.attributes.content) + '"'); + } else { + functionArgContents.push('"' + arg.attributes.content + '"') + } + } + + return { + names: functionArgNames, + methods: functionArgMethods, + contents: functionArgContents + }; +} + +function parseCmdLineArguments(inputElement, stmdFolderPath) { + // parse arguments + let clArgs = []; + + for (let arg of inputElement.CommandLineArgument) { + clArgs.push(arg.attributes.flag); + if (arg.attributes.argument !== undefined) { + if (arg.attributes.type == "path") { + var argPathLs = arg.attributes.argument.split(" "); + for (var argi in argPathLs) { + if (typeof argPathLs[argi] === "string") { + var argPath = path.resolve(stmdFolderPath, argPathLs[argi]); + var argFolderPath = path.dirname(argPath); + + if (!fs.existsSync(argFolderPath)) { + fs.mkdirSync(argFolderPath, { recursive: true }); + } + + clArgs.push(argPath); + } + } + } + else { + clArgs.push(arg.attributes.argument); + } + } + } + + return clArgs; +} + +module.exports = { + jsonToYaml, + allToYaml +} \ No newline at end of file diff --git a/workflow_utils/package-lock.json b/workflow_utils/package-lock.json new file mode 100644 index 0000000..eb658e4 --- /dev/null +++ b/workflow_utils/package-lock.json @@ -0,0 +1,30 @@ +{ + "name": "workflow_utils", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "yaml": "^2.6.0" + } + }, + "node_modules/yaml": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", + "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + } + }, + "dependencies": { + "yaml": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", + "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==" + } + } +} diff --git a/workflow_utils/package.json b/workflow_utils/package.json new file mode 100644 index 0000000..5672a89 --- /dev/null +++ b/workflow_utils/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "yaml": "^2.6.0" + } +} diff --git a/workflow_utils/parse_to_yaml.js b/workflow_utils/parse_to_yaml.js new file mode 100644 index 0000000..c29a539 --- /dev/null +++ b/workflow_utils/parse_to_yaml.js @@ -0,0 +1,55 @@ +const { StmdCrud } = require('./stmd-crud'); +const {jsonToYaml, allToYaml} = require('./json_to_yaml.js') +const fs = require('fs'); +const path = require('path') + +// node ./workflow_utils/parse_to_yaml.js -f "./SimulationTask.stmd" -o ./.github/workflows/ + +let stmdString; +let stmdFolderPath; +let outputFolder; + +// get STMD string, the STMD base path and the target output path from command line arguments +for (let i = 0; i < process.argv.length; i++) { + if (process.argv[i] === "-s" || process.argv[i] === "--stmd") { + stmdString = process.argv[i+1]; + } + if (process.argv[i] === "-f") { + stmdString = fs.readFileSync(process.argv[i+1]); + stmdFolderPath=path.resolve(path.dirname(process.argv[i+1])); + } + if (process.argv[i] === "-o") { + outputFolder = process.argv[i+1]; + } +} + +if (stmdString === undefined) + throw("STMD string must be given, use -s or --stmd") + +// extract the locations of all elements in the STMD +// the locations are given as string arrays like +// ["stmd:SimulationTaskMetaData", "stmd:ImplementationPhase", "stmd:ImplementModel", "stc:Rationale"] +let stmdCrud = new StmdCrud(stmdString); +var stmdRationales = stmdCrud.findAllParticleLocation("stc:Rationale") + +// create one workflow for each Rationale of a process phase +var count = 0; +var workflowNameList = []; + +for (let rationale of stmdRationales) { + let fileNameBase = rationale[1].split(":")[1]+"."+rationale[2].split(":")[1]; + let workflowName = `No_${count}_${fileNameBase}`.replace(".","_"); + + console.log(workflowName); + workflowNameList.push(workflowName); + + let outputFilename = `No.${count}.${fileNameBase}.cdkResult.json`; + let workflowFilename = `${outputFolder}/No.${count}.${fileNameBase}.yaml`; + let yamlContent = jsonToYaml(stmdCrud.getCdkElement(rationale), outputFilename, workflowName, stmdFolderPath); + fs.writeFileSync(workflowFilename, yamlContent); + + count++; +} + +let allYamlContent = allToYaml(workflowNameList) +fs.writeFileSync(outputFolder + "/all.yaml", allYamlContent); \ No newline at end of file diff --git a/workflow_utils/results.js b/workflow_utils/results.js new file mode 100644 index 0000000..311092b --- /dev/null +++ b/workflow_utils/results.js @@ -0,0 +1,110 @@ +const fs = require('fs'); + +let isSinglePhase = false; +let value; +let outputFilePath; + +for (let i = 0; i < process.argv.length; i++) { + if (process.argv[i] === "-p") { + value = JSON.parse(process.argv[i + 1]); + isSinglePhase = true; + } + if (process.argv[i] === "-s") { //summary, input as folder name + value = "summary"; + isSinglePhase = false; + } + if (process.argv[i] === "-o") { + outputFilePath = process.argv[i + 1]; + } +} + +if (!value || !outputFilePath) + throw ("Invalid input") + +var valueOutput; + +try { + valueOutput = JSON.parse(fs.readFileSync(outputFilePath)); +} catch (e) { +} + +if (!valueOutput) { + if (isSinglePhase) { + valueOutput = []; + } +} + +if (isSinglePhase == true) { + valueOutput.push(value); + fs.writeFileSync(outputFilePath, JSON.stringify(valueOutput)) +} else { + valueOutput = "### [CDK Report](" + "https://github.com/" + process.env.GithubOwner + "/" + process.env.GithubRepoName + "/blob/" + process.env.GithubBranch + "/.github/outputs/allPhaseStepReport.csv" + ") :rocket: \n| Phase | Step | Test passed | Reached Level | Result | \n | ---- | ---- | ---- | ---- | ---- | \n" + + var allPhaseStepReportCSV = "Phase, Step, Test passed, Reached Level, Result \n"; + + let allActionList = process.env.allActionList; + + let actionList = allActionList.split(","); + let reportByPhase = {}; + + for (let a in actionList) { + if (actionList[a]) { + let phaseName = actionList[a].split("_")[2]; + if (!reportByPhase[phaseName]) { + reportByPhase[phaseName] = { + "phaseName": phaseName, + "reachedLevel": 4 + } + } + let stepName = actionList[a].split("_")[3]; + let rs = JSON.parse(process.env[actionList[a]]); + let passedCount = 0; + let rsFullObj = []; + let minLevel = 4; + let maxLevel = 0; + + for (let e in rs) { + var rsobj = rs[e]; + if (typeof rs[e] == "string") { + rsobj = JSON.parse(rs[e]); + } + if (rsobj["result"] == true) { + passedCount++; + if (maxLevel < parseInt(rsobj["level"])) { + maxLevel = parseInt(rsobj["level"]); + } + } else if (parseInt(rsobj["level"]) <= minLevel) { + minLevel = parseInt(rsobj["level"]) - 1; + } + rsFullObj.push(rsobj); + } + if (minLevel > maxLevel) { + minLevel = maxLevel; + } + if (minLevel < reportByPhase[phaseName]["reachedLevel"]) { + reportByPhase[phaseName]["reachedLevel"] = minLevel; + } + let fileName = actionList[a].replaceAll("_", "."); + let fileURL = "https://github.com/" + process.env.GithubOwner + "/" + process.env.GithubRepoName + "/blob/" + process.env.GithubBranch + "/.github/outputs/" + fileName + ".json"; + + valueOutput += "| " + phaseName + " | " + stepName + " | " + passedCount + "/" + rs.length + "|" + minLevel + " | [view](" + fileURL + ") | \n" + allPhaseStepReportCSV += phaseName + "," + stepName + "," + passedCount + "/" + rs.length + "," + minLevel + "," + fileURL + " \n" + + fs.writeFileSync("./.github/outputs/" + fileName + ".json", JSON.stringify(rsFullObj, null, 4)); + } + } + fs.writeFileSync("./.github/outputs/allPhaseStepReport.csv", allPhaseStepReportCSV); + + var allPhaseReport = "Phase,Reached Level\n"; + + valueOutput += "#### [Phase Report](" + "https://github.com/" + process.env.GithubOwner + "/" + process.env.GithubRepoName + "/blob/" + process.env.GithubBranch + "/.github/outputs/allPhaseReport.csv" + ") \n| Phase | Reached Level | \n | ---- | ---- | \n" + for (let p in reportByPhase) { + valueOutput += "|" + reportByPhase[p]["phaseName"] + "|" + reportByPhase[p]["reachedLevel"] + "| \n" + allPhaseReport += reportByPhase[p]["phaseName"] + "," + reportByPhase[p]["reachedLevel"] + " \n" + } + + fs.writeFileSync("./.github/outputs/allPhaseReport.csv", allPhaseReport); + + fs.writeFileSync(outputFilePath, valueOutput) +} + diff --git a/workflow_utils/stmd-crud/README.md b/workflow_utils/stmd-crud/README.md new file mode 100644 index 0000000..81afa9a --- /dev/null +++ b/workflow_utils/stmd-crud/README.md @@ -0,0 +1,525 @@ +# STMD-CRUD + +The STMD CRUD package provides a simple interface to create, read, update and delete contents of [Simulation Task Meta Data](https://github.com/PMSFIT/SSPTraceability) files. + +## USAGE + +1. [Instantiation](#instantiation) +2. [Create elements](#create-elements) + - [`addResource`](#addresource) + - [`addResourceReference`](#addresourcereference) + - [`addLink`](#addlink) +3. [Read elements](#read-elements) + - [`getResources`](#getresources) + - [`getResourceFromId`](#getresourcefromid) + - [`getResourceFromReference`](#getresourcefromreference) + - [`getResourceReferences`](#getresourcereferences) + - [`getLinks`](#getlinks) + - [`getClassifications`](#getclassifications) + - [`getAnnotations`](#getannotations) + - [`getLifeCycleEntries`](#getlifecycleentries) + - [`getTopLevelInformation`](#gettoplevelinformation) + - [`getGeneralInformation`](#getgeneralinformation) +4. [Update elements](#update-elements) + - [`updateResource`](#updateresource) + - [`updateLink`](#updatelink) +5. [Delete elements](#delete-elements) + - [`deleteResource`](#deleteresource) + - [`deleteResourceReference`](#deleteresourcereference) + - [`deleteLink`](#deletelink) +6. [Exporters](#exporters) + - [`export`](#export) + - [`createGraphFromLink`](#creategraphfromlink) + + +### Instantiation + +For instantiation of a StmdCrud, use the xml string of the STMD file as the single argument: + +```javascript +stmdString = fs.readFileSync("path/to/stmd_file.stmd", "utf-8"); +stmdCrud = new StmdCrud(stmdString); // if stdmString is not set, a new, empty STMD will be instantiated. +``` + +Parsing of the complete STMD file is done in the background. In case the stmdString is not set, an empty STMD is instantiated. + +### Create elements + +#### `addResource` + +To add a resource to a specific location, pass the resource and its location (the list of enclosing parent elements) to `addResource`: + +```javascript +resource = { + attributes: { + kind: "requirement", + type: "application/json", + id: "req_01", + source: "./requirements/model_requierment_01.json" + } +}; +location = ["stmd:SimulationTaskMetaData", "stmd:RequirementsPhase", "stmd:DefineModelRequirements", "stc:Output"]; + +addedSuccessful = stmdCrud.addResource(resource, location); // if ID is not unique, addResource will return false +``` +`resource` must be of type [`ResourceType`](./types/specification.js) + +`location` must be the location (given as hierarchical string array of the element tree) where to add the resource. +Please note: In this first version, it will not be checked if the location is valid according to the XML schema of the STMD! + +#### `addResourceReference` + +To add a resource reference to a specific location, pass the resource reference and its location (the list of enclosing parent elements) to `addResourceReference`: + +```javascript +resourceReference = { + attributes: { + xlink_href: "#req_01", + description: "reference to requirement with ID req_01" + } +}; +location = ["stmd:SimulationTaskMetaData", "stmd:DesignPhase", "stmd:DefineModelDesignSpecification", "stc:Input"]; + +addedSuccessful = stmdCrud.addResourceReference(resourceReference, location); // if ID is not unique, addResourceReference will return false +``` +`resourceReference` must be of type [`ResourceReference`](./types/specification.js) + +`location` must be the location (given as hierarchical string array of the element tree) where to add the resource reference. +Please note: In this first version, it will not be checked if the location is valid according to the XML schema of the STMD! + +#### `addLink` + +To add a link to a specific location, pass the link and its location (the list of enclosing parent elements) to `addLink`: + +```javascript +link = { + locator: [ + { + attributes: { + xlink_href: "#req_01", + xlink_label: "a_requirement", + xlink_role: "model-requirement" + } + }, + { + attributes: { + xlink_href: "#spec_model", + xlink_label: "according_specification", + xlink_role: "model-specification" + } + } + ], + arc: [ + { + attributes: { + xlink_from: "according_specification", + xlink_to: "a_requirement" + xlink_arcrole: "derived-from" + } + } + ], + attributes: { + xlink_title: "requirement source of the model specification", + } +}; + +location = ["stmd:SimulationTaskMetaData", "stmd:DesignPhase", "stmd:DefineModelDesignSpecification"]; + +addedSuccessful = stmdCrud.addLink(link, location); +``` +`link` must be of type [`LinksType`](./types/specification.js) + +`location` must be the location (given as hierarchical string array of the element tree) where to add the link. +Please note: In this first version, it will not be checked if the location is valid according to the XML schema of the STMD! + +### Read elements + +#### `getResources` + +To get all resources of a specific location, pass the location (the list of enclosing parent elements) to `getResources`: + +```javascript +location = ["stmd:SimulationTaskMetaData", "stmd:RequirementsPhase", "stmd:DefineModelRequirements", "stc:Output"]; + +availableResources = stmdCrud.getResources(location); +``` +The function returns an array of type [`AvailableResource`](./types/internal_types.js). + +If you want to get additionally all resources of the sub-elements, pass `true` as second argument: + +```javascript +location = ["stmd:SimulationTaskMetaData", "stmd:RequirementsPhase", "stmd:DefineModelRequirements"]; + +availableResources = stmdCrud.getResources(location, true); +``` +In this case, the resources of all particles of `stmd:DefineModelRequirements` are returned, i.e., all elements in `stc:Input`, `stc:Procedure`, `stc:Output` and `stc:Rationale`. + +#### `getResourceFromId` + +To get a specific resource by its ID (given in its attributes, cf. [`addResource`](#addresource)), use `getResourceFromId`: + +```javascript +stmdCrud.getResourceFromId("req_01"); +``` + +The function will return the resource as [`AvailableResource`](./types/internal_types.js) or `undefined`, if no resource with the given ID exists. + +#### `getResourceFromReference` + +To get the resource, which is references by a resource reference, pass a ResourceReference object to the function `getResourceFromReference`: + +```javascript +resourceReference = { + attributes: { + xlink_href: "req_m_01", + ... + } +}; // typically requested via stmdCrud.getResourceReferences(...) + +resource = stmdCrud.getResourceFromReference(resourceReference); +``` + +The function will return the resource with the ID "req_m_01". + +#### `getResourceReferences` + +To get all resources references of a specific location, pass the location (the list of enclosing parent elements) to `getResourceReferences`: + +```javascript +location = ["stmd:SimulationTaskMetaData", "stmd:RequirementsPhase", "stmd:DefineModelRequirements", "stc:Output"]; + +availableResources = stmdCrud.getResourceReferences(location); +``` +The function returns an array of type [`AvailableResourceReference`](./types/internal_types.js). + +If you want to get additionally all resources references of the sub-elements, pass `true` as second argument: + +```javascript +location = ["stmd:SimulationTaskMetaData", "stmd:RequirementsPhase", "stmd:DefineModelRequirements"]; + +availableResources = stmdCrud.getResourceReferences(location, true); +``` +In this case, the resources references of all particles of `stmd:DefineModelRequirements` are returned, i.e., all resource references in `stc:Input`, `stc:Procedure`, `stc:Output` and `stc:Rationale`. + +#### `getLinks` + +To get all Link elements (converted stc:Link) of a given parent element in the STMD, pass the location of the enclosing parent elements to `getLinks`: + +**Please note**: The Link elements below the LinksType element (stc:Links) are returned here, so stc:Links must not be part of the location! + +```javascript +location = ["stmd:SimulationTaskMetaData", "stmd:ImplementationPhase", "stmd:IntegrateSimulation"]; + +availableLinks = stmdCrud.getLinks(location); +``` + +The function returns an array of type [`AvailableLinks`](./types/internal_types.js). + +If you want to get all links of sub-elements, pass `true` as second argument: + +```javascript +location = ["stmd:SimulationTaskMetaData", "stmd:ImplementationPhase"]; + +availableLinks = stmdCrud.getLinks(location, true); +``` +In this case, the links of all steps of `stmd:ImplementationPhase` are returned, i.e., all resource references in `stc:Input`, `stc:Procedure`, `stc:Output` and `stc:Rationale`. + +#### `getLifeCycleEntries` + +To get all lify cycle entries (converted stc:Drafted, stc:Defined, stc:Validated, ...) of a specific location, pass the the location of the stc:LifeCycleInformation element to `getLifeCycleEntries`: + +```javascript +location = ["stmd:SimulationTaskMetaData", "stmd:ImplementationPhase"]; + +availableLifecycleEntries = stmdCrud.getLifeCycleEntries(location); +``` +The function will return an array of [`AvailableLifeCycleEntry`](./types/internal_types.js) objects. Depending on which LifeCycleEntryTypes are given, the single objets carry the name of the LifeCycleEntryTypes as `status` argument: +```javascript +// example for availableLifecycleEntries, as requested above +[ + { + location: ["stmd:SimulationTaskMetaData", "stmd:ImplementationPhase"], + status: "Validated", + lifecycleEntry: { ... } + }, + { + location: ["stmd:SimulationTaskMetaData", "stmd:ImplementationPhase"], + status: "Approved", + lifecycleEntry: { ... } + }, +] +``` + +#### `getClassifications` + +To get all classification elements (converted stc:Classification) of a given parent element, pass the location (the list of enclosing parent elements) to `getClassifications`: + +```javascript +location = ["stmd:SimulationTaskMetaData", "stmd:RequirementsPhase", "stmd:VerifyRequirements"]; + +classifications = stmdCrud.getClassifications(location); +``` +The function returns an array of type [`Classification`](./types/specification.js). + +**Please note**: Classifications inside Resources or ResourceReferences can not be requested with this method. For this purpose, use [`getResources`](#getresources) and explore its child elements. + +#### `getAnnotations` + +To get all annotation elements (converted stc:Annotation) of a given parent element, pass the location (the list of enclosing parent elements) to `getAnnotations`: + +```javascript +location = ["stmd:SimulationTaskMetaData", "stmd:RequirementsPhase", "stmd:VerifyRequirements"]; + +annotations = stmdCrud.getAnnotations(location); +``` +The function returns an array of type [`Annotation`](./types/specification.js). + +**Please note**: Classifications inside Resources or ResourceReferences can not be requested with this method. For this purpose, use [`getResources`](#getresources) and explore its child elements. + +#### `getTopLevelInformation` + +To get the top-level information of the STMD (version, name, GUID, author, generationTool, etc.), use `getTopLevelInformation`: + +```javascript +stmdCrud.getTopLevelInformation(); +``` +The function will return an object of type [`SimulationTaskMetaDataAttributes`](./types/specification.js). + +#### `getGeneralInformation` + +To get the general information of the STMD (contains the derivation chain of the STMD), use `getGeneralInformation`: + +```javascript +stmdCrud.getGeneralInformation(); +``` +The function will return an object of type [`GeneralInformationType`](./types/specification.js). + + +### Update elements + +#### `updateResource` + +To update a specific resource, pass the UID of the available resource, the updated resource object and the target location to `updateResource`: + +(**Please note**: The UID is not the same as the ID of the resource, given in its attributes. The latter one is optional and therefore it can't be used as a unique reference. The UID is an internal UID, allocated within the instance of the STMD CRUD class. It can be identified by calling [`getResources`](#getresources)). + +```javascript +// the user wants to update the following resource: +availableResource = { + uid: "9afb41b78ea6b", + location: ["stmd:SimulationTaskMetaData", "stmd:EvaluationPhase", "stmd:EvaluateSimulationResults", "stc:Output"], + resource: { + attributes: { + kind: "result", + type: "application/json", + id: "result_uncertainty_quantification", + source: "./evaluation/20230830_uncertainty_quantification.json" + } + } +}; + +// change resource content +updatedResource = {...availableResource.resource}; +updatedResource.attributes.source = "./evaluation/20230905_uncertainty_quantification.json"; + +// set new location +newLocation = ["stmd:SimulationTaskMetaData", "stmd:EvaluationPhase", "stmd:EvaluateSimulationResults", "stc:Procedure"], + +updateSuccessful = stmdCrud.updateResource(updatedResource, newLocation, availableResource.uid); +``` + +If the UID does not exist, the operation will cancel and return `false`. + +#### `updateLink` + +To update a specific link, pass the UID of the available link, the updated link object and the target location to `updateLink`: + +(**Please note**: The UID is not the same as the ID of the resource, given in its attributes. The latter one is optional and therefore it can't be used as a unique reference. The UID is an internal UID, allocated within the instance of the STMD CRUD class. It can be identified by calling [`getLinks`](#getlinks)). + +```javascript +// the user wants to update the following link: +availableLink = { + uid: "a81e76447c3cd", + location: ["stmd:SimulationTaskMetaData", "stmd:DesignPhase", "stmd:DefineModelDesignSpecification"], + link: { + locator: [...], + arc: [...], + attributes: { + xlink_title: "requirement source of the model specification", + } + } +}; + +// change resource content +updatedLink = {...availableLink.link}; +updatedLink.Link[0].attributes.xlink_title = "any new title"; + +updateSuccessful = stmdCrud.updateLink(updatedLink, availableLink.location, availableLink.uid); +``` + +If the UID does not exist, the operation will cancel and return `false`. + +### Delete elements + +#### `deleteResource` + +To delete a specific resource, pass the UID of the resource you want to delete to `deleteResource`: + +(**Please note**: The UID is not the same as the ID of the resource, given in its attributes. The latter one is optional and therefore it can't be used as a unique reference. The UID is an internal UID, allocated within the instance of the STMD CRUD class. It can be identified by calling [`getResources`](#getresources)). + +```javascript +// the resource, the user wants to delete +availableResource = { + uid: "9afb41b78ea6b", + location: [...], + resource: {...} +}; + +deletedSuccessful = stmdCrud.deleteResource(availableResource.uid); +``` + +The operation will not only delete the resource itself, but also **all resource references** that point to this resource. + +If the UID does not exist, the operation will cancel and return `false`. + +#### `deleteResourceReference` + +To delete a specific resource reference, pass the UID of the resource reference you want to delete to `deleteResourceReference`: + +(**Please note**: The UID is not the same as the ID of the resource, given in its attributes. The latter one is optional and therefore it can't be used as a unique reference. The UID is an internal UID, allocated within the instance of the STMD CRUD class. It can be identified by calling [`getResourceReferences`](#getresourcereferences)). + +```javascript +// the resource reference, the user wants to delete +availableResourceReference = { + uid: "bb2557dc14053", + location: [...], + resourceReference: {...} +}; + +deletedSuccessful = stmdCrud.deleteResourceReference(availableResourceReference.uid); +``` + +If the UID does not exist, the operation will cancel and return `false`. + +#### `deleteLink` + +To delete a specific link, pass the UID of the link you want to delete to `deleteLink`: + +(**Please note**: The UID is not the same as the ID of the resource, given in its attributes. The latter one is optional and therefore it can't be used as a unique reference. The UID is an internal UID, allocated within the instance of the STMD CRUD class. It can be identified by calling [`getLinks`](#getlinks)). + +```javascript +// the link, the user wants to delete +availableLink = { + uid: "425f82460328e", + location: [...], + link: {...} +}; + +deletedSuccessful = stmdCrud.deleteLink(availableLink.uid); +``` + +If the UID does not exist, the operation will cancel and return `false`. + +### Exporters + +#### `export` + +To export the current instance of the STMD (including all changes done) to XML, use the `export` function: + +```javascript +xmlString = stmdCrud.export(); +``` + +#### `createGraphFromLink` + +To create a JSON-LD named graph (as defined in https://www.w3.org/TR/json-ld/#named-graphs) from a specific Link, use `createGraphFromLink`. Pass a [`Link`](./types/specification.js) and a context (as an array of [`ContextEntry`](./types/internal_types.js) elements) to give semantical meaning to the used vocabulary: + +```javascript +/* +STMD raw version of Link: + + + + + + +*/ + +// The upper raw link from the STMD will be considered as the link to translate. The STMD CRUD internal type looks the following +// (and may be requested by using stmdCrud.getLinks): +link = { + locator: [ + { + attributes: { + xlink_href: "#req_01", + xlink_label: "a_requirement", + xlink_role: "requirement" + } + }, + { + attributes: { + xlink_href: "#spec_model", + xlink_label: "according_specification", + xlink_role: "design-specification" + } + } + ], + arc: [ + { + attributes: { + xlink_from: "according_specification", + xlink_to: "a_requirement" + xlink_arcrole: "derived-from" + } + } + ], + attributes: { + xlink_title: "requirement source of the model specification", + } +}; + +// Describe context of used vocabulary +context = [ + { + term: "requirement", + iri: "https://docs.nomagic.com/display/SYSMLP190/Requirement" + }, + { + term: "design-specification", + iri: "https://dbpedia.org/page/Design_specification" + }, + { + term: "derived-from", + iri: "https://docs.nomagic.com/display/SYSMLP190/Derive" + } +]; + +stmdCrud.createGraphFromLink(link, context); +``` + +This function return the following named-graph, as a string: + +```javascript +{ + "@context": { + "requirement": "https://docs.nomagic.com/display/SYSMLP190/Requirement", + "design-specification": "https://dbpedia.org/page/Design_specification", + "derived-from": "https://docs.nomagic.com/display/SYSMLP190/Derive", + "title": "http://purl.org/dc/terms/title" + }, + "@graph": [ + { + "@id": "#req_01", + "@type": "requirement", + }, + { + "@id": "#spec_model", + "@type": "design-specification", + "derived-from": [ + "#req_01" + ] + } + ], + "title": "requirement source of the model specification" +} +``` diff --git a/workflow_utils/stmd-crud/index.js b/workflow_utils/stmd-crud/index.js new file mode 100644 index 0000000..21306ad --- /dev/null +++ b/workflow_utils/stmd-crud/index.js @@ -0,0 +1,3 @@ +const { StmdReader } = require('./src/stmd_class'); + +exports.StmdCrud = StmdReader; \ No newline at end of file diff --git a/workflow_utils/stmd-crud/package-lock.json b/workflow_utils/stmd-crud/package-lock.json new file mode 100644 index 0000000..3a29cbf --- /dev/null +++ b/workflow_utils/stmd-crud/package-lock.json @@ -0,0 +1,57 @@ +{ + "name": "stmd-reader", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "stmd-reader", + "version": "0.1.0", + "license": "BSD-2-Clause", + "dependencies": { + "fast-xml-parser": "^4.1.3" + } + }, + "node_modules/fast-xml-parser": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", + "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + } + }, + "dependencies": { + "fast-xml-parser": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", + "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "requires": { + "strnum": "^1.0.5" + } + }, + "strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + } + } +} diff --git a/workflow_utils/stmd-crud/package.json b/workflow_utils/stmd-crud/package.json new file mode 100644 index 0000000..3fd6882 --- /dev/null +++ b/workflow_utils/stmd-crud/package.json @@ -0,0 +1,14 @@ +{ + "name": "stmd-reader", + "version": "0.1.0", + "description": "glue particle reader", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "localhorst87", + "license": "BSD-2-Clause", + "dependencies": { + "fast-xml-parser": "^4.1.3" + } +} \ No newline at end of file diff --git a/workflow_utils/stmd-crud/src/constants.js b/workflow_utils/stmd-crud/src/constants.js new file mode 100644 index 0000000..239a221 --- /dev/null +++ b/workflow_utils/stmd-crud/src/constants.js @@ -0,0 +1,176 @@ +const particles = { + 'stc:Input': {}, + 'stc:Procedure': {}, + 'stc:Output': {}, + 'stc:Rationale': {} +}; + +const PHASE_TREE = { + 'stmd:SimulationTaskMetaData': { + 'stmd:AnalysisPhase': { + 'stmd:AnalyzeSimulationTaskAndObjectives': particles, + 'stmd:VerifyAnalysis': particles + }, + 'stmd:RequirementsPhase': { + 'stmd:DefineModelRequirements': particles, + 'stmd:DefineParameterRequirements': particles, + 'stmd:DefineSimulationEnvironmentRequirements': particles, + 'stmd:DefineSimulationIntegrationRequirements': particles, + 'stmd:DefineTestCaseRequirements': particles, + 'stmd:DefineQualityAssuranceRequirements': particles, + 'stmd:VerifyRequirements': particles + }, + 'stmd:DesignPhase': { + 'stmd:DefineModelDesignSpecification': particles, + 'stmd:DefineParameterDesignSpecification': particles, + 'stmd:DefineSimulationEnvironmentDesignSpecification': particles, + 'stmd:DefineSimulationIntegrationDesignSpecification': particles, + 'stmd:DefineTestCaseDesignSpecification': particles, + 'stmd:DefineQualityAssuranceDesignSpecification': particles, + 'stmd:VerifyDesignSpecification': particles + }, + 'stmd:ImplementationPhase': { + 'stmd:ImplementModel': particles, + 'stmd:ImplementParameter': particles, + 'stmd:ImplementSimulationEnvironment': particles, + 'stmd:ImplementTestCase': particles, + 'stmd:IntegrateSimulation': particles, + 'stmd:AssureSimulationSetupQuality': particles, + 'stmd:DeriveSimulationSetupQualityVerdict': particles + }, + 'stmd:ExecutionPhase': { + 'stmd:ExecuteSimulation': particles + }, + 'stmd:EvaluationPhase': { + 'stmd:EvaluateSimulationResults': particles, + 'stmd:AssureSimulationQuality': particles, + 'stmd:DeriveSimulationQualityVerdict': particles + }, + 'stmd:FulfillmentPhase': { + 'stmd:DecideSimulationObjectiveFulfillment': particles + } + } +}; + +const LIFECYCLE_ENTRY_NAMES = [ + 'stc:Drafted', + 'stc:Defined', + 'stc:Validated', + 'stc:Approved', + 'stc:Archived', + 'stc:Retracted' +]; + +const STEP_CHILDREN = { + 'stc:Input': {}, + 'stc:Procedure': {}, + 'stc:Output': {}, + 'stc:Rationale': {}, + 'stc:Links': {}, + 'stc:LifeCycleInformation': {}, + 'stc:Classification': {}, + 'stc:Annotations': {} +} + +const STMD_TREE = { + '?xml': {}, + 'stmd:SimulationTaskMetaData': { + 'stmd:GeneralInformation': { + 'stc:DerivationChain': {}, + 'stc:Links': {} + }, + 'stmd:AnalysisPhase': { + 'stmd:AnalyzeSimulationTaskAndObjectives': STEP_CHILDREN, + 'stmd:VerifyAnalysis': STEP_CHILDREN, + 'stc:Links': {}, + 'stc:LifeCycleInformation': {}, + 'stc:Classification': {}, + 'stc:Annotations': {} + }, + 'stmd:RequirementsPhase': { + 'stmd:DefineModelRequirements': STEP_CHILDREN, + 'stmd:DefineParameterRequirements': STEP_CHILDREN, + 'stmd:DefineSimulationEnvironmentRequirements': STEP_CHILDREN, + 'stmd:DefineSimulationIntegrationRequirements': STEP_CHILDREN, + 'stmd:DefineTestCaseRequirements': STEP_CHILDREN, + 'stmd:DefineQualityAssuranceRequirements': STEP_CHILDREN, + 'stmd:VerifyRequirements': STEP_CHILDREN, + 'stc:Links': {}, + 'stc:LifeCycleInformation': {}, + 'stc:Classification': {}, + 'stc:Annotations': {} + }, + 'stmd:DesignPhase': { + 'stmd:DefineModelDesignSpecification': STEP_CHILDREN, + 'stmd:DefineParameterDesignSpecification': STEP_CHILDREN, + 'stmd:DefineSimulationEnvironmentDesignSpecification': STEP_CHILDREN, + 'stmd:DefineSimulationIntegrationDesignSpecification': STEP_CHILDREN, + 'stmd:DefineTestCaseDesignSpecification': STEP_CHILDREN, + 'stmd:DefineQualityAssuranceDesignSpecification': STEP_CHILDREN, + 'stmd:VerifyDesignSpecification': STEP_CHILDREN, + 'stc:Links': {}, + 'stc:LifeCycleInformation': {}, + 'stc:Classification': {}, + 'stc:Annotations': {} + }, + 'stmd:ImplementationPhase': { + 'stmd:ImplementModel': STEP_CHILDREN, + 'stmd:ImplementParameter': STEP_CHILDREN, + 'stmd:ImplementSimulationEnvironment': STEP_CHILDREN, + 'stmd:ImplementTestCase': STEP_CHILDREN, + 'stmd:IntegrateSimulation': STEP_CHILDREN, + 'stmd:AssureSimulationSetupQuality': STEP_CHILDREN, + 'stmd:DeriveSimulationSetupQualityVerdict': STEP_CHILDREN, + 'stc:Links': {}, + 'stc:LifeCycleInformation': {}, + 'stc:Classification': {}, + 'stc:Annotations': {} + }, + 'stmd:ExecutionPhase': { + 'stmd:ExecuteSimulation': STEP_CHILDREN, + 'stc:Links': {}, + 'stc:LifeCycleInformation': {}, + 'stc:Classification': {}, + 'stc:Annotations': {} + }, + 'stmd:EvaluationPhase': { + 'stmd:EvaluateSimulationResults': STEP_CHILDREN, + 'stmd:AssureSimulationQuality': STEP_CHILDREN, + 'stmd:DeriveSimulationQualityVerdict': STEP_CHILDREN, + 'stc:Links': {}, + 'stc:LifeCycleInformation': {}, + 'stc:Classification': {}, + 'stc:Annotations': {} + }, + 'stmd:FulfillmentPhase': { + 'stmd:DecideSimulationObjectiveFulfillment': STEP_CHILDREN, + 'stc:Links': {}, + 'stc:LifeCycleInformation': {}, + 'stc:Classification': {}, + 'stc:Annotations': {} + }, + 'stc:Classification': {}, + 'stc:Annotations': {} + } +}; + +const ROOT_ELEMENT_NAME = 'stmd:SimulationTaskMetaData'; +const LIFECYCLE_PARENT_NAME = 'stc:LifeCycleInformation'; +const LINKS_PARENT_NAME = 'stc:Links'; +const GENERAL_INFORMATION_NAME = 'stmd:GeneralInformation'; +const CLASSIFICATION_PARENT_NAME = 'stc:Classification'; +const ANNOTATIONS_PARENT_NAME = 'stc:Annotations'; +const RESOURCE_NAME = 'stc:Resource'; +const RESOUCE_REFERENCE_NAME = 'stc:ResourceReference'; + +exports.STMD_TREE = STMD_TREE; +exports.PHASE_TREE = PHASE_TREE; +exports.ROOT_ELEMENT_NAME = ROOT_ELEMENT_NAME; +exports.LIFECYCLE_ENTRY_NAMES = LIFECYCLE_ENTRY_NAMES; +exports.LIFECYCLE_PARENT_NAME = LIFECYCLE_PARENT_NAME; +exports.LINKS_PARENT_NAME = LINKS_PARENT_NAME; +exports.GENERAL_INFORMATION_NAME = GENERAL_INFORMATION_NAME; +exports.CLASSIFICATION_PARENT_NAME = CLASSIFICATION_PARENT_NAME; +exports.ANNOTATIONS_PARENT_NAME = ANNOTATIONS_PARENT_NAME; +exports.RESOURCE_NAME = RESOURCE_NAME; +exports.RESOUCE_REFERENCE_NAME = RESOUCE_REFERENCE_NAME; \ No newline at end of file diff --git a/workflow_utils/stmd-crud/src/converted_to_converted_extractors.js b/workflow_utils/stmd-crud/src/converted_to_converted_extractors.js new file mode 100644 index 0000000..aa328f1 --- /dev/null +++ b/workflow_utils/stmd-crud/src/converted_to_converted_extractors.js @@ -0,0 +1,156 @@ +const util = require('./util'); + +/** + * @typedef {import('../types/specification').ResourceType} ResourceType + * @typedef {import('../types/specification').ResourceTypeAttributes} ResourceTypeAttributes + * @typedef {import('../types/specification').ResourceReference} ResourceReference + * @typedef {import('../types/specification').ResourceReferenceAttributes} ResourceReferenceAttributes + * @typedef {import('../types/specification').Classification} Classification + * @typedef {import('../types/specification').ClassificationEntry} ClassificationEntry + * @typedef {import('../types/specification').Annotations} Annotations + * @typedef {import('../types/specification').Annotation} Annotation + * @typedef {import('../types/specification').LinksType} LinksType + * @typedef {import('../types/specification').Link} Link + * @typedef {import('../types/specification').Locator} Locator + * @typedef {import('../types/specification').Arc} Arc + * @typedef {import('../types/internal_types').AvailableLink} AvailableLink + * @typedef {import('../types/internal_types').AvailableResource} AvailableResource + * @typedef {import('../types/internal_types').AvailableResourceReference} AvailableResourceReference + * @typedef {import('../types/internal_types').AvailableClassification} AvailableClassification + */ + +/** + * Extracts all corresponding locators from an Arc + * + * @param {Arc} arc + * @param {Locator[]} locators + * @returns {object} start and end locators + */ +function extractLocatorsFromArc(arc, locators) { + let startLocators, endLocators; + + let startLocatorLabel = arc.attributes.xlink_from; + let endLocatorLabel = arc.attributes.xlink_to; + + if (startLocatorLabel === undefined) { + // according to https://www.w3.org/TR/xlink11/#xlink-arcs + // If no value is supplied for a from or to attribute, the missing value is interpreted as standing for + // all the labels supplied on locator-type elements in that extended-type element. + startLocators = locators; + // at least two locators must be present to fulfill the XSD. So no need to check for empty array here + } + else { + // may be more than one, as a Locator label doesn't necessarily need to be unique, + // according to https://www.w3.org/TR/xlink11/#xlink-arcs + startLocators = locators.filter(locator => locator.attributes.xlink_label == arc.attributes.xlink_from); + if (startLocators.length == 0) + throw("Invalid Link: Locator with label " + arc.attributes.xlink_from + " is not existing!") + } + + if (endLocatorLabel === undefined) { + // same rules apply to end locators as for start locators (see above) + endLocators = locators; + } + else { + // same rules apply to end locators as for start locators (see above) + endLocators = locators.filter(locator => locator.attributes.xlink_label == arc.attributes.xlink_to); + if (endLocators.length == 0) + throw("Invalid Link: Locator with label " + arc.attributes.xlink_to + " is not existing!") + } + + return { + start: startLocators, + end: endLocators + }; +} + +/** + * Convert Resource array to AvailableResource array, based on the given location + * + * @param {ResourceType[]} resources + * @param {string[]} location + * @returns {AvailableResource[]} + */ +function convertToAvailableResources(resources, location) { + return resources.map(res => { + return { + uid: util.generateRandomId(), + location: location, + resource: res + } + }); +} + +/** + * Convert ResourceReference array to AvailableResourceReference array, based on the given location + * + * @param {ResourceReference[]} resourceReferences + * @param {string[]} location + * @returns {AvailableResourceReference[]} + */ +function convertToAvailableResourceReferences(resourceReferences, location) { + return resourceReferences.map(resRef => { + return { + uid: util.generateRandomId(), + location: location, + resourceReference: resRef + } + }); +} + +/** + * Converts a Link array to an AvailableLink array, based on the given location + * + * @param {Link[]} links + * @param {string[]} location + * @returns {AvailableLink[]} + */ +function convertToAvailableLinks(links, location) { + return links.map(link => { + return { + uid: util.generateRandomId(), + location: location, + link: link + } + }); +} + +/** + * Converts a Classification array into an AvailableClassification array, based on the given location + * + * @param {Classification[]} classifications + * @param {string[]} location + * @returns {AvailableClassification[]} + */ +function convertToAvailableClassifications(classifications, location) { + return classifications.map(classification => { + return { + uid: util.generateRandomId(), + location: location, + classification: classification + } + }); +} + +/** + * Converts an Annotation array into an AvailableAnnotation array, based on the given location + * @param {Annotation[]} annotations + * @param {string[]} location + * @returns {AvailableAnnotation[]} + */ +function convertToAvailableAnnotations(annotations, location) { + return annotations.map(annotation => { + return { + uid: util.generateRandomId(), + location: location, + annotation: annotation + } + }); +} + +exports.extractLocatorsFromArc = extractLocatorsFromArc; +exports.convertToAvailableResources = convertToAvailableResources; +exports.convertToAvailableResourceReferences = convertToAvailableResourceReferences; +exports.convertToAvailableLinks = convertToAvailableLinks; +exports.convertToAvailableClassifications = convertToAvailableClassifications; +exports.convertToAvailableAnnotations = convertToAvailableAnnotations; \ No newline at end of file diff --git a/workflow_utils/stmd-crud/src/converted_to_parsed_extractors.js b/workflow_utils/stmd-crud/src/converted_to_parsed_extractors.js new file mode 100644 index 0000000..f572d2f --- /dev/null +++ b/workflow_utils/stmd-crud/src/converted_to_parsed_extractors.js @@ -0,0 +1,511 @@ +/** + * @typedef {import('../types/specification').SimulationTaskMetaDataAttributes} SimulationTaskMetaDataAttributes + * @typedef {import('../types/specification').GeneralInformationType} GeneralInformationType + * @typedef {import('../types/specification').DerivationChain} DerivationChain + * @typedef {import('../types/specification').DerivationChainEntry} DerivationChainEntry + * @typedef {import('../types/specification').ResourceType} ResourceType + * @typedef {import('../types/specification').ResourceTypeAttributes} ResourceTypeAttributes + * @typedef {import('../types/specification').ResourceReference} ResourceReference + * @typedef {import('../types/specification').ResourceReferenceAttributes} ResourceReferenceAttributes + * @typedef {import('../types/specification').ContentType} ContentType + * @typedef {import('../types/specification').Summary} Summary + * @typedef {import('../types/specification').SummaryAttributes} SummaryAttributes + * @typedef {import('../types/specification').MetaData} MetaData + * @typedef {import('../types/specification').MetaDataAttributes} MetaDataAttributes + * @typedef {import('../types/specification').SignatureType} SignatureType + * @typedef {import('../types/specification').SignatureTypeAttributes} SignatureTypeAttributes + * @typedef {import('../types/specification').GElementCommon} GElementCommon + * @typedef {import('../types/specification').Classification} Classification + * @typedef {import('../types/specification').ClassificationAttributes} ClassificationAttributes + * @typedef {import('../types/specification').ClassificationEntry} ClassificationEntry + * @typedef {import('../types/specification').ClassificationEntryAttributes} ClassificationEntryAttributes + * @typedef {import('../types/specification').Annotations} Annotations + * @typedef {import('../types/specification').Annotation} Annotation + * @typedef {import('../types/specification').AnnotationAttributes} AnnotationAttributes + * @typedef {import('../types/specification').LinksType} LinksType + * @typedef {import('../types/specification').Link} Link + * @typedef {import('../types/specification').LinkAttributes} LinkAttributes + * @typedef {import('../types/specification').Locator} Locator + * @typedef {import('../types/specification').LocatorAttributes} LocatorAttributes + * @typedef {import('../types/specification').Arc} Arc + * @typedef {import('../types/specification').ArcAttributes} ArcAttributes + * @typedef {import('../types/specification').LifeCycleInformationType} LifeCycleInformationType + * @typedef {import('../types/specification').LifeCycleEntryType} LifeCycleEntryType + * @typedef {import('../types/specification').ResponsibleType} ResponsibleType + */ + +/** + * @param {SimulationTaskMetaDataAttributes} topLevelInformation + * @returns {object} + */ +function transformTopLevelInformation(topLevelInformation) { + return { + '@_version': topLevelInformation.version, + '@_name': topLevelInformation.name, + '@_GUID': topLevelInformation.GUID, + '@_id': topLevelInformation.id, + '@_description': topLevelInformation.description, + '@_author': topLevelInformation.author, + '@_fileversion': topLevelInformation.fileversion, + '@_copyright': topLevelInformation.copyright, + '@_license': topLevelInformation.license, + '@_generationTool': topLevelInformation.generationTool, + '@_generationDateAndTime': topLevelInformation.generationDateAndTime + }; +} + +/** + * + * @param {GeneralInformationType} generalInformation + * @returns {object} + */ +function transformGeneralInformation(generalInformation) { + return { + 'stc:DerivationChain': generalInformation.DerivationChain !== undefined ? transformDerivationChain(generalInformation.DerivationChain) : undefined, + 'stc:Links': generalInformation.Links !== undefined ? transformLinks(generalInformation.Links) : undefined + }; +} + +/** + * + * @param {DerivationChain} derivationChain + * @returns {object} + */ +function transformDerivationChain(derivationChain) { + let derivationChainEntries = []; + for (let entry of derivationChain.DerivationChainEntry) { + derivationChainEntries.push(transformDerivationChainEntry(entry)) + } + + return { + 'stc:DerivationChainEntry': derivationChainEntries.length > 0 ? derivationChainEntries : undefined + }; +} + +/** + * + * @param {DerivationChainEntry} derivationChainEntry + * @returns {object} + */ +function transformDerivationChainEntry(derivationChainEntry) { + return { + '@_GUID': derivationChainEntry.attributes !== undefined ? derivationChainEntry.attributes.GUID : undefined, + '@_author': derivationChainEntry.attributes !== undefined ? derivationChainEntry.attributes.author : undefined, + '@_fileversion': derivationChainEntry.attributes !== undefined ? derivationChainEntry.attributes.fileversion : undefined, + '@_copyright': derivationChainEntry.attributes !== undefined ? derivationChainEntry.attributes.copyright : undefined, + '@_license': derivationChainEntry.attributes !== undefined ? derivationChainEntry.attributes.license : undefined, + '@_generationTool': derivationChainEntry.attributes !== undefined ? derivationChainEntry.attributes.generationTool : undefined, + '@_generationDateAndTime': derivationChainEntry.attributes !== undefined ? derivationChainEntry.attributes.generationDateAndTime : undefined + }; +} + +// /** +// * +// * @param {LifeCycleInformationType} lifeCycleInformation +// * @returns {object} +// */ +// function transformLifeCycleInformation(lifeCycleInformation) { +// return { +// 'stc:Drafted': lifeCycleInformation.Drafted !== undefined ? transformLifeCycleEntry(lifeCycleInformation.Drafted) : undefined, +// 'stc:Defined': lifeCycleInformation.Defined !== undefined ? transformLifeCycleEntry(lifeCycleInformation.Defined) : undefined, +// 'stc:Validated': lifeCycleInformation.Validated !== undefined ? transformLifeCycleEntry(lifeCycleInformation.Validated) : undefined, +// 'stc:Approved': lifeCycleInformation.Validated !== undefined ? transformLifeCycleEntry(lifeCycleInformation.Approved) : undefined, +// 'stc:Archived': lifeCycleInformation.Archived !== undefined ? transformLifeCycleEntry(lifeCycleInformation.Archived) : undefined, +// 'stc:Retraced': lifeCycleInformation.Retracted !== undefined ? transformLifeCycleEntry(lifeCycleInformation.Retracted) : undefined +// }; +// } + +/** + * + * @param {LifeCycleEntryType} lifeCycleEntry + * @returns {object} + */ +function transformLifeCycleEntry(lifeCycleEntry) { + let resources = []; + if (lifeCycleEntry.Resource !== undefined) { + for (let resource of lifeCycleEntry.Resource) { + resources.push(transformResource(resource)); + } + } + + let references = []; + if (lifeCycleEntry.ResourceReference !== undefined) { + for (let reference of lifeCycleEntry.ResourceReference) { + references.push(transformResourceReference(reference)); + } + } + + return { + '@_date': lifeCycleEntry.attributes !== undefined ? lifeCycleEntry.attributes.date : undefined, + '@_checksum': lifeCycleEntry.attributes !== undefined ? lifeCycleEntry.attributes.checksum : undefined, + '@_checksumType': lifeCycleEntry.attributes !== undefined ? lifeCycleEntry.attributes.checksumType : undefined, + 'stc:Resource': resources.length > 0 ? resources : undefined, + 'stc:ResourceReference': references.length > 0 ? references : undefined, + 'stc:Responsible': transformResponsible(lifeCycleEntry.Responsible), + 'stc:Signature': lifeCycleEntry.Signature !== undefined ? transformSignature(lifeCycleEntry.Signature) : undefined, + 'stc:Annotations': lifeCycleEntry.Annotations !== undefined ? transformAnnotations(lifeCycleEntry.Annotations) : undefined, + 'stc:Classification': lifeCycleEntry.Classification !== undefined ? transformClassification(lifeCycleEntry.Classification) : undefined + } +} + +/** + * + * @param {ResponsibleType} responsible + * @returns {object} + */ +function transformResponsible(responsible) { + return { + '@_organization': responsible.attributes !== undefined ? responsible.attributes.organization : undefined, + '@_role': responsible.attributes !== undefined ? responsible.attributes.role : undefined, + '@_name': responsible.attributes !== undefined ? responsible.attributes.name : undefined + }; +} + +/** + * Transforms a ResourceType into a raw-parsed stc:Resource object + * + * @param {ResourceType} resource tranformed Resource + * @returns {object} raw-parsed stc:Resource object + */ +function transformResource(resource) { + let signatures; + if (resource.Signature !== undefined) { + signatures = []; + for (let signature of resource.Signature) + signatures.push(transformSignature(signature)); + } + + let metaDatas; + if (resource.MetaData !== undefined) { + metaDatas = []; + for (let metaData of resource.MetaData) + metaDatas.push(transformMetaData(metaData)); + } + + let classifications; + if (resource.Classification !== undefined) { + classifications = []; + for (let classification of resource.Classification) + classifications.push(transformClassification(classification)); + } + + return { + '@_kind': resource.attributes.kind, + '@_type': resource.attributes.type, + '@_id': resource.attributes.id, + '@_description': resource.attributes.description, + '@_master': resource.attributes.master, + '@_scope': resource.attributes.scope, + '@_source': resource.attributes.source, + 'stc:Content': resource.Content !== undefined ? transformContent(resource.Content) : undefined, + 'stc:Signature': signatures, + 'stc:MetaData': metaDatas, + 'stc:Summary': resource.Summary !== undefined ? transformSummary(resource.Summary) : undefined, + 'stc:Annotations': resource.Annotations !== undefined ? transformAnnotations(resource.Annotations) : undefined, + 'stc:Classification': classifications + }; +} + +/** + * + * @param {ResourceReference} resourceReference + * @returns + */ +function transformResourceReference(resourceReference) { + let classifications; + if (resourceReference.Classification !== undefined) { + classifications = []; + for (let classification of resourceReference.Classification) + classifications.push(transformClassification(classification)); + } + + return { + '@_xlink:type': resourceReference.attributes.xlink_type, + '@_xlink:href': resourceReference.attributes.xlink_href, + '@_id': resourceReference.attributes.id, + '@_description': resourceReference.attributes.description, + 'stc:Classification': classifications, + 'stc:Annotations': resourceReference.Annotations !== undefined ? transformAnnotations(resourceReference.Annotations) : undefined + }; +} + +/** + * + * @param {LinksType} links + * @returns {object} + */ +function transformLinks(links) { + let linkRaw = []; + // links.Link contains at least 1 element + for (let link of links.Link) { + linkRaw.push(transformLink(link)); + } + + return { + 'stc:Link': linkRaw + }; +} + +/** + * + * @param {Link} link + * @returns {object} + */ +function transformLink(link) { + let locators = []; + // link.Locator contains at least 2 elements + for (let locator of link.Locator) { + locators.push(transformLocator(locator)) + }; + + let arcs = []; + if (link.Arc !== undefined) { + for (let arc of link.Arc) { + arcs.push(transformArc(arc)) + } + } + + return { + '@_xlink:type': link.attributes !== undefined ? link.attributes.xlink_type : undefined, + '@_xlink:title': link.attributes !== undefined ? link.attributes.xlink_title : undefined, + '@_xlink:role': link.attributes !== undefined ? link.attributes.xlink_role : undefined, + 'stc:Locator': locators, + 'stc:Arc': arcs.length > 0 ? arcs : undefined + }; +} + +/** + * + * @param {Locator} locator + * @returns {object} + */ +function transformLocator(locator) { + return { + '@_xlink:type': locator.attributes !== undefined ? locator.attributes.xlink_type : undefined, + '@_xlink:href': locator.attributes !== undefined ? locator.attributes.xlink_href : undefined, + '@_xlink:label': locator.attributes !== undefined ? locator.attributes.xlink_label : undefined, + '@_xlink:title': locator.attributes !== undefined ? locator.attributes.xlink_title : undefined, + '@_xlink:role': locator.attributes !== undefined ? locator.attributes.xlink_role : undefined + }; +} + +/** + * + * @param {Arc} arc + * @returns {object} + */ +function transformArc(arc) { + return { + '@_xlink:type': arc.attributes !== undefined ? arc.attributes.xlink_type : undefined, + '@_xlink:from': arc.attributes !== undefined ? arc.attributes.xlink_from : undefined, + '@_xlink:to': arc.attributes !== undefined ? arc.attributes.xlink_to : undefined, + '@_xlink:title': arc.attributes !== undefined ? arc.attributes.xlink_title : undefined, + '@_xlink:arcrole': arc.attributes !== undefined ? arc.attributes.xlink_arcrole : undefined + } +} + +/** + * Transforms a ContentType into a raw-parsed stc:Content object + * + * @param {ContentType} content + * @returns {object} + */ +function transformContent(content) { + let contentRaw = {}; + + if (content.any !== undefined) { + if (typeof(content.any) === "object") { + for (let childName of Object.keys(content.any)) { + contentRaw[childName] = content.any[childName]; + } + } + else if (typeof(content.any) === "string") { + contentRaw["#text"] = content.any; + } + } + + return contentRaw; +} + +/** + * + * @param {SignatureType} signature + * @returns {object} + */ +function transformSignature(signature) { + let classifications; + if (signature.Classification !== undefined) { + classifications = []; + for (let classification of signature.Classification) + classifications.push(transformClassification(classification)); + } + + return { + '@_role': signature.attributes !== undefined ? signature.attributes.role : undefined, + '@_type': signature.attributes !== undefined ? signature.attributes.type : undefined, + '@_source': signature.attributes !== undefined ? signature.attributes.source : undefined, + '@_sourceBase': signature.attributes !== undefined ? signature.attributes.sourceBase : undefined, + 'stc:Content': transformContent(signature.Content), + 'stc:Annotations': transformAnnotations(signature.Annotations), + 'stc:Classification': classifications + }; +} + +/** + * + * @param {MetaData} metaData + * @returns {object} + */ +function transformMetaData(metaData) { + let signatures; + if (metaData.Signature !== undefined) { + signatures = []; + for (let signature of metaData.Signature) + signatures.push(transformSignature(signature)); + } + + let classifications; + if (metaData.Classification !== undefined) { + classifications = []; + for (let classification of metaData.Classification) + classifications.push(transformClassification(classification)); + } + + return { + '@_kind': metaData.attributes !== undefined ? metaData.attributes.kind : undefined, + '@_type': metaData.attributes !== undefined ? metaData.attributes.type : undefined, + '@_source': metaData.attributes !== undefined ? metaData.attributes.source : undefined, + '@_sourceBase': metaData.attributes !== undefined ? metaData.attributes.sourceBase : undefined, + 'stc:Content': metaData.Content !== undefined ? transformContent(metaData.Content) : undefined, + 'stc:Signature': signatures, + 'stc:Annotations': metaData.Annotations !== undefined ? transformAnnotations(metaData.Annotations) : undefined, + 'stc:Classification': classifications + }; +} + +/** + * + * @param {Summary} summary + * @returns {object} + */ +function transformSummary(summary) { + let signatures; + if (summary.Signature !== undefined) { + signatures = []; + for (let signature of summary.Signature) + signatures.push(transformSignature(signature)); + } + + let classifications; + if (summary.Classification !== undefined) { + classifications = []; + for (let classification of summary.Classification) + classifications.push(transformClassification(classification)); + } + + return { + '@_type': summary.attributes !== undefined ? summary.attributes.type : undefined, + '@_source': summary.attributes !== undefined ? summary.attributes.source : undefined, + '@_sourceBase': summary.attributes !== undefined ? summary.attributes.sourceBase : undefined, + 'stc:Content': summary.Content !== undefined ? transformContent(summary.Content) : undefined, + 'stc:Signature': signatures, + 'stc:Annotations': summary.Annotations !== undefined ? transformAnnotations(summary.Annotations) : undefined, + 'stc:Classification': classifications + }; +} + +/** + * + * @param {Annotations} annotations + * @returns {object} + */ +function transformAnnotations(annotations) { + let annotationsRaw = []; + // annotations.Annotation contains at least 1 element + for (let annotation of annotations.Annotation) { + annotationsRaw.push(transformAnnotation(annotation)); + } + + return { + 'ssc:Annotation': annotationsRaw + }; +} + +/** + * + * @param {Annotation} annotation + * @returns {object} + */ +function transformAnnotation(annotation) { + let annotationRaw = { + '@_type': annotation.attributes.type + }; + + if (annotation.any !== undefined) { + if (typeof(annotation.any) === "object") { + for (let childName of Object.keys(annotation.any)) { + annotationRaw[childName] = annotation.any[childName]; + } + } + else if (typeof(annotation.any) === "string") { + annotationRaw['#text'] = annotation.any; + } + } + + return annotationRaw; +} + +/** + * + * @param {Classification} classification + * @returns {object} + */ +function transformClassification(classification) { + let classificationEntryRaw = []; + + if (classification.ClassificationEntry !== undefined) { + for (let classificationEntry of classification.ClassificationEntry) { + classificationEntryRaw.push(transformClassificationEntry(classificationEntry)) + } + } + + return { + '@_type': classification.attributes.type, + 'stc:ClassificationEntry': classificationEntryRaw.length > 0 ? classificationEntryRaw : undefined + }; +} + +/** + * + * @param {ClassificationEntry} classificationEntry + * @returns {object} + */ +function transformClassificationEntry(classificationEntry) { + let classificationEntryRaw = { + '@_keyword': classificationEntry.attributes !== undefined ? classificationEntry.attributes.keyword : undefined, + '@_xlink:type': classificationEntry.attributes !== undefined ? classificationEntry.attributes.xlink_type : undefined, + '@_xlink:href': classificationEntry.attributes !== undefined ? classificationEntry.attributes.xlink_href : undefined, + }; + + if (classificationEntry.any !== undefined) { + if (typeof(classificationEntry.any) === "object") { + for (let childName of Object.keys(classificationEntry.any)) { + classificationEntryRaw[childName] = classificationEntry.any[childName]; + } + } + else if (typeof(classificationEntry.any === "string")) { + classificationEntryRaw['#text'] = classificationEntry.any; + } + }; + + return classificationEntryRaw; +} + +exports.transformTopLevelInformation = transformTopLevelInformation; +exports.transformGeneralInformation = transformGeneralInformation; +exports.transformLifeCycleEntry = transformLifeCycleEntry; +exports.transformResource = transformResource; +exports.transformResourceReference = transformResourceReference; +exports.transformLink = transformLink; +exports.transformClassification = transformClassification; +exports.transformAnnotation = transformAnnotation; \ No newline at end of file diff --git a/workflow_utils/stmd-crud/src/graph_converter.js b/workflow_utils/stmd-crud/src/graph_converter.js new file mode 100644 index 0000000..7b78439 --- /dev/null +++ b/workflow_utils/stmd-crud/src/graph_converter.js @@ -0,0 +1,124 @@ +/** + * @typedef {import('../types/specification').Link} Link + * @typedef {import('../types/specification').Locator} Locator + * @typedef {import('../types/specification').Arc} Arc + * @typedef {import('../types/internal_types').ContextEntry} ContextEntry + */ + +const TITLE_IRI = "http://purl.org/dc/terms/title"; + +/** + * Creates the starting node from the Arc for a named graph, according to https://www.w3.org/TR/json-ld/#named-graphs + * + * @param {Arc} arc the converted XLink Arc + * @param {Locator} startLocator the converted XLink start Locator + * @param {Locator} endLocator the converted XLink end Locator + * @returns {object} a start node for a JSON-LD named graph + */ +function createJsonLdStartNode(arc, startLocator, endLocator) { + let node = {}; + + // xlink_href is a mandatory attribute + node["@id"] = startLocator.attributes.xlink_href; + + // xlink_role is an optional attribute to define a semantic meaning of the node + if (startLocator.attributes.xlink_role !== undefined) + node["@type"] = startLocator.attributes.xlink_role; + + // xlink_arcrole is an optional attribute to define a semantic meaning for the edge. If not indicated, the + // simple "to" from the http://www.w3.org/1999/xlink namespace is used + let arcrole = arc.attributes.xlink_arcrole !== undefined ? arc.attributes.xlink_arcrole : "to"; + node[arcrole] = [endLocator.attributes.xlink_href]; + + return node; +} + +/** + * Creates the end node of a Locator for a named graph, according to https://www.w3.org/TR/json-ld/#named-graphs + * + * @param {Locator} endLocator the converted XLink Locator + * @returns {object} an end node for a JSON-LD named graph + */ +function createJsonLdEndNode(endLocator) { + let node = {}; + + // xlink_href is a mandatory attribute + node["@id"] = endLocator.attributes.xlink_href; + + // xlink_role is an optional attribute to define a semantic meaning of the node + if (endLocator.attributes.xlink_role !== undefined) + node["@type"] = endLocator.attributes.xlink_role; + + return node; +} + +/** + * Integrates a node from the functions createJsonLdStartNode or createJsonLdEndNode into the named JSON-LD graph. + * That means, it merges existing nodes with the same ID, if necessary. + * + * @param {object} node the JSON-LD node to integrate + * @param {object} graph the JSON-LD named graph where the node should be integrated + * @returns {object} the updated JSON-Ld named graph + */ +function integrateJsonLdNodeIntoGraph(node, graph) { + // first, check if the resource is already existing as a node in the graph + let idxNode = graph["@graph"].findIndex(graphNode => graphNode["@id"] === node["@id"]); + if (idxNode == -1) { + // if node of resource is not yet included in the graph, then add it as a new node + graph["@graph"].push(node); + } + else { + // if resource is already included in the graph, integrate the node in existing node of resource + // if no arcrole (edge) applies for this node, there is nothing to do + const arcrole = Object.keys(node).filter(key => key !== "@id" && key !== "@type"); + if (arcrole.length == 0) + return graph; + + if (graph["@graph"][idxNode][arcrole] !== undefined) { + // if the arcrole property is already existing, then add the target resource in the array + graph["@graph"][idxNode][arcrole].push(...node[arcrole]); + } + else { + // if property is not yet existing, add it as a new property + graph["@graph"][idxNode][arcrole] = node[arcrole]; + } + } + + return graph; +} + + +/** + * Adds a title to the graph from the extended Link xlink:title + * + * @param {object} graph the JSON-LD named graph to update + * @param {Link} link the converted XLink where the title will be extracted from + * @returns {object} the updated JSON-LD named graph + */ +function addTitleToGraph(graph, link) { + graph["title"] = link.attributes.xlink_title; + + return graph; +} + +/** + * Adds context to the JSON-LD named graph from a key-value-pair object + * + * @param {object} graph the JSON-LD named graph to update + * @param {ContextEntry[]} context key-value-pairs mapping an IRI to a term + * @returns {object} the updated JSON-LD named graph with added context + */ +function addContextToGraph(graph, context) { + for (let contextEntry of context) + graph["@context"][contextEntry.term] = contextEntry.iri; + + graph["@context"]["title"] = TITLE_IRI; + + return graph; +} + +exports.createJsonLdStartNode = createJsonLdStartNode; +exports.createJsonLdEndNode = createJsonLdEndNode; +exports.integrateJsonLdNodeIntoGraph = integrateJsonLdNodeIntoGraph; +exports.addTitleToGraph = addTitleToGraph; +exports.addContextToGraph = addContextToGraph; \ No newline at end of file diff --git a/workflow_utils/stmd-crud/src/parsed_to_converted_extractors.js b/workflow_utils/stmd-crud/src/parsed_to_converted_extractors.js new file mode 100644 index 0000000..14f3232 --- /dev/null +++ b/workflow_utils/stmd-crud/src/parsed_to_converted_extractors.js @@ -0,0 +1,894 @@ +const { + GENERAL_INFORMATION_NAME, + RESOURCE_NAME, + RESOUCE_REFERENCE_NAME, + LINKS_PARENT_NAME, + CLASSIFICATION_PARENT_NAME, + ANNOTATIONS_PARENT_NAME, + LIFECYCLE_PARENT_NAME, + LIFECYCLE_ENTRY_NAMES } = require('./constants'); + +/** + * @typedef {import('../types/specification').SimulationTaskMetaDataAttributes} SimulationTaskMetaDataAttributes + * @typedef {import('../types/specification').GeneralInformationType} GeneralInformationType + * @typedef {import('../types/specification').DerivationChain} DerivationChain + * @typedef {import('../types/specification').DerivationChainEntry} DerivationChainEntry + * @typedef {import('../types/specification').DerivationChainEntryAttributes} DerivationChainEntryAttributes + * @typedef {import('../types/specification').ResourceType} ResourceType + * @typedef {import('../types/specification').ResourceTypeAttributes} ResourceTypeAttributes + * @typedef {import('../types/specification').ResourceReference} ResourceReference + * @typedef {import('../types/specification').ResourceReferenceAttributes} ResourceReferenceAttributes + * @typedef {import('../types/specification').ContentType} ContentType + * @typedef {import('../types/specification').Summary} Summary + * @typedef {import('../types/specification').SummaryAttributes} SummaryAttributes + * @typedef {import('../types/specification').MetaData} MetaData + * @typedef {import('../types/specification').MetaDataAttributes} MetaDataAttributes + * @typedef {import('../types/specification').SignatureType} SignatureType + * @typedef {import('../types/specification').SignatureTypeAttributes} SignatureTypeAttributes + * @typedef {import('../types/specification').GElementCommon} GElementCommon + * @typedef {import('../types/specification').Classification} Classification + * @typedef {import('../types/specification').ClassificationAttributes} ClassificationAttributes + * @typedef {import('../types/specification').ClassificationEntry} ClassificationEntry + * @typedef {import('../types/specification').ClassificationEntryAttributes} ClassificationEntryAttributes + * @typedef {import('../types/specification').Annotations} Annotations + * @typedef {import('../types/specification').Annotation} Annotation + * @typedef {import('../types/specification').AnnotationAttributes} AnnotationAttributes + * @typedef {import('../types/specification').LinksType} LinksType + * @typedef {import('../types/specification').Link} Link + * @typedef {import('../types/specification').LinkAttributes} LinkAttributes + * @typedef {import('../types/specification').Locator} Locator + * @typedef {import('../types/specification').LocatorAttributes} LocatorAttributes + * @typedef {import('../types/specification').Arc} Arc + * @typedef {import('../types/specification').ArcAttributes} ArcAttributes + * @typedef {import('../types/specification').LifeCycleInformationType} LifeCycleInformationType + * @typedef {import('../types/specification').LifeCycleEntryType} LifeCycleEntryType + * @typedef {import('../types/cdk_types').CredibilityType} CredibilityType + * @typedef {import('../types/cdk_types').EvidenceType} EvidenceType + * @typedef {import('../types/cdk_types').EvidenceAttributes} EvidenceAttributes + * @typedef {import('../types/cdk_types').MetricType} MetricType + * @typedef {import('../types/cdk_types').MetricAttributes} MetricAttributes + * @typedef {import('../types/cdk_types').TestType} TestType + * @typedef {import('../types/cdk_types').FunctionArgumentType} FunctionArgumentType + * @typedef {import('../types/cdk_types').FunctionArgumentAttributes} FunctionArgumentAttributes + * @typedef {import('../types/cdk_types').ProcessingType} ProcessingType + * @typedef {import('../types/cdk_types').SimpleProcessingType} SimpleProcessingType + * @typedef {import('../types/cdk_types').SimpleProcessingTypeAttributes} SimpleProcessingTypeAttributes + * @typedef {import('../types/cdk_types').ComlexProcessingType} ComlexProcessingType + * @typedef {import('../types/cdk_types').ComlexProcessingTypeAttributes} ComlexProcessingTypeAttributes + * @typedef {import('../types/cdk_types').ProcessingPrerequisitesType} ProcessingPrerequisitesType + * @typedef {import('../types/cdk_types').ProcessingPrerequisitesTypeAttributes} ProcessingPrerequisitesTypeAttributes + * @typedef {import('../types/cdk_types').InputsType} InputsType + * @typedef {import('../types/cdk_types').GenericInputType} GenericInputType + * @typedef {import('../types/cdk_types').GenericInputTypeAttributes} GenericInputTypeAttributes + * @typedef {import('../types/cdk_types').OutputsType} OutputsType + * @typedef {import('../types/cdk_types').FunctionOutputType} FunctionOutputType + * @typedef {import('../types/cdk_types').FunctionOutputTypeAttributes} FunctionOutputTypeAttributes + * @typedef {import('../types/cdk_types').GenericOutputType} GenericOutputType + * @typedef {import('../types/cdk_types').GenericOutputTypeAttributes} GenericOutputTypeAttributes + */ + +/** + * Extracts top level information from the STMD root element + * + * @param {object} root + * @returns {SimulationTaskMetaDataAttributes} transformed SimulationTaskMetaDataAttributes object + */ +function extractTopLevelInformation(root) { + return { + name: root['@_name'], + version: root['@_version'], + GUID: root['@_GUID'], + id: root['@_id'], + description: root['@_description'], + author: root['@_author'], + fileversion: root['@_fileversion'], + copyright: root['@_copyright'], + license: root['@_license'], + generationTool: 'SETLabs STMD Writer', + generationDateAndTime: (new Date()).toISOString() + }; +} + +function extractGeneralInformation(parent) { + let generalInformation; + if (parent[GENERAL_INFORMATION_NAME] !== undefined) + generalInformation = transformGeneralInformation(parent[GENERAL_INFORMATION_NAME]); + + return generalInformation; +} + +/** + * Extracts all Classifications from a raw parsed parent element where Classifications are located + * + * @param {object} parent raw parsed parent + * @returns {Classification[]} transformed Classification array + */ +function extractClassification(parent) { + let classifications = []; + if (parent[CLASSIFICATION_PARENT_NAME] !== undefined) + classifications = transformClassification(parent[CLASSIFICATION_PARENT_NAME]); + + return classifications; +} + +/** + * Extracts all Annotations from a raw parsed parent element where Annotations are located + * + * @param {object} parent raw parsed parent + * @returns {Annotation[]} transformed Annotation array + */ +function extractAnnotations(parent) { + let annotations = []; + if (parent[ANNOTATIONS_PARENT_NAME] !== undefined) { + let annotationsParent = transformAnnotations(parent[ANNOTATIONS_PARENT_NAME]); // returns Annotations object + annotations = annotationsParent.Annotation; + } + + return annotations; +} + +/** + * Extracts Resources from a raw parsed parent element where Resources are located (may be a Particle or a + * LifeCycleEntry) + * + * @param {object} parent raw parsed parent + * @returns {ResourceType[]} transformed ResourceType array + */ +function extractResources(parent) { + let resources = []; + if (parent[RESOURCE_NAME] !== undefined) + resources = transformResource(parent[RESOURCE_NAME]); + + return resources; +} + +/** + * Extracts ResourceReferences from a raw parsed parent element where ResourceReferences are located (may be a Particle + * or a LifeCycleEntry) + * + * @param {object} parent raw parsed parent object + * @returns {ResourceReference[]} transformed ResourceReference array + */ +function extractResourceReferences(parent) { + let resourceReferences = []; + if (parent[RESOUCE_REFERENCE_NAME] !== undefined) + resourceReferences = transformResourceReference(parent[RESOUCE_REFERENCE_NAME]); + + return resourceReferences; +} + +/** + * Extracts all Links from a raw parsed parent element (may be GeneralInformation, a Phase, or a Step) + * + * @param {object} parent raw parsed parent object + * @returns {Link[]} transformed Link array + */ +function extractLinks(parent) { + let links = []; + if (parent[LINKS_PARENT_NAME] !== undefined) { + let linksType = transformLinks(parent[LINKS_PARENT_NAME]); + links = linksType.Link; + } + + return links; +} + +/** + * Extracts the LifeCycleInformation from a raw parsed parent element (should be a Phase) + * + * @param {object} parent raw parsed parent object (should be a raw parsed phase object) + * @returns {LifeCycleInformationType} transformed LifeCylceInformation object + */ +function extractLifeCycleInformation(parent) { + let lifeCycleInformation = {}; + if (parent[LIFECYCLE_PARENT_NAME] !== undefined) { + for (let lifeCycleEntryName of LIFECYCLE_ENTRY_NAMES) { + let lifeCycleEntryObject = parent[LIFECYCLE_PARENT_NAME][lifeCycleEntryName]; + if (lifeCycleEntryObject !== undefined) { + let propertyName = lifeCycleEntryName.slice(4); // e.g.: stc:Draft --> Draft + lifeCycleInformation[propertyName] = transformLifeCycleEntry(lifeCycleEntryObject); + } + } + } + + return lifeCycleInformation; +} + +/** + * Transforms a raw parsed stmd:GeneralInformation object into a GeneralInformationType object + * + * @param {object} generalInformationObject raw parsed + * @returns {GeneralInformationType} transformed + */ +function transformGeneralInformation(generalInformationObject) { + return { + attributes: { + id: generalInformationObject['@_id'], + description: generalInformationObject['@_description'] + }, + DerivationChain: generalInformationObject['stc:DerivationChain'] !== undefined ? transformDerivationChain(generalInformationObject['stc:DerivationChain']) : undefined, + Resource: generalInformationObject[RESOURCE_NAME] !== undefined ? transformResource(generalInformationObject[RESOURCE_NAME]) : undefined, + ResourceReference: generalInformationObject[RESOUCE_REFERENCE_NAME] !== undefined ? transformResourceReference(generalInformationObject[RESOUCE_REFERENCE_NAME]) : undefined, + Links: generalInformationObject['stc:Links'] !== undefined ? transformLinks(generalInformationObject['stc:Links']) : undefined, + Classification: generalInformationObject['stc:Classification'] !== undefined ? transformClassification(generalInformationObject['stc:Classification']) : undefined, + Annotations: generalInformationObject['stc:Annotations'] !== undefined ? transformAnnotations(generalInformationObject['stc:Annotations']) : undefined + }; +} + +/** + * Transforms a raw parsed stmd:DerivationChain object into a DerivationChain object + * + * @param {object} derivationChainObject raw parsed + * @returns {DerivationChain} transformed + */ +function transformDerivationChain(derivationChainObject) { + return { + DerivationChainEntry: transformDerivationChainEntries(derivationChainObject['stc:DerivationChainEntry']) + }; +} + +/** + * Transforms a raw parsed stc:DerivationChainEntry array into a DerivationChainEntry array + * + * @param {object[]} derivationChainEntryArray raw parsed + * @returns {DerivationChainEntry[]} transformed + */ +function transformDerivationChainEntries(derivationChainEntryArray) { + let derivationChainEntries = []; + for (let derivationChainEntry of derivationChainEntryArray) { + derivationChainEntries.push({ + attributes: { + GUID: derivationChainEntry['@_GUID'], + author: derivationChainEntry['@_author'], + fileversion: derivationChainEntry['@_fileversion'], + copyright: derivationChainEntry['@_copyright'], + license: derivationChainEntry['@_license'], + generationTool: derivationChainEntry['@_generationTool'], + generationDateAndTime: derivationChainEntry['@_generationDateAndTime'] + } + }); + } + + return derivationChainEntries; +} + +/** + * Transforms a raw parsed stc:Links object into a Links array + * + * @param {object} linksObject raw parsed stc:Links object + * @returns {LinksType} transformed LinksType object + */ +function transformLinks(linksObject) { + return { + Link: transformLink(linksObject['stc:Link']) + }; +} + +/** + * Transforms a raw parsed stc:Link array into a Link array + * + * @param {object[]} linkArray raw parsed stc:Link array + * @returns {Link[]} transformed Link array + */ +function transformLink(linkArray) { + let links = []; + for (let linkObject of linkArray) { + links.push({ + attributes: { + xlink_type: "extended", + xlink_title: linkObject['@_xlink:title'], + xlink_role: linkObject['@_xlink:role'] + }, + Locator: transformLocator(linkObject['stc:Locator']), + Arc: linkObject['stc:Arc'] !== undefined ? transformArc(linkObject['stc:Arc']) : undefined + }); + } + + return links; +} + +/** + * Transforms a raw parsed stc:Locator array into a Locator array + * + * @param {object[]} locatorArray raw parsed stc:Locator array + * @returns {Locator[]} transformed Locator array + */ +function transformLocator(locatorArray) { + let locators = []; + for (let locatorObject of locatorArray) { + locators.push({ + attributes: { + xlink_type: "locator", + xlink_href: locatorObject['@_xlink:href'], + xlink_label: locatorObject['@_xlink:label'], + xlink_title: locatorObject['@_xlink:title'], + xlink_role: locatorObject['@_xlink:role'] + } + }); + } + + return locators; +} + +/** + * Transforms a raw parsed stc:Arc array into a Arc array + * + * @param {object[]} arcArray raw parsed stc:Arc array + * @returns {Arc[]} transformed Arc array + */ +function transformArc(arcArray) { + let arcs = []; + for (let arcObject of arcArray) { + arcs.push({ + attributes: { + xlink_type: "arc", + xlink_from: arcObject['@_xlink:from'], + xlink_to: arcObject['@_xlink:to'], + xlink_title: arcObject['@_xlink:title'], + xlink_arcrole: arcObject['@_xlink:arcrole'] + } + }); + } + + return arcs; +} + +/** + * Transforms a raw parsed stc:Resource array into a ResourceType array + * + * @param {object[]} resourceArray raw parsed stc:Resource array + * @returns {ResourceType[]} transformed ResourceType array + */ +function transformResource(resourceArray) { + let resources = []; + for (let resourceObject of resourceArray) { + resources.push({ + attributes: { + kind: resourceObject['@_kind'], + type: resourceObject['@_type'], + description: resourceObject['@_description'], + id: resourceObject['@_id'], + master: resourceObject['@_master'], + scope: resourceObject['@_scope'], + source: resourceObject['@_source'] + }, + Content: resourceObject['stc:Content'] !== undefined ? transformContent(resourceObject['stc:Content']) : undefined, + Summary: resourceObject['stc:Summary'] !== undefined ? transformSummary(resourceObject['stc:Summary']) : undefined, + MetaData: resourceObject['stc:MetaData'] !== undefined ? transformMetaData(resourceObject['stc:MetaData']) : undefined, + Signature: resourceObject['stc:Signature'] !== undefined ? transformSignature(resourceObject['stc:Signature']) : undefined, + Classification: resourceObject['stc:Classification'] !== undefined ? transformClassification(resourceObject['stc:Classification']) : undefined, + Annotations: resourceObject['stc:Annotations'] !== undefined ? transformAnnotations(resourceObject['stc:Annotations']) : undefined + }); + } + + return resources; +} + +/** + * Transforms a raw parsed stc:ResourceReference array into a ResourceReference array + * + * @param {object[]} resourceReferenceArray raw parsed stc:ResourceReference array + * @returns {ResourceReference[]} transformed ResourceReference array + */ +function transformResourceReference(resourceReferenceArray) { + let resourceReferences = []; + for (let resourceReferenceObject of resourceReferenceArray) { + resourceReferences.push({ + attributes: { + xlink_type: "simple", + xlink_href: resourceReferenceObject['@_xlink:href'], + id: resourceReferenceObject['@_id'], + description: resourceReferenceObject['@_description'] + }, + Classification: resourceReferenceObject['stc:Classification'] !== undefined ? transformClassification(resourceReferenceObject['stc:Classification']) : undefined, + Annotations: resourceReferenceObject['stc:Annotations'] !== undefined ? transformAnnotations(resourceReferenceObject['stc:Annotations']) : undefined + }); + } + + return resourceReferences; +} + +/** + * Transforms a raw parsed stc:Content object into a ContentType object + * + * @param {object} contentObject raw parsed stc:Content object + * @returns {ContentType} transformed ContentType object + */ +function transformContent(contentObject) { + // no attributes to prune, as Content is not allowed to have attributes + return { + any: contentObject + }; +} + +/** + * Transforms a raw parsed stc:Summary object into a Summary object + * + * @param {object} summaryObject raw parsed stc:Summary object + * @returns {Summary} transformed Summary object + */ +function transformSummary(summaryObject) { + return { + Content: summaryObject['stc:Content'] !== undefined ? transformContent(summaryObject['stc:Content']) : undefined, + Signature: summaryObject['stc:Signature'] !== undefined ? transformSignature(summaryObject['stc:Signature']) : undefined, + Classification: summaryObject['stc:Classification'] !== undefined ? transformClassification(summaryObject['stc:Classification']) : undefined, + Annotations: summaryObject['stc:Annotations'] !== undefined ? transformAnnotations(summaryObject['stc:Annotations']) : undefined, + attributes: { + type: summaryObject['@_type'], + source: summaryObject['@_source'], + sourceBase: summaryObject['@_sourceBase'] + } + }; +} + +/** + * Transforms a raw parsed stc:MetaData array into a MetaData array + * + * @param {object[]} metaDataArray raw parsed stc:MetaData array + * @returns {MetaData[]} transformed MetaData array + */ +function transformMetaData(metaDataArray) { + let metaData = []; + for (let metaDataObject of metaDataArray) { + metaData.push({ + attributes: { + kind: metaDataObject['@_kind'], + type: metaDataObject['@_type'], + source: metaDataObject['@_source'], + sourceBase: metaDataObject['@_sourceBase'] + }, + Content: metaDataObject['stc:Content'] !== undefined ? transformContent(metaDataObject['stc:Content']) : undefined, + Signature: metaDataObject['stc:Signature'] !== undefined ? transformSignature(metaDataObject['stc:Signature']) : undefined, + Classification: metaDataObject['stc:Classification'] !== undefined ? transformClassification(metaDataObject['stc:Classification']) : undefined, + Annotations: metaDataObject['stc:Annotations'] !== undefined ? transformAnnotations(metaDataObject['stc:Annotations']) : undefined, + }); + } + + return metaData; +} + +/** + * Transforms a raw parsed stc:Signature array into a SignatureType array + * + * @param {object[]} signatureArray raw parsed stc:Signature array + * @returns {SignatureType[]} transformed SignatureType array + */ +function transformSignature(signatureArray) { + let signature = []; + for (let signatureObject of signatureArray) { + signature.push({ + attributes: { + role: signatureObject['@_role'], + type: signatureObject['@_type'], + source: signatureObject['@_source'], + sourceBase: signatureObject['@_sourceBase'], + }, + Content: signatureObject['stc:Content'] !== undefined ? transformContent(signatureObject['stc:Content']) : undefined, + Classification: signatureObject['stc:Classification'] !== undefined ? transformClassification(signatureObject['stc:Classification']) : undefined, + Annotations: signatureObject['stc:Annotations'] !== undefined ? transformAnnotations(signatureObject['stc:Annotations']) : undefined, + }); + } + + return signature; +} + +/** +* Transforms a raw parsed stc:Classification array into a Classification array +* +* @param {object[]} classificationArray raw parsed stc:Classification object +* @returns {Classification[]} transformed Classification object +*/ +function transformClassification(classificationArray) { + let classification = []; + for (let classificationObject of classificationArray) { + classification.push({ + attributes: { + type: classificationObject['@_type'] + }, + ClassificationEntry: classificationObject['stc:ClassificationEntry'] !== undefined ? transformClassificationEntry(classificationObject['stc:ClassificationEntry']) : undefined, + }) + } + + return classification; +} + +/** + * Transforms a raw parsed stc:ClassificationEntry array into a ClassificationEntry array + * + * @param {object[]} classificationEntryArray raw parsed stc:ClassificationEntry array + * @returns {ClassificationEntry[]} transformed ClassificationEntry array + */ +function transformClassificationEntry(classificationEntryArray) { + let classificationEntry = []; + for (let classificationEntryObject of classificationEntryArray) { + classificationEntry.push({ + attributes: { + keyword: classificationEntryObject['@_keyword'], + xlink_type: "simple", + xlink_href: classificationEntryObject['@_xlink:href'] + }, + any: pruneObject(classificationEntryObject, ['@_keyword', '@_xlink:type', '@_xlink:href']) + }); + } + + return classificationEntry; +} + +/** +* Transforms a raw parsed stc:Annotations object into a Annotations object +* +* @param {object} annotationsObject raw parsed stc:Annotations object +* @returns {Annotations} transformed Annotations object +*/ +function transformAnnotations(annotationsObject) { + return { + Annotation: transformAnnotation(annotationsObject['ssc:Annotation']) + }; +} + +/** + * Transforms a raw parsed ssc:Annotation array into an Annotation array + * + * @param {object[]} annotationArray raw parsed ssc:Annotation array + * @returns {Annotation[]} transformed Annotation array + */ +function transformAnnotation(annotationArray) { + let annotation = []; + for (let annotationObject of annotationArray) { + annotation.push({ + attributes: { + type: annotationObject['@_type'] + }, + any: pruneObject(annotationObject, ['@_type']) + }); + } + + return annotation; +} + +/** + * Transforms a raw parsed stc:LifeCycleEntry object into a LifeCycleEntryType object + * + * @param {object} lifeCycleEntryObject raw parsed stc:LifeCycleEntry object + * @returns {LifeCycleEntryType} transformed LifeCycleEntryType object + */ +function transformLifeCycleEntry(lifeCycleEntryObject) { + return { + Responsible: transformResponsible(lifeCycleEntryObject['stc:Responsible']), + Signature: lifeCycleEntryObject['stc:Signature'] !== undefined ? transformSignature(lifeCycleEntryObject['stc:Signature']) : undefined, + Resource: lifeCycleEntryObject[RESOURCE_NAME] !== undefined ? transformResource(lifeCycleEntryObject[RESOURCE_NAME]) : undefined, + ResourceReference: lifeCycleEntryObject[RESOUCE_REFERENCE_NAME] !== undefined ? transformResourceReference(lifeCycleEntryObject[RESOUCE_REFERENCE_NAME]) : undefined, + Classification: lifeCycleEntryObject['stc:Classification'] !== undefined ? transformClassification(lifeCycleEntryObject['stc:Classification']) : undefined, + Annotations: lifeCycleEntryObject['stc:Annotations'] !== undefined ? transformAnnotations(lifeCycleEntryObject['stc:Annotations']) : undefined, + attributes: { + date: lifeCycleEntryObject['@_date'], + checksum: lifeCycleEntryObject['@_checksum'], + checksumType: lifeCycleEntryObject['@_checksumType'] + } + }; +} + +/** + * Transforms a raw parsed stc:Responsible object into a ResponsibleType object + * + * @param {object} responsibleObject raw parsed stc:Responsible object + * @returns {ResponsibleType} transformed ResponsibleType object + */ +function transformResponsible(responsibleObject) { + return { + attributes: { + organization: responsibleObject['@_organization'], + role: responsibleObject['@_role'], + name: responsibleObject['@_name'] + } + }; +} + +/** + * Prunes an object, which means it deletes all the attributes from the object + * that names are given in keysToDelete + * + * @param {object} objectToPrune + * @param {string[]} keysToDelete + * @returns {object} the pruned object + */ +function pruneObject(objectToPrune, keysToDelete) { + let prunedObject = {}; + + for (let key of Object.keys(objectToPrune)) { + if (!keysToDelete.includes(key)) + prunedObject[key] = objectToPrune[key]; + } + + return prunedObject; +} + +// -------------------- CDK specific functions ------------------------ + +/** + * @param {object} credibilityObject + * @returns {CredibilityType} + */ +function transformCdkCredibility(credibilityObject) { + return { + Processing: credibilityObject["cdk:Processing"] !== undefined ? transformCdkProcessing(credibilityObject["cdk:Processing"]) : [], + Evidence: transformCdkEvidence(credibilityObject["cdk:Evidence"]) + }; +} + +/** + * @param {object[]} evidenceArray + * @returns {EvidenceType[]} + */ +function transformCdkEvidence(evidenceArray) { + let evidences = []; + + for (let evidenceObject of evidenceArray) { + evidences.push({ + Metric: transformCdkMetric(evidenceObject["cdk:Metric"]), + attributes: { + level: evidenceObject["@_level"] + } + }); + } + + return evidences; +} + +/** + * @param {object[]} metricArray + * @returns {MetricType[]} + */ +function transformCdkMetric(metricArray) { + let metrics = []; + + for (let metricObject of metricArray) { + metrics.push({ + Test: transformCdkTest(metricObject["cdk:Test"]), + attributes: { + module: metricObject["@_module"], + interpreter: metricObject["@_interpreter"], + interpreterVersion: metricObject["@_interpreterVersion"], + function: metricObject["@_function"] + } + }); + } + + return metrics; +} + +/** + * @param {object[]} testArray + * @returns {TestType[]} + */ +function transformCdkTest(testArray) { + let tests = []; + if (!testArray) { + return [] + } + for (let testObject of testArray) { + var transTest = { + FunctionArgument: transformCdkFunctionArguments(testObject["cdk:FunctionArgument"]), + attributes: { + id: testObject["@_id"] + } + }; + if (testObject["cdk:Return"]) { + transTest["Return"] = { + type : testObject["cdk:Return"]["@_type"], + path : testObject["cdk:Return"]["@_path"], + } + } + tests.push(transTest); + } + + return tests; +} + +/** + * @param {object[]} functionArgumentArray + * @returns {FunctionArgumentType[]} + */ +function transformCdkFunctionArguments(functionArgumentArray) { + let fcnArguments = []; + + for (let argumentObject of functionArgumentArray) { + fcnArguments.push({ + attributes: { + name: argumentObject["@_name"], + method: argumentObject["@_method"], + type: argumentObject["@_type"], + source: argumentObject["@_source"], + content: argumentObject["@_content"] + } + }); + } + + return fcnArguments; +} + +/** + * + * @param {object[]} clArgumentArray + * @returns + */ +function transformCdkCommandLineArguments(clArgumentArray) { + let clArguments = []; + + for (let clArgument of clArgumentArray) { + clArguments.push({ + attributes: { + flag: clArgument['@_flag'], + argument: clArgument['@_argument'], + type: clArgument['@_type'] + } + }); + } + + return clArguments; +} + +/** + * @param {object[]} processingArray + * @returns {ProcessingType[]} + */ +function transformCdkProcessing(processingArray) { + let transformedProcessings = []; + + for (let processing of processingArray) { + transformedProcessings.push({ + attributes: { + description: processing['@_description'] + }, + SimpleProcessing: processing["cdk:SimpleProcessing"] !== undefined ? transformCdkSimpleProcessing(processing["cdk:SimpleProcessing"]) : undefined, + ComplexProcessing: processing["cdk:ComplexProcessing"] !== undefined ? transformCdkComplexProcessing(processing["cdk:ComplexProcessing"]) : undefined, + Prerequisites: processing["cdk:Prerequisites"] !== undefined ? transformCdkProcessPrerequisites(processing["cdk:Prerequisites"]) : [], + Inputs: processing["cdk:Inputs"] !== undefined ? transformCdkInputs(processing["cdk:Inputs"]) : undefined, + Outputs: processing["cdk:Outputs"] !== undefined ? transformCdkOutputs(processing["cdk:Outputs"]) : undefined, + }); + } + + return transformedProcessings; +} + +/** + * @param {object} simpleProcessing + * @returns {SimpleProcessingType} + */ +function transformCdkSimpleProcessing(simpleProcessing) { + return { + attributes: { + module: simpleProcessing["@_module"], + interpreter: simpleProcessing["@_interpreter"], + interpreterVersion: simpleProcessing["@_interpreterVersion"], + function: simpleProcessing["@_function"], + id: simpleProcessing["@_id"] + } + }; +} + +/** + * @param {object} complexProcessing + * @returns {ComlexProcessingType} + */ +function transformCdkComplexProcessing(complexProcessing) { + return { + attributes: { + interpreter: complexProcessing['@_interpreter'], + source: complexProcessing['@_source'], + description: complexProcessing['@_description'], + id: complexProcessing['@_id'] + } + }; +} + +/** + * + * @param {object[]} prerequisites + * @returns {ProcessingPrerequisitesType} + */ +function transformCdkProcessPrerequisites(prerequisitesArray) { + let transformedPrerequisites = []; + + for (let prerequisite of prerequisitesArray) { + transformedPrerequisites.push({ + attributes: { + source: prerequisite['@_source'] + } + }); + } + + return transformedPrerequisites; +} + +/** + * @param {object} inputs + * @returns {InputsType} + */ +function transformCdkInputs(inputs) { + return { + attributes: { + description: inputs['@_description'] + }, + FunctionArgument: inputs['cdk:FunctionArgument'] !== undefined ? transformCdkFunctionArguments(inputs['cdk:FunctionArgument']) : [], + Input: inputs['cdk:Input'] !== undefined ? transformCdkInput(inputs['cdk:Input']) : [], + CommandLineArgument: inputs['cdk:CommandLineArgument'] !== undefined ? transformCdkCommandLineArguments(inputs['cdk:CommandLineArgument']) : [] + }; +} + +/** + * @param {object[]} inputs + * @returns {GenericInputType[]} + */ +function transformCdkInput(inputs) { + let transformedInputs = []; + + for (let input of inputs) { + transformedInputs.push({ + attributes: { + description: input['@_description'], + type: input['@_type'], + path: input['@_path'] + } + }); + } + + return transformedInputs; +} + +/** + * @param {object} outputs + * @returns {OutputsType} + */ +function transformCdkOutputs(outputs) { + return { + attributes: { + description: outputs['@_description'] + }, + Return: outputs['cdk:Return'] !== undefined ? transformCdkFunctionOutput(outputs['cdk:Return']) : [], + Output: outputs['cdk:Output'] !== undefined ? transformCdkGenericOutput(outputs['cdk:Output']) : [] + }; +} + +/** + * @param {object} returnObj + * @returns {FunctionOutputType} + */ +function transformCdkFunctionOutput(returnObj) { + return { + attributes: { + type: returnObj['@_type'], + path: returnObj['@_path'] + } + }; +} + +/** + * @param {object[]} outputs + * @returns {GenericOutputType[]} + */ +function transformCdkGenericOutput(outputs) { + let transformedOutputs = []; + + for (let output of outputs) { + transformedOutputs.push({ + attributes: { + description: output['@_description'], + type: output['@_type'], + path: output['@_path'] + } + }); + } + + return transformedOutputs; +} + +exports.extractGeneralInformation = extractGeneralInformation; +exports.extractTopLevelInformation = extractTopLevelInformation; +exports.extractResources = extractResources; +exports.extractResourceReferences = extractResourceReferences; +exports.extractLinks = extractLinks; +exports.extractClassification = extractClassification; +exports.extractAnnotations = extractAnnotations; +exports.extractLifeCycleInformation = extractLifeCycleInformation; +exports.transformCdkCredibility = transformCdkCredibility; \ No newline at end of file diff --git a/workflow_utils/stmd-crud/src/parser.js b/workflow_utils/stmd-crud/src/parser.js new file mode 100644 index 0000000..c041309 --- /dev/null +++ b/workflow_utils/stmd-crud/src/parser.js @@ -0,0 +1,70 @@ +const { XMLParser, XMLBuilder } = require("fast-xml-parser"); + +/** + * + * @param {string} stmdString + * @returns {object} + */ +function parseSTMD(stmdString) { + const options = { + ignoreAttributes: false, + attributeNamePrefix: "@_", + isArray: (tagName, _) => { + if(tagName == 'stc:Resource' || + tagName == 'stc:ResourceReference' || + tagName == 'stc:Signature' || + tagName == 'stc:MetaData' || + tagName == 'stc:Classification' || + tagName == 'ssc:Annotation' || + tagName == 'stc:ClassificationEntry' || + tagName == 'stc:Link' || + tagName == 'stc:Arc' || + tagName == 'stc:DerivationChainEntry' || + tagName == 'cdk:Evidence' || + tagName == 'cdk:Prerequisites' || + tagName == 'cdk:Processing' || + tagName == 'cdk:Test' || + tagName == 'cdk:Metric' || + tagName == 'cdk:Input' || + tagName == 'cdk:Output' || + tagName == 'cdk:FunctionArgument' || + tagName == 'cdk:CommandLineArgument') return true; + else return false; + } + }; + const parser = new XMLParser(options); + + return parser.parse(stmdString); +} + +/** + * + * @param {object} stmdObject + * @returns {string} + */ +function writeSTMD(stmdObject) { + const options = { + ignoreAttributes: false, + attributeNamePrefix: "@_", + suppressUnpairedNode: false, + unpairedTags: [ + "stc:Resource", + "stc:ResourceReference", + "stc:DerivationChainEntry", + "stc:Classification", + "stc:ClassificationEntry", + "ssc:Annotation", + "stc:Locator", + "stc:Arc", + "stc:Responsible", + "stc:Signature", + "stc:MetaData", + "stc:Summary"] + }; + const writer = new XMLBuilder(options); + + return writer.build(stmdObject); +} + +exports.parseSTMD = parseSTMD; +exports.writeSTMD = writeSTMD; \ No newline at end of file diff --git a/workflow_utils/stmd-crud/src/stmd_class.js b/workflow_utils/stmd-crud/src/stmd_class.js new file mode 100644 index 0000000..b0efec1 --- /dev/null +++ b/workflow_utils/stmd-crud/src/stmd_class.js @@ -0,0 +1,1055 @@ +const parser = require('./parser'); +const p2c_extractors = require('./parsed_to_converted_extractors'); +const c2c_extractors = require('./converted_to_converted_extractors'); +const graph_converter = require('./graph_converter'); +const util = require('./util'); +const { + PHASE_TREE, + ROOT_ELEMENT_NAME, + LIFECYCLE_PARENT_NAME, + LIFECYCLE_ENTRY_NAMES, + GENERAL_INFORMATION_NAME } = require('./constants'); + +const {StmdWriter} = require('./stmd_writer'); + +/** + * @typedef {import('../types/specification').SimulationTaskMetaDataAttributes} SimulationTaskMetaDataAttributes + * @typedef {import('../types/specification').GeneralInformationType} GeneralInformationType + * @typedef {import('../types/specification').ResourceType} ResourceType + * @typedef {import('../types/specification').ResourceTypeAttributes} ResourceTypeAttributes + * @typedef {import('../types/specification').ResourceReference} ResourceReference + * @typedef {import('../types/specification').ResourceReferenceAttributes} ResourceReferenceAttributes + * @typedef {import('../types/specification').ContentType} ContentType + * @typedef {import('../types/specification').Summary} Summary + * @typedef {import('../types/specification').SummaryAttributes} SummaryAttributes + * @typedef {import('../types/specification').MetaData} MetaData + * @typedef {import('../types/specification').MetaDataAttributes} MetaDataAttributes + * @typedef {import('../types/specification').SignatureType} SignatureType + * @typedef {import('../types/specification').SignatureTypeAttributes} SignatureTypeAttributes + * @typedef {import('../types/specification').GElementCommon} GElementCommon + * @typedef {import('../types/specification').Classification} Classification + * @typedef {import('../types/specification').ClassificationAttributes} ClassificationAttributes + * @typedef {import('../types/specification').ClassificationEntry} ClassificationEntry + * @typedef {import('../types/specification').ClassificationEntryAttributes} ClassificationEntryAttributes + * @typedef {import('../types/specification').Annotations} Annotations + * @typedef {import('../types/specification').Annotation} Annotation + * @typedef {import('../types/specification').AnnotationAttributes} AnnotationAttributes + * @typedef {import('../types/specification').LinksType} LinksType + * @typedef {import('../types/specification').Link} Link + * @typedef {import('../types/specification').LinkAttributes} LinkAttributes + * @typedef {import('../types/specification').Locator} Locator + * @typedef {import('../types/specification').LocatorAttributes} LocatorAttributes + * @typedef {import('../types/specification').Arc} Arc + * @typedef {import('../types/specification').ArcAttributes} ArcAttributes + * @typedef {import('../types/internal_types').AvailableResource} AvailableResource + * @typedef {import('../types/internal_types').AvailableResourceReference} AvailableResourceReference + * @typedef {import('../types/internal_types').AvailableLink} AvailableLink + * @typedef {import('../types/internal_types').AvailableClassification} AvailableClassification + * @typedef {import('../types/internal_types').AvailableAnnotation} AvailableAnnotation + * @typedef {import('../types/internal_types').AvailableLifeCycleEntry} AvailableLifeCycleEntry + * @typedef {import('../types/internal_types').ContextEntry} ContextEntry + * @typedef {import('../types/cdk_types').CredibilityType} CredibilityType + */ + +/** + * Pushes a new ContextEntry into a ContextEntry array only then, if the term is not yet included + * + * @param {ContextEntry} newItem + */ +Array.prototype.pushContextEntry = function (newItem, ...moreNewItems) { + for (let item of [newItem].concat(moreNewItems)) + if (this.filter(existingItem => existingItem.term === item.term).length == 0) this.push(item); +} + +exports.StmdReader = class StmdReader { + /** + * The raw parsed STMD file, parsed by fast-xml-parser + * + * @private + * @type {object} + */ + #stmdRawParsed; + + /** + * The top level information of the STMD file + * + * @private + * @type {SimulationTaskMetaDataAttributes} + */ + #topLevelInformation; + + /** + * The GeneralInformation element of the STMD file (there's only one) + * + * @private + * @type {GeneralInformationType} + */ + #generalInformation; + + /** + * All available Resources and the corresponding location inside the STMD + * + * @private + * @type {AvailableResource[]} + */ + #resources = []; + + /** + * All available ResourceReferences and the corresponding location inside the STMD + * + * @private + * @type {AvailableResourceReference[]} + */ + #resourceReferences = []; + + /** + * All available Links in the STMD + * + * @private + * @type {AvailableLink[]} + */ + #links = []; + + /** + * All super Classifications in the STMD, that means, all classifications that can be found in the STMD, without + * the Classifications inside Resources/ResourceReferences and their child elements. + * + * @private + * @type {AvailableClassification[]} + */ + #superClassifications = []; + + /** + * All super Annotations in the STMD, that means, all annotations that can be found in the STMD, without + * the Annotations inside Resources/ResourceReferences and their child elements. + * + * @private + * @type {AvailableAnnotation[]} + */ + #superAnnotations = []; + + /** + * All LifeCycleEntries in the STMD + * + * @private + * @type {AvailableLifeCycleEntry[]} + */ + #lifeCycleEntries = []; + + /** + * Creates an object to access any element inside a [STMD file](https://pmsfit.github.io/SSPTraceability/master/). + * To use it, the string of the STMD file must be handed over on initialization of the StmdCrud. + * + * Please note: The conformance of the STMD file to its schema definition must be given for proper functionality + * of the StmdReader. + * + * @param {string} stmdString the stringified STMD file + */ + constructor(stmdString = "") { + if (stmdString !== "") { + this.#stmdRawParsed = parser.parseSTMD(stmdString); + this.#extractTopLevelInformation(); + this.#extractGeneralInformation(); + this.#extractAllResourcesAndReferences(); + this.#extractAllLinks(); + this.#extractSuperGElementsCommon(); + this.#extractLifeCylceInformation(); + } + } + + /** + * Exports the current STMD + * + * @returns {string} the STMD XML-string + */ + export() { + let stmdWriter = new StmdWriter(); + stmdWriter.setTopLevelInformation(this.#topLevelInformation); + stmdWriter.setGeneralInformation(this.#generalInformation); + stmdWriter.addSuperAnnotations(this.#superAnnotations); + stmdWriter.addSuperClassifications(this.#superClassifications); + stmdWriter.addLifeCycleInformation(this.#lifeCycleEntries); + stmdWriter.addResources(this.#resources); + stmdWriter.addResourceReferences(this.#resourceReferences); + stmdWriter.addLinks(this.#links); + + return stmdWriter.export(); + } + + /** + * Returns the top level information of the STMD file. That is all attributes, defined in the root element + * (at least name, version and GUID) + * + * @author localhorst87 + * @returns {SimulationTaskMetaDataAttributes} the top-level information + */ + getTopLevelInformation() { + return this.#topLevelInformation; + } + + /** + * Returns the General Information of the STMD file. + * + * @author localhorst87 + * @returns {GeneralInformationType} the General Information + */ + getGeneralInformation() { + return this.#generalInformation; + } + + /** + * Returns all Resource elements (converted stc:Resource) of a given element in the STMD, given by a location. + * + * A Resource element defines the structure and attributes of information about a resource that is related to the + * particular step and particle. + * + * The location must be given as an array of all parent elements + * + * @author localhorst87 + * @example getResources(['stmd:SimulationTaskMetaData', 'stmd:RequirementsPhase', 'stmd:VerifyRequirements', 'stc:Output']) + * @param {string[]} location the location as array of strings + * @param {boolean} [ingestSubElements=false] if true, all Resources from child elements will also be considered + * @returns {AvailableResource[]} all Resources of the given location + */ + getResources(location, ingestSubElements = false) { + if (ingestSubElements) { + // accesses the tree elements, based on the given location, like PHASE_TREE[ location[0] ][ location[1] ][...] + let startTree = location.reduce((treeElement, xmlElement) => treeElement[xmlElement], PHASE_TREE); + + return this.#collectRecursively(startTree, location, this.#collectResources, this.#resources); + } + else + return this.#collectResources(location); + } + + /** + * Adds a new Resource to the given location + * + * @param {ResourceType} resource + * @param {string[]} location + * @returns {boolean} operation successful + */ + addResource(resource, location) { + if (this.#isResourceIdExisting(resource.attributes.id)) + return false; + + const availableResource = c2c_extractors.convertToAvailableResources([resource], location); + this.#resources.push(...availableResource); + + return true; + } + + /** + * Updates the Resource with the same UID. If the uid can not be found, the operation will be cancelled. + * + * @author localhorst87 + * @param {ResourceType} resource + * @param {string[]} location + * @param {string} uid + * @returns {boolean} operation successful + */ + updateResource(resource, location, uid) { + const idxRes = this.#resources.findIndex(availableResource => availableResource.uid == uid); + if (idxRes < 0) + return false; + + if (this.#isResourceIdExisting(resource.attributes.id)) + return false; + + const availableResource = { + uid: uid, + location: location, + resource: resource + }; + this.#resources.splice(idxRes, 1, availableResource); + + return true; + } + + /** + * Deletes the Resource with the given UID. + * + * @param {string} uid + * @returns {boolean} operation successful + */ + deleteResource(uid) { + const idxRes = this.#resources.findIndex(availableResource => availableResource.uid == uid); + if (idxRes < 0) + return false; + + // if resource has defined an ID, search for all ResourceReferences that reference to this resource and delete them! + const resourceId = this.#resources[idxRes].resource.attributes.id; + if (resourceId !== undefined) { + const href = "#" + resourceId; + const resourceRefs = this.#resourceReferences.filter(resourceRef => resourceRef.resourceReference.attributes.xlink_href == href); + for (let ref of resourceRefs) + this.deleteResourceReference(ref.uid); + } + + this.#resources.splice(idxRes, 1); + + return true; + } + + /** + * Returns all ResourceReference elements (converted stc:ResourceReference) of a given element in the STMD, given by + * the location. + * + * A ResourceReference element references a resource element defined in another place, that is related to the + * particular step and particle. + * + * The location must be given as an array of all parent elements. + * + * @author localhorst87 + * @example getResourceReferences(['stmd:SimulationTaskMetaData', 'stmd:RequirementsPhase','stmd:VerifyRequirements', 'stc:Output']) + * @param {string[]} location the location as array of strings + * @param {boolean} [ingestSubElements=false] if true, all ResourceReferences from child elements will also be considered + * @returns {AvailableResourceReference[]} all ResourceReferences of the given location + */ + getResourceReferences(location, ingestSubElements = false) { + if (ingestSubElements) { + // accesses the tree elements, based on the given location, like PHASE_TREE[ location[0] ][ location[1] ][...] + let startTree = location.reduce((treeElement, xmlElement) => treeElement[xmlElement], PHASE_TREE); + + return this.#collectRecursively(startTree, location, this.#collectResourceReferences, this.#resourceReferences); + } + else + return this.#collectResourceReferences(location); + } + + /** + * Adds a new ResourceReference to the given location + * + * @param {ResourceReference} resourceReference + * @param {string[]} location + * @returns {boolean} operation successful + */ + addResourceReference(resourceReference, location) { + if (this.#isResourceReferenceIdExisting(resourceReference.attributes.id)) + return false; + + const availableResourceReference = c2c_extractors.convertToAvailableResourceReferences([resourceReference], location); + this.#resourceReferences.push(...availableResourceReference); + + return true; + } + + /** + * Deletes the ResourceReference with the given UID + * + * @param {string} uid the uid of the ResourceReference to delete + * @returns {boolean} true/false upon successful/failed deletion + */ + deleteResourceReference(uid) { + const idxRes = this.#resourceReferences.findIndex(availableReference => availableReference.uid == uid); + if (idxRes < 0) + return false; + + this.#resourceReferences.splice(idxRes, 1); + + return true; + } + + /** + * Returns all Link elements (converted stc:Link) of a given parent element in the STMD, given by a location. + * + * The Link element represents a single link no matter if it is an STMD file internal link or a link targeted to the + * outside of the STMD file. + * + * The location must be given as an array of all parent elements. + * + * Please note: The Link elements below the LinksType element (stc:Links) are returned here, so stc:Links must not + * be part of the location! + * + * @author localhorst87 + * @example getLinks(['stmd:SimulationTaskMetaData', 'stmd:ImplementationPhase', 'stmd:IntegrateSimulation']) + * // returns all Link elements given in the stc:Links elements of the stmd:IntegrateSimulation step + * @param {string[]} location the location as array of strings + * @param {boolean} [ingestSubElements=false] if true, all Links from child elements will also be considered + * @returns {AvailableLink[]} all Links of the given location + */ + getLinks(location, ingestSubElements = false) { + if (ingestSubElements) { + // accesses the tree elements, based on the given location, like PHASE_TREE[ location[0] ][ location[1] ][...] + let startTree = location.reduce((treeElement, xmlElement) => treeElement[xmlElement], PHASE_TREE); + + return this.#collectRecursively(startTree, location, this.#collectLinks, this.#links); + } + else + return this.#collectLinks(location); + } + + /** + * Adds a new Link to the given location + * + * @param {Link} link + * @param {string[]} location + * @returns {boolean} + */ + addLink(link, location) { + const availableLink = c2c_extractors.convertToAvailableLinks([link], location); + this.#links.push(...availableLink); + + return true; + } + + /** + * Updates the Link with the same UID. If the uid can not be found, the operation will be cancelled. + * + * @param {Link} link + * @param {string[]} location + * @param {string} uid + * @returns {boolean} + */ + updateLink(link, location, uid) { + const idxLink = this.#links.findIndex(availableLink => availableLink.uid == uid); + if (idxLink < 0) + return false; + + const availableLink = { + uid: uid, + location: location, + link: link + }; + this.#links.splice(idxLink, 1, availableLink); + + return true; + } + + /** + * Deletes the link with the given UID. + * + * @param {string} uid + * @returns {boolean} + */ + deleteLink(uid) { + const idxLink = this.#links.findIndex(availableLink => availableLink.uid == uid); + if (idxLink < 0) + return false; + + this.#links.splice(idxLink, 1); + + return true; + } + + /** + * Returns all Classification elements (converted stc:Classification) of a given parent element in the STMD, given + * by a location. + * + * The Classification element, which can occur multiple times inside the parent element, provides a set of + * ClassificationEntry elements of an STMD modeling element, provided as Keyword Value Pairs (KWP), the meaning of + * which is interpreted according to the name of the classification provided in the name attribute. + * This approach is used, for example, to provide searchable keywords for content, or to assign and track quality or + * validation level requirements across the STMD process, or to maintain variant or other classification content + * across the process. + * + * The location must be given as an array of all parent elements. + * + * Please note: Classifications inside Resources or ResourceReferences can not be requested with this method. + * For this purpose, use getResources and explore its child elements + * + * @author localhorst87 + * @example getClassifications(['stmd:SimulationTaskMetaData', 'stmd:RequirementsPhase', 'stmd:VerifyRequirements']) + * // returns all stc:Classification elements of the stmd:VerifyRequirements step + * @param {string[]} location the location as array of strings + * @returns {Classification[]} all Classifications of the given location + */ + getClassifications(location) { + location = location.map(elementName => elementName.trim()); // remove possible whitespaces from element names + let classifications = this.#superClassifications.filter(avilableClassification => avilableClassification.location.toString() == location.toString()); + + return classifications.map(avilableClassification => avilableClassification.classification); + } + + /** + * Add classifications to a specific location + * + * @param {Classification} classification + * @param {string[]} location + * @returns {boolean} + */ + addClassification(classification, location) { + const availableClassifications = c2c_extractors.convertToAvailableClassifications([classification], location) + this.#superClassifications.push(...availableClassifications); + + return true; + } + + /** + * Adds a ClassificationEntry to the Classification with the given UID. + * + * Returns true if the ClassificationEntry has been added successfully. + * + * Returns false if the Classification, with the given UID does not exist. + * + * + * @param {ClassificationEntry} classificationEntry + * @param {string} classificationUid + * @returns {boolean} + */ + addClassificationEntry(classificationEntry, classificationUid) { + const idxClassification = this.#superClassifications.findIndex(avilableClassification => avilableClassification.uid === classificationUid); + + if (idxClassification > -1) { + if (this.#superClassifications[idxClassification].classification.ClassificationEntry !== undefined) + this.#superClassifications[idxClassification].classification.ClassificationEntry.push(classificationEntry); + else + this.#superClassifications[idxClassification].classification.ClassificationEntry = [classificationEntry]; + + return true; + } + else + return false; + } + + /** + * Deletes the Classification with the given UID + * + * @param {string} uid + */ + deleteClassification(uid) { + const idxClassification = this.#superClassifications.findIndex(availableClassification => availableClassification.uid === uid); + if (idxClassification < 0) + return false; + + this.#superClassifications.splice(idxClassification, 1); + + return true; + } + + // deleteClassificationEntry(classificationUid, entryUid) { + + // } + + // updateClassificationEntry(classificationUid, entryUid, newClassificationEntry) { + + // } + + /** + * Returns all Annotation elements (converted stc:Annotation) of a given parent element in the STMD, given + * by a location. + * + * The Annotations element is used to add a list of additional free style annotations. + * + * Please note: Annotations inside Resources or ResourceReferences can not be requested with this method. + * For this purpose, use getResources and explore its child elements. + * + * Please note: The Annotation elements below the Annotations element (stc:Annotations) are returned here, so stc:Annotations + * must not be part of the location! + * + * @author localhorst87 + * @example getAnnotations(['stmd:SimulationTaskMetaData', 'stmd:RequirementsPhase', 'stmd:VerifyRequirements']) + * // + * @param {string[]} location the location as array of strings + * @returns {Annotation[]} all Annotations of the given location + */ + getAnnotations(location) { + location = location.map(elementName => elementName.trim()); // remove possible whitespaces from element names + let annotations = this.#superAnnotations.filter(avilableAnnotation => avilableAnnotation.location.toString() == location.toString()); + + return annotations.map(avilableAnnotation => avilableAnnotation.annotation); + } + + /** + * Returns all LifeCycleEntry elements as AvailableLifeCycleEntry element of a given parent element in the STMD, + * given by a location. The location should point to a phase, as only phases contain lifecycle information. + * + * @author localhorst87 + * @param {string[]} location the location as array of strings (should be a phase) + * @returns {AvailableLifeCycleEntry[]} all AvailableLifeCycleEntry elements of the given location + */ + getLifeCycleEntries(location) { + location = location.map(elementName => elementName.trim()); // remove possible whitespaces from element names + return this.#lifeCycleEntries.filter(availableEntry => availableEntry.location.toString() == location.toString()); + } + + /** + * Returns the Resource the given ResourceReference is referencing to + * + * @author localhorst87 + * @param {ResourceReference} resourceReference the ResourceReference with the reference to the target Resource + * @returns {AvailableResource} the target Resource + */ + getResourceFromReference(resourceReference) { + const targetId = util.getResourceIdFromHref(resourceReference.attributes.xlink_href); + const targetResource = this.#resources.filter(availableRes => availableRes.resource.attributes.id == targetId); + + if (targetResource.length == 0) + throw("Resource with id '" + targetId + "' does not exist"); + + return targetResource[0]; + } + + /** + * Returns the Resource with the given ID + * + * @author localhorst87 + * @param {string} id the target ID of the Resource + * @returns {AvailableResource | undefined} the target Resource or undefined if a Resource with the given ID does + * not exist + */ + getResourceFromId(id) { + return this.#resources + .find(availableRes => availableRes.resource.attributes.id === id); + } + + /** + * Creates a JSON-LD graph from the given link, using the given vocabulary + * + * @author localhorst87 + * @param {Link} link a converted XLink, e.g. received by the getLinks method + * @param {ContextEntry[]} context context for the named-graph in the shape of key-value-pairs as it is returned by + * the getVocabulary method (key-value-pairs) + * @returns {string} a JSON-LD named graph, as defined in + * https://www.w3.org/TR/json-ld/#named-graphs + */ + createGraphFromLink(link, context) { + let graph = { + "@context": { }, + "@graph": [ ] + }; + + for (let arc of link.Arc) { + let locatorTuple = c2c_extractors.extractLocatorsFromArc(arc, link.Locator); + for (let startLocator of locatorTuple.start) { + for (let endLocator of locatorTuple.end) { + let startNode = graph_converter.createJsonLdStartNode(arc, startLocator, endLocator); + let endNode = graph_converter.createJsonLdEndNode(endLocator); + graph = graph_converter.integrateJsonLdNodeIntoGraph(startNode, graph); + graph = graph_converter.integrateJsonLdNodeIntoGraph(endNode, graph); + } + } + } + + graph = graph_converter.addContextToGraph(graph, context); + graph = graph_converter.addTitleToGraph(graph, link); + + return JSON.stringify(graph); + } + + /** + * Returns a CredibilityType object from a cdk:Credibility element of a specific location + * + * @param {string[]} location + * @returns {CredibilityType} + */ + getCdkElement(location) { + const credibility = this.#resources + .filter(availableResource => availableResource.location.toString() === location.toString()) + .map(availableResource => availableResource.resource) + .filter(resource => resource.attributes.kind == "rationale") + .filter(resource => resource.Content !== undefined) + .filter(resource => resource.Content.any["cdk:Credibility"] !== undefined) + .map(resource => resource.Content.any["cdk:Credibility"]) + .map(rawParsedCredibility => p2c_extractors.transformCdkCredibility(rawParsedCredibility)); + + if (credibility.length > 0) + return credibility[0]; + else + return {Processing: [], Evidence: []}; + } + + /** + * Adds the top level information of the STMD file to the private property + */ + #extractTopLevelInformation() { + this.#topLevelInformation = p2c_extractors.extractTopLevelInformation(this.#stmdRawParsed[ROOT_ELEMENT_NAME]); + } + + #extractGeneralInformation() { + this.#generalInformation = p2c_extractors.extractGeneralInformation(this.#stmdRawParsed[ROOT_ELEMENT_NAME]); + } + + /** + * Loops through all subelements of the given (sub-)phase tree and triggers the given collect function that is used + * to collect assets + * + * @param {object} tree the tree to loop recursively + * @param {string[]} location the current location in the tree + * @param {Function} collectFcn the triggered function that collects certain assets + * @param {any[]} collectSource the list of all elements where to collect the assets from + * @param {any[]} [collector=[]] the accumulated list of collected assets. Will be an empty array if not given + * explicitely + + * @returns {any[]} the collector accumulated with new data + */ + #collectRecursively(tree, location, collectFcn, collectSource, collector = []) { + const newElements = collectFcn(location, collectSource); + collector.push(...newElements); + + let subelements = Object.keys(tree); + while (subelements.length > 0) { + let subLocation = subelements.shift(); + this.#collectRecursively(tree[subLocation], [...location, subLocation], collectFcn, collectSource, collector); + } + + return collector; + } + + /** + * Function to collect Resources to be used in #collectRecursively + * + * @param {string[]} resourceLocation + * @param {AvailableResource[]} [allResources=this.#resources] + * @returns {AvailableResource[]} + */ + #collectResources(resourceLocation, allResources = this.#resources) { + // remove possible whitespaces from element names + resourceLocation = resourceLocation.map(elementName => elementName.trim()); + + return allResources.filter(res => res.location.toString() == resourceLocation.toString()); + } + + /** + * Function to collect ResourceReferences to be used in #collectRecursively + * + * @param {string[]} resReferenceLocation + * @param {AvailableResourceReference[]} allResReferences + * @returns {AvailableResourceReference[]} + */ + #collectResourceReferences(resReferenceLocation, allResReferences = this.#resourceReferences) { + // remove possible whitespaces from element names + resReferenceLocation = resReferenceLocation.map(elementName => elementName.trim()); + + return allResReferences + .filter(availableResRef => availableResRef.location.toString() == resReferenceLocation.toString()); + } + + /** + * Function to collect Links to be used in #collectRecursively + * + * @param {string[]} linkLocation + * @param {AvailableLink[]} [allLinks = this.#links] + * @returns {AvailableLink[]} + */ + #collectLinks(linkLocation, allLinks = this.#links) { + // remove possible whitespaces from element names + linkLocation = linkLocation.map(elementName => elementName.trim()); + + return allLinks + .filter(link => link.location.toString() == linkLocation.toString()); + } + + /** + * Checks if the resource id (not the internal uid!) is already taken + * + * @param {string} id + * @returns {boolean} + */ + #isResourceIdExisting(id) { + if (id === undefined) + return false; + else { + return this.#resources + .filter(availRes => availRes.resource.attributes.id !== undefined) + .filter(availRes => availRes.resource.attributes.id === id) + .length > 0; + } + } + + /** + * Checks if the resource reference id (not the internal uid!) is already taken + * + * @param {string} id + * @returns {boolean} + */ + #isResourceReferenceIdExisting(id) { + if (id === undefined) + return false; + else { + return this.#resourceReferences + .filter(availRef => availRef.resourceReference.attributes.id !== undefined) + .filter(availRef => availRef.resourceReference.attributes.id === id) + .length() > 0; + } + } + + /** + * Adds all References and ReferenceResources to the private properties + */ + #extractAllResourcesAndReferences() { + let phaseRawParsed, stepRawParsed, lcEntryRawParsed, particleRawParsed, currentLocation; + + // loop through all phases (stmd:AnalysisPhase, stmd:RequirementsPhase, ...) + const stmdPhaseNames = Object.keys(PHASE_TREE[ROOT_ELEMENT_NAME]); + for (let phase of stmdPhaseNames) { + phaseRawParsed = this.#stmdRawParsed[ROOT_ELEMENT_NAME][phase]; + if (phaseRawParsed === undefined) continue; + + // loop through all steps of a phase (stmd:DefineModelRequirements, stmd:DefineParameterRequirements, ...) + const stmdStepNames = Object.keys(PHASE_TREE[ROOT_ELEMENT_NAME][phase]); + for (let step of stmdStepNames) { + stepRawParsed = phaseRawParsed[step]; + if (stepRawParsed === undefined) continue; + + // loop through all particles (stc:Input, stc:Procedure, ...) + const particleNames = Object.keys(PHASE_TREE[ROOT_ELEMENT_NAME][phase][step]); + for (let particle of particleNames) { + particleRawParsed = stepRawParsed[particle]; + if (particleRawParsed === undefined) continue; + + currentLocation = [ROOT_ELEMENT_NAME, phase, step, particle]; + this.#addResources(particleRawParsed, currentLocation); + this.#addResourceReferences(particleRawParsed, currentLocation); + } + + if (stepRawParsed[LIFECYCLE_PARENT_NAME] === undefined) continue; + + // loop through all LifeCycleEntries (stc:Drafted, stc:Defined, ...) + for (let lcEntry of LIFECYCLE_ENTRY_NAMES) { + lcEntryRawParsed = stepRawParsed[LIFECYCLE_PARENT_NAME][lcEntry]; + if (lcEntryRawParsed === undefined) continue; + + currentLocation = [ROOT_ELEMENT_NAME, phase, step, LIFECYCLE_PARENT_NAME, lcEntry]; + this.#addResources(lcEntryRawParsed, currentLocation); + this.#addResourceReferences(lcEntryRawParsed, currentLocation); + } + } + + if (phaseRawParsed[LIFECYCLE_PARENT_NAME] === undefined) continue; + + // loop through all LifeCycleEntries (stc:Drafted, stc:Defined, ...) + for (let lcEntry of LIFECYCLE_ENTRY_NAMES) { + lcEntryRawParsed = phaseRawParsed[LIFECYCLE_PARENT_NAME][lcEntry]; + if (lcEntryRawParsed === undefined) continue; + + currentLocation = [ROOT_ELEMENT_NAME, phase, LIFECYCLE_PARENT_NAME, lcEntry]; + this.#addResources(lcEntryRawParsed, currentLocation); + this.#addResourceReferences(lcEntryRawParsed, currentLocation); + } + } + } + + /** + * Adds all Links to the private property + */ + #extractAllLinks() { + let phaseRawParsed, stepRawParsed, currentLocation; + + // loop through all phases (stmd:AnalysisPhase, stmd:RequirementsPhase, ...) + const stmdPhaseNames = Object.keys(PHASE_TREE[ROOT_ELEMENT_NAME]); + for (let phase of stmdPhaseNames) { + phaseRawParsed = this.#stmdRawParsed[ROOT_ELEMENT_NAME][phase]; + if (phaseRawParsed === undefined) continue; + + currentLocation = [ROOT_ELEMENT_NAME, phase]; + this.#addLinks(phaseRawParsed, currentLocation); + + // loop through all steps of a phase (stmd:DefineModelRequirements, stmd:DefineParameterRequirements, ...) + const stmdStepNames = Object.keys(PHASE_TREE[ROOT_ELEMENT_NAME][phase]); + for (let step of stmdStepNames) { + stepRawParsed = phaseRawParsed[step]; + if (stepRawParsed === undefined) continue; + + currentLocation = [ROOT_ELEMENT_NAME, phase, step]; + this.#addLinks(stepRawParsed, currentLocation); + } + } + + // extract Links from GeneralInformation + let generalInfoRawParsed = this.#stmdRawParsed[GENERAL_INFORMATION_NAME]; + if (generalInfoRawParsed === undefined) return; + + currentLocation = [ROOT_ELEMENT_NAME, GENERAL_INFORMATION_NAME]; + this.#addLinks(generalInfoRawParsed, currentLocation); + } + + /** + * Adds all the Classifications and Annotations in the STMD that are not located below Resources and + * ResourceReferences to the private properties + */ + #extractSuperGElementsCommon() { + let phaseRawParsed, stepRawParsed, particleRawParsed, lcEntryRawParsed, currentLocation; + + // add possible Classification below root element + const rootRawParsed = this.#stmdRawParsed[ROOT_ELEMENT_NAME]; + currentLocation = [ROOT_ELEMENT_NAME]; + this.#addClassification(rootRawParsed, currentLocation); + this.#addAnnotation(rootRawParsed, currentLocation); + + // loop through all Phases (stmd:AnalysisPhase, stmd:RequirementsPhase, ...) + const stmdPhaseNames = Object.keys(PHASE_TREE[ROOT_ELEMENT_NAME]); + for (let phase of stmdPhaseNames) { + phaseRawParsed = this.#stmdRawParsed[ROOT_ELEMENT_NAME][phase]; + if (phaseRawParsed === undefined) continue; + + currentLocation = [ROOT_ELEMENT_NAME, phase]; + this.#addClassification(phaseRawParsed, currentLocation); + this.#addAnnotation(phaseRawParsed, currentLocation); + + // loop through all Steps of a phase (stmd:DefineModelRequirements, stmd:DefineParameterRequirements, ...) + const stmdStepNames = Object.keys(PHASE_TREE[ROOT_ELEMENT_NAME][phase]); + for (let step of stmdStepNames) { + stepRawParsed = phaseRawParsed[step]; + if (stepRawParsed === undefined) continue; + + currentLocation = [ROOT_ELEMENT_NAME, phase, step]; + this.#addClassification(stepRawParsed, currentLocation); + this.#addAnnotation(stepRawParsed, currentLocation); + + // loop through all particles (stc:Input, stc:Procedure, ...) + const particleNames = Object.keys(PHASE_TREE[ROOT_ELEMENT_NAME][phase][step]); + for (let particle of particleNames) { + particleRawParsed = stepRawParsed[particle]; + if (particleRawParsed === undefined) continue; + + currentLocation = [ROOT_ELEMENT_NAME, phase, step, particle]; + this.#addClassification(particleRawParsed, currentLocation); + this.#addAnnotation(particleRawParsed, currentLocation); + } + + if (stepRawParsed[LIFECYCLE_PARENT_NAME] === undefined) continue; + + // loop through all LifeCycleEntries (stc:Drafted, stc:Defined, ...) + for (let lcEntry of LIFECYCLE_ENTRY_NAMES) { + lcEntryRawParsed = stepRawParsed[LIFECYCLE_PARENT_NAME][lcEntry]; + if (lcEntryRawParsed === undefined) continue; + + currentLocation = [ROOT_ELEMENT_NAME, phase, step, LIFECYCLE_PARENT_NAME, lcEntry]; + this.#addClassification(lcEntryRawParsed, currentLocation); + this.#addAnnotation(lcEntryRawParsed, currentLocation); + } + } + } + } + + /** + * Adds LifeCycleEntry elements from all phases to the private property + */ + #extractLifeCylceInformation() { + let phaseRawParsed, stepRawParsed, currentLocation; + + const stmdPhaseNames = Object.keys(PHASE_TREE[ROOT_ELEMENT_NAME]); + for (let phase of stmdPhaseNames) { + phaseRawParsed = this.#stmdRawParsed[ROOT_ELEMENT_NAME][phase]; + if (phaseRawParsed === undefined) continue; + + currentLocation = [ROOT_ELEMENT_NAME, phase]; + this.#addLifeCycleEntries(phaseRawParsed, currentLocation); + + const stmdStepNames = Object.keys(PHASE_TREE[ROOT_ELEMENT_NAME][phase]); + for (let step of stmdStepNames) { + stepRawParsed = this.#stmdRawParsed[ROOT_ELEMENT_NAME][phase][step]; + if (stepRawParsed === undefined) continue; + + currentLocation = [ROOT_ELEMENT_NAME, phase, step]; + this.#addLifeCycleEntries(stepRawParsed, currentLocation); + } + } + } + + /** + * Adds all available Resources of the given parent element to the private property #resources + * + * @param {object} parentRawParsed raw-parsed parent element + * @param {string[]} currentLocation the location, as expected in a AvailableResource type + */ + #addResources(parentRawParsed, currentLocation) { + let resources = p2c_extractors.extractResources(parentRawParsed); + resources = c2c_extractors.convertToAvailableResources(resources, currentLocation); + this.#resources.push(...resources); + } + + /** + * Adds all available ResourceReferences of the given parent element to the private property #resourceReferences + * + * @param {object} parentRawParsed raw-parsed parent element + * @param {string[]} currentLocation the location, as expected in a AvailableResourceReference type + */ + #addResourceReferences(parentRawParsed, currentLocation) { + let resourceReferences = p2c_extractors.extractResourceReferences(parentRawParsed); + resourceReferences = c2c_extractors.convertToAvailableResourceReferences(resourceReferences, currentLocation) + this.#resourceReferences.push(...resourceReferences); + } + + /** + * Adds all available Link elements of the given parent element to the private property #links + * + * @param {object} parentRawParsed raw-parsed parent element + * @param {string[]} currentLocation the location, as expected in a AvailableLink type + */ + #addLinks(parentRawParsed, currentLocation) { + let links = p2c_extractors.extractLinks(parentRawParsed); + links = c2c_extractors.convertToAvailableLinks(links, currentLocation); + this.#links.push(...links); + } + + /** + * Adds all available super Classification elements of the given parent element to the private property + * #superClassifications (that is, all Classification elements that can be found in the STMD, without the + * Classification elements inside Resources/ResourceReferences and their child elements) + * + * @param {object} parentRawParsed + * @param {string[]} currentLocation + */ + #addClassification(parentRawParsed, currentLocation) { + let classifications = p2c_extractors.extractClassification(parentRawParsed); // Classification[] + classifications = c2c_extractors.convertToAvailableClassifications(classifications, currentLocation); + this.#superClassifications.push(...classifications); + } + + /** + * Adds all available super Annotation elements of the given parent element to the private property + * #superAnnotations (that is, all Annotation elements that can be found in the STMD, without the Annotation + * elements inside Resources/ResourceReferences and their child elements) + * + * @param {object} parentRawParsed + * @param {string[]} currentLocation + */ + #addAnnotation(parentRawParsed, currentLocation) { + let annotations = p2c_extractors.extractAnnotations(parentRawParsed); // Annotation[] + annotations = c2c_extractors.convertToAvailableAnnotations(annotations, currentLocation); + this.#superAnnotations.push(...annotations) + } + + /** + * Adds all availabe LifeCycleEntry elements of the given phase to the private property #lifeCycleEntries + * @param {object} phaseRawParsed + * @param {string[]} currentLocation + */ + #addLifeCycleEntries(phaseRawParsed, currentLocation) { + // extract LifeCycleInformationType; e.g. {Drafted: {...}, Validated: {...}, ... } + let lifeCycleInformation = p2c_extractors.extractLifeCycleInformation(phaseRawParsed); + + // get array of available LifeCycleEntries, e.g. ["Drafted", "Validated"] + let lifeCycleEntryNames = Object.keys(lifeCycleInformation); + + for (let entryName of lifeCycleEntryNames) { + this.#lifeCycleEntries.push({ + location: currentLocation, + status: entryName, + lifeCycleEntry: lifeCycleInformation[entryName] + }); + } + } + + /** + * Find all Rational Tag location + */ + findAllParticleLocation(particleName) { + let locationList = []; + let phaseRawParsed, stepRawParsed, currentLocation; + + // loop through all phases (stmd:AnalysisPhase, stmd:RequirementsPhase, ...) + const stmdPhaseNames = Object.keys(PHASE_TREE[ROOT_ELEMENT_NAME]); + for (let phase of stmdPhaseNames) { + phaseRawParsed = this.#stmdRawParsed[ROOT_ELEMENT_NAME][phase]; + if (phaseRawParsed === undefined) continue; + + currentLocation = [ROOT_ELEMENT_NAME, phase]; + this.#addLinks(phaseRawParsed, currentLocation); + + // loop through all steps of a phase (stmd:DefineModelRequirements, stmd:DefineParameterRequirements, ...) + const stmdStepNames = Object.keys(PHASE_TREE[ROOT_ELEMENT_NAME][phase]); + for (let step of stmdStepNames) { + stepRawParsed = phaseRawParsed[step]; + if (stepRawParsed === undefined) continue; + if (stepRawParsed[particleName] == undefined) continue; + currentLocation = [ROOT_ELEMENT_NAME, phase, step, particleName]; + // this.#addLinks(stepRawParsed, currentLocation); + locationList.push(currentLocation) + } + } + return locationList; + } +} \ No newline at end of file diff --git a/workflow_utils/stmd-crud/src/stmd_writer.js b/workflow_utils/stmd-crud/src/stmd_writer.js new file mode 100644 index 0000000..1276323 --- /dev/null +++ b/workflow_utils/stmd-crud/src/stmd_writer.js @@ -0,0 +1,224 @@ +const c2p_extractors = require('./converted_to_parsed_extractors'); +const { STMD_TREE, + ROOT_ELEMENT_NAME, + GENERAL_INFORMATION_NAME, + LINKS_PARENT_NAME, + RESOURCE_NAME, + RESOUCE_REFERENCE_NAME, + ANNOTATIONS_PARENT_NAME, + CLASSIFICATION_PARENT_NAME, + LIFECYCLE_PARENT_NAME} = require('./constants'); +const parser = require('./parser'); + +/** + * @typedef {import('../types/specification').SimulationTaskMetaDataAttributes} SimulationTaskMetaDataAttributes + * @typedef {import('../types/specification').GeneralInformationType} GeneralInformationType + * @typedef {import('../types/internal_types').AvailableResource} AvailableResource + * @typedef {import('../types/internal_types').AvailableResourceReference} AvailableResourceReference + * @typedef {import('../types/internal_types').AvailableLink} AvailableLink + * @typedef {import('../types/internal_types').AvailableClassification} AvailableClassification + * @typedef {import('../types/internal_types').AvailableAnnotation} AvailableAnnotation + * @typedef {import('../types/internal_types').AvailableLifeCycleEntry} AvailableLifeCycleEntry + */ + +exports.StmdWriter = class StmdWriter { + /** + * The generated object for usage in XML builder from fast-xml-parser + * + * @private + * @type {object} + */ + #stmdObject; + + /** + * Top-level information to be temporally stored and appended after calling the sorting function (otherwise it gets + * lost!) + * + * @private + * @type {object} + */ + #topLevelInformationXmlObject; + + constructor() { + this.#init(); + } + + /** + * + */ + #init() { + this.#stmdObject = { + '?xml': { + '@_version': '1.0', + '@_encoding': 'UTF-8' + } + } + this.#stmdObject[ROOT_ELEMENT_NAME] = {}; + } + + + /** + * + * @returns {string} + */ + export() { + var sortedStmdObject = {}; + this.#sortStmdObject(this.#stmdObject, sortedStmdObject, STMD_TREE); + sortedStmdObject = this.#addNamespaceAttributes(sortedStmdObject); + sortedStmdObject[ROOT_ELEMENT_NAME] = {...sortedStmdObject[ROOT_ELEMENT_NAME], ...this.#topLevelInformationXmlObject}; + + return parser.writeSTMD(sortedStmdObject); + } + + /** + * + * @param {SimulationTaskMetaDataAttributes} topLevelInformation + */ + setTopLevelInformation(topLevelInformation) { + this.#topLevelInformationXmlObject = c2p_extractors.transformTopLevelInformation(topLevelInformation); + }; + + /** + * + * @param {GeneralInformationType} generalInformation + */ + setGeneralInformation(generalInformation) { + this.#stmdObject[ROOT_ELEMENT_NAME][GENERAL_INFORMATION_NAME] = c2p_extractors.transformGeneralInformation(generalInformation); + } + + /** + * + * @param {AvailableResource[]} availableResources + */ + addResources(availableResources) { + for (let availableResource of availableResources) { + let xmlObject = c2p_extractors.transformResource(availableResource.resource); + let location = [...availableResource.location]; + this.#addXmlObject(xmlObject, location, RESOURCE_NAME); + } + } + + /** + * + * @param {AvailableResourceReference[]} availableResourceReferences + */ + addResourceReferences(availableResourceReferences) { + for (let availableReference of availableResourceReferences) { + let xmlObject = c2p_extractors.transformResourceReference(availableReference.resourceReference); + let location = [...availableReference.location]; + this.#addXmlObject(xmlObject, location, RESOUCE_REFERENCE_NAME); + } + } + + /** + * + * @param {AvailableLink[]} availableLinks + */ + addLinks(availableLinks) { + for (let availableLink of availableLinks) { + let xmlObject = c2p_extractors.transformLink(availableLink.link); + let location = [...availableLink.location, LINKS_PARENT_NAME]; + this.#addXmlObject(xmlObject, location, 'stc:Link'); + } + } + + /** + * + * @param {AvailableAnnotation[]} availableAnnotations + */ + addSuperAnnotations(availableAnnotations) { + for (let availableAnnotation of availableAnnotations) { + let xmlObject = c2p_extractors.transformAnnotation(availableAnnotation.annotation); + let location = [...availableAnnotation.location, ANNOTATIONS_PARENT_NAME]; + this.#addXmlObject(xmlObject, location, 'ssc:Annotation'); + } + } + + /** + * + * @param {AvailableClassification[]} availableClassifications + */ + addSuperClassifications(availableClassifications) { + for (let availableClassification of availableClassifications) { + let xmlObject = c2p_extractors.transformClassification(availableClassification.classification); + let location = [...availableClassification.location]; + this.#addXmlObject(xmlObject, location, CLASSIFICATION_PARENT_NAME); + } + } + + /** + * + * @param {AvailableLifeCycleEntry[]} availableLifeCycleEntries + */ + addLifeCycleInformation(availableLifeCycleEntries) { + for (let availableLifeCycleEntry of availableLifeCycleEntries) { + let xmlObject = c2p_extractors.transformLifeCycleEntry(availableLifeCycleEntry.lifeCycleEntry); + let location = [...availableLifeCycleEntry.location, LIFECYCLE_PARENT_NAME]; + let lcIdentifier = 'stc:' + availableLifeCycleEntry.status; + this.#addXmlObject(xmlObject, location, lcIdentifier); + } + } + + /** + * Adds the namespace information to the root element + * + * @param {object} stmdObject + * @returns {object} + */ + #addNamespaceAttributes(stmdObject) { + stmdObject[ROOT_ELEMENT_NAME]['@_xmlns:stmd'] = 'http://apps.pmsf.net/STMD/SimulationTaskMetaData'; + stmdObject[ROOT_ELEMENT_NAME]['@_xmlns:stc'] = 'http://apps.pmsf.net/SSPTraceability/SSPTraceabilityCommon'; + stmdObject[ROOT_ELEMENT_NAME]['@_xmlns:ssc'] = 'http://ssp-standard.org/SSP1/SystemStructureCommon'; + stmdObject[ROOT_ELEMENT_NAME]['@_xmlns:xlink'] = 'http://www.w3.org/1999/xlink'; + stmdObject[ROOT_ELEMENT_NAME]['@_xmlns:xsi'] = 'http://www.w3.org/2001/XMLSchema-instance'; + stmdObject[ROOT_ELEMENT_NAME]['@_xsi:schemaLocation'] = 'http://apps.pmsf.net/STMD/SimulationTaskMetaData https://raw.githubusercontent.com/PMSFIT/SSPTraceability/v1.0-beta2/STMD.xsd'; + + return stmdObject; + } + + /** + * + * @param {object} element + * @param {string[]} location + * @param {string} elementIdentifier + */ + #addXmlObject(xmlObject, location, elementIdentifier) { + let stmdObjectReference = this.#stmdObject; + + while(location.length > 0) { + let propertyName = location.shift(); + if (stmdObjectReference[propertyName] === undefined) + stmdObjectReference[propertyName] = {}; + + // once final child property is reached, add the element + if (location.length == 0) { + if (stmdObjectReference[propertyName][elementIdentifier] === undefined) + stmdObjectReference[propertyName][elementIdentifier] = []; + + stmdObjectReference[propertyName][elementIdentifier].push(xmlObject); + } + + stmdObjectReference = stmdObjectReference[propertyName]; + } + } + + #sortStmdObject(unsortedObject, sortedObject, targetTree, treeElementList = []) { + let childElements = Object.keys(targetTree); + + while (childElements.length > 0) { + let childElement = childElements.shift(); + + if (unsortedObject[childElement] !== undefined) { + if (Object.keys(targetTree[childElement]).length == 0) + sortedObject[childElement] = unsortedObject[childElement]; + + else + sortedObject[childElement] = {}; + + this.#sortStmdObject(unsortedObject[childElement], sortedObject[childElement], targetTree[childElement], [...treeElementList, childElement]) + } + } + + return sortedObject; + } +} \ No newline at end of file diff --git a/workflow_utils/stmd-crud/src/util.js b/workflow_utils/stmd-crud/src/util.js new file mode 100644 index 0000000..d1d20cf --- /dev/null +++ b/workflow_utils/stmd-crud/src/util.js @@ -0,0 +1,24 @@ +/** + * Extracts an ID of a Resource that is given by a internal reference + * + * @param {string} href the internal reference + * @returns {string} the ID + */ +function getResourceIdFromHref(href) { + if (href[0] !== "#") + throw("For ResourceReferences only internal references must be used"); + + return href.slice(1); +} + +/** + * Generates a random hexa-decimal ID as string + * + * @returns {string} + */ +function generateRandomId() { + return Math.random().toString(16).slice(2); +} + +exports.getResourceIdFromHref = getResourceIdFromHref; +exports.generateRandomId = generateRandomId; \ No newline at end of file diff --git a/workflow_utils/stmd-crud/types/cdk_types.js b/workflow_utils/stmd-crud/types/cdk_types.js new file mode 100644 index 0000000..4fd102a --- /dev/null +++ b/workflow_utils/stmd-crud/types/cdk_types.js @@ -0,0 +1,277 @@ +/** + * CredibilityType + * + * Defines the enclosing root element to signal that the following elements serve as input for a credibility assessment + * + * @typedef {object} CredibilityType + * @property {ProcessingType[]} Processing + * @property {EvidenceType[]} Evidence All enclosing elements that define collections of metrics for a specific credibility level + */ + +/** + * EvidenceType + * + * This element is used to collect metrics that serve as evidence for fulfilling a specific credibility level + * + * @typedef {object} EvidenceType + * @property {MetricType[]} Metric All metrics to be used + * @property {EvidenceTypeAttributes} attributes + */ + +/** + * EvidenceTypeAttributes + * + * @typedef {object} EvidenceTypeAttributes + * @property {number} level + */ + +/** + * MetricType + * + * This element is used to indicate that a metric from the Credibility Development Kit is used as supporting evidence + * for the corresponding credibility level. + * + * @typedef {object} MetricType + * @property {TestType[]} Test All Test executions of a concrete Metric + * @property {MetricTypeAttributes} attributes + */ + +/** + * MetricTypecAttributes + * + * @typedef {object} MetricTypeAttributes + * @property {string} module + * @property {string} interpreter + * @property {string} interpreterVersion + * @property {string} function + */ + +/** + * TestType + * + * @typedef {object} TestType + * @property {FunctionArgumentType[]} FunctionArgument Defines the sources to use for the function arguments + * @property {FunctionOutputType} [Return] + * @property {TestTypeAttributes} attributes + */ + +/** + * TestTypeAttributes + * + * @typedef {object} TestTypeAttributes + * @property {string} id This mandatory attribute specifies a unique identification of a test execution + */ + +/** + * FunctionArgumentType + * + * This element is used to map a file to a function argument of the function that has been defined in a parent element + * + * @typedef {object} FunctionArgumentType + * @property {FunctionArgumentTypeAttributes} attributes + */ + +/** + * CommandLineInputType + * + * @typedef {object} CommandLineInputType + * @property {CommandLineInputTypeAttributes} attributes + */ + +/** + * CommandLineInputTypeAttributes + * + * @typedef {object} CommandLineInputTypeAttributes + * @property {string} flag + * @property {string} [argument] + * @property {string} [type] + */ + +/** + * FunctionArgumentTypeAttributes + * + * @typedef {object} FunctionArgumentTypeAttributes + * @property {string} name This mandatory attribute specifies the name of the argument as used in the target function + * @property {string} method This mandatory attribute specifies if the input content it fed inline via the "content" attribute or by means of a specified file source + * @property {string} type This mandatory attribute specifies the MIME type of the file that shall be used as a + * function argument. The file type must be convertible to a string, i.e. no binary data + * should be used. + * @property {string} [source] This attribute indicates the source of the file that shall be used as a function argument as an URI (cf. RFC 3986). + * @property {string} [content] content inline instead of by a file + */ + +/** + * ProcessingType + * + * @typedef {object} ProcessingType + * @property {ProcessingTypeAttributes} attributes + * @property {SimpleProcessingType} [SimpleProcessing] + * @property {ComlexProcessingType} [ComplexProcessing] + * @property {ProcessingPrerequisitesType[]} Prerequisites + * @property {InputsType} [Inputs] + * @property {OutputsType} [Outputs] + */ + +/** + * ProcessingTypeAttributes + * + * @typedef {object} ProcessingTypeAttributes + * @property {string} [description] + */ + +/** + * SimpleProcessingType + * + * @typedef {object} SimpleProcessingType + * @property {SimpleProcessingTypeAttributes} attributes + */ + +/** + * SimpleProcessingTypeAttributes + * + * @typedef {object} SimpleProcessingTypeAttributes + * @property {string} module + * @property {string} interpreter + * @property {string} interpreterVersion + * @property {string} function + * @property {string} id + */ + +/** + * ComlexProcessingType + * + * @typedef {object} ComlexProcessingType + * @property {ComlexProcessingTypeAttributes} attributes + */ + +/** + * ComlexProcessingTypeAttributes + * + * @typedef {object} ComlexProcessingTypeAttributes + * @property {string} interpreter + * @property {string} [source] + * @property {string} [description] + * @property {string} id + */ + +/** + * ProcessingPrerequisitesType + * + * @typedef {object} ProcessingPrerequisitesType + * @property {ProcessingPrerequisitesTypeAttributes} attributes + */ + +/** + * ProcessingPrerequisitesTypeAttributes + * + * @typedef {object} ProcessingPrerequisitesTypeAttributes + * @property {string} source + */ + +/** + * InputsType + * + * @typedef {object} InputsType + * @property {InputsTypeAttributes} attributes + * @property {FunctionArgumentType[]} FunctionArgument + * @property {GenericInputType[]} Input + */ + +/** + * InputsTypeAttributes + * + * @typedef {object} InputsTypeAttributes + * @property {string} [description] + */ + +/** + * GenericInputType + * + * @typedef {object} GenericInputType + * @property {GenericInputTypeAttributes} attributes + */ + +/** + * GenericInputTypeAttributes + * + * @typedef {object} GenericInputTypeAttributes + * @property {string} description + * @property {string} type + * @property {string} path + */ + +/** + * OutputsType + * + * @typedef {object} OutputsType + * @property {OutputsTypeAttributes} attributes + * @property {FunctionOutputType} [Return] + * @property {GenericOutputType[]} Output + */ + +/** + * OutputsTypeAttributes + * + * @typedef {object} OutputsTypeAttributes + * @property {string} [description] + */ + +/** + * FunctionOutputType + * + * @typedef {object} FunctionOutputType + * @property {FunctionOutputTypeAttributes} attributes + */ + +/** + * FunctionOutputTypeAttributes + * + * @typedef {object} FunctionOutputTypeAttributes + * @property {string} type + * @property {string} path + */ + +/** + * GenericOutputType + * + * @typedef {object} GenericOutputType + * @property {GenericOutputTypeAttributes} attributes + */ + +/** + * GenericOutputTypeAttributes + * + * @typedef {object} GenericOutputTypeAttributes + * @property {string} description + * @property {string} type + * @property {string} path + */ + +module.exports = { + /** + * @type {CredibilityType} + * @type {EvidenceType} + * @type {EvidenceAttributes} + * @type {MetricType} + * @type {MetricAttributes} + * @type {TestType} + * @type {FunctionArgumentType} + * @type {FunctionArgumentAttributes} + * @type {ProcessingType} + * @type {SimpleProcessingType} + * @type {SimpleProcessingTypeAttributes} + * @type {ComlexProcessingType} + * @type {ComlexProcessingTypeAttributes} + * @type {ProcessingPrerequisitesType} + * @type {ProcessingPrerequisitesTypeAttributes} + * @type {InputsType} + * @type {GenericInputType} + * @type {GenericInputTypeAttributes} + * @type {OutputsType} + * @type {FunctionOutputType} + * @type {FunctionOutputTypeAttributes} + * @type {GenericOutputType} + * @type {GenericOutputTypeAttributes} + */ +} + diff --git a/workflow_utils/stmd-crud/types/internal_types.js b/workflow_utils/stmd-crud/types/internal_types.js new file mode 100644 index 0000000..fd8f21a --- /dev/null +++ b/workflow_utils/stmd-crud/types/internal_types.js @@ -0,0 +1,128 @@ + /** + * @typedef {import('./specification').ResourceType} ResourceType + * @typedef {import('./specification').ResourceReference} ResourceReference + * @typedef {import('./specification').Link} Link + * @typedef {import('./specification').Classification} Classification + * @typedef {import('./specification').LifeCycleEntryType} LifeCycleEntryType + */ + +/** + * AvailableResource + * + * This internal type is a representative of a concrete Resource, as available in the specific STMD file. Next to the + * Resource itself, it gives information about the location of the Resource + * + * @typedef {object} AvailableResource + * @property {string} uid a unique id of the resource to identify the resource in the document + * (this is needed, because resource.attributes.id is optional!) + * @property {string[]} location the location inside the STMD, where this Resource is located. Must be given as + * an array of all parent elements + * Example: ['stmd:SimulationTaskMetaData', 'stmd:ImplementationPhase', + * 'stmd:ImplementParameter', 'stc:Output'] + * @property {ResourceType} resource the converted Resource + */ + +/** + * AvailableResourceReference + * + * This internal type is a representative of a concrete ResourceReference, as available in the specific STMD file. + * Next to the ResourceReference itself, it gives information about the location of the ResourceReference + * + * @typedef {object} AvailableResourceReference + * @property {string} uid a unique id of the reference to identify the reference in the document + * (this is needed, because resource.attributes.id is optional!) + * @property {string[]} location the location inside the STMD, where this Resource is located. Must + * be given as an array of all parent elements + * Example: ['stmd:SimulationTaskMetaData', 'stmd:RequirementsPhase', + * 'stmd:VerifyRequirements', 'stc:Output'] + * @property {ResourceReference} resourceReference the converted ResourceReference + */ + +/** + * AvailableLink + * + * This internal type is a representative of a concrete Link, as available in the specific STMD file. + * Next to the Link itself, it gives information about the location of the Link + * + * @typedef {object} AvailableLink + * @property {string} uid a unique id of the link to identify the link in the document + * @property {string[]} location the location inside the STMD, where this Link is located. Must be given as + * an array of all parent elements. + * Example: ['stmd:SimulationTaskMetaData', 'stmd:ImplementationPhase', + * 'stmd:ImplementParameter', 'stc:Output'] + * @property {Link} link the converted Link + */ + +/** + * AvailableClassification + * + * This internal type is a representative of a concrete Classification, as available in the specific STMD file. + * Next to the Classification itself, it gives information about the location of the Classification + * + * @typedef {object} AvailableClassification + * @property {string} uid a unique id of the Classification to identify the Classification in the + * document + * @property {string[]} location the location inside the STMD, where this Classification is located. + * Must be given as an array of all parent elements + * Example: ['stmd:SimulationTaskMetaData', 'stmd:AnalysisPhase', + * 'stmd:VerifyAnalysis'] + * @property {Classification} classification the converted classification + */ + +/** + * AvailableAnnotation + * + * This internal type is a representative of a concrete Annotation, as available in the specific STMD file. + * Next to the Annotation itself, it gives information about the location of the Annotation + * + * @typedef {object} AvailableAnnotation + * @property {string} uid a unique id of the Annotation to identify the Annotation in the + * document + * @property {string[]} location the location inside the STMD, where this Classification is located. + * Must be given as an array of all parent elements + * Example: ['stmd:SimulationTaskMetaData', 'stmd:AnalysisPhase', + * 'stmd:VerifyAnalysis'] + * @property {Annotation} annotation the converted Annotation + */ + +/** + * AvailableLifeCycleEntry + * + * This internal type is a representative of a Lifecycle entry, as available in the specific STMD file + * + * @typedef {object} AvailableLifeCycleEntry + * @property {string[]} location the location of the LifeCycleInformation inside the STMD. Must be given + * as an array of all parent elements, without the root element. + * @property {string} status the status of the LifeCycleEntry. Must be one of the following: + * Drafted, Defined, Validated, Approved, Archived, Retraced + * @property {LifeCycleEntryType} lifeCycleEntry the LifeCycleEntryType object + */ + +/** + * ContextEntry + * + * This internal type is a specific key-value-pair representative that can be used for JSON-LD context. + * + * A context is used to map terms to [IRIs](https://www.rfc-editor.org/rfc/rfc3987#section-2). Terms are case sensitive + * and most valid strings that are not JSON-LD keywords can be used as a term. + * Exceptions are the empty string "" and strings that have the form of a keyword (i.e., starting with "@" followed + * exclusively by one or more ALPHA characters (see [RFC5234])), which must not be used as terms. Strings that have the + * form of an IRI (e.g., containing a ":") should not be used as terms. + * + * @typedef {object} ContextEntry + * @property {string} term valid strings (no JSON-LD keywords, no empty strings, no strings starting + * with "@", no strings containing ":") + * @property {string} iri must be an [IRI](https://www.rfc-editor.org/rfc/rfc3987#section-2) + */ + +module.exports = { + /** + * @type {AvailableResource} + * @type {AvailableResourceReference} + * @type {AvailableLink} + * @type {AvailableClassification} + * @type {AvailableAnnotation} + * @type {ContextEntry} + * @type {AvailableLifeCycleEntry} + */ +} \ No newline at end of file diff --git a/workflow_utils/stmd-crud/types/specification.js b/workflow_utils/stmd-crud/types/specification.js new file mode 100644 index 0000000..033ead3 --- /dev/null +++ b/workflow_utils/stmd-crud/types/specification.js @@ -0,0 +1,549 @@ +/** + * SimulationTaskMetaDataAttributes + * + * @typedef {object} SimulationTaskMetaDataAttributes + * @property {string} version + * @property {string} name + * @property {string} GUID + * @property {string} [id] + * @property {string} [description] + * @property {string} [author] + * @property {string} [fileversion] + * @property {string} [copyright] + * @property {string} [license] + * @property {string} [generationTool] + * @property {string} [generationDateAndTime] + */ + +/** + * GeneralInformationType + * + * @typedef {object} GeneralInformationType + * @property {GeneralInformationTypeAttributes} attributes + * @property {DerivationChain} [DerivationChain] + * @property {ResourceType[]} [Resource] + * @property {ResourceReference[]} [ResourceReference] + * @property {LinksType} [Links] + * @property {Classification[]} [Classification] + * @property {Annotations} [Annotations] + */ + +/** + * GeneralInformationTypeAttributes + * + * @typedef {object} GeneralInformationTypeAttributes + * @property {string} [id] + * @property {string} [description] + */ + +/** + * DerivationChain + * + * @typedef {object} DerivationChain + * @property {DerivationChainEntry[]} [DerivationChainEntry] + */ + +/** + * DerivationChainEntry + * + * @typedef {object} DerivationChainEntry + * @property {DerivationChainEntryAttributes} attributes + */ + +/** + * DerivationChainEntryAttributes + * + * @typedef {object} DerivationChainEntryAttributes + * @property {string} GUID + * @property {string} [author] + * @property {string} [fileversion] + * @property {string} [copyright] + * @property {string} [license] + * @property {string} [generationTool] + * @property {string} [generationDateAndTime] + */ + +/** + * ResourceType + * + * The ResourceType element defines the structure and attributes of information about a resource that is related to the + * particular step and particle. Multiple (or no) resources may be present. + * + * @typedef {object} ResourceType + * @property {ContentType} [Content] + * @property {Summary} [Summary] + * @property {MetaData[]} [MetaData] + * @property {SignatureType[]} [Signature] + * @property {Classification[]} [Classification] + * @property {Annotations} [Annotations] + * @property {ResourceTypeAttributes} attributes + */ + +/** + * ResourceTypeAttributes + * + * @typedef {object} ResourceTypeAttributes + * @property {string} kind This attribute indicates the kind of resource that is referenced, + * i.e. what role it plays in relation to the particle being described. + * @property {string} type This mandatory attribute specifies the MIME type of the resource, + * which does not have a default value. If no specific MIME type can be + * indicated, then the type application/octet-stream is to be used. + * @property {string} [description] This attribute gives a human readable longer description of the model element, + * which can be shown to the user where appropriate. + * @property {string} [id] This attribute gives the model element a file-wide unique id which can be + * referenced from other elements or via URI fragment identifier. + * @property {string} [master] This attribute, if present, indicates the original, canonical master source + * for the resource. If it is present, it indicates that the content provided + * via source attribute and/or Content element is only a copy of the original, + * canonical data, and this attributes provides the URI reference to that original + * canonical master data. + * @property {string} [scope] This attribute indicates the scope or level that a resource is specific to, i.e. + * whether the resource applies at a global, system, subsystem or component level. + * The use of this attribute is optional, i.e. it should only be specified where it + * makes sense to give this kind of information. + * @property {string} [source] This attribute gives a human readable longer description of the model element, + * which can be shown to the user where appropriate. + */ + +/** + * ResourceReference + * + * This element references a resource element defined in another place, that is related to the particular step and + * particle. + * + * @typedef {object} ResourceReference + * @property {Classification[]} [Classification] + * @property {Annotations} [Annotations] + * @property {ResourceReferenceAttributes} attributes + */ + +/** + * ResourceReferenceAttributes + * + * @typedef {object} ResourceReferenceAttributes + * @property {string} xlink_type Has the fixed value simple to indicate an XLink simple link. + * @property {string} xlink_href A mandatory attribute that supplies the data that allows an XLink application to + * find a remote resource (or resource fragment). The value of the href attribute is a + * [Legacy extended IRIs] (LEIRI). Processing a relative identifier against a base is + * handled straightforwardly; the algorithms of [RFC 3986] can be applied directly, + * treating the characters additionally allowed in LEIRIs in the same way that + * unreserved characters are in URI references. + * @property {string} [id] This attribute gives the element a file-wide unique id which can be + * referenced from other elements or via URI fragment identifier. + * @property {string} [description] This attribute gives a human readable longer description of the reference, which + * can be shown to the user where appropriate. + */ + +/** + * ContentType + * + * The ContentType element defines the structure and attributes of inline content of an entity. If it is present, + * then the attribute source of the enclosing element must not be present. + * + * @typedef {object} ContentType + * @property {object} [any] The ContentType may contain XML Elements of any kind, i.e. the STMD Schema provides + * the possibility and capability to code any kind of information regardless of what + * the STMD specifies. This mean the name, structure and attributes of XML elements + * enclosed by a contentType element is completely free. + */ + +/** + * Summary + * + * The Summary element provides an optional summary of the resource being referenced. The summary information is + * intended for human consumption to get an overview of the resource content without looking at the content itself. + * The summary content can be provided inline through the Content element, or it can be provided through the source URI + * attribute. + * + * @typedef {object} Summary + * @property {ContentType} [Content] + * @property {SignatureType[]} [Signature] + * @property {Classification[]} [Classification] + * @property {Annotations} [Annotations] + * @property {SummaryAttributes} attributes + */ + +/** + * SummaryAttributes + * + * @typedef {object} SummaryAttributes + * @property {string} type This mandatory attribute specifies the MIME type of the resource summary, which + * does not have a default value. If no specific MIME type can be indicated, then + * the type application/octet-stream is to be used. If markdown content is used, + * then the type text/markdown shall be used. + * @property {string} [source] This attribute indicates the source of the resource summary as a URI (cf. RFC + * 3986). For purposes of the resolution of relative URIs the base URI is the URI + * of the STMD, if the sourceBase attribute is not specified or is specified as + * STMD, and the URI of the referenced resource if the sourceBase attribute is + * specified as resource. This allows the specification of summary sources that + * reside inside the resource (e.g. an FMU) through relative URIs. For summaries + * that are located alongside the STMD, relative URIs without scheme and authority + * can and should be used to specify the summary sources. + * For summaries that are packaged inside an SSP that contains this STMD, this is + * mandatory (in this way, the STMD URIs remain valid after unpacking the SSP into + * the filesystem). If the source attribute is missing, the summary is provided + * inline as contents of the Content element, which must not be resent otherwise. + * @property {string} [sourceBase] Defines the base the source URI is resolved against: If the attribute is missing + * or is specified as STMD, the source is resolved against the URI of the STMD, if + * the attribute is specified as resource the URI is resolved against the + * (resolved) URI of the resource source. + */ + +/** + * MetaData + * + * The MetaData element can specify additional metadata for the given resource. Multiple (or no) MetaData elements may + * be present. + * + * @typedef {object} MetaData + * @property {ContentType} [Content] + * @property {SignatureType[]} [Signature] + * @property {Classification[]} [Classification] + * @property {Annotations} [Annotations] + * @property {MetaDataAttributes} attributes + * + */ + +/** + * MetaDataAttributes + * + * @typedef {object} MetaDataAttributes + * @property {string} kind This attribute indicates the kind of resource meta data that is referenced, i.e. + * what role it plays in relation to the resource being described. + * @property {string} type This mandatory attribute specifies the MIME type of the resource summary, which + * does not have a default value. If no specific MIME type can be indicated, then + * the type application/octet-stream is to be used. If markdown content is used, + * then the type text/markdown shall be used. + * @property {string} [source] This attribute indicates the source of the resource summary as a URI (cf. RFC + * 3986). For purposes of the resolution of relative URIs the base URI is the URI + * of the STMD, if the sourceBase attribute is not specified or is specified as + * STMD, and the URI of the referenced resource if the sourceBase attribute is + * specified as resource. This allows the specification of summary sources that + * reside inside the resource (e.g. an FMU) through relative URIs. For summaries + * that are located alongside the STMD, relative URIs without scheme and authority + * can and should be used to specify the summary sources. + * For summaries that are packaged inside an SSP that contains this STMD, this is + * mandatory (in this way, the STMD URIs remain valid after unpacking the SSP into + * the filesystem). If the source attribute is missing, the summary is provided + * inline as contents of the Content element, which must not be resent otherwise. + * @property {string} [sourceBase] Defines the base the source URI is resolved against: If the attribute is missing + * or is specified as STMD, the source is resolved against the URI of the STMD, if + * the attribute is specified as resource the URI is resolved against the + * (resolved) URI of the resource source. + */ + +/** + * SignatureType + * + * The SignatureType element defines the structure and attributes of the signature entity for a given step or phase. + * + * @typedef {object} SignatureType + * @property {ContentType} [Content] + * @property {Classification[]} [Classification] + * @property {Annotations} [Annotations] + * @property {SignatureTypeAttributes} attributes + * + */ + +/** + * SignatureTypeAttributes + * + * @typedef {object} SignatureTypeAttributes + * @property {string} role This mandatory attribute specifies the role this signature has in the overall + * process. It indicates whether the digital signature is intended to just convey + * the authenticity of the information, or whether a claim for suitability of the + * information for certain purposes is made. In the latter case, the digital + * signature format should include detailed information about what suitability + * claims are being made. + * @property {string} type This mandatory attribute specifies the MIME type of the resource summary, which + * does not have a default value. If no specific MIME type can be indicated, then + * the type application/octet-stream is to be used. If markdown content is used, + * then the type text/markdown shall be used. + * @property {string} [source] This attribute indicates the source of the resource summary as a URI (cf. RFC + * 3986). For purposes of the resolution of relative URIs the base URI is the URI + * of the STMD, if the sourceBase attribute is not specified or is specified as + * STMD, and the URI of the referenced resource if the sourceBase attribute is + * specified as resource. This allows the specification of summary sources that + * reside inside the resource (e.g. an FMU) through relative URIs. For summaries + * that are located alongside the STMD, relative URIs without scheme and authority + * can and should be used to specify the summary sources. + * For summaries that are packaged inside an SSP that contains this STMD, this is + * mandatory (in this way, the STMD URIs remain valid after unpacking the SSP into + * the filesystem). If the source attribute is missing, the summary is provided + * inline as contents of the Content element, which must not be resent otherwise. + * @property {string} [sourceBase] Defines the base the source URI is resolved against: If the attribute is missing + * or is specified as STMD, the source is resolved against the URI of the STMD, if + * the attribute is specified as resource the URI is resolved against the + * (resolved) URI of the resource source. + * + */ + +/** + * Classification + * + * The Classification element, which can occur multiple times, provides a set of classifications of an STMD modeling + * element, provided as Keyword Value Pairs (KWP), the meaning of which is interpreted according to the name of the + * classification provided in the name attribute. This approach can be used, for example, to provide searchable keywords + * for content, or to assign and track quality or validation level requirements across the STMD process, or to maintain + * variant or other classification content across the process. + * + * @typedef {object} Classification + * @property {ClassificationEntry[]} [ClassificationEntry] + * @property {ClassificationAttributes} attributes + */ + +/** + * ClassificationAttributes + * + * @typedef {object} ClassificationAttributes + * @property {string} [type] This attribute provides the name of the type of classification being provided. The name + * should be unique across the Classification elements of the immediately enclosing + * element. In order to ensure uniqueness all types should be identified with reverse + * domain name notation (cf. Java package names or Apple UTIs) of a domain that is + * controlled by the entity defining the semantics and content of the classification. + */ + +/** + * ClassificationEntry + * + * @typedef {object} ClassificationEntry + * @property {object} [any] The ClassificationEntry element may contain XML Elements of any kind, i.e. the + * STMDSchema provides the possibility and capability to code any kind of information + * regardless of what the STMD specifies. This means, the name, structure and attributes of + * XML elements enclosed by a ClassificationEntry element are completely free. + * @property {ClassificationEntryAttributes} attributes + */ + +/** + * ClassificationEntryAttributes + * + * @typedef {object} ClassificationEntryAttributes + * @property {string} keyword This attribute gives the keyword for the classification entry (i.e. keyword value + * pair). It is left undefined whether this must be unique across the entries of the + * Classification element, or whether repeated entries are allowed. This will depend on + * the definition of the classification. + * @property {string} xlink_type Has the fixed value simple to indicate an XLink simple link. + * @property {string} [xlink_href] This attribute gives an optional link for the classification entry (i.e. keyword + * value pair). This link can be given in addition to any content of the classification + * entry, or it can be the sole information of the classification entry. The rules will + * depend on the definition of the classification. + */ + +/** + * Annotations + * + * The Annotations element can be used to add a list of additional free style annotations. + * + * @typedef {object} Annotations + * @property {Annotation[]} Annotation + */ + +/** + * Annotation + * + * The Annotation element can be used to add a single free style annotation to the list of annotations. + * + * @typedef {object} Annotation + * @property {object[]} [any] The Annotation element may contain XML Elements of any kind, i.e. the + * STMD Schema provides the possibility and capability to code any kind of + * information regardless of what the STMD specifies. This means, the name, + * structure and attributes of XML elements enclosed by an Annotation + * element are completely free. + * @property {AnnotationAttributes} attributes + */ + +/** + * AnnotationAttributes + * + * @typedef {object} AnnotationAttributes + * @property {string} type The unique name of the type of the annotation. In order to ensure uniqueness all types + * should be identified with reverse domain name notation (cf. Java package names or Apple + * UTIs) of a domain that is controlled by the entity defining the semantics and content of the + * annotation. For vendor-specific annotations this would e.g. be a domain controlled by the + * tool vendor. For MAP-SSP defined annotations, this will be a domain under the org.modelica + * prefix. + */ + +/** + * LinksType + * + * The LinksType element defines the structure and attributes for the linkage mechanism to use links within the stmd + * file as well as links to external resources outside the STMD file. + * + * @typedef {object} LinksType + * @property {Link[]} Link + */ + +/** + * Link + * + * The Link element represents a single links no mater if it is an STMD file internal link or a link targeted to the + * outside of the STMD file. + * + * @typedef {object} Link + * @property {Locator[]} Locator + * @property {Arc[]} [Arc] + * @property {LinkAttributes} attributes + */ + +/** + * LinkAttributes + * + * @typedef {object} LinkAttributes + * @property {string} xlink_type Has the fixed value extended to indicate an XLink extended link + * @property {string} [xlink_title] This optional attributes specifies a meaningful title of the link + * @property {string} [xlink_role] This optional attribute specifies the role this link has in the overall process + */ + +/** + * Locator + * + * This element provides a locator conforming to the XLink specification. It identifies aparticular piece of information + * through an URI, that is taking part in the linking relationship. Locators can further specify their semantic meaning + * through an optional role attribute. The SSP Traceability specification currently provides no predefined roles. + * + * @typedef {object} Locator + * @property {LocatorAttributes} attributes + */ + +/** + * LocatorAttributes + * + * @typedef {object} LocatorAttributes + * @property {string} xlink_type Has the fixed value locator to indicate an XLink locator + * @property {string} xlink_href A mandatory attribute that supplies the data that allows an XLink application to + * find a remote resource (or resource fragment). The value of the href attribute is a + * [Legacy extended IRIs] (LEIRI). Processing a relative identifier against a base is + * handled straightforwardly; the algorithms of [RFC 3986] can be applied directly, + * treating the characters additionally allowed in LEIRIs in the same way that + * unreserved characters are in URI references. + * @property {string} [xlink_label] This optional attribute specifies a label that can be referenced by an XLink Arc. + * It does not necessarily be unique. If the label is not unique and an Arc references + * this label, a link is drawn from/to all the Locators with this label. + * @property {string} [xlink_title] Specifies a meaningful title for the Locator + * @property {string} [xlink_role] Specifies the semantic meaning of the Locator + */ + +/** + * Arc + * + * This element provides an arc conforming to the XLink specification. An arc relates sets of locators in a navigable + * relationship, including directionality information. Arcs can further specify their semantic meaning through an + * optional arcrole attribute. The SSP Traceability specification currently provides no predefined roles. + * + * @typedef {object} Arc + * @property {ArcAttributes} attributes + */ + +/** + * ArcAttributes + * + * @typedef {object} ArcAttributes + * @property {string} xlink_type Has the fixed value arc to indicate an XLink arc + * @property {string} [xlink_from] This optional attribute specifies the locator from where the link is starting. + * If no value is supplied for a from attribute, the missing value is interpreted + * as standing for all locators + * @property {string} [xlink_to] This optional attribute specifies the locator for the destination of the link. + * If no value is supplied for a to attribute, the missing value is interpreted as + * standing for all locators. + * @property {string} [xlink_title] Optional attribute that specifies a title for the Arc + * @property {string} [xlink_arcrole] Optional attribute that specifies the semantic meaning of the Arc + */ + +/** + * LifeCycleInformationType + * + * The LifeCycleInformationType element defines the structure and attributes of lifecycle information about the + * enclosing phase or step element. + * + * @typedef {object} LifeCycleInformationType + * @property {LifeCycleEntryType} [Drafted] + * @property {LifeCycleEntryType} [Defined] + * @property {LifeCycleEntryType} [Validated] + * @property {LifeCycleEntryType} [Approved] + * @property {LifeCycleEntryType} [Archived] + * @property {LifeCycleEntryType} [Retracted] + */ + +/** + * LifeCycleEntryType + * + * The LifeCycleEntryType element defines the structure and the attributes of lifecycle information entries and + * therefore is the basis of the Drafted, Defined, Validated, Approved, Archived and Retracted XML elements. + * + * @typedef {object} LifeCycleEntryType + * @property {ResponsibleType} Responsible + * @property {SignatureType[]} [Signature] + * @property {ResourceType[]} [Resource] + * @property {ResourceReference[]} [ResourceReference] + * @property {Classification[]} [Classification] + * @property {Annotations} [Annotations] + * @property {LifeCycleEntryTypeAttributes} attributes + */ + +/** + * LifeCycleEntryTypeAttributes + * + * @typedef {object} LifeCycleEntryTypeAttributes + * @property {string} date + * @property {string} [checksum] + * @property {string} [checksumType] + */ + +/** + * ResponsibleType + * + * The ResponsibleType element defines the structure and attributes of the responsible entry for a lifecycle entry of a + * step or a phase of the overall simulation task. + * + * @typedef {object} ResponsibleType + * @property {ResponsibleTypeAttributes} attributes + */ + +/** + * ResponsibleTypeAttributes + * + * @typedef {object} ResponsibleTypeAttributes + * @property {string} [organization] This attribute gives the organization that is responsible for a given step. + * @property {string} [role] This attribute gives the role of the person that is responsible for a given step. + * @property {string} [name] This attribute gives the name of the person that is responsible for a given step. + */ + +module.exports = { + /** + * @type {SimulationTaskMetaDataAttributes} + * @type {GeneralInformationType} + * @type {DerivationChain} + * @type {DerivationChainEntry} + * @type {ResourceType} + * @type {ResourceTypeAttributes} + * @type {ResourceReference} + * @type {ResourceReferenceAttributes} + * @type {ContentType} + * @type {Summary} + * @type {SummaryAttributes} + * @type {MetaData} + * @type {MetaDataAttributes} + * @type {SignatureType} + * @type {SignatureTypeAttributes} + * @type {Classification} + * @type {ClassificationAttributes} + * @type {ClassificationEntry} + * @type {ClassificationEntryAttributes} + * @type {Annotations} + * @type {Annotation} + * @type {AnnotationAttributes} + * @type {LinksType} + * @type {Link} + * @type {LinkAttributes} + * @type {Locator} + * @type {LocatorAttributes} + * @type {Arc} + * @type {ArcAttributes} + * @type {LifeCycleInformationType} + * @type {LifeCycleEntryType} + * @type {LifeCycleEntryTypeAttributes} + * @type {ResponsibleType} + * @type {ResponsibleTypeAttributes} + */ +} \ No newline at end of file diff --git a/workflow_utils/wrapper/__pycache__/fcnWrapperPython.cpython-312.pyc b/workflow_utils/wrapper/__pycache__/fcnWrapperPython.cpython-312.pyc new file mode 100644 index 0000000..c77e032 Binary files /dev/null and b/workflow_utils/wrapper/__pycache__/fcnWrapperPython.cpython-312.pyc differ diff --git a/workflow_utils/wrapper/fcnWrapperNode.js b/workflow_utils/wrapper/fcnWrapperNode.js new file mode 100644 index 0000000..b6fdbee --- /dev/null +++ b/workflow_utils/wrapper/fcnWrapperNode.js @@ -0,0 +1,77 @@ +const fs = require('fs'); +const path = require('path') + +/** + * Extracts all parameter names from a function as an array of strings + * + * @param {Function} func the function to investigate + * @returns {string[]} + */ +function getParamNames(func) { + const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; + const ARGUMENT_NAMES = /([^\s,]+)/g; + + let fnStr = func.toString().replace(STRIP_COMMENTS, ''); + let result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES); + + return result !== null ? result : []; +} + +/** + * @param {string} moduleUri + * @param {string} functionToRun + * @param {string[]} functionArgumentNames + * @param {string[]} argumentContents + * @param {string[]} argumentTypes + * @param {string} stmdFolderPath + * @param {object} metadata + * @param {boolean} [wrapToReslog=true] + * @returns {string} + */ +exports.wrapper = (moduleUri, functionToRun, functionArgumentNames, argumentContents, argumentTypes, stmdFolderPath, metadata, wrapToReslog = true) => { + let allProcessedArguments = []; + + // const stmdFolderPath = process.env["STMDFOLDERPATH"]; + const modulePath = path.resolve(stmdFolderPath, moduleUri); + const moduleToImport = require(modulePath); + + const functionParameters = getParamNames(moduleToImport[functionToRun]); + + for (let parameterName of functionParameters) { + let requiredArgumentIdx = functionArgumentNames.findIndex(farg => farg == parameterName); + if (requiredArgumentIdx == -1) + throw(`Error: For test ${metadata.id} of metric ${functionToRun} the required function parameter ${parameterName} is missing!`); + + let argumentProcessed; + if (argumentTypes[requiredArgumentIdx] == "file") + argumentProcessed = fs.readFileSync(path.resolve(stmdFolderPath, String(argumentContents[requiredArgumentIdx])), "utf-8"); + else if (argumentTypes[requiredArgumentIdx] == "path") + argumentProcessed = path.resolve(stmdFolderPath, String(argumentContents[requiredArgumentIdx])); + else // method == "inline" + argumentProcessed = String(argumentContents[requiredArgumentIdx]); + + allProcessedArguments.push(argumentProcessed); + } + + let res; + + try { + res = moduleToImport[functionToRun](...allProcessedArguments); + } catch (e) { + if (wrapToReslog) { + res = { + "result":false, + "log": e.toString() + }; + } + else { + res = e.toString(); + } + } + + if (wrapToReslog) { + res = {...res, ...metadata} + } + + return res; +} \ No newline at end of file diff --git a/workflow_utils/wrapper/fcnWrapperPython.py b/workflow_utils/wrapper/fcnWrapperPython.py new file mode 100644 index 0000000..d8e64a0 --- /dev/null +++ b/workflow_utils/wrapper/fcnWrapperPython.py @@ -0,0 +1,106 @@ +from pathlib import PurePath +import re +import json +import sys + +def get_param_names(module, function_name, stmd_folder_path): + module_path = str(PurePath(stmd_folder_path, module)) + ".py" + f = open(module_path, "r") + pyModuleString = f.read() + f.close() + + regexp = rf"{function_name}*\((.*?)\)" + params_raw = re.findall(regexp, pyModuleString)[0].split(",") + + params = [] + for param_raw in params_raw: + param_processed = param_raw.strip() + if param_processed != "": + params.append(param_processed) + + return params + +def include_metadata(result, metadata): + result_dict = json.loads(result) + metadata_dict = json.loads(metadata) + result_dict.update(metadata_dict) + + return json.dumps(result_dict) + +def wrapper(module, function_to_run, function_arguments, argument_contents, argument_types, stmd_folder_path, metadata, wrap_to_reslog = True): + """ + Parameters + ------------------------- + module : str + The path of the module, relative to stmd_folder_path, without .py + suffix + function_to_run : str + The name of the function to run + function_arguments : list + The list of all names of the arguments, that must be passed to + function_to_run and will be given in "argument_content" + argument_contents : list + The list of all argument contents that will be passed to + function_to_run to be interpreted by its type, given accordingly in + argument_types + argument_types : list + The list of all types of arguments (file, path, or inline), adhering + to the order of function_arguments. + If "file" is given, then the according element in function_arguments + will be interpreted as a path to the file whose content must be passed + as function argument. + If "path" is given, then the according element in function_arguments + will be interpreted as a path and this path is passed to the function, + contrary to its content (as in file). + If "inline" is given, then the according element in function_arguments + is interpreted to be passed as it is to function_to_run. + stmd_folder_path : str + The base bath to the module path + metadata : dict + Arbitrary metadata, given as stringified JSON + wrap_to_reslog : bool + If set to true, the result will be wrapped in a JSON structure + + Returns + ------------------------- + The output of function_to_run in combination with the given arguments + """ + module_path = PurePath(str(PurePath(stmd_folder_path, module)) + ".py") + module_dir = str(module_path.parent) + module_name = module_path.stem + + all_processed_arguments = [] + function_parameters = get_param_names(module, function_to_run, stmd_folder_path) + + for param in function_parameters: + try: + index_required_arg = function_arguments.index(param) + except: + raise Exception("Error: For test ${} of metric ${} the required function parameter ${} is missing!".format(metadata["id"], function_to_run, param)) + + if argument_types[index_required_arg] == "file": + file_location = str(PurePath(stmd_folder_path, argument_contents[index_required_arg])) + f = open(file_location, "r") + argument_processed = f.read() + f.close() + elif argument_types[index_required_arg] == "path": + argument_processed = str(PurePath(stmd_folder_path, argument_contents[index_required_arg])) + else: + argument_processed = str(argument_contents[index_required_arg]) + + all_processed_arguments.append('"' + argument_processed + '"') + + try: + sys.path.append(module_dir) + module = __import__(module_name) + result = eval("module." + function_to_run + "(" + ",".join(all_processed_arguments) + ")") + except Exception as error: + if wrap_to_reslog: + result = '{"result": false, "log": "' + str(error) + '"}' + else: + result = str(error) + + if wrap_to_reslog: + result = include_metadata(result, json.dumps(metadata)) + + return result \ No newline at end of file